Compare commits
No commits in common. "c8-stream-DL1" and "changed/a9/ipa-4.10.1-9.el9_2.alma.1" have entirely different histories.
c8-stream-
...
changed/a9
2
.gitignore
vendored
2
.gitignore
vendored
@ -1 +1 @@
|
|||||||
SOURCES/freeipa-4.9.13.tar.gz
|
SOURCES/freeipa-4.10.1.tar.gz
|
||||||
|
@ -1 +1 @@
|
|||||||
da1bb0220894d8dc06afb98dcf087fea38076a79 SOURCES/freeipa-4.9.13.tar.gz
|
6203cf7c2e003c35eb9ac40e4fd2954c6bea1856 SOURCES/freeipa-4.10.1.tar.gz
|
||||||
|
@ -1,73 +0,0 @@
|
|||||||
From 06b4c61b4484efe2093501caf21b03f1fc14093b Mon Sep 17 00:00:00 2001
|
|
||||||
From: Florence Blanc-Renaud <flo@redhat.com>
|
|
||||||
Date: Thu, 19 Oct 2023 12:47:03 +0200
|
|
||||||
Subject: [PATCH] group-add-member fails with an external member
|
|
||||||
|
|
||||||
The command ipa group-add-member --external aduser@addomain.test
|
|
||||||
fails with an internal error when used with samba 4.19.
|
|
||||||
|
|
||||||
The command internally calls samba.security.dom_sid(sid) which
|
|
||||||
used to raise a TypeError but now raises a ValueError
|
|
||||||
(commit 9abdd67 on https://github.com/samba-team/samba).
|
|
||||||
|
|
||||||
IPA source code needs to handle properly both exception types.
|
|
||||||
|
|
||||||
Fixes: https://pagure.io/freeipa/issue/9466
|
|
||||||
|
|
||||||
Signed-off-by: Florence Blanc-Renaud <flo@redhat.com>
|
|
||||||
Reviewed-By: Rob Crittenden <rcritten@redhat.com>
|
|
||||||
---
|
|
||||||
ipaserver/dcerpc.py | 2 +-
|
|
||||||
1 file changed, 1 insertion(+), 1 deletion(-)
|
|
||||||
|
|
||||||
diff --git a/ipaserver/dcerpc.py b/ipaserver/dcerpc.py
|
|
||||||
index c1db2f9a499..ee0a229d1f0 100644
|
|
||||||
--- a/ipaserver/dcerpc.py
|
|
||||||
+++ b/ipaserver/dcerpc.py
|
|
||||||
@@ -303,7 +303,7 @@ def get_domain_by_sid(self, sid, exact_match=False):
|
|
||||||
# Parse sid string to see if it is really in a SID format
|
|
||||||
try:
|
|
||||||
test_sid = security.dom_sid(sid)
|
|
||||||
- except TypeError:
|
|
||||||
+ except (TypeError, ValueError):
|
|
||||||
raise errors.ValidationError(name='sid',
|
|
||||||
error=_('SID is not valid'))
|
|
||||||
|
|
||||||
From aa3397378acf1a03fc8bbe34b9fae33e84588b34 Mon Sep 17 00:00:00 2001
|
|
||||||
From: Florence Blanc-Renaud <flo@redhat.com>
|
|
||||||
Date: Fri, 20 Oct 2023 10:20:57 +0200
|
|
||||||
Subject: [PATCH] Handle samba changes in samba.security.dom_sid()
|
|
||||||
|
|
||||||
samba.security.dom_sid() in 4.19 now raises ValueError instead of
|
|
||||||
TypeError. Fix the expected exception.
|
|
||||||
|
|
||||||
Related: https://pagure.io/freeipa/issue/9466
|
|
||||||
|
|
||||||
Signed-off-by: Florence Blanc-Renaud <flo@redhat.com>
|
|
||||||
Reviewed-By: Alexander Bokovoy <abokovoy@redhat.com>
|
|
||||||
---
|
|
||||||
ipaserver/dcerpc.py | 4 ++--
|
|
||||||
1 file changed, 2 insertions(+), 2 deletions(-)
|
|
||||||
|
|
||||||
diff --git a/ipaserver/dcerpc.py b/ipaserver/dcerpc.py
|
|
||||||
index ee0a229d1f0..3e4c71d9976 100644
|
|
||||||
--- a/ipaserver/dcerpc.py
|
|
||||||
+++ b/ipaserver/dcerpc.py
|
|
||||||
@@ -97,7 +97,7 @@
|
|
||||||
def is_sid_valid(sid):
|
|
||||||
try:
|
|
||||||
security.dom_sid(sid)
|
|
||||||
- except TypeError:
|
|
||||||
+ except (TypeError, ValueError):
|
|
||||||
return False
|
|
||||||
else:
|
|
||||||
return True
|
|
||||||
@@ -457,7 +457,7 @@ def get_trusted_domain_object_sid(self, object_name,
|
|
||||||
try:
|
|
||||||
test_sid = security.dom_sid(sid)
|
|
||||||
return unicode(test_sid)
|
|
||||||
- except TypeError:
|
|
||||||
+ except (TypeError, ValueError):
|
|
||||||
raise errors.ValidationError(name=_('trusted domain object'),
|
|
||||||
error=_('Trusted domain did not '
|
|
||||||
'return a valid SID for '
|
|
@ -0,0 +1,44 @@
|
|||||||
|
From 42be04fe4ff317efe599dcbc2637f94ecc6fa220 Mon Sep 17 00:00:00 2001
|
||||||
|
From: Alexander Bokovoy <abokovoy@redhat.com>
|
||||||
|
Date: Mon, 21 Nov 2022 16:12:46 +0200
|
||||||
|
Subject: [PATCH] updates: fix memberManager ACI to allow managers from a
|
||||||
|
specified group
|
||||||
|
|
||||||
|
The original implementation of the member manager added support for both
|
||||||
|
user and group managers but left out upgrade scenario. This means when
|
||||||
|
upgrading existing installation a manager whose rights defined by the
|
||||||
|
group membership would not be able to add group members until the ACI is
|
||||||
|
fixed.
|
||||||
|
|
||||||
|
Remove old ACI and add a full one during upgrade step.
|
||||||
|
|
||||||
|
Fixes: https://pagure.io/freeipa/issue/9286
|
||||||
|
Signed-off-by: Alexander Bokovoy <abokovoy@redhat.com>
|
||||||
|
Reviewed-By: Florence Blanc-Renaud <flo@redhat.com>
|
||||||
|
---
|
||||||
|
install/updates/20-aci.update | 6 ++++--
|
||||||
|
1 file changed, 4 insertions(+), 2 deletions(-)
|
||||||
|
|
||||||
|
diff --git a/install/updates/20-aci.update b/install/updates/20-aci.update
|
||||||
|
index a168bb9573a9fbb9ff15f0b19bb8ec75b48d82a9..4a7ba137c4711aa3f8b064fdd482ffee76c59949 100644
|
||||||
|
--- a/install/updates/20-aci.update
|
||||||
|
+++ b/install/updates/20-aci.update
|
||||||
|
@@ -141,11 +141,13 @@ add:aci:(targetattr = "usercertificate")(version 3.0;acl "selfservice:Users can
|
||||||
|
|
||||||
|
# Allow member managers to modify members of user groups
|
||||||
|
dn: cn=groups,cn=accounts,$SUFFIX
|
||||||
|
-add:aci: (targetattr = "member")(targetfilter = "(objectclass=ipaUserGroup)")(version 3.0; acl "Allow member managers to modify members of user groups"; allow (write) userattr = "memberManager#USERDN";)
|
||||||
|
+remove:aci: (targetattr = "member")(targetfilter = "(objectclass=ipaUserGroup)")(version 3.0; acl "Allow member managers to modify members of user groups"; allow (write) userattr = "memberManager#USERDN";)
|
||||||
|
+add:aci: (targetattr = "member")(targetfilter = "(objectclass=ipaUserGroup)")(version 3.0; acl "Allow member managers to modify members of user groups"; allow (write) userattr = "memberManager#USERDN" or userattr = "memberManager#GROUPDN";)
|
||||||
|
|
||||||
|
# Allow member managers to modify members of host groups
|
||||||
|
dn: cn=hostgroups,cn=accounts,$SUFFIX
|
||||||
|
-add:aci: (targetattr = "member")(targetfilter = "(objectclass=ipaHostGroup)")(version 3.0; acl "Allow member managers to modify members of host groups"; allow (write) userattr = "memberManager#USERDN";)
|
||||||
|
+remove:aci: (targetattr = "member")(targetfilter = "(objectclass=ipaHostGroup)")(version 3.0; acl "Allow member managers to modify members of host groups"; allow (write) userattr = "memberManager#USERDN";)
|
||||||
|
+add:aci: (targetattr = "member")(targetfilter = "(objectclass=ipaHostGroup)")(version 3.0; acl "Allow member managers to modify members of host groups"; allow (write) userattr = "memberManager#USERDN" or userattr = "memberManager#GROUPDN";)
|
||||||
|
|
||||||
|
# Hosts can add and delete their own services
|
||||||
|
dn: cn=services,cn=accounts,$SUFFIX
|
||||||
|
--
|
||||||
|
2.38.1
|
||||||
|
|
@ -1,121 +0,0 @@
|
|||||||
From ae006b436cfb4ccee5972cf1db0a309fcd80e669 Mon Sep 17 00:00:00 2001
|
|
||||||
From: Rob Crittenden <rcritten@redhat.com>
|
|
||||||
Date: Fri, 6 Oct 2023 20:16:29 +0000
|
|
||||||
Subject: [PATCH] Check the HTTP Referer header on all requests
|
|
||||||
|
|
||||||
The referer was only checked in WSGIExecutioner classes:
|
|
||||||
|
|
||||||
- jsonserver
|
|
||||||
- KerberosWSGIExecutioner
|
|
||||||
- xmlserver
|
|
||||||
- jsonserver_kerb
|
|
||||||
|
|
||||||
This left /i18n_messages, /session/login_kerberos,
|
|
||||||
/session/login_x509, /session/login_password,
|
|
||||||
/session/change_password and /session/sync_token unprotected
|
|
||||||
against CSRF attacks.
|
|
||||||
|
|
||||||
CVE-2023-5455
|
|
||||||
|
|
||||||
Signed-off-by: Rob Crittenden <rcritten@redhat.com>
|
|
||||||
---
|
|
||||||
ipaserver/rpcserver.py | 34 +++++++++++++++++++++++++++++++---
|
|
||||||
1 file changed, 31 insertions(+), 3 deletions(-)
|
|
||||||
|
|
||||||
diff --git a/ipaserver/rpcserver.py b/ipaserver/rpcserver.py
|
|
||||||
index 4e8a08b66..3555014ca 100644
|
|
||||||
--- a/ipaserver/rpcserver.py
|
|
||||||
+++ b/ipaserver/rpcserver.py
|
|
||||||
@@ -156,6 +156,19 @@ _success_template = """<html>
|
|
||||||
</html>"""
|
|
||||||
|
|
||||||
class HTTP_Status(plugable.Plugin):
|
|
||||||
+ def check_referer(self, environ):
|
|
||||||
+ if "HTTP_REFERER" not in environ:
|
|
||||||
+ logger.error("Rejecting request with missing Referer")
|
|
||||||
+ return False
|
|
||||||
+ if (not environ["HTTP_REFERER"].startswith(
|
|
||||||
+ "https://%s/ipa" % self.api.env.host)
|
|
||||||
+ and not self.env.in_tree):
|
|
||||||
+ logger.error("Rejecting request with bad Referer %s",
|
|
||||||
+ environ["HTTP_REFERER"])
|
|
||||||
+ return False
|
|
||||||
+ logger.debug("Valid Referer %s", environ["HTTP_REFERER"])
|
|
||||||
+ return True
|
|
||||||
+
|
|
||||||
def not_found(self, environ, start_response, url, message):
|
|
||||||
"""
|
|
||||||
Return a 404 Not Found error.
|
|
||||||
@@ -331,9 +344,6 @@ class wsgi_dispatch(Executioner, HTTP_Status):
|
|
||||||
self.__apps[key] = app
|
|
||||||
|
|
||||||
|
|
||||||
-
|
|
||||||
-
|
|
||||||
-
|
|
||||||
class WSGIExecutioner(Executioner):
|
|
||||||
"""
|
|
||||||
Base class for execution backends with a WSGI application interface.
|
|
||||||
@@ -897,6 +907,9 @@ class jsonserver_session(jsonserver, KerberosSession):
|
|
||||||
|
|
||||||
logger.debug('WSGI jsonserver_session.__call__:')
|
|
||||||
|
|
||||||
+ if not self.check_referer(environ):
|
|
||||||
+ return self.bad_request(environ, start_response, 'denied')
|
|
||||||
+
|
|
||||||
# Redirect to login if no Kerberos credentials
|
|
||||||
ccache_name = self.get_environ_creds(environ)
|
|
||||||
if ccache_name is None:
|
|
||||||
@@ -949,6 +962,9 @@ class KerberosLogin(Backend, KerberosSession):
|
|
||||||
def __call__(self, environ, start_response):
|
|
||||||
logger.debug('WSGI KerberosLogin.__call__:')
|
|
||||||
|
|
||||||
+ if not self.check_referer(environ):
|
|
||||||
+ return self.bad_request(environ, start_response, 'denied')
|
|
||||||
+
|
|
||||||
# Redirect to login if no Kerberos credentials
|
|
||||||
user_ccache_name = self.get_environ_creds(environ)
|
|
||||||
if user_ccache_name is None:
|
|
||||||
@@ -967,6 +983,9 @@ class login_x509(KerberosLogin):
|
|
||||||
def __call__(self, environ, start_response):
|
|
||||||
logger.debug('WSGI login_x509.__call__:')
|
|
||||||
|
|
||||||
+ if not self.check_referer(environ):
|
|
||||||
+ return self.bad_request(environ, start_response, 'denied')
|
|
||||||
+
|
|
||||||
if 'KRB5CCNAME' not in environ:
|
|
||||||
return self.unauthorized(
|
|
||||||
environ, start_response, 'KRB5CCNAME not set',
|
|
||||||
@@ -1015,6 +1034,9 @@ class login_password(Backend, KerberosSession):
|
|
||||||
|
|
||||||
logger.debug('WSGI login_password.__call__:')
|
|
||||||
|
|
||||||
+ if not self.check_referer(environ):
|
|
||||||
+ return self.bad_request(environ, start_response, 'denied')
|
|
||||||
+
|
|
||||||
# Get the user and password parameters from the request
|
|
||||||
content_type = environ.get('CONTENT_TYPE', '').lower()
|
|
||||||
if not content_type.startswith('application/x-www-form-urlencoded'):
|
|
||||||
@@ -1147,6 +1169,9 @@ class change_password(Backend, HTTP_Status):
|
|
||||||
def __call__(self, environ, start_response):
|
|
||||||
logger.info('WSGI change_password.__call__:')
|
|
||||||
|
|
||||||
+ if not self.check_referer(environ):
|
|
||||||
+ return self.bad_request(environ, start_response, 'denied')
|
|
||||||
+
|
|
||||||
# Get the user and password parameters from the request
|
|
||||||
content_type = environ.get('CONTENT_TYPE', '').lower()
|
|
||||||
if not content_type.startswith('application/x-www-form-urlencoded'):
|
|
||||||
@@ -1364,6 +1389,9 @@ class xmlserver_session(xmlserver, KerberosSession):
|
|
||||||
|
|
||||||
logger.debug('WSGI xmlserver_session.__call__:')
|
|
||||||
|
|
||||||
+ if not self.check_referer(environ):
|
|
||||||
+ return self.bad_request(environ, start_response, 'denied')
|
|
||||||
+
|
|
||||||
ccache_name = environ.get('KRB5CCNAME')
|
|
||||||
|
|
||||||
# Redirect to /ipa/xml if no Kerberos credentials
|
|
||||||
--
|
|
||||||
2.41.0
|
|
||||||
|
|
@ -0,0 +1,42 @@
|
|||||||
|
From 2d0a0cc40fb8674f30ba62980b1953cef840009e Mon Sep 17 00:00:00 2001
|
||||||
|
From: Florence Blanc-Renaud <flo@redhat.com>
|
||||||
|
Date: Thu, 1 Dec 2022 13:58:58 +0100
|
||||||
|
Subject: [PATCH] Spec file: ipa-client depends on krb5-pkinit-openssl
|
||||||
|
|
||||||
|
Now that ipa-client-installs supports pkinit, the package
|
||||||
|
depends on krb5-pkinit-openssl.
|
||||||
|
Update the spec file, move the dependency from ipa-server
|
||||||
|
to ipa-client subpackage.
|
||||||
|
|
||||||
|
Fixes: https://pagure.io/freeipa/issue/9290
|
||||||
|
|
||||||
|
Signed-off-by: Florence Blanc-Renaud <flo@redhat.com>
|
||||||
|
Reviewed-By: Alexander Bokovoy <abokovoy@redhat.com>
|
||||||
|
---
|
||||||
|
freeipa.spec.in | 3 ++-
|
||||||
|
1 file changed, 2 insertions(+), 1 deletion(-)
|
||||||
|
|
||||||
|
diff --git a/freeipa.spec.in b/freeipa.spec.in
|
||||||
|
index f09741d7ad6c09e52c4bd24fcc9300584f83a49d..7dcf2e66abe40e6bde3491268b9c012f7578a8b6 100755
|
||||||
|
--- a/freeipa.spec.in
|
||||||
|
+++ b/freeipa.spec.in
|
||||||
|
@@ -449,7 +449,6 @@ Requires: nss-tools >= %{nss_version}
|
||||||
|
Requires(post): krb5-server >= %{krb5_version}
|
||||||
|
Requires(post): krb5-server >= %{krb5_base_version}
|
||||||
|
Requires: krb5-kdb-version = %{krb5_kdb_version}
|
||||||
|
-Requires: krb5-pkinit-openssl >= %{krb5_version}
|
||||||
|
Requires: cyrus-sasl-gssapi%{?_isa}
|
||||||
|
Requires: chrony
|
||||||
|
Requires: httpd >= %{httpd_version}
|
||||||
|
@@ -675,6 +674,8 @@ Requires: python3-sssdconfig >= %{sssd_version}
|
||||||
|
Requires: cyrus-sasl-gssapi%{?_isa}
|
||||||
|
Requires: chrony
|
||||||
|
Requires: krb5-workstation >= %{krb5_version}
|
||||||
|
+# support pkinit with client install
|
||||||
|
+Requires: krb5-pkinit-openssl >= %{krb5_version}
|
||||||
|
# authselect: sssd profile with-subid
|
||||||
|
%if 0%{?fedora} >= 36
|
||||||
|
Requires: authselect >= 1.4.0
|
||||||
|
--
|
||||||
|
2.38.1
|
||||||
|
|
@ -1,359 +0,0 @@
|
|||||||
From f1f8b16def3e809f5773bb8aa40aefb21699347b Mon Sep 17 00:00:00 2001
|
|
||||||
From: Rob Crittenden <rcritten@redhat.com>
|
|
||||||
Date: Thu, 12 Oct 2023 20:34:01 +0000
|
|
||||||
Subject: [PATCH] Integration tests for verifying Referer header in the UI
|
|
||||||
|
|
||||||
Validate that the change_password and login_password endpoints
|
|
||||||
verify the HTTP Referer header. There is some overlap in the
|
|
||||||
tests: belt and suspenders.
|
|
||||||
|
|
||||||
All endpoints except session/login_x509 are covered, sometimes
|
|
||||||
having to rely on expected bad results (see the i18n endpoint).
|
|
||||||
|
|
||||||
session/login_x509 is not tested yet as it requires significant
|
|
||||||
additional setup in order to associate a user certificate with
|
|
||||||
a user entry, etc.
|
|
||||||
|
|
||||||
This can be manually verified by modifying /etc/httpd/conf.d/ipa.conf
|
|
||||||
and adding:
|
|
||||||
|
|
||||||
Satisfy Any
|
|
||||||
Require all granted
|
|
||||||
|
|
||||||
Then comment out Auth and SSLVerify, etc. and restart httpd.
|
|
||||||
|
|
||||||
With a valid Referer will fail with a 401 and log that there is no
|
|
||||||
KRB5CCNAME. This comes after the referer check.
|
|
||||||
|
|
||||||
With an invalid Referer it will fail with a 400 Bad Request as
|
|
||||||
expected.
|
|
||||||
|
|
||||||
CVE-2023-5455
|
|
||||||
|
|
||||||
Signed-off-by: Rob Crittenden <rcritten@redhat.com>
|
|
||||||
---
|
|
||||||
ipatests/test_ipaserver/httptest.py | 7 +-
|
|
||||||
ipatests/test_ipaserver/test_changepw.py | 12 +-
|
|
||||||
.../test_ipaserver/test_login_password.py | 88 ++++++++++++
|
|
||||||
ipatests/test_ipaserver/test_referer.py | 136 ++++++++++++++++++
|
|
||||||
ipatests/util.py | 4 +-
|
|
||||||
5 files changed, 242 insertions(+), 5 deletions(-)
|
|
||||||
create mode 100644 ipatests/test_ipaserver/test_login_password.py
|
|
||||||
create mode 100644 ipatests/test_ipaserver/test_referer.py
|
|
||||||
|
|
||||||
diff --git a/ipatests/test_ipaserver/httptest.py b/ipatests/test_ipaserver/httptest.py
|
|
||||||
index 6cd034a71..8924798fc 100644
|
|
||||||
--- a/ipatests/test_ipaserver/httptest.py
|
|
||||||
+++ b/ipatests/test_ipaserver/httptest.py
|
|
||||||
@@ -36,7 +36,7 @@ class Unauthorized_HTTP_test:
|
|
||||||
content_type = 'application/x-www-form-urlencoded'
|
|
||||||
accept_language = 'en-us'
|
|
||||||
|
|
||||||
- def send_request(self, method='POST', params=None):
|
|
||||||
+ def send_request(self, method='POST', params=None, host=None):
|
|
||||||
"""
|
|
||||||
Send a request to HTTP server
|
|
||||||
|
|
||||||
@@ -45,7 +45,10 @@ class Unauthorized_HTTP_test:
|
|
||||||
if params is not None:
|
|
||||||
if self.content_type == 'application/x-www-form-urlencoded':
|
|
||||||
params = urllib.parse.urlencode(params, True)
|
|
||||||
- url = 'https://' + self.host + self.app_uri
|
|
||||||
+ if host:
|
|
||||||
+ url = 'https://' + host + self.app_uri
|
|
||||||
+ else:
|
|
||||||
+ url = 'https://' + self.host + self.app_uri
|
|
||||||
|
|
||||||
headers = {'Content-Type': self.content_type,
|
|
||||||
'Accept-Language': self.accept_language,
|
|
||||||
diff --git a/ipatests/test_ipaserver/test_changepw.py b/ipatests/test_ipaserver/test_changepw.py
|
|
||||||
index c3a47ab26..df38ddb3d 100644
|
|
||||||
--- a/ipatests/test_ipaserver/test_changepw.py
|
|
||||||
+++ b/ipatests/test_ipaserver/test_changepw.py
|
|
||||||
@@ -53,10 +53,11 @@ class test_changepw(XMLRPC_test, Unauthorized_HTTP_test):
|
|
||||||
|
|
||||||
request.addfinalizer(fin)
|
|
||||||
|
|
||||||
- def _changepw(self, user, old_password, new_password):
|
|
||||||
+ def _changepw(self, user, old_password, new_password, host=None):
|
|
||||||
return self.send_request(params={'user': str(user),
|
|
||||||
'old_password' : str(old_password),
|
|
||||||
'new_password' : str(new_password)},
|
|
||||||
+ host=host
|
|
||||||
)
|
|
||||||
|
|
||||||
def _checkpw(self, user, password):
|
|
||||||
@@ -89,6 +90,15 @@ class test_changepw(XMLRPC_test, Unauthorized_HTTP_test):
|
|
||||||
# make sure that password is NOT changed
|
|
||||||
self._checkpw(testuser, old_password)
|
|
||||||
|
|
||||||
+ def test_invalid_referer(self):
|
|
||||||
+ response = self._changepw(testuser, old_password, new_password,
|
|
||||||
+ 'attacker.test')
|
|
||||||
+
|
|
||||||
+ assert_equal(response.status, 400)
|
|
||||||
+
|
|
||||||
+ # make sure that password is NOT changed
|
|
||||||
+ self._checkpw(testuser, old_password)
|
|
||||||
+
|
|
||||||
def test_pwpolicy_error(self):
|
|
||||||
response = self._changepw(testuser, old_password, '1')
|
|
||||||
|
|
||||||
diff --git a/ipatests/test_ipaserver/test_login_password.py b/ipatests/test_ipaserver/test_login_password.py
|
|
||||||
new file mode 100644
|
|
||||||
index 000000000..9425cb797
|
|
||||||
--- /dev/null
|
|
||||||
+++ b/ipatests/test_ipaserver/test_login_password.py
|
|
||||||
@@ -0,0 +1,88 @@
|
|
||||||
+# Copyright (C) 2023 Red Hat
|
|
||||||
+# see file 'COPYING' for use and warranty information
|
|
||||||
+#
|
|
||||||
+# This program is free software; you can redistribute it and/or modify
|
|
||||||
+# it under the terms of the GNU General Public License as published by
|
|
||||||
+# the Free Software Foundation, either version 3 of the License, or
|
|
||||||
+# (at your option) any later version.
|
|
||||||
+#
|
|
||||||
+# This program is distributed in the hope that it will be useful,
|
|
||||||
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
+# GNU General Public License for more details.
|
|
||||||
+#
|
|
||||||
+# You should have received a copy of the GNU General Public License
|
|
||||||
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
+
|
|
||||||
+import os
|
|
||||||
+import pytest
|
|
||||||
+import uuid
|
|
||||||
+
|
|
||||||
+from ipatests.test_ipaserver.httptest import Unauthorized_HTTP_test
|
|
||||||
+from ipatests.test_xmlrpc.xmlrpc_test import XMLRPC_test
|
|
||||||
+from ipatests.util import assert_equal
|
|
||||||
+from ipalib import api, errors
|
|
||||||
+from ipapython.ipautil import run
|
|
||||||
+
|
|
||||||
+testuser = u'tuser'
|
|
||||||
+password = u'password'
|
|
||||||
+
|
|
||||||
+
|
|
||||||
+@pytest.mark.tier1
|
|
||||||
+class test_login_password(XMLRPC_test, Unauthorized_HTTP_test):
|
|
||||||
+ app_uri = '/ipa/session/login_password'
|
|
||||||
+
|
|
||||||
+ @pytest.fixture(autouse=True)
|
|
||||||
+ def login_setup(self, request):
|
|
||||||
+ ccache = os.path.join('/tmp', str(uuid.uuid4()))
|
|
||||||
+ try:
|
|
||||||
+ api.Command['user_add'](uid=testuser, givenname=u'Test', sn=u'User')
|
|
||||||
+ api.Command['passwd'](testuser, password=password)
|
|
||||||
+ run(['kinit', testuser], stdin='{0}\n{0}\n{0}\n'.format(password),
|
|
||||||
+ env={"KRB5CCNAME": ccache})
|
|
||||||
+ except errors.ExecutionError as e:
|
|
||||||
+ pytest.skip(
|
|
||||||
+ 'Cannot set up test user: %s' % e
|
|
||||||
+ )
|
|
||||||
+
|
|
||||||
+ def fin():
|
|
||||||
+ try:
|
|
||||||
+ api.Command['user_del']([testuser])
|
|
||||||
+ except errors.NotFound:
|
|
||||||
+ pass
|
|
||||||
+ os.unlink(ccache)
|
|
||||||
+
|
|
||||||
+ request.addfinalizer(fin)
|
|
||||||
+
|
|
||||||
+ def _login(self, user, password, host=None):
|
|
||||||
+ return self.send_request(params={'user': str(user),
|
|
||||||
+ 'password' : str(password)},
|
|
||||||
+ host=host)
|
|
||||||
+
|
|
||||||
+ def test_bad_options(self):
|
|
||||||
+ for params in (
|
|
||||||
+ None, # no params
|
|
||||||
+ {"user": "foo"}, # missing options
|
|
||||||
+ {"user": "foo", "password": ""}, # empty option
|
|
||||||
+ ):
|
|
||||||
+ response = self.send_request(params=params)
|
|
||||||
+ assert_equal(response.status, 400)
|
|
||||||
+ assert_equal(response.reason, 'Bad Request')
|
|
||||||
+
|
|
||||||
+ def test_invalid_auth(self):
|
|
||||||
+ response = self._login(testuser, 'wrongpassword')
|
|
||||||
+
|
|
||||||
+ assert_equal(response.status, 401)
|
|
||||||
+ assert_equal(response.getheader('X-IPA-Rejection-Reason'),
|
|
||||||
+ 'invalid-password')
|
|
||||||
+
|
|
||||||
+ def test_invalid_referer(self):
|
|
||||||
+ response = self._login(testuser, password, 'attacker.test')
|
|
||||||
+
|
|
||||||
+ assert_equal(response.status, 400)
|
|
||||||
+
|
|
||||||
+ def test_success(self):
|
|
||||||
+ response = self._login(testuser, password)
|
|
||||||
+
|
|
||||||
+ assert_equal(response.status, 200)
|
|
||||||
+ assert response.getheader('X-IPA-Rejection-Reason') is None
|
|
||||||
diff --git a/ipatests/test_ipaserver/test_referer.py b/ipatests/test_ipaserver/test_referer.py
|
|
||||||
new file mode 100644
|
|
||||||
index 000000000..4eade8bba
|
|
||||||
--- /dev/null
|
|
||||||
+++ b/ipatests/test_ipaserver/test_referer.py
|
|
||||||
@@ -0,0 +1,136 @@
|
|
||||||
+# Copyright (C) 2023 Red Hat
|
|
||||||
+# see file 'COPYING' for use and warranty information
|
|
||||||
+#
|
|
||||||
+# This program is free software; you can redistribute it and/or modify
|
|
||||||
+# it under the terms of the GNU General Public License as published by
|
|
||||||
+# the Free Software Foundation, either version 3 of the License, or
|
|
||||||
+# (at your option) any later version.
|
|
||||||
+#
|
|
||||||
+# This program is distributed in the hope that it will be useful,
|
|
||||||
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
||||||
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
||||||
+# GNU General Public License for more details.
|
|
||||||
+#
|
|
||||||
+# You should have received a copy of the GNU General Public License
|
|
||||||
+# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
||||||
+
|
|
||||||
+import os
|
|
||||||
+import pytest
|
|
||||||
+import uuid
|
|
||||||
+
|
|
||||||
+from ipatests.test_ipaserver.httptest import Unauthorized_HTTP_test
|
|
||||||
+from ipatests.test_xmlrpc.xmlrpc_test import XMLRPC_test
|
|
||||||
+from ipatests.util import assert_equal
|
|
||||||
+from ipalib import api, errors
|
|
||||||
+from ipapython.ipautil import run
|
|
||||||
+
|
|
||||||
+testuser = u'tuser'
|
|
||||||
+password = u'password'
|
|
||||||
+
|
|
||||||
+
|
|
||||||
+@pytest.mark.tier1
|
|
||||||
+class test_referer(XMLRPC_test, Unauthorized_HTTP_test):
|
|
||||||
+
|
|
||||||
+ @pytest.fixture(autouse=True)
|
|
||||||
+ def login_setup(self, request):
|
|
||||||
+ ccache = os.path.join('/tmp', str(uuid.uuid4()))
|
|
||||||
+ tokenid = None
|
|
||||||
+ try:
|
|
||||||
+ api.Command['user_add'](uid=testuser, givenname=u'Test', sn=u'User')
|
|
||||||
+ api.Command['passwd'](testuser, password=password)
|
|
||||||
+ run(['kinit', testuser], stdin='{0}\n{0}\n{0}\n'.format(password),
|
|
||||||
+ env={"KRB5CCNAME": ccache})
|
|
||||||
+ result = api.Command["otptoken_add"](
|
|
||||||
+ type='HOTP', description='testotp',
|
|
||||||
+ ipatokenotpalgorithm='sha512', ipatokenowner=testuser,
|
|
||||||
+ ipatokenotpdigits='6')
|
|
||||||
+ tokenid = result['result']['ipatokenuniqueid'][0]
|
|
||||||
+ except errors.ExecutionError as e:
|
|
||||||
+ pytest.skip(
|
|
||||||
+ 'Cannot set up test user: %s' % e
|
|
||||||
+ )
|
|
||||||
+
|
|
||||||
+ def fin():
|
|
||||||
+ try:
|
|
||||||
+ api.Command['user_del']([testuser])
|
|
||||||
+ api.Command['otptoken_del']([tokenid])
|
|
||||||
+ except errors.NotFound:
|
|
||||||
+ pass
|
|
||||||
+ os.unlink(ccache)
|
|
||||||
+
|
|
||||||
+ request.addfinalizer(fin)
|
|
||||||
+
|
|
||||||
+ def _request(self, params={}, host=None):
|
|
||||||
+ # implicit is that self.app_uri is set to the appropriate value
|
|
||||||
+ return self.send_request(params=params, host=host)
|
|
||||||
+
|
|
||||||
+ def test_login_password_valid(self):
|
|
||||||
+ """Valid authentication of a user"""
|
|
||||||
+ self.app_uri = "/ipa/session/login_password"
|
|
||||||
+ response = self._request(
|
|
||||||
+ params={'user': 'tuser', 'password': password})
|
|
||||||
+ assert_equal(response.status, 200, self.app_uri)
|
|
||||||
+
|
|
||||||
+ def test_change_password_valid(self):
|
|
||||||
+ """This actually changes the user password"""
|
|
||||||
+ self.app_uri = "/ipa/session/change_password"
|
|
||||||
+ response = self._request(
|
|
||||||
+ params={'user': 'tuser',
|
|
||||||
+ 'old_password': password,
|
|
||||||
+ 'new_password': 'new_password'}
|
|
||||||
+ )
|
|
||||||
+ assert_equal(response.status, 200, self.app_uri)
|
|
||||||
+
|
|
||||||
+ def test_sync_token_valid(self):
|
|
||||||
+ """We aren't testing that sync works, just that we can get there"""
|
|
||||||
+ self.app_uri = "/ipa/session/sync_token"
|
|
||||||
+ response = self._request(
|
|
||||||
+ params={'user': 'tuser',
|
|
||||||
+ 'first_code': '1234',
|
|
||||||
+ 'second_code': '5678',
|
|
||||||
+ 'password': 'password'})
|
|
||||||
+ assert_equal(response.status, 200, self.app_uri)
|
|
||||||
+
|
|
||||||
+ def test_i18n_messages_valid(self):
|
|
||||||
+ # i18n_messages requires a valid JSON request and we send
|
|
||||||
+ # nothing. If we get a 500 error then it got past the
|
|
||||||
+ # referer check.
|
|
||||||
+ self.app_uri = "/ipa/i18n_messages"
|
|
||||||
+ response = self._request()
|
|
||||||
+ assert_equal(response.status, 500, self.app_uri)
|
|
||||||
+
|
|
||||||
+ # /ipa/session/login_x509 is not tested yet as it requires
|
|
||||||
+ # significant additional setup.
|
|
||||||
+ # This can be manually verified by adding
|
|
||||||
+ # Satisfy Any and Require all granted to the configuration
|
|
||||||
+ # section and comment out all Auth directives. The request
|
|
||||||
+ # will fail and log that there is no KRB5CCNAME which comes
|
|
||||||
+ # after the referer check.
|
|
||||||
+
|
|
||||||
+ def test_endpoints_auth_required(self):
|
|
||||||
+ """Test endpoints that require pre-authorization which will
|
|
||||||
+ fail before we even get to the Referer check
|
|
||||||
+ """
|
|
||||||
+ self.endpoints = {
|
|
||||||
+ "/ipa/xml",
|
|
||||||
+ "/ipa/session/login_kerberos",
|
|
||||||
+ "/ipa/session/json",
|
|
||||||
+ "/ipa/session/xml"
|
|
||||||
+ }
|
|
||||||
+ for self.app_uri in self.endpoints:
|
|
||||||
+ response = self._request(host="attacker.test")
|
|
||||||
+
|
|
||||||
+ # referer is checked after auth
|
|
||||||
+ assert_equal(response.status, 401, self.app_uri)
|
|
||||||
+
|
|
||||||
+ def notest_endpoints_invalid(self):
|
|
||||||
+ """Pass in a bad Referer, expect a 400 Bad Request"""
|
|
||||||
+ self.endpoints = {
|
|
||||||
+ "/ipa/session/login_password",
|
|
||||||
+ "/ipa/session/change_password",
|
|
||||||
+ "/ipa/session/sync_token",
|
|
||||||
+ }
|
|
||||||
+ for self.app_uri in self.endpoints:
|
|
||||||
+ response = self._request(host="attacker.test")
|
|
||||||
+
|
|
||||||
+ assert_equal(response.status, 400, self.app_uri)
|
|
||||||
diff --git a/ipatests/util.py b/ipatests/util.py
|
|
||||||
index 5c0152b90..c69d98790 100644
|
|
||||||
--- a/ipatests/util.py
|
|
||||||
+++ b/ipatests/util.py
|
|
||||||
@@ -163,12 +163,12 @@ class ExceptionNotRaised(Exception):
|
|
||||||
return self.msg % self.expected.__name__
|
|
||||||
|
|
||||||
|
|
||||||
-def assert_equal(val1, val2):
|
|
||||||
+def assert_equal(val1, val2, msg=''):
|
|
||||||
"""
|
|
||||||
Assert ``val1`` and ``val2`` are the same type and of equal value.
|
|
||||||
"""
|
|
||||||
assert type(val1) is type(val2), '%r != %r' % (val1, val2)
|
|
||||||
- assert val1 == val2, '%r != %r' % (val1, val2)
|
|
||||||
+ assert val1 == val2, '%r != %r %r' % (val1, val2, msg)
|
|
||||||
|
|
||||||
|
|
||||||
def assert_not_equal(val1, val2):
|
|
||||||
--
|
|
||||||
2.41.0
|
|
||||||
|
|
@ -0,0 +1,54 @@
|
|||||||
|
From 894dca12c120f0bfa705307a0609da47326b8fb2 Mon Sep 17 00:00:00 2001
|
||||||
|
From: Florence Blanc-Renaud <flo@redhat.com>
|
||||||
|
Date: Thu, 12 Jan 2023 11:26:53 +0100
|
||||||
|
Subject: [PATCH] server install: remove error log about missing bkup file
|
||||||
|
|
||||||
|
The client installer code can be called in 3 different ways:
|
||||||
|
- from ipa-client-install CLI
|
||||||
|
- from ipa-replica-install CLI if the client is not already installed
|
||||||
|
- from ipa-server-install
|
||||||
|
|
||||||
|
In the last case, the client installer is called with
|
||||||
|
options.on_master=True
|
||||||
|
As a result, it's skipping the part that is creating the krb5
|
||||||
|
configuration:
|
||||||
|
if not options.on_master:
|
||||||
|
nolog = tuple()
|
||||||
|
configure_krb5_conf(...)
|
||||||
|
|
||||||
|
The configure_krb5_conf method is the place where the krb5.conf file is
|
||||||
|
backup'ed with the extention ".ipabkp". For a master installation, this
|
||||||
|
code is not called and the ipabkp file does not exist => delete raises
|
||||||
|
an error.
|
||||||
|
|
||||||
|
When delete fails because the file does not exist, no need to log an
|
||||||
|
error message.
|
||||||
|
|
||||||
|
Fixes: https://pagure.io/freeipa/issue/9306
|
||||||
|
Signed-off-by: Florence Blanc-Renaud <flo@redhat.com>
|
||||||
|
Reviewed-By: Rob Crittenden <rcritten@redhat.com>
|
||||||
|
---
|
||||||
|
ipaclient/install/client.py | 7 +++----
|
||||||
|
1 file changed, 3 insertions(+), 4 deletions(-)
|
||||||
|
|
||||||
|
diff --git a/ipaclient/install/client.py b/ipaclient/install/client.py
|
||||||
|
index e5d3e8223efa1ebb69a1b7e963c394f9e1f38816..6e7f17d5b69581320866627cb5747a050cb6e32e 100644
|
||||||
|
--- a/ipaclient/install/client.py
|
||||||
|
+++ b/ipaclient/install/client.py
|
||||||
|
@@ -124,10 +124,9 @@ def cleanup(func):
|
||||||
|
os.rmdir(ccache_dir)
|
||||||
|
except OSError:
|
||||||
|
pass
|
||||||
|
- try:
|
||||||
|
- os.remove(krb_name + ".ipabkp")
|
||||||
|
- except OSError:
|
||||||
|
- logger.error("Could not remove %s.ipabkp", krb_name)
|
||||||
|
+ # During master installation, the .ipabkp file is not created
|
||||||
|
+ # Ignore the delete error if it is "file does not exist"
|
||||||
|
+ remove_file(krb_name + ".ipabkp")
|
||||||
|
|
||||||
|
return inner
|
||||||
|
|
||||||
|
--
|
||||||
|
2.39.1
|
||||||
|
|
@ -1,265 +0,0 @@
|
|||||||
From 013be398bced31f567ef01ac2471cb7529789b4a Mon Sep 17 00:00:00 2001
|
|
||||||
From: Julien Rische <jrische@redhat.com>
|
|
||||||
Date: Mon, 9 Oct 2023 15:47:03 +0200
|
|
||||||
Subject: [PATCH] ipa-kdb: Detect and block Bronze-Bit attacks
|
|
||||||
|
|
||||||
The C8S/RHEL8 version of FreeIPA is vulnerable to the Bronze-Bit attack
|
|
||||||
because it does not implement PAC ticket signature to protect the
|
|
||||||
"forwardable" flag. However, it does implement the PAC extended KDC
|
|
||||||
signature, which protects against PAC spoofing.
|
|
||||||
|
|
||||||
Based on information available in the PAC and the
|
|
||||||
"ok-to-auth-as-delegate" attribute in the database. It is possible to
|
|
||||||
detect and reject requests where the "forwardable" flag was flipped by
|
|
||||||
the attacker in the evidence ticket.
|
|
||||||
---
|
|
||||||
daemons/ipa-kdb/ipa_kdb.h | 13 +++
|
|
||||||
daemons/ipa-kdb/ipa_kdb_kdcpolicy.c | 6 +
|
|
||||||
daemons/ipa-kdb/ipa_kdb_mspac.c | 173 ++++++++++++++++++++++++++++
|
|
||||||
ipaserver/install/server/install.py | 8 ++
|
|
||||||
4 files changed, 200 insertions(+)
|
|
||||||
|
|
||||||
diff --git a/daemons/ipa-kdb/ipa_kdb.h b/daemons/ipa-kdb/ipa_kdb.h
|
|
||||||
index 7aa5be494..02b2cb631 100644
|
|
||||||
--- a/daemons/ipa-kdb/ipa_kdb.h
|
|
||||||
+++ b/daemons/ipa-kdb/ipa_kdb.h
|
|
||||||
@@ -367,6 +367,19 @@ krb5_error_code ipadb_is_princ_from_trusted_realm(krb5_context kcontext,
|
|
||||||
const char *test_realm, size_t size,
|
|
||||||
char **trusted_realm);
|
|
||||||
|
|
||||||
+/* Try to detect a Bronze-Bit attack based on the content of the request and
|
|
||||||
+ * data from the KDB.
|
|
||||||
+ *
|
|
||||||
+ * context krb5 context
|
|
||||||
+ * request KDB request
|
|
||||||
+ * detected Set to "true" if a bronze bit attack is detected and the
|
|
||||||
+ * pointer is not NULL. Remains unset otherwise.
|
|
||||||
+ * status If the call fails and the pointer is not NULL, set it with a
|
|
||||||
+ * message describing the cause of the failure. */
|
|
||||||
+krb5_error_code
|
|
||||||
+ipadb_check_for_bronze_bit_attack(krb5_context context, krb5_kdc_req *request,
|
|
||||||
+ bool *detected, const char **status);
|
|
||||||
+
|
|
||||||
/* DELEGATION CHECKS */
|
|
||||||
|
|
||||||
krb5_error_code ipadb_check_allowed_to_delegate(krb5_context kcontext,
|
|
||||||
diff --git a/daemons/ipa-kdb/ipa_kdb_kdcpolicy.c b/daemons/ipa-kdb/ipa_kdb_kdcpolicy.c
|
|
||||||
index f2804c9b2..1032dff0b 100644
|
|
||||||
--- a/daemons/ipa-kdb/ipa_kdb_kdcpolicy.c
|
|
||||||
+++ b/daemons/ipa-kdb/ipa_kdb_kdcpolicy.c
|
|
||||||
@@ -185,6 +185,12 @@ ipa_kdcpolicy_check_tgs(krb5_context context, krb5_kdcpolicy_moddata moddata,
|
|
||||||
const char **status, krb5_deltat *lifetime_out,
|
|
||||||
krb5_deltat *renew_lifetime_out)
|
|
||||||
{
|
|
||||||
+ krb5_error_code kerr;
|
|
||||||
+
|
|
||||||
+ kerr = ipadb_check_for_bronze_bit_attack(context, request, NULL, status);
|
|
||||||
+ if (kerr)
|
|
||||||
+ return KRB5KDC_ERR_POLICY;
|
|
||||||
+
|
|
||||||
*status = NULL;
|
|
||||||
*lifetime_out = 0;
|
|
||||||
*renew_lifetime_out = 0;
|
|
||||||
diff --git a/daemons/ipa-kdb/ipa_kdb_mspac.c b/daemons/ipa-kdb/ipa_kdb_mspac.c
|
|
||||||
index 83cb9914d..b4e22d431 100644
|
|
||||||
--- a/daemons/ipa-kdb/ipa_kdb_mspac.c
|
|
||||||
+++ b/daemons/ipa-kdb/ipa_kdb_mspac.c
|
|
||||||
@@ -3298,3 +3298,176 @@ krb5_error_code ipadb_is_princ_from_trusted_realm(krb5_context kcontext,
|
|
||||||
|
|
||||||
return KRB5_KDB_NOENTRY;
|
|
||||||
}
|
|
||||||
+
|
|
||||||
+krb5_error_code
|
|
||||||
+ipadb_check_for_bronze_bit_attack(krb5_context context, krb5_kdc_req *request,
|
|
||||||
+ bool *detected, const char **status)
|
|
||||||
+{
|
|
||||||
+ krb5_error_code kerr;
|
|
||||||
+ const char *st = NULL;
|
|
||||||
+ size_t i, j;
|
|
||||||
+ krb5_ticket *evidence_tkt;
|
|
||||||
+ krb5_authdata **authdata, **ifrel = NULL;
|
|
||||||
+ krb5_pac pac = NULL;
|
|
||||||
+ TALLOC_CTX *tmpctx = NULL;
|
|
||||||
+ krb5_data fullsign = { 0, 0, NULL }, linfo_blob = { 0, 0, NULL };
|
|
||||||
+ DATA_BLOB linfo_data;
|
|
||||||
+ struct PAC_LOGON_INFO_CTR linfo;
|
|
||||||
+ enum ndr_err_code ndr_err;
|
|
||||||
+ struct dom_sid asserted_identity_sid;
|
|
||||||
+ bool evtkt_is_s4u2self = false;
|
|
||||||
+ krb5_db_entry *proxy_entry = NULL;
|
|
||||||
+
|
|
||||||
+ /* If no additional ticket, this is not a constrained delegateion request.
|
|
||||||
+ * Skip checks. */
|
|
||||||
+ if (!(request->kdc_options & KDC_OPT_CNAME_IN_ADDL_TKT)) {
|
|
||||||
+ kerr = 0;
|
|
||||||
+ goto end;
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ evidence_tkt = request->second_ticket[0];
|
|
||||||
+
|
|
||||||
+ /* No need to check the Forwardable flag. If it was not set, this request
|
|
||||||
+ * would have failed earlier. */
|
|
||||||
+
|
|
||||||
+ /* We only support general constrained delegation (not RBCD), which is not
|
|
||||||
+ * available for cross-realms. */
|
|
||||||
+ if (!krb5_realm_compare(context, evidence_tkt->server, request->server)) {
|
|
||||||
+ st = "S4U2PROXY_NOT_SUPPORTED_FOR_CROSS_REALMS";
|
|
||||||
+ kerr = ENOTSUP;
|
|
||||||
+ goto end;
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ authdata = evidence_tkt->enc_part2->authorization_data;
|
|
||||||
+
|
|
||||||
+ /* Search for the PAC. */
|
|
||||||
+ for (i = 0; authdata != NULL && authdata[i] != NULL; i++) {
|
|
||||||
+ if (authdata[i]->ad_type != KRB5_AUTHDATA_IF_RELEVANT)
|
|
||||||
+ continue;
|
|
||||||
+
|
|
||||||
+ kerr = krb5_decode_authdata_container(context,
|
|
||||||
+ KRB5_AUTHDATA_IF_RELEVANT,
|
|
||||||
+ authdata[i], &ifrel);
|
|
||||||
+ if (kerr) {
|
|
||||||
+ st = "S4U2PROXY_CANNOT_DECODE_EVIDENCE_TKT_AUTHDATA";
|
|
||||||
+ goto end;
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ for (j = 0; ifrel[j] != NULL; j++) {
|
|
||||||
+ if (ifrel[j]->ad_type == KRB5_AUTHDATA_WIN2K_PAC)
|
|
||||||
+ break;
|
|
||||||
+ }
|
|
||||||
+ if (ifrel[j] != NULL)
|
|
||||||
+ break;
|
|
||||||
+
|
|
||||||
+ krb5_free_authdata(context, ifrel);
|
|
||||||
+ ifrel = NULL;
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ if (ifrel == NULL) {
|
|
||||||
+ st = "S4U2PROXY_EVIDENCE_TKT_WITHOUT_PAC";
|
|
||||||
+ kerr = ENOENT;
|
|
||||||
+ goto end;
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ /* Parse the PAC. */
|
|
||||||
+ kerr = krb5_pac_parse(context, ifrel[j]->contents, ifrel[j]->length, &pac);
|
|
||||||
+ if (kerr) {
|
|
||||||
+ st = "S4U2PROXY_CANNOT_DECODE_EVICENCE_TKT_PAC";
|
|
||||||
+ goto end;
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ /* Check that the PAC extanded KDC signature is present. If it is, it was
|
|
||||||
+ * already tested.
|
|
||||||
+ * If absent, the context of the PAC cannot be trusted. */
|
|
||||||
+ kerr = krb5_pac_get_buffer(context, pac, KRB5_PAC_FULL_CHECKSUM, &fullsign);
|
|
||||||
+ if (kerr) {
|
|
||||||
+ st = "S4U2PROXY_MISSING_EXTENDED_KDC_SIGN_IN_EVIDENCE_TKT_PAC";
|
|
||||||
+ goto end;
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ /* Get the PAC Logon Info. */
|
|
||||||
+ kerr = krb5_pac_get_buffer(context, pac, KRB5_PAC_LOGON_INFO, &linfo_blob);
|
|
||||||
+ if (kerr) {
|
|
||||||
+ st = "S4U2PROXY_NO_PAC_LOGON_INFO_IN_EVIDENCE_TKT";
|
|
||||||
+ goto end;
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ /* Parse the PAC Logon Info. */
|
|
||||||
+ tmpctx = talloc_new(NULL);
|
|
||||||
+ if (!tmpctx) {
|
|
||||||
+ st = "OUT_OF_MEMORY";
|
|
||||||
+ kerr = ENOMEM;
|
|
||||||
+ goto end;
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ linfo_data.length = linfo_blob.length;
|
|
||||||
+ linfo_data.data = (uint8_t *)linfo_blob.data;
|
|
||||||
+ ndr_err = ndr_pull_union_blob(&linfo_data, tmpctx, &linfo,
|
|
||||||
+ PAC_TYPE_LOGON_INFO,
|
|
||||||
+ (ndr_pull_flags_fn_t)ndr_pull_PAC_INFO);
|
|
||||||
+ if (!NDR_ERR_CODE_IS_SUCCESS(ndr_err)) {
|
|
||||||
+ st = "S4U2PROXY_CANNOT_PARSE_ENVIDENCE_TKT_PAC_LOGON_INFO";
|
|
||||||
+ kerr = EINVAL;
|
|
||||||
+ goto end;
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ /* Check that the extra SIDs array is not empty. */
|
|
||||||
+ if (linfo.info->info3.sidcount == 0) {
|
|
||||||
+ st = "S4U2PROXY_NO_EXTRA_SID";
|
|
||||||
+ kerr = ENOENT;
|
|
||||||
+ goto end;
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ /* Search for the S-1-18-2 domain SID, which indicates the ticket was
|
|
||||||
+ * obtained using S4U2Self */
|
|
||||||
+ kerr = ipadb_string_to_sid("S-1-18-2", &asserted_identity_sid);
|
|
||||||
+ if (kerr) {
|
|
||||||
+ st = "S4U2PROXY_CANNOT_CREATE_ASSERTED_IDENTITY_SID";
|
|
||||||
+ goto end;
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ for (i = 0; i < linfo.info->info3.sidcount; i++) {
|
|
||||||
+ if (dom_sid_check(&asserted_identity_sid,
|
|
||||||
+ linfo.info->info3.sids[0].sid, true)) {
|
|
||||||
+ evtkt_is_s4u2self = true;
|
|
||||||
+ break;
|
|
||||||
+ }
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ /* If the ticket was obtained using S4U2Self, the proxy principal entry must
|
|
||||||
+ * have the "ok_to_auth_as_delegate" attribute set to true. */
|
|
||||||
+ if (evtkt_is_s4u2self) {
|
|
||||||
+ kerr = ipadb_get_principal(context, evidence_tkt->server, 0,
|
|
||||||
+ &proxy_entry);
|
|
||||||
+ if (kerr) {
|
|
||||||
+ st = "S4U2PROXY_CANNOT_FIND_PROXY_PRINCIPAL";
|
|
||||||
+ goto end;
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ if (!(proxy_entry->attributes & KRB5_KDB_OK_TO_AUTH_AS_DELEGATE)) {
|
|
||||||
+ /* This evidence ticket cannot be forwardable given the privileges
|
|
||||||
+ * of the proxy principal.
|
|
||||||
+ * This is a Bronze Bit attack. */
|
|
||||||
+ if (detected)
|
|
||||||
+ *detected = true;
|
|
||||||
+ st = "S4U2PROXY_BRONZE_BIT_ATTACK_DETECTED";
|
|
||||||
+ kerr = EBADE;
|
|
||||||
+ goto end;
|
|
||||||
+ }
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ kerr = 0;
|
|
||||||
+
|
|
||||||
+end:
|
|
||||||
+ if (st && status)
|
|
||||||
+ *status = st;
|
|
||||||
+
|
|
||||||
+ krb5_free_authdata(context, ifrel);
|
|
||||||
+ krb5_pac_free(context, pac);
|
|
||||||
+ krb5_free_data_contents(context, &linfo_blob);
|
|
||||||
+ krb5_free_data_contents(context, &fullsign);
|
|
||||||
+ talloc_free(tmpctx);
|
|
||||||
+ ipadb_free_principal(context, proxy_entry);
|
|
||||||
+ return kerr;
|
|
||||||
+}
|
|
||||||
diff --git a/ipaserver/install/server/install.py b/ipaserver/install/server/install.py
|
|
||||||
index 4e4076410..bfbb83bcb 100644
|
|
||||||
--- a/ipaserver/install/server/install.py
|
|
||||||
+++ b/ipaserver/install/server/install.py
|
|
||||||
@@ -981,6 +981,14 @@ def install(installer):
|
|
||||||
# Set the admin user kerberos password
|
|
||||||
ds.change_admin_password(admin_password)
|
|
||||||
|
|
||||||
+ # Force KDC to refresh the cached value of ipaKrbAuthzData by restarting.
|
|
||||||
+ # ipaKrbAuthzData has to be set with "MS-PAC" to trigger PAC generation,
|
|
||||||
+ # which is required to handle S4U2Proxy with the Bronze-Bit fix.
|
|
||||||
+ # Not doing so would cause API malfunction for around a minute, which is
|
|
||||||
+ # long enough to cause the hereafter client installation to fail.
|
|
||||||
+ service.print_msg("Restarting the KDC")
|
|
||||||
+ krb.restart()
|
|
||||||
+
|
|
||||||
# Call client install script
|
|
||||||
service.print_msg("Configuring client side components")
|
|
||||||
try:
|
|
||||||
--
|
|
||||||
2.41.0
|
|
||||||
|
|
@ -0,0 +1,118 @@
|
|||||||
|
From 2520a7adff7a49ddcddaaf19f0e586425dc0d878 Mon Sep 17 00:00:00 2001
|
||||||
|
From: Filip Dvorak <fdvorak@redhat.com>
|
||||||
|
Date: Tue, 6 Dec 2022 15:51:27 +0100
|
||||||
|
Subject: [PATCH] ipa tests: Add LANG before kinit command to fix issue with
|
||||||
|
locale settings
|
||||||
|
|
||||||
|
Reviewed-By: Rob Crittenden <rcritten@redhat.com>
|
||||||
|
Reviewed-By: Alexander Bokovoy <abokovoy@redhat.com>
|
||||||
|
Reviewed-By: Florence Blanc-Renaud <flo@redhat.com>
|
||||||
|
Reviewed-By: Michal Polovka <mpolovka@redhat.com>
|
||||||
|
---
|
||||||
|
ipatests/test_integration/test_krbtpolicy.py | 20 ++++++++++----------
|
||||||
|
1 file changed, 10 insertions(+), 10 deletions(-)
|
||||||
|
|
||||||
|
diff --git a/ipatests/test_integration/test_krbtpolicy.py b/ipatests/test_integration/test_krbtpolicy.py
|
||||||
|
index eae16247bdfb195c1d91209cf2d11eac4c25018f..269cfb0a191821c229aaeb5a3eda0181c6e3ae62 100644
|
||||||
|
--- a/ipatests/test_integration/test_krbtpolicy.py
|
||||||
|
+++ b/ipatests/test_integration/test_krbtpolicy.py
|
||||||
|
@@ -23,7 +23,7 @@ PASSWORD = "Secret123"
|
||||||
|
USER1 = "testuser1"
|
||||||
|
USER2 = "testuser2"
|
||||||
|
MAXLIFE = 86400
|
||||||
|
-
|
||||||
|
+LANG_PKG = ["langpacks-en"]
|
||||||
|
|
||||||
|
def maxlife_within_policy(input, maxlife, slush=3600):
|
||||||
|
"""Given klist output of the TGT verify that it is within policy
|
||||||
|
@@ -45,7 +45,6 @@ def maxlife_within_policy(input, maxlife, slush=3600):
|
||||||
|
|
||||||
|
return maxlife >= diff >= maxlife - slush
|
||||||
|
|
||||||
|
-
|
||||||
|
@pytest.fixture
|
||||||
|
def reset_to_default_policy():
|
||||||
|
"""Reset default user authentication and user authentication type"""
|
||||||
|
@@ -70,7 +69,7 @@ def reset_to_default_policy():
|
||||||
|
def kinit_check_life(master, user):
|
||||||
|
"""Acquire a TGT and check if it's within the lifetime window"""
|
||||||
|
master.run_command(["kinit", user], stdin_text=f"{PASSWORD}\n")
|
||||||
|
- result = master.run_command("klist | grep krbtgt")
|
||||||
|
+ result = master.run_command("LANG=en_US.utf-8 klist | grep krbtgt")
|
||||||
|
assert maxlife_within_policy(result.stdout_text, MAXLIFE) is True
|
||||||
|
|
||||||
|
|
||||||
|
@@ -81,6 +80,7 @@ class TestPWPolicy(IntegrationTest):
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def install(cls, mh):
|
||||||
|
+ tasks.install_packages(cls.master, LANG_PKG)
|
||||||
|
tasks.install_master(cls.master)
|
||||||
|
tasks.create_active_user(cls.master, USER1, PASSWORD)
|
||||||
|
tasks.create_active_user(cls.master, USER2, PASSWORD)
|
||||||
|
@@ -100,7 +100,7 @@ class TestPWPolicy(IntegrationTest):
|
||||||
|
|
||||||
|
master.run_command(['kinit', USER1],
|
||||||
|
stdin_text=PASSWORD + '\n')
|
||||||
|
- result = master.run_command('klist | grep krbtgt')
|
||||||
|
+ result = master.run_command("LANG=en_US.utf-8 klist | grep krbtgt")
|
||||||
|
assert maxlife_within_policy(result.stdout_text, MAXLIFE) is True
|
||||||
|
|
||||||
|
def test_krbtpolicy_password_and_hardended(self):
|
||||||
|
@@ -122,7 +122,7 @@ class TestPWPolicy(IntegrationTest):
|
||||||
|
|
||||||
|
master.run_command(['kinit', USER1],
|
||||||
|
stdin_text=PASSWORD + '\n')
|
||||||
|
- result = master.run_command('klist | grep krbtgt')
|
||||||
|
+ result = master.run_command('LANG=en_US.utf-8 klist | grep krbtgt')
|
||||||
|
assert maxlife_within_policy(result.stdout_text, 600,
|
||||||
|
slush=600) is True
|
||||||
|
|
||||||
|
@@ -131,7 +131,7 @@ class TestPWPolicy(IntegrationTest):
|
||||||
|
# Verify that the short policy only applies to USER1
|
||||||
|
master.run_command(['kinit', USER2],
|
||||||
|
stdin_text=PASSWORD + '\n')
|
||||||
|
- result = master.run_command('klist | grep krbtgt')
|
||||||
|
+ result = master.run_command('LANG=en_US.utf-8 klist | grep krbtgt')
|
||||||
|
assert maxlife_within_policy(result.stdout_text, MAXLIFE) is True
|
||||||
|
|
||||||
|
def test_krbtpolicy_hardended(self):
|
||||||
|
@@ -151,7 +151,7 @@ class TestPWPolicy(IntegrationTest):
|
||||||
|
|
||||||
|
master.run_command(['kinit', USER1],
|
||||||
|
stdin_text=PASSWORD + '\n')
|
||||||
|
- result = master.run_command('klist | grep krbtgt')
|
||||||
|
+ result = master.run_command('LANG=en_US.utf-8 klist | grep krbtgt')
|
||||||
|
assert maxlife_within_policy(result.stdout_text, 1800,
|
||||||
|
slush=1800) is True
|
||||||
|
|
||||||
|
@@ -160,7 +160,7 @@ class TestPWPolicy(IntegrationTest):
|
||||||
|
# Verify that the short policy only applies to USER1
|
||||||
|
master.run_command(['kinit', USER2],
|
||||||
|
stdin_text=PASSWORD + '\n')
|
||||||
|
- result = master.run_command('klist | grep krbtgt')
|
||||||
|
+ result = master.run_command('LANG=en_US.utf-8 klist | grep krbtgt')
|
||||||
|
assert maxlife_within_policy(result.stdout_text, MAXLIFE) is True
|
||||||
|
|
||||||
|
def test_krbtpolicy_password(self):
|
||||||
|
@@ -173,7 +173,7 @@ class TestPWPolicy(IntegrationTest):
|
||||||
|
|
||||||
|
master.run_command(['kinit', USER2],
|
||||||
|
stdin_text=PASSWORD + '\n')
|
||||||
|
- result = master.run_command('klist | grep krbtgt')
|
||||||
|
+ result = master.run_command('LANG=en_US.utf-8 klist | grep krbtgt')
|
||||||
|
assert maxlife_within_policy(result.stdout_text, 1200,
|
||||||
|
slush=1200) is True
|
||||||
|
|
||||||
|
@@ -183,7 +183,7 @@ class TestPWPolicy(IntegrationTest):
|
||||||
|
master.run_command(['ipa', 'krbtpolicy-reset', USER2])
|
||||||
|
master.run_command(['kinit', USER2],
|
||||||
|
stdin_text=PASSWORD + '\n')
|
||||||
|
- result = master.run_command('klist | grep krbtgt')
|
||||||
|
+ result = master.run_command('LANG=en_US.utf-8 klist | grep krbtgt')
|
||||||
|
assert maxlife_within_policy(result.stdout_text, MAXLIFE) is True
|
||||||
|
|
||||||
|
def test_krbtpolicy_otp(self, reset_to_default_policy):
|
||||||
|
--
|
||||||
|
2.39.1
|
||||||
|
|
@ -1,212 +0,0 @@
|
|||||||
From 3add9ba03a0af913d03b1f5ecaa8e48e46a93f91 Mon Sep 17 00:00:00 2001
|
|
||||||
From: Rob Crittenden <rcritten@redhat.com>
|
|
||||||
Date: Jan 15 2024 13:42:08 +0000
|
|
||||||
Subject: Server affinity: Retain user-requested remote server
|
|
||||||
|
|
||||||
|
|
||||||
We want to avoid splitting a replica server installation between
|
|
||||||
two hosts where possible so if a CA or KRA is requested then
|
|
||||||
we only try to install against a remote server that also provides
|
|
||||||
those capabilities. This avoids race conditions.
|
|
||||||
|
|
||||||
If a CA or KRA is not requested and the user has provided a
|
|
||||||
server to install against then use that instead of overriding it.
|
|
||||||
|
|
||||||
Extend the logic of picking the remote Custodia mode
|
|
||||||
(KRA, CA, *MASTER*) to include considering whether the
|
|
||||||
CA and KRA services are requested. If the service(s) are
|
|
||||||
not requested the the associated hostname may not be
|
|
||||||
reliable.
|
|
||||||
|
|
||||||
Fixes: https://pagure.io/freeipa/issue/9491
|
|
||||||
Related: https://pagure.io/freeipa/issue/9289
|
|
||||||
|
|
||||||
Signed-off-by: Rob Crittenden <rcritten@redhat.com>
|
|
||||||
Reviewed-By: Florence Blanc-Renaud <flo@redhat.com>
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
diff --git a/ipaserver/install/server/replicainstall.py b/ipaserver/install/server/replicainstall.py
|
|
||||||
index 27fbdef..8096b6a 100644
|
|
||||||
--- a/ipaserver/install/server/replicainstall.py
|
|
||||||
+++ b/ipaserver/install/server/replicainstall.py
|
|
||||||
@@ -782,6 +782,7 @@ def promotion_check_host_principal_auth_ind(conn, hostdn):
|
|
||||||
|
|
||||||
|
|
||||||
def remote_connection(config):
|
|
||||||
+ logger.debug("Creating LDAP connection to %s", config.master_host_name)
|
|
||||||
ldapuri = 'ldaps://%s' % ipautil.format_netloc(config.master_host_name)
|
|
||||||
xmlrpc_uri = 'https://{}/ipa/xml'.format(
|
|
||||||
ipautil.format_netloc(config.master_host_name))
|
|
||||||
@@ -1087,7 +1088,7 @@ def promote_check(installer):
|
|
||||||
'CA', conn, preferred_cas
|
|
||||||
)
|
|
||||||
if ca_host is not None:
|
|
||||||
- if config.master_host_name != ca_host:
|
|
||||||
+ if options.setup_ca and config.master_host_name != ca_host:
|
|
||||||
conn.disconnect()
|
|
||||||
del remote_api
|
|
||||||
config.master_host_name = ca_host
|
|
||||||
@@ -1096,8 +1097,7 @@ def promote_check(installer):
|
|
||||||
conn = remote_api.Backend.ldap2
|
|
||||||
conn.connect(ccache=installer._ccache)
|
|
||||||
config.ca_host_name = ca_host
|
|
||||||
- config.master_host_name = ca_host
|
|
||||||
- ca_enabled = True
|
|
||||||
+ ca_enabled = True # There is a CA somewhere in the topology
|
|
||||||
if options.dirsrv_cert_files:
|
|
||||||
logger.error("Certificates could not be provided when "
|
|
||||||
"CA is present on some master.")
|
|
||||||
@@ -1135,7 +1135,7 @@ def promote_check(installer):
|
|
||||||
'KRA', conn, preferred_kras
|
|
||||||
)
|
|
||||||
if kra_host is not None:
|
|
||||||
- if config.master_host_name != kra_host:
|
|
||||||
+ if options.setup_kra and config.master_host_name != kra_host:
|
|
||||||
conn.disconnect()
|
|
||||||
del remote_api
|
|
||||||
config.master_host_name = kra_host
|
|
||||||
@@ -1143,10 +1143,9 @@ def promote_check(installer):
|
|
||||||
installer._remote_api = remote_api
|
|
||||||
conn = remote_api.Backend.ldap2
|
|
||||||
conn.connect(ccache=installer._ccache)
|
|
||||||
- config.kra_host_name = kra_host
|
|
||||||
- config.ca_host_name = kra_host
|
|
||||||
- config.master_host_name = kra_host
|
|
||||||
- kra_enabled = True
|
|
||||||
+ config.kra_host_name = kra_host
|
|
||||||
+ config.ca_host_name = kra_host
|
|
||||||
+ kra_enabled = True # There is a KRA somewhere in the topology
|
|
||||||
if options.setup_kra and options.server and \
|
|
||||||
kra_host != options.server:
|
|
||||||
# Installer was provided with a specific master
|
|
||||||
@@ -1372,10 +1371,10 @@ def install(installer):
|
|
||||||
otpd.create_instance('OTPD', config.host_name,
|
|
||||||
ipautil.realm_to_suffix(config.realm_name))
|
|
||||||
|
|
||||||
- if kra_enabled:
|
|
||||||
+ if options.setup_kra and kra_enabled:
|
|
||||||
# A KRA peer always provides a CA, too.
|
|
||||||
mode = custodiainstance.CustodiaModes.KRA_PEER
|
|
||||||
- elif ca_enabled:
|
|
||||||
+ elif options.setup_ca and ca_enabled:
|
|
||||||
mode = custodiainstance.CustodiaModes.CA_PEER
|
|
||||||
else:
|
|
||||||
mode = custodiainstance.CustodiaModes.MASTER_PEER
|
|
||||||
|
|
||||||
From 701339d4fed539713eb1a13495992879f56a6daa Mon Sep 17 00:00:00 2001
|
|
||||||
From: Rob Crittenden <rcritten@redhat.com>
|
|
||||||
Date: Jan 18 2024 14:53:28 +0000
|
|
||||||
Subject: Server affinity: Don't rely just on [ca|kra]_enabled for installs
|
|
||||||
|
|
||||||
|
|
||||||
ca_enable and kra_enabled are intended to be used to identify that
|
|
||||||
a CA or KRA is available in the topology. It was also being used
|
|
||||||
to determine whether a CA or KRA service is desired on a replica
|
|
||||||
install, rather than options.setup_[ca|kra]
|
|
||||||
|
|
||||||
Fixes: https://pagure.io/freeipa/issue/9510
|
|
||||||
|
|
||||||
Signed-off-by: Rob Crittenden <rcritten@redhat.com>
|
|
||||||
Reviewed-By: Florence Blanc-Renaud <flo@redhat.com>
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
diff --git a/ipaserver/install/server/replicainstall.py b/ipaserver/install/server/replicainstall.py
|
|
||||||
index 8096b6a..191913d 100644
|
|
||||||
--- a/ipaserver/install/server/replicainstall.py
|
|
||||||
+++ b/ipaserver/install/server/replicainstall.py
|
|
||||||
@@ -1143,7 +1143,8 @@ def promote_check(installer):
|
|
||||||
installer._remote_api = remote_api
|
|
||||||
conn = remote_api.Backend.ldap2
|
|
||||||
conn.connect(ccache=installer._ccache)
|
|
||||||
- config.kra_host_name = kra_host
|
|
||||||
+ config.kra_host_name = kra_host
|
|
||||||
+ if options.setup_kra: # only reset ca_host if KRA is requested
|
|
||||||
config.ca_host_name = kra_host
|
|
||||||
kra_enabled = True # There is a KRA somewhere in the topology
|
|
||||||
if options.setup_kra and options.server and \
|
|
||||||
@@ -1381,7 +1382,7 @@ def install(installer):
|
|
||||||
custodia = custodiainstance.get_custodia_instance(config, mode)
|
|
||||||
custodia.create_instance()
|
|
||||||
|
|
||||||
- if ca_enabled:
|
|
||||||
+ if options.setup_ca and ca_enabled:
|
|
||||||
options.realm_name = config.realm_name
|
|
||||||
options.domain_name = config.domain_name
|
|
||||||
options.host_name = config.host_name
|
|
||||||
@@ -1397,7 +1398,7 @@ def install(installer):
|
|
||||||
service.print_msg("Finalize replication settings")
|
|
||||||
ds.finalize_replica_config()
|
|
||||||
|
|
||||||
- if kra_enabled:
|
|
||||||
+ if options.setup_kra and kra_enabled:
|
|
||||||
kra.install(api, config, options, custodia=custodia)
|
|
||||||
|
|
||||||
service.print_msg("Restarting the KDC")
|
|
||||||
|
|
||||||
From e6014a5c1996528b255480b67fe2937203bff81b Mon Sep 17 00:00:00 2001
|
|
||||||
From: Rob Crittenden <rcritten@redhat.com>
|
|
||||||
Date: Jan 23 2024 15:32:58 +0000
|
|
||||||
Subject: Server affinity: call ca.install() if there is a CA in the topology
|
|
||||||
|
|
||||||
|
|
||||||
This should not have been gated on options.setup_ca because we need
|
|
||||||
the RA agent on all servers if there is a CA in the topology otherwise
|
|
||||||
the non-CA servers won't be able to communicate with the CA.
|
|
||||||
|
|
||||||
Fixes: https://pagure.io/freeipa/issue/9510
|
|
||||||
|
|
||||||
Signed-off-by: Rob Crittenden <rcritten@redhat.com>
|
|
||||||
Reviewed-By: Florence Blanc-Renaud <flo@redhat.com>
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
diff --git a/ipaserver/install/ca.py b/ipaserver/install/ca.py
|
|
||||||
index c93ae1f..187f803 100644
|
|
||||||
--- a/ipaserver/install/ca.py
|
|
||||||
+++ b/ipaserver/install/ca.py
|
|
||||||
@@ -387,9 +387,10 @@ def install_step_0(standalone, replica_config, options, custodia):
|
|
||||||
promote = False
|
|
||||||
else:
|
|
||||||
cafile = os.path.join(replica_config.dir, 'cacert.p12')
|
|
||||||
- custodia.get_ca_keys(
|
|
||||||
- cafile,
|
|
||||||
- replica_config.dirman_password)
|
|
||||||
+ if replica_config.setup_ca:
|
|
||||||
+ custodia.get_ca_keys(
|
|
||||||
+ cafile,
|
|
||||||
+ replica_config.dirman_password)
|
|
||||||
|
|
||||||
ca_signing_algorithm = None
|
|
||||||
ca_type = None
|
|
||||||
diff --git a/ipaserver/install/server/replicainstall.py b/ipaserver/install/server/replicainstall.py
|
|
||||||
index f8d4733..4c1c07c 100644
|
|
||||||
--- a/ipaserver/install/server/replicainstall.py
|
|
||||||
+++ b/ipaserver/install/server/replicainstall.py
|
|
||||||
@@ -1359,11 +1359,13 @@ def install(installer):
|
|
||||||
custodia = custodiainstance.get_custodia_instance(config, mode)
|
|
||||||
custodia.create_instance()
|
|
||||||
|
|
||||||
- if options.setup_ca and ca_enabled:
|
|
||||||
+ if ca_enabled:
|
|
||||||
options.realm_name = config.realm_name
|
|
||||||
options.domain_name = config.domain_name
|
|
||||||
options.host_name = config.host_name
|
|
||||||
options.dm_password = config.dirman_password
|
|
||||||
+ # Always call ca.install() if there is a CA in the topology
|
|
||||||
+ # to ensure the RA agent is present.
|
|
||||||
ca.install(False, config, options, custodia=custodia)
|
|
||||||
|
|
||||||
# configure PKINIT now that all required services are in place
|
|
||||||
@@ -1375,7 +1377,8 @@ def install(installer):
|
|
||||||
service.print_msg("Finalize replication settings")
|
|
||||||
ds.finalize_replica_config()
|
|
||||||
|
|
||||||
- if options.setup_kra and kra_enabled:
|
|
||||||
+ if kra_enabled:
|
|
||||||
+ # The KRA installer checks for itself the status of setup_kra
|
|
||||||
kra.install(api, config, options, custodia=custodia)
|
|
||||||
|
|
||||||
service.print_msg("Restarting the KDC")
|
|
||||||
|
|
@ -0,0 +1,48 @@
|
|||||||
|
From 97fc368df2db3b559a9def236d3c3e0a12bcdd0a Mon Sep 17 00:00:00 2001
|
||||||
|
From: Florence Blanc-Renaud <flo@redhat.com>
|
||||||
|
Date: Mon, 23 Jan 2023 20:28:17 +0100
|
||||||
|
Subject: [PATCH] trust-add: handle missing msSFU30MaxGidNumber
|
||||||
|
|
||||||
|
When ipa trust-add is executed with --range-type ad-trust-posix,
|
||||||
|
the server tries to find the max uidnumber and max gidnumber
|
||||||
|
from AD domain controller.
|
||||||
|
The values are extracted from the entry
|
||||||
|
CN=<domain>,CN=ypservers,CN=ypServ30,CN=RpcServices,CN=System,<AD suffix>
|
||||||
|
in the msSFU30MaxUidNumber and msSFU30MaxGidNumber attributes.
|
||||||
|
|
||||||
|
msSFU30MaxUidNumber is required but not msSFU30MaxGidNumber.
|
||||||
|
In case msSFU30MaxGidNumber is missing, the code is currently assigning
|
||||||
|
a "None" value and later on evaluates the max between this value and
|
||||||
|
msSFU30MaxUidNumber. The max function cannot compare None and a list
|
||||||
|
of string and triggers an exception.
|
||||||
|
|
||||||
|
To avoid the exception, assign [b'0'] to max gid if msSFU30MaxGidNumber
|
||||||
|
is missing. This way, the comparison succeeds and max returns the
|
||||||
|
value from msSFU30MaxUidNumber.
|
||||||
|
|
||||||
|
Fixes: https://pagure.io/freeipa/issue/9310
|
||||||
|
Signed-off-by: Florence Blanc-Renaud <flo@redhat.com>
|
||||||
|
Reviewed-By: Rob Crittenden <rcritten@redhat.com>
|
||||||
|
---
|
||||||
|
ipaserver/plugins/trust.py | 5 ++++-
|
||||||
|
1 file changed, 4 insertions(+), 1 deletion(-)
|
||||||
|
|
||||||
|
diff --git a/ipaserver/plugins/trust.py b/ipaserver/plugins/trust.py
|
||||||
|
index c074f6d6e609476e416c95bcbe607654718ae9ce..79264b8d8a3b15dd4e5d0553e4ce42194b0ae044 100644
|
||||||
|
--- a/ipaserver/plugins/trust.py
|
||||||
|
+++ b/ipaserver/plugins/trust.py
|
||||||
|
@@ -379,7 +379,10 @@ def add_range(myapi, trustinstance, range_name, dom_sid, *keys, **options):
|
||||||
|
range_type = u'ipa-ad-trust-posix'
|
||||||
|
|
||||||
|
max_uid = info.get('msSFU30MaxUidNumber')
|
||||||
|
- max_gid = info.get('msSFU30MaxGidNumber', None)
|
||||||
|
+ # if max_gid is missing, assume 0 and the max will
|
||||||
|
+ # be obtained from max_uid. We just checked that
|
||||||
|
+ # msSFU30MaxUidNumber is defined
|
||||||
|
+ max_gid = info.get('msSFU30MaxGidNumber', [b'0'])
|
||||||
|
max_id = int(max(max_uid, max_gid)[0])
|
||||||
|
|
||||||
|
base_id = int(info.get('msSFU30OrderNumber')[0])
|
||||||
|
--
|
||||||
|
2.39.1
|
||||||
|
|
337
SOURCES/0006-doc-Design-for-certificate-pruning.patch
Normal file
337
SOURCES/0006-doc-Design-for-certificate-pruning.patch
Normal file
@ -0,0 +1,337 @@
|
|||||||
|
From 51b1c22d025bf40e9ef488bb0faf0c8dff303ccd Mon Sep 17 00:00:00 2001
|
||||||
|
From: Rob Crittenden <rcritten@redhat.com>
|
||||||
|
Date: Thu, 8 Dec 2022 16:18:07 -0500
|
||||||
|
Subject: [PATCH] doc: Design for certificate pruning
|
||||||
|
|
||||||
|
This describes how the certificate pruning capability of PKI
|
||||||
|
introduced in v11.3.0 will be integrated into IPA, primarily for
|
||||||
|
ACME.
|
||||||
|
|
||||||
|
Related: https://pagure.io/freeipa/issue/9294
|
||||||
|
|
||||||
|
Signed-off-by: Rob Crittenden <rcritten@redhat.com>
|
||||||
|
Reviewed-By: Florence Blanc-Renaud <frenaud@redhat.com>
|
||||||
|
---
|
||||||
|
doc/designs/expired_certificate_pruning.md | 297 +++++++++++++++++++++
|
||||||
|
doc/designs/index.rst | 1 +
|
||||||
|
2 files changed, 298 insertions(+)
|
||||||
|
create mode 100644 doc/designs/expired_certificate_pruning.md
|
||||||
|
|
||||||
|
diff --git a/doc/designs/expired_certificate_pruning.md b/doc/designs/expired_certificate_pruning.md
|
||||||
|
new file mode 100644
|
||||||
|
index 0000000000000000000000000000000000000000..2c10d914020d3c12b6abb028323cd6796ec33e00
|
||||||
|
--- /dev/null
|
||||||
|
+++ b/doc/designs/expired_certificate_pruning.md
|
||||||
|
@@ -0,0 +1,297 @@
|
||||||
|
+# Expired Certificate Pruning
|
||||||
|
+
|
||||||
|
+## Overview
|
||||||
|
+
|
||||||
|
+https://pagure.io/dogtagpki/issue/1750
|
||||||
|
+
|
||||||
|
+When using short-lived certs and regular issuance, the expired certs can build up in the PKI database and cause issues with replication, performance and overall database size.
|
||||||
|
+
|
||||||
|
+PKI has provided a new feature in 11.3.0, pruning, which is a job that can be executed on a schedule or manually to remove expired certificates and requests.
|
||||||
|
+
|
||||||
|
+Random Serial Numbers v3 (RSNv3) is mandatory to enable pruning.
|
||||||
|
+
|
||||||
|
+Both pruning and RSNv3 require PKI 11.3.0 or higher.
|
||||||
|
+
|
||||||
|
+## Use Cases
|
||||||
|
+
|
||||||
|
+ACME certificates in particular are generally short-lived and expired certificates can build up quickly in a dynamic environment. An example is a CI system that requests one or more certificates per run. These will build up infinitely without a way to remove the expired certificates.
|
||||||
|
+
|
||||||
|
+Another case is simply a very long-lived installation. Over time as hosts come and go certificates build up.
|
||||||
|
+
|
||||||
|
+## How to Use
|
||||||
|
+
|
||||||
|
+https://github.com/dogtagpki/pki/wiki/Configuring-CA-Database-Pruning provides a thorough description of the capabilities of the pruning job.
|
||||||
|
+
|
||||||
|
+The default configuration is to remove expired certificates and incomplete requests after 30 days.
|
||||||
|
+
|
||||||
|
+Pruning is disabled by default.
|
||||||
|
+
|
||||||
|
+Configuration is a four-step process:
|
||||||
|
+
|
||||||
|
+1. Configure the expiration thresholds
|
||||||
|
+2. Enable the job
|
||||||
|
+3. Schedule the job
|
||||||
|
+4. Restart the CA
|
||||||
|
+
|
||||||
|
+The job will be scheduled to use the PKI built-in cron-like timer. It is configured nearly identically to `crontab(5)`. On execution it will remove certificates and requests that fall outside the configured thresholds. LDAP search/time limits can be used to control how many are removed at once.
|
||||||
|
+
|
||||||
|
+In addition to the automated schedule it is possible to manually run the pruning job.
|
||||||
|
+
|
||||||
|
+The tool will not restart the CA. It will be left as an exercise for the user, who will be notified as needed.
|
||||||
|
+
|
||||||
|
+### Where to use
|
||||||
|
+
|
||||||
|
+The pruning configuration is not replicated. It should not be necessary to enable this task on all IPA servers, or more than one.
|
||||||
|
+
|
||||||
|
+Running the task simultaneously on multiple servers has a few downsides:
|
||||||
|
+
|
||||||
|
+* Additional stress on the LDAP server searching for expired certificates and requests
|
||||||
|
+* Unnecessary replication load deleting the same entries on multiple servers
|
||||||
|
+
|
||||||
|
+While enabling this on a single server represents a single-point-of-failure there should be no catastrophic consequences other than expired certificates and requests potentially building up. This can be cleared by enabling pruning on a different server. Depending on the size of the backlog this could take a couple of executions to catch up.
|
||||||
|
+
|
||||||
|
+## Design
|
||||||
|
+
|
||||||
|
+There are several operations, most of which act locally and one of which uses the PKI REST API.
|
||||||
|
+
|
||||||
|
+1. Updating the job configuration (enable, thresholds, etc). This will be done by running the `pki-server ca-config-set` command which modifies CS.cfg directly per the PKI wiki. A restart is required.
|
||||||
|
+
|
||||||
|
+2. Retrieving the current configuration for display. The `pki-server ca-config-find` command returns the entire configuration so the results will need to be filtered.
|
||||||
|
+
|
||||||
|
+3. Managing the job. This can be done using the REST API, https://github.com/dogtagpki/pki/wiki/PKI-REST-API . Operations include enabling the job and triggering it to run now.
|
||||||
|
+
|
||||||
|
+Theoretically for operations 1 and 2 we could use existing code to manually update `CS.cfg` and retrieve values. For future-proofing purposes calling `pki-server` is probably the better long-term option given the limited number of times this will be used. Configuration is likely to be one and done.
|
||||||
|
+
|
||||||
|
+There are four values each that can be managed for pruning certificates and requests:
|
||||||
|
+
|
||||||
|
+* expired cert/incomplete request time
|
||||||
|
+* time unit
|
||||||
|
+* LDAP search size limit
|
||||||
|
+* LDAP search time limit
|
||||||
|
+
|
||||||
|
+The first two configure when an expired certificate or incomplete request will be deleted. The unit can be one of: minute, hour, day, year. By default it is 30 days.
|
||||||
|
+
|
||||||
|
+The LDAP limits control how many entries are returned and how long the search can take. By default it is 1000 entries and unlimited time.
|
||||||
|
+
|
||||||
|
+### Configuration settings
|
||||||
|
+
|
||||||
|
+The configuration values will be set by running `pki-server ca-config-set` This will ensure best forward compatibility. The options are case-sensitive and not validated by the CA until restart. The values are not applied until the CA is restarted.
|
||||||
|
+
|
||||||
|
+### Configuring job execution time
|
||||||
|
+
|
||||||
|
+The CA provides a cron-like interface for scheduling jobs. To configure the job to run at midnight on the first of every month the PKI equivalent command-line is:
|
||||||
|
+
|
||||||
|
+```
|
||||||
|
+pki-server ca-config-set jobsScheduler.job.pruning.cron `"0 0 1 * *"`
|
||||||
|
+```
|
||||||
|
+
|
||||||
|
+This will be the default when pruning is enabled. A separate configuration option will be available for fine-tuning execution time.
|
||||||
|
+
|
||||||
|
+The format is defined https://access.redhat.com/documentation/en-us/red_hat_certificate_system/9/html/administration_guide/setting_up_specific_jobs#Frequency_Settings_for_Automated_Jobs
|
||||||
|
+
|
||||||
|
+### REST Authentication and Authorization
|
||||||
|
+
|
||||||
|
+The REST API for pruning is documented at https://github.com/dogtagpki/pki/wiki/PKI-Start-Job-REST-API
|
||||||
|
+
|
||||||
|
+A PKI job can define an owner that can manage the job over the REST API. We will automatically define the owner as `ipara` when pruning is enabled.
|
||||||
|
+
|
||||||
|
+Manually running the job will be done using the PKI REST API. Authentication to this API for our purposes is done at the `/ca/rest/account/login` endpoint. A cookie is returned which will be used in any subsequent calls. The IPA RA agent certificate will be used for authentication and authorization.
|
||||||
|
+
|
||||||
|
+### Commands
|
||||||
|
+
|
||||||
|
+This will be implemented in the ipa-acme-manage command. While strictly not completely ACME-related this is the primary driver for pruning.
|
||||||
|
+
|
||||||
|
+A new verb will be added, pruning, to be used for enabling and configuring pruning.
|
||||||
|
+
|
||||||
|
+### Enabling pruning
|
||||||
|
+
|
||||||
|
+`# ipa-acme-manage pruning --enable=TRUE`
|
||||||
|
+
|
||||||
|
+Enabling the job will call
|
||||||
|
+
|
||||||
|
+`# pki-server ca-config-set jobsScheduler.job.pruning.enabled true`
|
||||||
|
+
|
||||||
|
+This will also set jobsScheduler.job.pruning.cron to `"0 0 1 * *"` if it has not already been set.
|
||||||
|
+
|
||||||
|
+Additionally it will set the job owner to `ipara` with:
|
||||||
|
+
|
||||||
|
+`# pki-server ca-config-set jobsScheduler.job.pruning.owner ipara`
|
||||||
|
+
|
||||||
|
+Disabling the job will call
|
||||||
|
+
|
||||||
|
+`# pki-server ca-config-unset jobsScheduler.job.pruning.enabled`
|
||||||
|
+
|
||||||
|
+### Cron settings
|
||||||
|
+
|
||||||
|
+To modify the cron settings:
|
||||||
|
+
|
||||||
|
+`# ipa-acme-manage pruning --cron="Minute Hour Day_of_month Month_of_year Day_of_week"`
|
||||||
|
+
|
||||||
|
+Validation of the value will be:
|
||||||
|
+* each of the options is an integer
|
||||||
|
+* minute is within 0-59
|
||||||
|
+* hour is within 0-23
|
||||||
|
+* day of month is within 0-31
|
||||||
|
+* month of year is within 1-12
|
||||||
|
+* day of week is within 0-6
|
||||||
|
+
|
||||||
|
+No validation of setting February 31st will be done. That will be left to PKI. Buyer beware.
|
||||||
|
+
|
||||||
|
+### Disabling pruning
|
||||||
|
+
|
||||||
|
+`$ ipa-acme-manage pruning --enable=FALSE`
|
||||||
|
+
|
||||||
|
+This will remove the configuration option for `jobsScheduler.job.pruning.cron` just to be sure it no longer runs.
|
||||||
|
+
|
||||||
|
+### Configuration
|
||||||
|
+
|
||||||
|
+#### Pruning certificates
|
||||||
|
+
|
||||||
|
+`$ ipa-acme-manage pruning --certretention=VALUE --certretentionunit=UNIT`
|
||||||
|
+
|
||||||
|
+will be the equivalent of:
|
||||||
|
+
|
||||||
|
+`$ pki-server ca-config-set jobsScheduler.job.pruning.certRetentionTime 30`
|
||||||
|
+
|
||||||
|
+`$ pki-server ca-config-set jobsScheduler.job.pruning.certRetentionUnit day`
|
||||||
|
+
|
||||||
|
+The unit will always be required when modifying the time.
|
||||||
|
+
|
||||||
|
+`$ ipa-acme-manage pruning --certsearchsizelimit=VALUE --certsearchtimelimit=VALUE`
|
||||||
|
+
|
||||||
|
+will be the equivalent of:
|
||||||
|
+
|
||||||
|
+`$ pki-server ca-config-set jobsScheduler.job.pruning.certSearchSizeLimit 1000`
|
||||||
|
+
|
||||||
|
+`$ pki-server ca-config-set jobsScheduler.job.pruning.certSearchTimeLimit 0`
|
||||||
|
+
|
||||||
|
+A value of 0 for searchtimelimit is unlimited.
|
||||||
|
+
|
||||||
|
+#### Pruning requests
|
||||||
|
+
|
||||||
|
+`$ ipa-acme-manage pruning --requestretention=VALUE --requestretentionunit=UNIT`
|
||||||
|
+
|
||||||
|
+will be the equivalent of:
|
||||||
|
+
|
||||||
|
+`$ pki-server ca-config-set jobsScheduler.job.pruning.requestRetentionTime 30`
|
||||||
|
+
|
||||||
|
+`$ pki-server ca-config-set jobsScheduler.job.pruning.requestRetentionUnit day`
|
||||||
|
+
|
||||||
|
+The unit will always be required when modifying the time.
|
||||||
|
+
|
||||||
|
+`$ ipa-acme-manage pruning --requestsearchsizelimit=VALUE --requestsearchtimelimit=VALUE`
|
||||||
|
+
|
||||||
|
+
|
||||||
|
+will be the equivalent of:
|
||||||
|
+
|
||||||
|
+`$ pki-server ca-config-set jobsScheduler.job.pruning.requestSearchSizeLimit 1000`
|
||||||
|
+
|
||||||
|
+`$ pki-server ca-config-set jobsScheduler.job.pruning.requestSearchTimeLimit 0`
|
||||||
|
+
|
||||||
|
+A value of 0 for searchtimelimit is unlimited.
|
||||||
|
+
|
||||||
|
+These options set the client-side limits. The server imposes its own search size and look through limits. This can be tuned for the uid=pkidbuser,ou=people,o=ipaca user via https://access.redhat.com/documentation/en-us/red_hat_directory_server/11/html/administration_guide/ldapsearch-ex-complex-range
|
||||||
|
+
|
||||||
|
+### Showing the Configuration
|
||||||
|
+
|
||||||
|
+To display the current configuration run `pki-server ca-config-find` and filter the results to only those that contain `jobsScheduler.job.pruning`.
|
||||||
|
+
|
||||||
|
+Default values are not included so will need to be set by `ipa-acme-manage` before displaying.
|
||||||
|
+
|
||||||
|
+Output may look something like:
|
||||||
|
+
|
||||||
|
+```console
|
||||||
|
+# ipa-acme-manage pruning --config-show
|
||||||
|
+Enabled: TRUE
|
||||||
|
+Certificate retention time: 30 days
|
||||||
|
+Certificate search size limit: 1000
|
||||||
|
+Certificate search time limit: 0
|
||||||
|
+Request retention time: 30 days
|
||||||
|
+Request search size limit: 1000
|
||||||
|
+Request search time limit: 0
|
||||||
|
+Cron: 0 0 1 * *
|
||||||
|
+```
|
||||||
|
+
|
||||||
|
+## Implementation
|
||||||
|
+
|
||||||
|
+For online REST operations (login, run job) we will use the `ipaserver/plugins/dogtag.py::RestClient` class to manage the requests. This will take care of the authentication cookie, etc.
|
||||||
|
+
|
||||||
|
+The class uses dogtag.https_request() will can take PEM cert and key files as arguments. These will be used for authentication.
|
||||||
|
+
|
||||||
|
+For the non-REST operations (configuration, cron settings) the tool will fork out to pki-server ca-config-set.
|
||||||
|
+
|
||||||
|
+### UI
|
||||||
|
+
|
||||||
|
+This will only be configurable on the command-line.
|
||||||
|
+
|
||||||
|
+### CLI
|
||||||
|
+
|
||||||
|
+Overview of the CLI commands. Example:
|
||||||
|
+
|
||||||
|
+
|
||||||
|
+| Command | Options |
|
||||||
|
+| --- | ----- |
|
||||||
|
+| ipa-acme-manage pruning | --enable=TRUE |
|
||||||
|
+| ipa-acme-manage pruning | --enable=FALSE |
|
||||||
|
+| ipa-acme-manage pruning | --cron=`"0 0 1 * *"` |
|
||||||
|
+| ipa-acme-manage pruning | --certretention=30 --certretentionunit=day |
|
||||||
|
+| ipa-acme-manage pruning | --certsearchsizelimit=1000 --certsearchtimelimit=0 |
|
||||||
|
+| ipa-acme-manage pruning | --requestretention=30 --requestretentionunit=day |
|
||||||
|
+| ipa-acme-manage pruning | --requestsearchsizelimit=1000 --requestsearchtimelimit=0 |
|
||||||
|
+| ipa-acme-manage pruning | --config-show |
|
||||||
|
+
|
||||||
|
+ipa-acme-manage can only be run as root.
|
||||||
|
+
|
||||||
|
+### Configuration
|
||||||
|
+
|
||||||
|
+Configuration changes will be made to /etc/pki/pki-tomcat/ca/CS.cfg
|
||||||
|
+
|
||||||
|
+## Upgrade
|
||||||
|
+
|
||||||
|
+No expected impact on upgrades.
|
||||||
|
+
|
||||||
|
+## Test plan
|
||||||
|
+
|
||||||
|
+Testing will consist of:
|
||||||
|
+
|
||||||
|
+* Use the default configuration
|
||||||
|
+* enabling the pruning job
|
||||||
|
+* issue one or more certificates
|
||||||
|
+* move time forward +1 days after expiration
|
||||||
|
+* manually running the job
|
||||||
|
+* validating that the certificates are removed
|
||||||
|
+
|
||||||
|
+For size/time limit testing, create a large number of certificates/requests and set the search limit to a low value, then ensure that the number of deleted certs is equal to the search limit. Testing timelimit in this way may be less predictable as it may require a massive number of entries to find to timeout on a non-busy server.
|
||||||
|
+
|
||||||
|
+## Troubleshooting and debugging
|
||||||
|
+
|
||||||
|
+The PKI debug log will contain job information.
|
||||||
|
+
|
||||||
|
+```
|
||||||
|
+2022-12-08 21:14:25 [https-jsse-nio-8443-exec-8] INFO: JobService: Starting job pruning
|
||||||
|
+2022-12-08 21:14:25 [https-jsse-nio-8443-exec-8] INFO: JobService: - principal: null
|
||||||
|
+2022-12-08 21:14:51 [https-jsse-nio-8443-exec-10] INFO: JobService: Starting job pruning 2022-12-08 21:14:51 [https-jsse-nio-8443-exec-10] INFO: JobService: - principal: null
|
||||||
|
+2022-12-08 21:15:11 [https-jsse-nio-8443-exec-11] INFO: PKIRealm: Authenticating certificate chain:
|
||||||
|
+2022-12-08 21:15:11 [https-jsse-nio-8443-exec-11] INFO: PKIRealm: - CN=IPA RA,O=EXAMPLE.TEST
|
||||||
|
+2022-12-08 21:15:11 [https-jsse-nio-8443-exec-11] INFO: PKIRealm: - CN=Certificate Authority,O=EXAMPLE.TEST
|
||||||
|
+2022-12-08 21:15:11 [https-jsse-nio-8443-exec-11] INFO: LDAPSession: Retrieving cn=19072098145751813471503860299601579276,ou=certificateRepository, ou=ca,o=ipaca
|
||||||
|
+2022-12-08 21:15:11 [https-jsse-nio-8443-exec-11] INFO: CertUserDBAuthentication: UID ipara authenticated.
|
||||||
|
+2022-12-08 21:15:11 [https-jsse-nio-8443-exec-11] INFO: PKIRealm: User ipara authenticated
|
||||||
|
+2022-12-08 21:15:11 [https-jsse-nio-8443-exec-11] INFO: UGSubsystem: Retrieving user uid=ipara,ou=People,o=ipaca
|
||||||
|
+2022-12-08 21:15:11 [https-jsse-nio-8443-exec-11] INFO: PKIRealm: User DN: uid=ipara,ou=people,o=ipaca
|
||||||
|
+2022-12-08 21:15:11 [https-jsse-nio-8443-exec-11] INFO: PKIRealm: Roles:
|
||||||
|
+2022-12-08 21:15:11 [https-jsse-nio-8443-exec-11] INFO: PKIRealm: - Certificate Manager Agents
|
||||||
|
+2022-12-08 21:15:11 [https-jsse-nio-8443-exec-11] INFO: PKIRealm: - Registration Manager Agents
|
||||||
|
+2022-12-08 21:15:11 [https-jsse-nio-8443-exec-11] INFO: PKIRealm: - Security Domain Administrators
|
||||||
|
+2022-12-08 21:15:11 [https-jsse-nio-8443-exec-11] INFO: PKIRealm: - Enterprise ACME Administrators
|
||||||
|
+2022-12-08 21:15:24 [https-jsse-nio-8443-exec-12] INFO: JobService: Starting job pruning
|
||||||
|
+2022-12-08 21:15:24 [https-jsse-nio-8443-exec-12] INFO: JobService: - principal: GenericPrincipal[ipara(Certificate Manager Agents,Enterprise ACME Administrators,Registration Manager Agents,Security Domain Administrators,)]
|
||||||
|
+2022-12-08 21:15:24 [https-jsse-nio-8443-exec-12] INFO: JobsScheduler: Starting job pruning
|
||||||
|
+2022-12-08 21:15:24 [pruning] INFO: PruningJob: Running pruning job at Thu Dec 08 21:15:24 UTC 2022
|
||||||
|
+2022-12-08 21:15:24 [pruning] INFO: PruningJob: Pruning certs expired before Tue Nov 08 21:15:24 UTC 2022
|
||||||
|
+2022-12-08 21:15:24 [pruning] INFO: PruningJob: - filter: (&(x509Cert.notAfter<=1667942124527)(!(x509Cert.notAfter=1667942124527)))
|
||||||
|
+2022-12-08 21:15:24 [pruning] INFO: LDAPSession: Searching ou=certificateRepository, ou=ca,o=ipaca for (&(notAfter<=20221108211524Z)(!(notAfter=20221108211524Z)))
|
||||||
|
+2022-12-08 21:15:24 [pruning] INFO: PruningJob: Pruning incomplete requests last modified before Tue Nov 08 21:15:24 UTC 2022
|
||||||
|
+2022-12-08 21:15:24 [pruning] INFO: PruningJob: - filter: (&(!(requestState=complete))(requestModifyTime<=1667942124527)(!(requestModifyTime=1667942124527)))
|
||||||
|
+2022-12-08 21:15:24 [pruning] INFO: LDAPSession: Searching ou=ca, ou=requests,o=ipaca for (&(!(requestState=complete))(dateOfModify<=20221108211524Z)(!(dateOfModify=20221108211524Z)))
|
||||||
|
+```
|
||||||
|
diff --git a/doc/designs/index.rst b/doc/designs/index.rst
|
||||||
|
index 570e526fe35d510feeac62a44dd59224289e0506..1d41c0f84f0d7d3d5f184a47e31b4e71a890805d 100644
|
||||||
|
--- a/doc/designs/index.rst
|
||||||
|
+++ b/doc/designs/index.rst
|
||||||
|
@@ -14,6 +14,7 @@ FreeIPA design documentation
|
||||||
|
hsm.md
|
||||||
|
krb-ticket-policy.md
|
||||||
|
extdom-plugin-protocol.md
|
||||||
|
+ expired_certificate_pruning.md
|
||||||
|
expiring-password-notification.md
|
||||||
|
ldap_grace_period.md
|
||||||
|
ldap_pam_passthrough.md
|
||||||
|
--
|
||||||
|
2.39.1
|
||||||
|
|
@ -1,97 +0,0 @@
|
|||||||
From 3842116185de6ae8714f30b57bd75c7eddde53d8 Mon Sep 17 00:00:00 2001
|
|
||||||
From: Alexander Bokovoy <abokovoy@redhat.com>
|
|
||||||
Date: Jan 15 2024 13:50:10 +0000
|
|
||||||
Subject: host: update System: Manage Host Keytab permission
|
|
||||||
|
|
||||||
|
|
||||||
Since commit 5c0e7a5fb420377dcc06a956695afdcb35196444, a new extended
|
|
||||||
operation to get a keytab is supposed to be used. This keytab
|
|
||||||
setting/retrieval extended operation checks access rights of the bound
|
|
||||||
DN to write to a virtual attribute 'ipaProtectedOperation;write_keys'.
|
|
||||||
|
|
||||||
If the write isn't allowed, the operation is rejected and ipa-getkeytab
|
|
||||||
tool falls back to an older code that generates the keytab on the client
|
|
||||||
and forcibly sets to the LDAP entry. For the latter, a check is done to
|
|
||||||
make sure the bound DN is allowed to write to 'krbPrincipalKey' attribute.
|
|
||||||
|
|
||||||
This fallback should never happen for newer deployments. When enrollemnt
|
|
||||||
operation is delegated to non-administrative user with the help of 'Host
|
|
||||||
Enrollment' role, a host can be pre-created or created at enrollment
|
|
||||||
time, if this non-administrative user has 'Host Administrators' role. In
|
|
||||||
the latter case a system permission 'System: Manage Host Keytab' grants
|
|
||||||
write access to 'krbPrincipalKey' attribute but lacks any access to the
|
|
||||||
virtual attributes expected by the new extended operation.
|
|
||||||
|
|
||||||
There is a second virtual attribute, 'ipaProtectedOperation;read_keys',
|
|
||||||
that allows to retrieve existing keys for a host. However, during
|
|
||||||
initial enrollment we do not allow to retrieve and reuse existing
|
|
||||||
Kerberos key: while 'ipa-getkeytab -r' would give ability to retrieve
|
|
||||||
the existing key, 'ipa-join' has no way to trigger that operation.
|
|
||||||
Hence, permission 'System: Manage Host Keytab' will not grant the right
|
|
||||||
to read the Kerberos key via extended operation used by 'ipa-getkeytab
|
|
||||||
-r'. Such operation can be done later by utilizing 'ipa
|
|
||||||
service/host-allow-retrieve-keytab' commands.
|
|
||||||
|
|
||||||
Fix 'System: Manage Host Keytab' permission and extend a permission test
|
|
||||||
to see that we do not fallback to the old extended operation.
|
|
||||||
|
|
||||||
Fixes: https://pagure.io/freeipa/issue/9496
|
|
||||||
|
|
||||||
Signed-off-by: Alexander Bokovoy <abokovoy@redhat.com>
|
|
||||||
Reviewed-By: Rob Crittenden <rcritten@redhat.com>
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
diff --git a/ACI.txt b/ACI.txt
|
|
||||||
index e6d6e3d..236bb43 100644
|
|
||||||
--- a/ACI.txt
|
|
||||||
+++ b/ACI.txt
|
|
||||||
@@ -147,7 +147,7 @@ aci: (targetattr = "usercertificate")(targetfilter = "(objectclass=ipahost)")(ve
|
|
||||||
dn: cn=computers,cn=accounts,dc=ipa,dc=example
|
|
||||||
aci: (targetattr = "userpassword")(targetfilter = "(objectclass=ipahost)")(version 3.0;acl "permission:System: Manage Host Enrollment Password";allow (write) groupdn = "ldap:///cn=System: Manage Host Enrollment Password,cn=permissions,cn=pbac,dc=ipa,dc=example";)
|
|
||||||
dn: cn=computers,cn=accounts,dc=ipa,dc=example
|
|
||||||
-aci: (targetattr = "krblastpwdchange || krbprincipalkey")(targetfilter = "(&(!(memberOf=cn=ipaservers,cn=hostgroups,cn=accounts,dc=ipa,dc=example))(objectclass=ipahost))")(version 3.0;acl "permission:System: Manage Host Keytab";allow (write) groupdn = "ldap:///cn=System: Manage Host Keytab,cn=permissions,cn=pbac,dc=ipa,dc=example";)
|
|
||||||
+aci: (targetattr = "ipaprotectedoperation;write_keys || krblastpwdchange || krbprincipalkey")(targetfilter = "(&(!(memberOf=cn=ipaservers,cn=hostgroups,cn=accounts,dc=ipa,dc=example))(objectclass=ipahost))")(version 3.0;acl "permission:System: Manage Host Keytab";allow (write) groupdn = "ldap:///cn=System: Manage Host Keytab,cn=permissions,cn=pbac,dc=ipa,dc=example";)
|
|
||||||
dn: cn=computers,cn=accounts,dc=ipa,dc=example
|
|
||||||
aci: (targetattr = "createtimestamp || entryusn || ipaallowedtoperform;read_keys || ipaallowedtoperform;write_keys || modifytimestamp || objectclass")(targetfilter = "(objectclass=ipahost)")(version 3.0;acl "permission:System: Manage Host Keytab Permissions";allow (compare,read,search,write) groupdn = "ldap:///cn=System: Manage Host Keytab Permissions,cn=permissions,cn=pbac,dc=ipa,dc=example";)
|
|
||||||
dn: cn=computers,cn=accounts,dc=ipa,dc=example
|
|
||||||
diff --git a/ipaserver/plugins/host.py b/ipaserver/plugins/host.py
|
|
||||||
index 3ef510e..b02c8b5 100644
|
|
||||||
--- a/ipaserver/plugins/host.py
|
|
||||||
+++ b/ipaserver/plugins/host.py
|
|
||||||
@@ -409,7 +409,8 @@ class host(LDAPObject):
|
|
||||||
api.env.container_hostgroup,
|
|
||||||
api.env.basedn),
|
|
||||||
],
|
|
||||||
- 'ipapermdefaultattr': {'krblastpwdchange', 'krbprincipalkey'},
|
|
||||||
+ 'ipapermdefaultattr': {'krblastpwdchange', 'krbprincipalkey',
|
|
||||||
+ 'ipaprotectedoperation;write_keys'},
|
|
||||||
'replaces': [
|
|
||||||
'(targetattr = "krbprincipalkey || krblastpwdchange")(target = "ldap:///fqdn=*,cn=computers,cn=accounts,$SUFFIX")(version 3.0;acl "permission:Manage host keytab";allow (write) groupdn = "ldap:///cn=Manage host keytab,cn=permissions,cn=pbac,$SUFFIX";)',
|
|
||||||
],
|
|
||||||
diff --git a/ipatests/test_integration/test_user_permissions.py b/ipatests/test_integration/test_user_permissions.py
|
|
||||||
index 3333a4f..cd1096f 100644
|
|
||||||
--- a/ipatests/test_integration/test_user_permissions.py
|
|
||||||
+++ b/ipatests/test_integration/test_user_permissions.py
|
|
||||||
@@ -277,6 +277,9 @@ class TestInstallClientNoAdmin(IntegrationTest):
|
|
||||||
self.master.run_command(['ipa', 'privilege-add-permission',
|
|
||||||
'--permissions', 'System: Add Hosts',
|
|
||||||
'Add Hosts'])
|
|
||||||
+ self.master.run_command(['ipa', 'privilege-add-permission',
|
|
||||||
+ '--permissions', 'System: Manage Host Keytab',
|
|
||||||
+ 'Add Hosts'])
|
|
||||||
|
|
||||||
self.master.run_command(['ipa', 'role-add-privilege', 'useradmin',
|
|
||||||
'--privileges', 'Host Enrollment'])
|
|
||||||
@@ -301,6 +304,10 @@ class TestInstallClientNoAdmin(IntegrationTest):
|
|
||||||
encoding='utf-8')
|
|
||||||
assert msg in install_log
|
|
||||||
|
|
||||||
+ # Make sure we do not fallback to an old keytab retrieval method anymore
|
|
||||||
+ msg = "Retrying with pre-4.0 keytab retrieval method..."
|
|
||||||
+ assert msg not in install_log
|
|
||||||
+
|
|
||||||
# check that user is able to request a host cert, too
|
|
||||||
result = tasks.run_certutil(client, ['-L'], paths.IPA_NSSDB_DIR)
|
|
||||||
assert 'Local IPA host' in result.stdout_text
|
|
||||||
|
|
@ -1,32 +0,0 @@
|
|||||||
From 2f17319df6147832dceff7c06154363f8d58b194 Mon Sep 17 00:00:00 2001
|
|
||||||
From: Alexander Bokovoy <abokovoy@redhat.com>
|
|
||||||
Date: Jan 18 2024 09:07:31 +0000
|
|
||||||
Subject: adtrustinstance: make sure NetBIOS name defaults are set properly
|
|
||||||
|
|
||||||
|
|
||||||
Some tools may pass None as NetBIOS name if not put explicitly by a
|
|
||||||
user. This meant to use default NetBIOS name generator based on the
|
|
||||||
domain (realm) name. However, this wasn't done properly, so None is
|
|
||||||
passed later to python-ldap and it rejects such LDAP entry.
|
|
||||||
|
|
||||||
Fixes: https://pagure.io/freeipa/issue/9514
|
|
||||||
|
|
||||||
Signed-off-by: Alexander Bokovoy <abokovoy@redhat.com>
|
|
||||||
Reviewed-By: Florence Blanc-Renaud <flo@redhat.com>
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
diff --git a/ipaserver/install/adtrustinstance.py b/ipaserver/install/adtrustinstance.py
|
|
||||||
index bf0cc3b..bb5b61a 100644
|
|
||||||
--- a/ipaserver/install/adtrustinstance.py
|
|
||||||
+++ b/ipaserver/install/adtrustinstance.py
|
|
||||||
@@ -189,6 +189,8 @@ class ADTRUSTInstance(service.Service):
|
|
||||||
self.fqdn = self.fqdn or api.env.host
|
|
||||||
self.host_netbios_name = make_netbios_name(self.fqdn)
|
|
||||||
self.realm = self.realm or api.env.realm
|
|
||||||
+ if not self.netbios_name:
|
|
||||||
+ self.netbios_name = make_netbios_name(self.realm)
|
|
||||||
|
|
||||||
self.suffix = ipautil.realm_to_suffix(self.realm)
|
|
||||||
self.ldapi_socket = "%%2fvar%%2frun%%2fslapd-%s.socket" % \
|
|
||||||
|
|
@ -0,0 +1,684 @@
|
|||||||
|
From 9246a8a003b2b0062e07c289cd7cde8fe902b16f Mon Sep 17 00:00:00 2001
|
||||||
|
From: Rob Crittenden <rcritten@redhat.com>
|
||||||
|
Date: Thu, 12 Jan 2023 15:06:27 -0500
|
||||||
|
Subject: [PATCH] ipa-acme-manage: add certificate/request pruning management
|
||||||
|
|
||||||
|
Configures PKI to remove expired certificates and non-resolved
|
||||||
|
requests on a schedule.
|
||||||
|
|
||||||
|
This is geared towards ACME which can generate a lot of certificates
|
||||||
|
over a short period of time but is general purpose. It lives in
|
||||||
|
ipa-acme-manage because that is the primary reason for including it.
|
||||||
|
|
||||||
|
Random Serial Numbers v3 must be enabled for this to work.
|
||||||
|
|
||||||
|
Enabling pruning enables the job scheduler within CS and sets the
|
||||||
|
job user as the IPA RA user which has full rights to certificates
|
||||||
|
and requests.
|
||||||
|
|
||||||
|
Disabling pruning does not disable the job scheduler because the
|
||||||
|
tool is stateless. Having the scheduler enabled should not be a
|
||||||
|
problem.
|
||||||
|
|
||||||
|
A restart of PKI is required to apply any changes. This tool forks
|
||||||
|
out to pki-server which does direct writes to CS.cfg. It might
|
||||||
|
be easier to use our own tooling for this but this makes the
|
||||||
|
integration tighter so we pick up any improvements in PKI.
|
||||||
|
|
||||||
|
The "cron" setting is quite limited, taking only integer values
|
||||||
|
and *. It does not accept ranges, either - or /.
|
||||||
|
|
||||||
|
No error checking is done in PKI when setting a value, only when
|
||||||
|
attempting to use it, so some rudimentary validation is done.
|
||||||
|
|
||||||
|
Fixes: https://pagure.io/freeipa/issue/9294
|
||||||
|
|
||||||
|
Signed-off-by: Rob Crittenden rcritten@redhat.com
|
||||||
|
Reviewed-By: Florence Blanc-Renaud <flo@redhat.com>
|
||||||
|
---
|
||||||
|
install/tools/man/ipa-acme-manage.1 | 83 +++++++
|
||||||
|
ipaserver/install/ipa_acme_manage.py | 303 ++++++++++++++++++++++++-
|
||||||
|
ipatests/test_integration/test_acme.py | 158 +++++++++++++
|
||||||
|
3 files changed, 534 insertions(+), 10 deletions(-)
|
||||||
|
|
||||||
|
diff --git a/install/tools/man/ipa-acme-manage.1 b/install/tools/man/ipa-acme-manage.1
|
||||||
|
index e15d25bd0017d8bd71e425fcb633827fa6f67693..e6cec4e4a7fd460c514a72456a2dc9a2e3682ebd 100644
|
||||||
|
--- a/install/tools/man/ipa-acme-manage.1
|
||||||
|
+++ b/install/tools/man/ipa-acme-manage.1
|
||||||
|
@@ -27,6 +27,89 @@ Disable the ACME service on this host.
|
||||||
|
.TP
|
||||||
|
\fBstatus\fR
|
||||||
|
Display the status of the ACME service.
|
||||||
|
+.TP
|
||||||
|
+\fBpruning\fR
|
||||||
|
+Configure certificate and request pruning.
|
||||||
|
+
|
||||||
|
+.SH "PRUNING"
|
||||||
|
+Pruning is a job that runs in the CA that can remove expired
|
||||||
|
+certificates and certificate requests which have not been issued.
|
||||||
|
+This is particularly important when using short-lived certificates
|
||||||
|
+like those issued with the ACME protocol. Pruning requires that
|
||||||
|
+the IPA server be installed with random serial numbers enabled.
|
||||||
|
+
|
||||||
|
+The CA needs to be restarted after modifying the pruning configuration.
|
||||||
|
+
|
||||||
|
+The job is a cron-like task within the CA that is controlled by a
|
||||||
|
+number of options which dictate how long after the certificate or
|
||||||
|
+request is considered no longer valid and removed from the LDAP
|
||||||
|
+database.
|
||||||
|
+
|
||||||
|
+The cron time and date fields are:
|
||||||
|
+.IP
|
||||||
|
+.ta 1.5i
|
||||||
|
+field allowed values
|
||||||
|
+.br
|
||||||
|
+----- --------------
|
||||||
|
+.br
|
||||||
|
+minute 0-59
|
||||||
|
+.br
|
||||||
|
+hour 0-23
|
||||||
|
+.br
|
||||||
|
+day of month 1-31
|
||||||
|
+.br
|
||||||
|
+month 1-12
|
||||||
|
+.br
|
||||||
|
+day of week 0-6 (0 is Sunday)
|
||||||
|
+.br
|
||||||
|
+.PP
|
||||||
|
+
|
||||||
|
+The cron syntax is limited to * or specific numbers. Ranges are not supported.
|
||||||
|
+
|
||||||
|
+.TP
|
||||||
|
+\fB\-\-enable\fR
|
||||||
|
+Enable certificate pruning.
|
||||||
|
+.TP
|
||||||
|
+\fB\-\-disable\fR
|
||||||
|
+Disable certificate pruning.
|
||||||
|
+.TP
|
||||||
|
+\fB\-\-cron=CRON\fR
|
||||||
|
+Configure the pruning cron job. The syntax is similar to crontab(5) syntax.
|
||||||
|
+For example, "0 0 1 * *" schedules the job to run at 12:00am on the first
|
||||||
|
+day of each month.
|
||||||
|
+.TP
|
||||||
|
+\fB\-\-certretention=CERTRETENTION\fR
|
||||||
|
+Certificate retention time. The default is 30.
|
||||||
|
+.TP
|
||||||
|
+\fB\-\-certretentionunit=CERTRETENTIONUNIT\fR
|
||||||
|
+Certificate retention units. Valid units are: minute, hour, day, year.
|
||||||
|
+The default is days.
|
||||||
|
+.TP
|
||||||
|
+\fB\-\-certsearchsizelimit=CERTSEARCHSIZELIMIT\fR
|
||||||
|
+LDAP search size limit searching for expired certificates. The default is 1000. This is a client-side limit. There may be additional server-side limitations.
|
||||||
|
+.TP
|
||||||
|
+\fB\-\-certsearchtimelimit=CERTSEARCHTIMELIMIT\fR
|
||||||
|
+LDAP search time limit searching for expired certificates. The default is 0, no limit. This is a client-side limit. There may be additional server-side limitations.
|
||||||
|
+.TP
|
||||||
|
+\fB\-\-requestretention=REQUESTRETENTION\fR
|
||||||
|
+Request retention time. The default is 30.
|
||||||
|
+.TP
|
||||||
|
+\fB\-\-requestretentionunit=REQUESTRETENTIONUNIT\fR
|
||||||
|
+Request retention units. Valid units are: minute, hour, day, year.
|
||||||
|
+The default is days.
|
||||||
|
+.TP
|
||||||
|
+\fB\-\-requestsearchsizelimit=REQUESTSEARCHSIZELIMIT\fR
|
||||||
|
+LDAP search size limit searching for unfulfilled requests. The default is 1000. There may be additional server-side limitations.
|
||||||
|
+.TP
|
||||||
|
+\fB\-\-requestsearchtimelimit=REQUESTSEARCHTIMELIMIT\fR
|
||||||
|
+LDAP search time limit searching for unfulfilled requests. The default is 0, no limit. There may be additional server-side limitations.
|
||||||
|
+.TP
|
||||||
|
+\fB\-\-config\-show\fR
|
||||||
|
+Show the current pruning configuration
|
||||||
|
+.TP
|
||||||
|
+\fB\-\-run\fR
|
||||||
|
+Run the pruning job now. The IPA RA certificate is used to authenticate to the PKI REST backend.
|
||||||
|
+
|
||||||
|
|
||||||
|
.SH "EXIT STATUS"
|
||||||
|
0 if the command was successful
|
||||||
|
diff --git a/ipaserver/install/ipa_acme_manage.py b/ipaserver/install/ipa_acme_manage.py
|
||||||
|
index 0474b9f4a051063ac6df41a81877a2af9d4a2096..b7b2111d9edcec2580aa4a485d7a7340146ff065 100644
|
||||||
|
--- a/ipaserver/install/ipa_acme_manage.py
|
||||||
|
+++ b/ipaserver/install/ipa_acme_manage.py
|
||||||
|
@@ -2,7 +2,12 @@
|
||||||
|
# Copyright (C) 2020 FreeIPA Contributors see COPYING for license
|
||||||
|
#
|
||||||
|
|
||||||
|
+
|
||||||
|
import enum
|
||||||
|
+import pki.util
|
||||||
|
+import logging
|
||||||
|
+
|
||||||
|
+from optparse import OptionGroup # pylint: disable=deprecated-module
|
||||||
|
|
||||||
|
from ipalib import api, errors, x509
|
||||||
|
from ipalib import _
|
||||||
|
@@ -10,10 +15,64 @@ from ipalib.facts import is_ipa_configured
|
||||||
|
from ipaplatform.paths import paths
|
||||||
|
from ipapython.admintool import AdminTool
|
||||||
|
from ipapython import cookie, dogtag
|
||||||
|
+from ipapython.ipautil import run
|
||||||
|
+from ipapython.certdb import NSSDatabase, EXTERNAL_CA_TRUST_FLAGS
|
||||||
|
from ipaserver.install import cainstance
|
||||||
|
+from ipaserver.install.ca import lookup_random_serial_number_version
|
||||||
|
|
||||||
|
from ipaserver.plugins.dogtag import RestClient
|
||||||
|
|
||||||
|
+logger = logging.getLogger(__name__)
|
||||||
|
+
|
||||||
|
+default_pruning_options = {
|
||||||
|
+ 'certRetentionTime': '30',
|
||||||
|
+ 'certRetentionUnit': 'day',
|
||||||
|
+ 'certSearchSizeLimit': '1000',
|
||||||
|
+ 'certSearchTimeLimit': '0',
|
||||||
|
+ 'requestRetentionTime': 'day',
|
||||||
|
+ 'requestRetentionUnit': '30',
|
||||||
|
+ 'requestSearchSizeLimit': '1000',
|
||||||
|
+ 'requestSearchTimeLimit': '0',
|
||||||
|
+ 'cron': ''
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+pruning_labels = {
|
||||||
|
+ 'certRetentionTime': 'Certificate Retention Time',
|
||||||
|
+ 'certRetentionUnit': 'Certificate Retention Unit',
|
||||||
|
+ 'certSearchSizeLimit': 'Certificate Search Size Limit',
|
||||||
|
+ 'certSearchTimeLimit': 'Certificate Search Time Limit',
|
||||||
|
+ 'requestRetentionTime': 'Request Retention Time',
|
||||||
|
+ 'requestRetentionUnit': 'Request Retention Unit',
|
||||||
|
+ 'requestSearchSizeLimit': 'Request Search Size Limit',
|
||||||
|
+ 'requestSearchTimeLimit': 'Request Search Time Limit',
|
||||||
|
+ 'cron': 'cron Schedule'
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+
|
||||||
|
+def validate_range(val, min, max):
|
||||||
|
+ """dogtag appears to have no error checking in the cron
|
||||||
|
+ entry so do some minimum amount of validation. It is
|
||||||
|
+ left as an exercise for the user to do month/day
|
||||||
|
+ validation so requesting Feb 31 will be accepted.
|
||||||
|
+
|
||||||
|
+ Only * and a number within a min/max range are allowed.
|
||||||
|
+ """
|
||||||
|
+ if val == '*':
|
||||||
|
+ return
|
||||||
|
+
|
||||||
|
+ if '-' in val or '/' in val:
|
||||||
|
+ raise ValueError(f"{val} ranges are not supported")
|
||||||
|
+
|
||||||
|
+ try:
|
||||||
|
+ int(val)
|
||||||
|
+ except ValueError:
|
||||||
|
+ # raise a clearer error
|
||||||
|
+ raise ValueError(f"{val} is not a valid integer")
|
||||||
|
+
|
||||||
|
+ if int(val) < min or int(val) > max:
|
||||||
|
+ raise ValueError(f"{val} not within the range {min}-{max}")
|
||||||
|
+
|
||||||
|
+
|
||||||
|
# Manages the FreeIPA ACME service on a per-server basis.
|
||||||
|
#
|
||||||
|
# This program is a stop-gap until the deployment-wide management of
|
||||||
|
@@ -66,32 +125,121 @@ class acme_state(RestClient):
|
||||||
|
status, unused, _unused = self._request('/acme/disable',
|
||||||
|
headers=headers)
|
||||||
|
if status != 200:
|
||||||
|
- raise RuntimeError('Failed to disble ACME')
|
||||||
|
+ raise RuntimeError('Failed to disable ACME')
|
||||||
|
|
||||||
|
|
||||||
|
class Command(enum.Enum):
|
||||||
|
ENABLE = 'enable'
|
||||||
|
DISABLE = 'disable'
|
||||||
|
STATUS = 'status'
|
||||||
|
+ PRUNE = 'pruning'
|
||||||
|
|
||||||
|
|
||||||
|
class IPAACMEManage(AdminTool):
|
||||||
|
command_name = "ipa-acme-manage"
|
||||||
|
- usage = "%prog [enable|disable|status]"
|
||||||
|
+ usage = "%prog [enable|disable|status|pruning]"
|
||||||
|
description = "Manage the IPA ACME service"
|
||||||
|
|
||||||
|
+ @classmethod
|
||||||
|
+ def add_options(cls, parser):
|
||||||
|
+
|
||||||
|
+ group = OptionGroup(parser, 'Pruning')
|
||||||
|
+ group.add_option(
|
||||||
|
+ "--enable", dest="enable", action="store_true",
|
||||||
|
+ default=False, help="Enable certificate pruning")
|
||||||
|
+ group.add_option(
|
||||||
|
+ "--disable", dest="disable", action="store_true",
|
||||||
|
+ default=False, help="Disable certificate pruning")
|
||||||
|
+ group.add_option(
|
||||||
|
+ "--cron", dest="cron", action="store",
|
||||||
|
+ default=None, help="Configure the pruning cron job")
|
||||||
|
+ group.add_option(
|
||||||
|
+ "--certretention", dest="certretention", action="store",
|
||||||
|
+ default=None, help="Certificate retention time", type=int)
|
||||||
|
+ group.add_option(
|
||||||
|
+ "--certretentionunit", dest="certretentionunit", action="store",
|
||||||
|
+ choices=['minute', 'hour', 'day', 'year'],
|
||||||
|
+ default=None, help="Certificate retention units")
|
||||||
|
+ group.add_option(
|
||||||
|
+ "--certsearchsizelimit", dest="certsearchsizelimit",
|
||||||
|
+ action="store",
|
||||||
|
+ default=None, help="LDAP search size limit", type=int)
|
||||||
|
+ group.add_option(
|
||||||
|
+ "--certsearchtimelimit", dest="certsearchtimelimit", action="store",
|
||||||
|
+ default=None, help="LDAP search time limit", type=int)
|
||||||
|
+ group.add_option(
|
||||||
|
+ "--requestretention", dest="requestretention", action="store",
|
||||||
|
+ default=None, help="Request retention time", type=int)
|
||||||
|
+ group.add_option(
|
||||||
|
+ "--requestretentionunit", dest="requestretentionunit",
|
||||||
|
+ choices=['minute', 'hour', 'day', 'year'],
|
||||||
|
+ action="store", default=None, help="Request retention units")
|
||||||
|
+ group.add_option(
|
||||||
|
+ "--requestsearchsizelimit", dest="requestsearchsizelimit",
|
||||||
|
+ action="store",
|
||||||
|
+ default=None, help="LDAP search size limit", type=int)
|
||||||
|
+ group.add_option(
|
||||||
|
+ "--requestsearchtimelimit", dest="requestsearchtimelimit",
|
||||||
|
+ action="store",
|
||||||
|
+ default=None, help="LDAP search time limit", type=int)
|
||||||
|
+ group.add_option(
|
||||||
|
+ "--config-show", dest="config_show", action="store_true",
|
||||||
|
+ default=False, help="Show the current pruning configuration")
|
||||||
|
+ group.add_option(
|
||||||
|
+ "--run", dest="run", action="store_true",
|
||||||
|
+ default=False, help="Run the pruning job now")
|
||||||
|
+ parser.add_option_group(group)
|
||||||
|
+ super(IPAACMEManage, cls).add_options(parser, debug_option=True)
|
||||||
|
+
|
||||||
|
+
|
||||||
|
def validate_options(self):
|
||||||
|
- # needs root now - if/when this program changes to an API
|
||||||
|
- # wrapper we will no longer need root.
|
||||||
|
super(IPAACMEManage, self).validate_options(needs_root=True)
|
||||||
|
|
||||||
|
if len(self.args) < 1:
|
||||||
|
self.option_parser.error(f'missing command argument')
|
||||||
|
- else:
|
||||||
|
- try:
|
||||||
|
- self.command = Command(self.args[0])
|
||||||
|
- except ValueError:
|
||||||
|
- self.option_parser.error(f'unknown command "{self.args[0]}"')
|
||||||
|
+
|
||||||
|
+ if self.args[0] == "pruning":
|
||||||
|
+ if self.options.enable and self.options.disable:
|
||||||
|
+ self.option_parser.error("Cannot both enable and disable")
|
||||||
|
+ elif (
|
||||||
|
+ any(
|
||||||
|
+ [
|
||||||
|
+ self.options.enable,
|
||||||
|
+ self.options.disable,
|
||||||
|
+ self.options.cron,
|
||||||
|
+ self.options.certretention,
|
||||||
|
+ self.options.certretentionunit,
|
||||||
|
+ self.options.requestretention,
|
||||||
|
+ self.options.requestretentionunit,
|
||||||
|
+ self.options.certsearchsizelimit,
|
||||||
|
+ self.options.certsearchtimelimit,
|
||||||
|
+ self.options.requestsearchsizelimit,
|
||||||
|
+ self.options.requestsearchtimelimit,
|
||||||
|
+ ]
|
||||||
|
+ )
|
||||||
|
+ and (self.options.config_show or self.options.run)
|
||||||
|
+ ):
|
||||||
|
+
|
||||||
|
+ self.option_parser.error(
|
||||||
|
+ "Cannot change and show config or run at the same time"
|
||||||
|
+ )
|
||||||
|
+ elif self.options.cron:
|
||||||
|
+ if len(self.options.cron.split()) != 5:
|
||||||
|
+ self.option_parser.error("Invalid format for --cron")
|
||||||
|
+ # dogtag does no validation when setting an option so
|
||||||
|
+ # do the minimum. The dogtag cron is limited compared to
|
||||||
|
+ # crontab(5).
|
||||||
|
+ opt = self.options.cron.split()
|
||||||
|
+ validate_range(opt[0], 0, 59)
|
||||||
|
+ validate_range(opt[1], 0, 23)
|
||||||
|
+ validate_range(opt[2], 1, 31)
|
||||||
|
+ validate_range(opt[3], 1, 12)
|
||||||
|
+ validate_range(opt[4], 0, 6)
|
||||||
|
+
|
||||||
|
+ try:
|
||||||
|
+ self.command = Command(self.args[0])
|
||||||
|
+ except ValueError:
|
||||||
|
+ self.option_parser.error(f'unknown command "{self.args[0]}"')
|
||||||
|
|
||||||
|
def check_san_status(self):
|
||||||
|
"""
|
||||||
|
@@ -100,6 +248,140 @@ class IPAACMEManage(AdminTool):
|
||||||
|
cert = x509.load_certificate_from_file(paths.HTTPD_CERT_FILE)
|
||||||
|
cainstance.check_ipa_ca_san(cert)
|
||||||
|
|
||||||
|
+ def pruning(self):
|
||||||
|
+ def run_pki_server(command, directive, prefix, value=None):
|
||||||
|
+ """Take a set of arguments to append to pki-server"""
|
||||||
|
+ args = [
|
||||||
|
+ 'pki-server', command,
|
||||||
|
+ f'{prefix}.{directive}'
|
||||||
|
+ ]
|
||||||
|
+ if value:
|
||||||
|
+ args.extend([str(value)])
|
||||||
|
+ logger.debug(args)
|
||||||
|
+ result = run(args, raiseonerr=False, capture_output=True,
|
||||||
|
+ capture_error=True)
|
||||||
|
+ if result.returncode != 0:
|
||||||
|
+ raise RuntimeError(result.error_output)
|
||||||
|
+ return result
|
||||||
|
+
|
||||||
|
+ def ca_config_set(directive, value,
|
||||||
|
+ prefix='jobsScheduler.job.pruning'):
|
||||||
|
+ run_pki_server('ca-config-set', directive, prefix, value)
|
||||||
|
+ # ca-config-set always succeeds, even if the option is
|
||||||
|
+ # not supported.
|
||||||
|
+ newvalue = ca_config_show(directive)
|
||||||
|
+ if str(value) != newvalue.strip():
|
||||||
|
+ raise RuntimeError('Updating %s failed' % directive)
|
||||||
|
+
|
||||||
|
+ def ca_config_show(directive):
|
||||||
|
+ result = run_pki_server('ca-config-show', directive,
|
||||||
|
+ prefix='jobsScheduler.job.pruning')
|
||||||
|
+ return result.output.strip()
|
||||||
|
+
|
||||||
|
+ def config_show():
|
||||||
|
+ status = ca_config_show('enabled')
|
||||||
|
+ if status.strip() == 'true':
|
||||||
|
+ print("Status: enabled")
|
||||||
|
+ else:
|
||||||
|
+ print("Status: disabled")
|
||||||
|
+ for option in (
|
||||||
|
+ 'certRetentionTime', 'certRetentionUnit',
|
||||||
|
+ 'certSearchSizeLimit', 'certSearchTimeLimit',
|
||||||
|
+ 'requestRetentionTime', 'requestRetentionUnit',
|
||||||
|
+ 'requestSearchSizeLimit', 'requestSearchTimeLimit',
|
||||||
|
+ 'cron',
|
||||||
|
+ ):
|
||||||
|
+ value = ca_config_show(option)
|
||||||
|
+ if value:
|
||||||
|
+ print("{}: {}".format(pruning_labels[option], value))
|
||||||
|
+ else:
|
||||||
|
+ print("{}: {}".format(pruning_labels[option],
|
||||||
|
+ default_pruning_options[option]))
|
||||||
|
+
|
||||||
|
+ def run_pruning():
|
||||||
|
+ """Run the pruning job manually"""
|
||||||
|
+
|
||||||
|
+ with NSSDatabase() as tmpdb:
|
||||||
|
+ print("Preparing...")
|
||||||
|
+ tmpdb.create_db()
|
||||||
|
+ tmpdb.import_files((paths.RA_AGENT_PEM, paths.RA_AGENT_KEY),
|
||||||
|
+ import_keys=True)
|
||||||
|
+ tmpdb.import_files((paths.IPA_CA_CRT,))
|
||||||
|
+ for nickname, trust_flags in tmpdb.list_certs():
|
||||||
|
+ if trust_flags.has_key:
|
||||||
|
+ ra_nickname = nickname
|
||||||
|
+ continue
|
||||||
|
+ # external is suffucient for our purposes: C,,
|
||||||
|
+ tmpdb.trust_root_cert(nickname, EXTERNAL_CA_TRUST_FLAGS)
|
||||||
|
+ print("Starting job...")
|
||||||
|
+ args = ['pki', '-C', tmpdb.pwd_file, '-d', tmpdb.secdir,
|
||||||
|
+ '-n', ra_nickname,
|
||||||
|
+ 'ca-job-start', 'pruning']
|
||||||
|
+ logger.debug(args)
|
||||||
|
+ run(args, stdin='y')
|
||||||
|
+
|
||||||
|
+ pki_version = pki.util.Version(pki.specification_version())
|
||||||
|
+ if pki_version < pki.util.Version("11.3.0"):
|
||||||
|
+ raise RuntimeError(
|
||||||
|
+ 'Certificate pruning is not supported in PKI version %s'
|
||||||
|
+ % pki_version
|
||||||
|
+ )
|
||||||
|
+
|
||||||
|
+ if lookup_random_serial_number_version(api) == 0:
|
||||||
|
+ raise RuntimeError(
|
||||||
|
+ 'Certificate pruning requires random serial numbers'
|
||||||
|
+ )
|
||||||
|
+
|
||||||
|
+ if self.options.config_show:
|
||||||
|
+ config_show()
|
||||||
|
+ return
|
||||||
|
+
|
||||||
|
+ if self.options.run:
|
||||||
|
+ run_pruning()
|
||||||
|
+ return
|
||||||
|
+
|
||||||
|
+ # Don't play the enable/disable at the same time game
|
||||||
|
+ if self.options.enable:
|
||||||
|
+ ca_config_set('owner', 'ipara')
|
||||||
|
+ ca_config_set('enabled', 'true')
|
||||||
|
+ ca_config_set('enabled', 'true', 'jobsScheduler')
|
||||||
|
+ elif self.options.disable:
|
||||||
|
+ ca_config_set('enabled', 'false')
|
||||||
|
+
|
||||||
|
+ # pki-server ca-config-set can only set one option at a time so
|
||||||
|
+ # loop through all the options and set what is there.
|
||||||
|
+ if self.options.certretention:
|
||||||
|
+ ca_config_set('certRetentionTime',
|
||||||
|
+ self.options.certretention)
|
||||||
|
+ if self.options.certretentionunit:
|
||||||
|
+ ca_config_set('certRetentionUnit',
|
||||||
|
+ self.options.certretentionunit)
|
||||||
|
+ if self.options.certsearchtimelimit:
|
||||||
|
+ ca_config_set('certSearchTimeLimit',
|
||||||
|
+ self.options.certsearchtimelimit)
|
||||||
|
+ if self.options.certsearchsizelimit:
|
||||||
|
+ ca_config_set('certSearchSizeLimit',
|
||||||
|
+ self.options.certsearchsizelimit)
|
||||||
|
+ if self.options.requestretention:
|
||||||
|
+ ca_config_set('requestRetentionTime',
|
||||||
|
+ self.options.requestretention)
|
||||||
|
+ if self.options.requestretentionunit:
|
||||||
|
+ ca_config_set('requestRetentionUnit',
|
||||||
|
+ self.options.requestretentionunit)
|
||||||
|
+ if self.options.requestsearchsizelimit:
|
||||||
|
+ ca_config_set('requestSearchSizeLimit',
|
||||||
|
+ self.options.requestsearchsizelimit)
|
||||||
|
+ if self.options.requestsearchtimelimit:
|
||||||
|
+ ca_config_set('requestSearchTimeLimit',
|
||||||
|
+ self.options.requestsearchtimelimit)
|
||||||
|
+ if self.options.cron:
|
||||||
|
+ ca_config_set('cron', self.options.cron)
|
||||||
|
+
|
||||||
|
+ config_show()
|
||||||
|
+
|
||||||
|
+ print("The CA service must be restarted for changes to take effect")
|
||||||
|
+
|
||||||
|
+
|
||||||
|
def run(self):
|
||||||
|
if not is_ipa_configured():
|
||||||
|
print("IPA is not configured.")
|
||||||
|
@@ -123,7 +405,8 @@ class IPAACMEManage(AdminTool):
|
||||||
|
elif self.command == Command.STATUS:
|
||||||
|
status = "enabled" if dogtag.acme_status() else "disabled"
|
||||||
|
print("ACME is {}".format(status))
|
||||||
|
- return 0
|
||||||
|
+ elif self.command == Command.PRUNE:
|
||||||
|
+ self.pruning()
|
||||||
|
else:
|
||||||
|
raise RuntimeError('programmer error: unhandled enum case')
|
||||||
|
|
||||||
|
diff --git a/ipatests/test_integration/test_acme.py b/ipatests/test_integration/test_acme.py
|
||||||
|
index 15d7543cfb0fa0fcb921166f7cd8f13d0535a41d..93e785d8febd9fa8d7b3ef87ecb3f2eb42ac5da2 100644
|
||||||
|
--- a/ipatests/test_integration/test_acme.py
|
||||||
|
+++ b/ipatests/test_integration/test_acme.py
|
||||||
|
@@ -12,6 +12,9 @@ from ipalib.constants import IPA_CA_RECORD
|
||||||
|
from ipatests.test_integration.base import IntegrationTest
|
||||||
|
from ipatests.pytest_ipa.integration import tasks
|
||||||
|
from ipatests.test_integration.test_caless import CALessBase, ipa_certs_cleanup
|
||||||
|
+from ipatests.test_integration.test_random_serial_numbers import (
|
||||||
|
+ pki_supports_RSNv3
|
||||||
|
+)
|
||||||
|
from ipaplatform.osinfo import osinfo
|
||||||
|
from ipaplatform.paths import paths
|
||||||
|
from ipatests.test_integration.test_external_ca import (
|
||||||
|
@@ -388,6 +391,16 @@ class TestACME(CALessBase):
|
||||||
|
status = check_acme_status(self.replicas[0], 'disabled')
|
||||||
|
assert status == 'disabled'
|
||||||
|
|
||||||
|
+ def test_acme_pruning_no_random_serial(self):
|
||||||
|
+ """This ACME install is configured without random serial
|
||||||
|
+ numbers. Verify that we can't enable pruning on it."""
|
||||||
|
+ self.master.run_command(['ipa-acme-manage', 'enable'])
|
||||||
|
+ result = self.master.run_command(
|
||||||
|
+ ['ipa-acme-manage', 'pruning', '--enable'],
|
||||||
|
+ raiseonerr=False)
|
||||||
|
+ assert result.returncode == 1
|
||||||
|
+ assert "requires random serial numbers" in result.stderr_text
|
||||||
|
+
|
||||||
|
@server_install_teardown
|
||||||
|
def test_third_party_certs(self):
|
||||||
|
"""Require ipa-ca SAN on replacement web certificates"""
|
||||||
|
@@ -630,3 +643,148 @@ class TestACMERenew(IntegrationTest):
|
||||||
|
renewed_expiry = cert.not_valid_after
|
||||||
|
|
||||||
|
assert initial_expiry != renewed_expiry
|
||||||
|
+
|
||||||
|
+
|
||||||
|
+class TestACMEPrune(IntegrationTest):
|
||||||
|
+ """Validate that ipa-acme-manage configures dogtag for pruning"""
|
||||||
|
+
|
||||||
|
+ random_serial = True
|
||||||
|
+
|
||||||
|
+ @classmethod
|
||||||
|
+ def install(cls, mh):
|
||||||
|
+ if not pki_supports_RSNv3(mh.master):
|
||||||
|
+ raise pytest.skip("RNSv3 not supported")
|
||||||
|
+ tasks.install_master(cls.master, setup_dns=True,
|
||||||
|
+ random_serial=True)
|
||||||
|
+
|
||||||
|
+ @classmethod
|
||||||
|
+ def uninstall(cls, mh):
|
||||||
|
+ if not pki_supports_RSNv3(mh.master):
|
||||||
|
+ raise pytest.skip("RSNv3 not supported")
|
||||||
|
+ super(TestACMEPrune, cls).uninstall(mh)
|
||||||
|
+
|
||||||
|
+ def test_enable_pruning(self):
|
||||||
|
+ if (tasks.get_pki_version(self.master)
|
||||||
|
+ < tasks.parse_version('11.3.0')):
|
||||||
|
+ raise pytest.skip("Certificate pruning is not available")
|
||||||
|
+ cs_cfg = self.master.get_file_contents(paths.CA_CS_CFG_PATH)
|
||||||
|
+ assert "jobsScheduler.job.pruning.enabled=false".encode() in cs_cfg
|
||||||
|
+
|
||||||
|
+ self.master.run_command(['ipa-acme-manage', 'pruning', '--enable'])
|
||||||
|
+
|
||||||
|
+ cs_cfg = self.master.get_file_contents(paths.CA_CS_CFG_PATH)
|
||||||
|
+ assert "jobsScheduler.enabled=true".encode() in cs_cfg
|
||||||
|
+ assert "jobsScheduler.job.pruning.enabled=true".encode() in cs_cfg
|
||||||
|
+ assert "jobsScheduler.job.pruning.owner=ipara".encode() in cs_cfg
|
||||||
|
+
|
||||||
|
+ def test_pruning_options(self):
|
||||||
|
+ if (tasks.get_pki_version(self.master)
|
||||||
|
+ < tasks.parse_version('11.3.0')):
|
||||||
|
+ raise pytest.skip("Certificate pruning is not available")
|
||||||
|
+
|
||||||
|
+ self.master.run_command(
|
||||||
|
+ ['ipa-acme-manage', 'pruning',
|
||||||
|
+ '--certretention=60',
|
||||||
|
+ '--certretentionunit=minute',
|
||||||
|
+ '--certsearchsizelimit=2000',
|
||||||
|
+ '--certsearchtimelimit=5',]
|
||||||
|
+ )
|
||||||
|
+ cs_cfg = self.master.get_file_contents(paths.CA_CS_CFG_PATH)
|
||||||
|
+ assert (
|
||||||
|
+ "jobsScheduler.job.pruning.certRetentionTime=60".encode()
|
||||||
|
+ in cs_cfg
|
||||||
|
+ )
|
||||||
|
+ assert (
|
||||||
|
+ "jobsScheduler.job.pruning.certRetentionUnit=minute".encode()
|
||||||
|
+ in cs_cfg
|
||||||
|
+ )
|
||||||
|
+ assert (
|
||||||
|
+ "jobsScheduler.job.pruning.certSearchSizeLimit=2000".encode()
|
||||||
|
+ in cs_cfg
|
||||||
|
+ )
|
||||||
|
+ assert (
|
||||||
|
+ "jobsScheduler.job.pruning.certSearchTimeLimit=5".encode()
|
||||||
|
+ in cs_cfg
|
||||||
|
+ )
|
||||||
|
+
|
||||||
|
+ self.master.run_command(
|
||||||
|
+ ['ipa-acme-manage', 'pruning',
|
||||||
|
+ '--requestretention=60',
|
||||||
|
+ '--requestretentionunit=minute',
|
||||||
|
+ '--requestresearchsizelimit=2000',
|
||||||
|
+ '--requestsearchtimelimit=5',]
|
||||||
|
+ )
|
||||||
|
+ cs_cfg = self.master.get_file_contents(paths.CA_CS_CFG_PATH)
|
||||||
|
+ assert (
|
||||||
|
+ "jobsScheduler.job.pruning.requestRetentionTime=60".encode()
|
||||||
|
+ in cs_cfg
|
||||||
|
+ )
|
||||||
|
+ assert (
|
||||||
|
+ "jobsScheduler.job.pruning.requestRetentionUnit=minute".encode()
|
||||||
|
+ in cs_cfg
|
||||||
|
+ )
|
||||||
|
+ assert (
|
||||||
|
+ "jobsScheduler.job.pruning.requestSearchSizeLimit=2000".encode()
|
||||||
|
+ in cs_cfg
|
||||||
|
+ )
|
||||||
|
+ assert (
|
||||||
|
+ "jobsScheduler.job.pruning.requestSearchTimeLimit=5".encode()
|
||||||
|
+ in cs_cfg
|
||||||
|
+ )
|
||||||
|
+
|
||||||
|
+ self.master.run_command(
|
||||||
|
+ ['ipa-acme-manage', 'pruning',
|
||||||
|
+ '--cron="0 23 1 * *',]
|
||||||
|
+ )
|
||||||
|
+ cs_cfg = self.master.get_file_contents(paths.CA_CS_CFG_PATH)
|
||||||
|
+ assert (
|
||||||
|
+ "jobsScheduler.job.pruning.cron=0 23 1 * *".encode()
|
||||||
|
+ in cs_cfg
|
||||||
|
+ )
|
||||||
|
+
|
||||||
|
+ def test_pruning_negative_options(self):
|
||||||
|
+ """Negative option testing for things we directly cover"""
|
||||||
|
+ if (tasks.get_pki_version(self.master)
|
||||||
|
+ < tasks.parse_version('11.3.0')):
|
||||||
|
+ raise pytest.skip("Certificate pruning is not available")
|
||||||
|
+
|
||||||
|
+ result = self.master.run_command(
|
||||||
|
+ ['ipa-acme-manage', 'pruning',
|
||||||
|
+ '--enable', '--disable'],
|
||||||
|
+ raiseonerr=False
|
||||||
|
+ )
|
||||||
|
+ assert result.returncode == 1
|
||||||
|
+ assert "Cannot both enable and disable" in result.stderr_text
|
||||||
|
+
|
||||||
|
+ for cmd in ('--config-show', '--run'):
|
||||||
|
+ result = self.master.run_command(
|
||||||
|
+ ['ipa-acme-manage', 'pruning',
|
||||||
|
+ cmd, '--enable'],
|
||||||
|
+ raiseonerr=False
|
||||||
|
+ )
|
||||||
|
+ assert result.returncode == 1
|
||||||
|
+ assert "Cannot change and show config" in result.stderr_text
|
||||||
|
+
|
||||||
|
+ result = self.master.run_command(
|
||||||
|
+ ['ipa-acme-manage', 'pruning',
|
||||||
|
+ '--cron="* *"'],
|
||||||
|
+ raiseonerr=False
|
||||||
|
+ )
|
||||||
|
+ assert result.returncode == 1
|
||||||
|
+ assert "Invalid format format --cron" in result.stderr_text
|
||||||
|
+
|
||||||
|
+ result = self.master.run_command(
|
||||||
|
+ ['ipa-acme-manage', 'pruning',
|
||||||
|
+ '--cron="100 * * * *"'],
|
||||||
|
+ raiseonerr=False
|
||||||
|
+ )
|
||||||
|
+ assert result.returncode == 1
|
||||||
|
+ assert "100 not within the range 0-59" in result.stderr_text
|
||||||
|
+
|
||||||
|
+ result = self.master.run_command(
|
||||||
|
+ ['ipa-acme-manage', 'pruning',
|
||||||
|
+ '--cron="10 1-5 * * *"'],
|
||||||
|
+ raiseonerr=False
|
||||||
|
+ )
|
||||||
|
+ assert result.returncode == 1
|
||||||
|
+ assert "1-5 ranges are not supported" in result.stderr_text
|
||||||
|
--
|
||||||
|
2.39.1
|
||||||
|
|
@ -0,0 +1,138 @@
|
|||||||
|
From f10d1a0f84ed0f16ab4a1469f16ffadb3e79e59e Mon Sep 17 00:00:00 2001
|
||||||
|
From: Rob Crittenden <rcritten@redhat.com>
|
||||||
|
Date: Fri, 27 Jan 2023 14:05:37 -0500
|
||||||
|
Subject: [PATCH] doc: add the --run command for manual job execution
|
||||||
|
|
||||||
|
A manual method was mentioned with no specificity. Include
|
||||||
|
the --run command. Also update the troubleshooting section
|
||||||
|
to show what failure to restart the CA after configuration
|
||||||
|
looks like.
|
||||||
|
|
||||||
|
Import the IPA CA chain for manual execution.
|
||||||
|
|
||||||
|
Also fix up some $ -> # to indicate root is needed.
|
||||||
|
|
||||||
|
Related: https://pagure.io/freeipa/issue/9294
|
||||||
|
|
||||||
|
Signed-off-by: Rob Crittenden <rcritten@redhat.com>
|
||||||
|
Reviewed-By: Florence Blanc-Renaud <flo@redhat.com>
|
||||||
|
---
|
||||||
|
doc/designs/expired_certificate_pruning.md | 46 +++++++++++++++-------
|
||||||
|
1 file changed, 32 insertions(+), 14 deletions(-)
|
||||||
|
|
||||||
|
diff --git a/doc/designs/expired_certificate_pruning.md b/doc/designs/expired_certificate_pruning.md
|
||||||
|
index 2c10d914020d3c12b6abb028323cd6796ec33e00..a23e452696ba2a150c4ad5a3e57360ae0a16a338 100644
|
||||||
|
--- a/doc/designs/expired_certificate_pruning.md
|
||||||
|
+++ b/doc/designs/expired_certificate_pruning.md
|
||||||
|
@@ -139,7 +139,7 @@ No validation of setting February 31st will be done. That will be left to PKI. B
|
||||||
|
|
||||||
|
### Disabling pruning
|
||||||
|
|
||||||
|
-`$ ipa-acme-manage pruning --enable=FALSE`
|
||||||
|
+`# ipa-acme-manage pruning --enable=FALSE`
|
||||||
|
|
||||||
|
This will remove the configuration option for `jobsScheduler.job.pruning.cron` just to be sure it no longer runs.
|
||||||
|
|
||||||
|
@@ -147,46 +147,46 @@ This will remove the configuration option for `jobsScheduler.job.pruning.cron` j
|
||||||
|
|
||||||
|
#### Pruning certificates
|
||||||
|
|
||||||
|
-`$ ipa-acme-manage pruning --certretention=VALUE --certretentionunit=UNIT`
|
||||||
|
+`# ipa-acme-manage pruning --certretention=VALUE --certretentionunit=UNIT`
|
||||||
|
|
||||||
|
will be the equivalent of:
|
||||||
|
|
||||||
|
-`$ pki-server ca-config-set jobsScheduler.job.pruning.certRetentionTime 30`
|
||||||
|
+`# pki-server ca-config-set jobsScheduler.job.pruning.certRetentionTime 30`
|
||||||
|
|
||||||
|
-`$ pki-server ca-config-set jobsScheduler.job.pruning.certRetentionUnit day`
|
||||||
|
+`# pki-server ca-config-set jobsScheduler.job.pruning.certRetentionUnit day`
|
||||||
|
|
||||||
|
The unit will always be required when modifying the time.
|
||||||
|
|
||||||
|
-`$ ipa-acme-manage pruning --certsearchsizelimit=VALUE --certsearchtimelimit=VALUE`
|
||||||
|
+`# ipa-acme-manage pruning --certsearchsizelimit=VALUE --certsearchtimelimit=VALUE`
|
||||||
|
|
||||||
|
will be the equivalent of:
|
||||||
|
|
||||||
|
-`$ pki-server ca-config-set jobsScheduler.job.pruning.certSearchSizeLimit 1000`
|
||||||
|
+`# pki-server ca-config-set jobsScheduler.job.pruning.certSearchSizeLimit 1000`
|
||||||
|
|
||||||
|
-`$ pki-server ca-config-set jobsScheduler.job.pruning.certSearchTimeLimit 0`
|
||||||
|
+`# pki-server ca-config-set jobsScheduler.job.pruning.certSearchTimeLimit 0`
|
||||||
|
|
||||||
|
A value of 0 for searchtimelimit is unlimited.
|
||||||
|
|
||||||
|
#### Pruning requests
|
||||||
|
|
||||||
|
-`$ ipa-acme-manage pruning --requestretention=VALUE --requestretentionunit=UNIT`
|
||||||
|
+`# ipa-acme-manage pruning --requestretention=VALUE --requestretentionunit=UNIT`
|
||||||
|
|
||||||
|
will be the equivalent of:
|
||||||
|
|
||||||
|
-`$ pki-server ca-config-set jobsScheduler.job.pruning.requestRetentionTime 30`
|
||||||
|
+`# pki-server ca-config-set jobsScheduler.job.pruning.requestRetentionTime 30`
|
||||||
|
|
||||||
|
-`$ pki-server ca-config-set jobsScheduler.job.pruning.requestRetentionUnit day`
|
||||||
|
+`# pki-server ca-config-set jobsScheduler.job.pruning.requestRetentionUnit day`
|
||||||
|
|
||||||
|
The unit will always be required when modifying the time.
|
||||||
|
|
||||||
|
-`$ ipa-acme-manage pruning --requestsearchsizelimit=VALUE --requestsearchtimelimit=VALUE`
|
||||||
|
+`# ipa-acme-manage pruning --requestsearchsizelimit=VALUE --requestsearchtimelimit=VALUE`
|
||||||
|
|
||||||
|
|
||||||
|
will be the equivalent of:
|
||||||
|
|
||||||
|
-`$ pki-server ca-config-set jobsScheduler.job.pruning.requestSearchSizeLimit 1000`
|
||||||
|
+`# pki-server ca-config-set jobsScheduler.job.pruning.requestSearchSizeLimit 1000`
|
||||||
|
|
||||||
|
-`$ pki-server ca-config-set jobsScheduler.job.pruning.requestSearchTimeLimit 0`
|
||||||
|
+`# pki-server ca-config-set jobsScheduler.job.pruning.requestSearchTimeLimit 0`
|
||||||
|
|
||||||
|
A value of 0 for searchtimelimit is unlimited.
|
||||||
|
|
||||||
|
@@ -212,10 +212,15 @@ Request search time limit: 0
|
||||||
|
Cron: 0 0 1 * *
|
||||||
|
```
|
||||||
|
|
||||||
|
+### Manual pruning
|
||||||
|
+
|
||||||
|
+`# ipa-acme-manage pruning --run`
|
||||||
|
+
|
||||||
|
+This is useful for testing the configuration or if the user wants to use the system cron or systemd timers for handling automation.
|
||||||
|
+
|
||||||
|
## Implementation
|
||||||
|
|
||||||
|
For online REST operations (login, run job) we will use the `ipaserver/plugins/dogtag.py::RestClient` class to manage the requests. This will take care of the authentication cookie, etc.
|
||||||
|
-
|
||||||
|
The class uses dogtag.https_request() will can take PEM cert and key files as arguments. These will be used for authentication.
|
||||||
|
|
||||||
|
For the non-REST operations (configuration, cron settings) the tool will fork out to pki-server ca-config-set.
|
||||||
|
@@ -239,6 +244,7 @@ Overview of the CLI commands. Example:
|
||||||
|
| ipa-acme-manage pruning | --requestretention=30 --requestretentionunit=day |
|
||||||
|
| ipa-acme-manage pruning | --requestsearchsizelimit=1000 --requestsearchtimelimit=0 |
|
||||||
|
| ipa-acme-manage pruning | --config-show |
|
||||||
|
+| ipa-acme-manage pruning | --run |
|
||||||
|
|
||||||
|
ipa-acme-manage can only be run as root.
|
||||||
|
|
||||||
|
@@ -295,3 +301,15 @@ The PKI debug log will contain job information.
|
||||||
|
2022-12-08 21:15:24 [pruning] INFO: PruningJob: - filter: (&(!(requestState=complete))(requestModifyTime<=1667942124527)(!(requestModifyTime=1667942124527)))
|
||||||
|
2022-12-08 21:15:24 [pruning] INFO: LDAPSession: Searching ou=ca, ou=requests,o=ipaca for (&(!(requestState=complete))(dateOfModify<=20221108211524Z)(!(dateOfModify=20221108211524Z)))
|
||||||
|
```
|
||||||
|
+
|
||||||
|
+### Manual execution fails with Forbidden
|
||||||
|
+
|
||||||
|
+If manually running pruning fails with a message like:
|
||||||
|
+
|
||||||
|
+```console
|
||||||
|
+# ipa-acme-manage pruning --run
|
||||||
|
+CalledProcessError(Command ['pki', '-C', '/tmp/tmppyyd3hfq/pwdfile.txt', '-d', '/tmp/tmppyyd3hfq', '-n', 'CN=IPA RA,O=EXAMPLE.TEST', 'ca-job-start', 'pruning'] returned non-zero exit status 255: 'PKIException: Forbidden\n')
|
||||||
|
+The ipa-acme-manage command failed.
|
||||||
|
+```
|
||||||
|
+
|
||||||
|
+You probably forgot to restart the CA after enabling pruning.
|
||||||
|
--
|
||||||
|
2.39.1
|
||||||
|
|
@ -1,175 +0,0 @@
|
|||||||
From 5afda72afc6fd626359411b55f092989fdd7d82d Mon Sep 17 00:00:00 2001
|
|
||||||
From: Rob Crittenden <rcritten@redhat.com>
|
|
||||||
Date: Jan 15 2024 13:39:21 +0000
|
|
||||||
Subject: ipatests: ignore nsslapd-accesslog-logbuffering WARN in healthcheck
|
|
||||||
|
|
||||||
|
|
||||||
Log buffering is disabled in the integration tests so we can have all
|
|
||||||
the logs at the end. This is causing a warning to show in the 389-ds
|
|
||||||
checks and causing tests to fail that expect all SUCCESS.
|
|
||||||
|
|
||||||
Add an exclude for this specific key so tests will pass again.
|
|
||||||
|
|
||||||
We may eventually want a more sophisiticated mechanism to handle
|
|
||||||
excludes, or updating the config in general, but this is fine for now.
|
|
||||||
|
|
||||||
Fixes: https://pagure.io/freeipa/issue/9400
|
|
||||||
|
|
||||||
Signed-off-by: Rob Crittenden <rcritten@redhat.com>
|
|
||||||
Reviewed-By: Florence Blanc-Renaud <frenaud@redhat.com>
|
|
||||||
Reviewed-By: Michal Polovka <mpolovka@redhat.com>
|
|
||||||
Reviewed-By: Florence Blanc-Renaud <frenaud@redhat.com>
|
|
||||||
Reviewed-By: Michal Polovka <mpolovka@redhat.com>
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
diff --git a/ipatests/test_integration/test_ipahealthcheck.py b/ipatests/test_integration/test_ipahealthcheck.py
|
|
||||||
index 7fb8e40..14fba26 100644
|
|
||||||
--- a/ipatests/test_integration/test_ipahealthcheck.py
|
|
||||||
+++ b/ipatests/test_integration/test_ipahealthcheck.py
|
|
||||||
@@ -9,6 +9,7 @@ from __future__ import absolute_import
|
|
||||||
|
|
||||||
from configparser import RawConfigParser, NoOptionError
|
|
||||||
from datetime import datetime, timedelta
|
|
||||||
+import io
|
|
||||||
import json
|
|
||||||
import os
|
|
||||||
import re
|
|
||||||
@@ -208,6 +209,28 @@ def run_healthcheck(host, source=None, check=None, output_type="json",
|
|
||||||
return result.returncode, data
|
|
||||||
|
|
||||||
|
|
||||||
+def set_excludes(host, option, value,
|
|
||||||
+ config_file='/etc/ipahealthcheck/ipahealthcheck.conf'):
|
|
||||||
+ """Mark checks that should be excluded from the results
|
|
||||||
+
|
|
||||||
+ This will set in the [excludes] section on host:
|
|
||||||
+ option=value
|
|
||||||
+ """
|
|
||||||
+ EXCLUDES = "excludes"
|
|
||||||
+
|
|
||||||
+ conf = host.get_file_contents(config_file, encoding='utf-8')
|
|
||||||
+ cfg = RawConfigParser()
|
|
||||||
+ cfg.read_string(conf)
|
|
||||||
+ if not cfg.has_section(EXCLUDES):
|
|
||||||
+ cfg.add_section(EXCLUDES)
|
|
||||||
+ if not cfg.has_option(EXCLUDES, option):
|
|
||||||
+ cfg.set(EXCLUDES, option, value)
|
|
||||||
+ out = io.StringIO()
|
|
||||||
+ cfg.write(out)
|
|
||||||
+ out.seek(0)
|
|
||||||
+ host.put_file_contents(config_file, out.read())
|
|
||||||
+
|
|
||||||
+
|
|
||||||
@pytest.fixture
|
|
||||||
def restart_service():
|
|
||||||
"""Shut down and restart a service as a fixture"""
|
|
||||||
@@ -265,6 +288,7 @@ class TestIpaHealthCheck(IntegrationTest):
|
|
||||||
setup_dns=True,
|
|
||||||
extra_args=['--no-dnssec-validation']
|
|
||||||
)
|
|
||||||
+ set_excludes(cls.master, "key", "DSCLE0004")
|
|
||||||
|
|
||||||
def test_ipa_healthcheck_install_on_master(self):
|
|
||||||
"""
|
|
||||||
@@ -552,6 +576,7 @@ class TestIpaHealthCheck(IntegrationTest):
|
|
||||||
setup_dns=True,
|
|
||||||
extra_args=['--no-dnssec-validation']
|
|
||||||
)
|
|
||||||
+ set_excludes(self.replicas[0], "key", "DSCLE0004")
|
|
||||||
|
|
||||||
# Init a user on replica to assign a DNA range
|
|
||||||
tasks.kinit_admin(self.replicas[0])
|
|
||||||
@@ -692,6 +717,7 @@ class TestIpaHealthCheck(IntegrationTest):
|
|
||||||
'output_type=human'
|
|
||||||
])
|
|
||||||
)
|
|
||||||
+ set_excludes(self.master, "key", "DSCLE0004", config_file)
|
|
||||||
returncode, output = run_healthcheck(
|
|
||||||
self.master, failures_only=True, config=config_file
|
|
||||||
)
|
|
||||||
@@ -707,6 +733,7 @@ class TestIpaHealthCheck(IntegrationTest):
|
|
||||||
'output_file=%s' % HC_LOG,
|
|
||||||
])
|
|
||||||
)
|
|
||||||
+ set_excludes(self.master, "key", "DSCLE0004")
|
|
||||||
returncode, _unused = run_healthcheck(
|
|
||||||
self.master, config=config_file
|
|
||||||
)
|
|
||||||
@@ -2396,6 +2423,7 @@ class TestIpaHealthCLI(IntegrationTest):
|
|
||||||
cls.master, setup_dns=True, extra_args=['--no-dnssec-validation']
|
|
||||||
)
|
|
||||||
tasks.install_packages(cls.master, HEALTHCHECK_PKG)
|
|
||||||
+ set_excludes(cls.master, "key", "DSCLE0004")
|
|
||||||
|
|
||||||
def test_indent(self):
|
|
||||||
"""
|
|
||||||
diff --git a/ipatests/test_integration/test_replica_promotion.py b/ipatests/test_integration/test_replica_promotion.py
|
|
||||||
index d477c3a..b71f2d5 100644
|
|
||||||
--- a/ipatests/test_integration/test_replica_promotion.py
|
|
||||||
+++ b/ipatests/test_integration/test_replica_promotion.py
|
|
||||||
@@ -13,7 +13,7 @@ import pytest
|
|
||||||
|
|
||||||
from ipatests.test_integration.base import IntegrationTest
|
|
||||||
from ipatests.test_integration.test_ipahealthcheck import (
|
|
||||||
- run_healthcheck, HEALTHCHECK_PKG
|
|
||||||
+ run_healthcheck, set_excludes, HEALTHCHECK_PKG
|
|
||||||
)
|
|
||||||
from ipatests.pytest_ipa.integration import tasks
|
|
||||||
from ipatests.pytest_ipa.integration.tasks import (
|
|
||||||
@@ -983,6 +983,9 @@ class TestHiddenReplicaPromotion(IntegrationTest):
|
|
||||||
# manually install KRA to verify that hidden state is synced
|
|
||||||
tasks.install_kra(cls.replicas[0])
|
|
||||||
|
|
||||||
+ set_excludes(cls.master, "key", "DSCLE0004")
|
|
||||||
+ set_excludes(cls.replicas[0], "key", "DSCLE0004")
|
|
||||||
+
|
|
||||||
def _check_dnsrecords(self, hosts_expected, hosts_unexpected=()):
|
|
||||||
domain = DNSName(self.master.domain.name).make_absolute()
|
|
||||||
rset = [
|
|
||||||
|
|
||||||
From f1cfe7d9ff2489dbb6cad70999b0e1bd433c0537 Mon Sep 17 00:00:00 2001
|
|
||||||
From: Rob Crittenden <rcritten@redhat.com>
|
|
||||||
Date: Jan 15 2024 13:39:21 +0000
|
|
||||||
Subject: ipatests: fix expected output for ipahealthcheck.ipa.host
|
|
||||||
|
|
||||||
|
|
||||||
ipa-healthcheck commit e69589d5 changed the output when a service
|
|
||||||
keytab is missing to not report the GSSAPI error but to report
|
|
||||||
that the keytab doesn't exist at all. This distinguishes from real
|
|
||||||
Kerberos issues like kvno.
|
|
||||||
|
|
||||||
Fixes: https://pagure.io/freeipa/issue/9482
|
|
||||||
|
|
||||||
Signed-off-by: Rob Crittenden <rcritten@redhat.com>
|
|
||||||
Reviewed-By: Florence Blanc-Renaud <frenaud@redhat.com>
|
|
||||||
Reviewed-By: Michal Polovka <mpolovka@redhat.com>
|
|
||||||
Reviewed-By: Florence Blanc-Renaud <frenaud@redhat.com>
|
|
||||||
Reviewed-By: Michal Polovka <mpolovka@redhat.com>
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
diff --git a/ipatests/test_integration/test_ipahealthcheck.py b/ipatests/test_integration/test_ipahealthcheck.py
|
|
||||||
index 14fba26..8aae9fa 100644
|
|
||||||
--- a/ipatests/test_integration/test_ipahealthcheck.py
|
|
||||||
+++ b/ipatests/test_integration/test_ipahealthcheck.py
|
|
||||||
@@ -629,9 +629,15 @@ class TestIpaHealthCheck(IntegrationTest):
|
|
||||||
ipahealthcheck.ipa.host when GSSAPI credentials cannot be obtained
|
|
||||||
from host's keytab.
|
|
||||||
"""
|
|
||||||
- msg = (
|
|
||||||
- "Minor (2529639107): No credentials cache found"
|
|
||||||
- )
|
|
||||||
+ version = tasks.get_healthcheck_version(self.master)
|
|
||||||
+ if parse_version(version) >= parse_version("0.15"):
|
|
||||||
+ msg = (
|
|
||||||
+ "Service {service} keytab {path} does not exist."
|
|
||||||
+ )
|
|
||||||
+ else:
|
|
||||||
+ msg = (
|
|
||||||
+ "Minor (2529639107): No credentials cache found"
|
|
||||||
+ )
|
|
||||||
|
|
||||||
with tasks.FileBackup(self.master, paths.KRB5_KEYTAB):
|
|
||||||
self.master.run_command(["rm", "-f", paths.KRB5_KEYTAB])
|
|
||||||
|
|
@ -1,45 +0,0 @@
|
|||||||
From dcb9d6edc7ae4278cd552e87f644705faa13d558 Mon Sep 17 00:00:00 2001
|
|
||||||
From: Alexander Bokovoy <abokovoy@redhat.com>
|
|
||||||
Date: Jan 31 2024 08:31:13 +0000
|
|
||||||
Subject: kdb: PAC generator: do not fail if canonical principal is missing
|
|
||||||
|
|
||||||
|
|
||||||
krbCanonicalName is mandatory for services but IPA services created
|
|
||||||
before commit e6ff83e (FreeIPA 4.4.0, ~2016) had no normalization done
|
|
||||||
to set krbCanonicalName; services created after that version were
|
|
||||||
upgraded to do have krbCanonicalName.
|
|
||||||
|
|
||||||
Accept krbPrincipalName alone since they have no alias either */
|
|
||||||
|
|
||||||
Fixes: https://pagure.io/freeipa/issue/9465
|
|
||||||
|
|
||||||
Signed-off-by: Alexander Bokovoy <abokovoy@redhat.com>
|
|
||||||
Reviewed-By: Florence Blanc-Renaud <flo@redhat.com>
|
|
||||||
Reviewed-By: Thierry Bordaz <tbordaz@redhat.com>
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
diff --git a/daemons/ipa-kdb/ipa_kdb_mspac.c b/daemons/ipa-kdb/ipa_kdb_mspac.c
|
|
||||||
index 9e1431c..8035036 100644
|
|
||||||
--- a/daemons/ipa-kdb/ipa_kdb_mspac.c
|
|
||||||
+++ b/daemons/ipa-kdb/ipa_kdb_mspac.c
|
|
||||||
@@ -496,8 +496,16 @@ static krb5_error_code ipadb_fill_info3(struct ipadb_context *ipactx,
|
|
||||||
ret = ipadb_ldap_attr_to_str(ipactx->lcontext, lentry,
|
|
||||||
"krbCanonicalName", &strres);
|
|
||||||
if (ret) {
|
|
||||||
- /* krbCanonicalName is mandatory for services */
|
|
||||||
- return ret;
|
|
||||||
+ /* krbCanonicalName is mandatory for services but IPA services
|
|
||||||
+ * created before commit e6ff83e (FreeIPA 4.4.0, ~2016) had no
|
|
||||||
+ * normalization to set krbCanonicalName; services created after
|
|
||||||
+ * that version were upgraded to do have krbCanonicalName.
|
|
||||||
+ *
|
|
||||||
+ * Accept krbPrincipalName alone since they have no alias either */
|
|
||||||
+ ret = ipadb_ldap_attr_to_str(ipactx->lcontext, lentry,
|
|
||||||
+ "krbPrincipalName", &strres);
|
|
||||||
+ if (ret)
|
|
||||||
+ return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
ret = krb5_parse_name(ipactx->kcontext, strres, &princ);
|
|
||||||
|
|
43
SOURCES/0009-tests-add-wrapper-around-ACME-RSNv3-test.patch
Normal file
43
SOURCES/0009-tests-add-wrapper-around-ACME-RSNv3-test.patch
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
From d24b69981d94fce7b1e1aa4a5c1ab88a123f96b5 Mon Sep 17 00:00:00 2001
|
||||||
|
From: Rob Crittenden <rcritten@redhat.com>
|
||||||
|
Date: Fri, 3 Feb 2023 10:04:31 -0500
|
||||||
|
Subject: [PATCH] tests: add wrapper around ACME RSNv3 test
|
||||||
|
|
||||||
|
This test is located outside of the TestACMEPrune because
|
||||||
|
it enables RSNv3 while the server installed by TestACME doesn't.
|
||||||
|
|
||||||
|
It still needs a wrapper to enforce a version of PKI that
|
||||||
|
supports pruning because that is checked first in the tool.
|
||||||
|
Re-ordering that wouldn't be a good user experience.
|
||||||
|
|
||||||
|
https://pagure.io/freeipa/issue/9322
|
||||||
|
|
||||||
|
Signed-off-by: Rob Crittenden <rcritten@redhat.com>
|
||||||
|
Reviewed-By: Rob Crittenden <rcritten@redhat.com>
|
||||||
|
---
|
||||||
|
ipatests/test_integration/test_acme.py | 9 ++++++++-
|
||||||
|
1 file changed, 8 insertions(+), 1 deletion(-)
|
||||||
|
|
||||||
|
diff --git a/ipatests/test_integration/test_acme.py b/ipatests/test_integration/test_acme.py
|
||||||
|
index 93e785d8febd9fa8d7b3ef87ecb3f2eb42ac5da2..5ceba05976059de69414a79634d98045c3ab68bb 100644
|
||||||
|
--- a/ipatests/test_integration/test_acme.py
|
||||||
|
+++ b/ipatests/test_integration/test_acme.py
|
||||||
|
@@ -393,7 +393,14 @@ class TestACME(CALessBase):
|
||||||
|
|
||||||
|
def test_acme_pruning_no_random_serial(self):
|
||||||
|
"""This ACME install is configured without random serial
|
||||||
|
- numbers. Verify that we can't enable pruning on it."""
|
||||||
|
+ numbers. Verify that we can't enable pruning on it.
|
||||||
|
+
|
||||||
|
+ This test is located here because by default installs
|
||||||
|
+ don't enable RSNv3.
|
||||||
|
+ """
|
||||||
|
+ if (tasks.get_pki_version(self.master)
|
||||||
|
+ < tasks.parse_version('11.3.0')):
|
||||||
|
+ raise pytest.skip("Certificate pruning is not available")
|
||||||
|
self.master.run_command(['ipa-acme-manage', 'enable'])
|
||||||
|
result = self.master.run_command(
|
||||||
|
['ipa-acme-manage', 'pruning', '--enable'],
|
||||||
|
--
|
||||||
|
2.39.1
|
||||||
|
|
@ -0,0 +1,68 @@
|
|||||||
|
From 2857bc69957bde7e59fff1c66c5a83c7f560616b Mon Sep 17 00:00:00 2001
|
||||||
|
From: Florence Blanc-Renaud <flo@redhat.com>
|
||||||
|
Date: Tue, 31 Jan 2023 15:53:08 +0100
|
||||||
|
Subject: [PATCH] automember-rebuild: add a notice about high CPU usage
|
||||||
|
|
||||||
|
The automember-rebuild task may require high CPU usage
|
||||||
|
if many users/hosts/groups are processed.
|
||||||
|
Add a note in the ipa automember-rebuild CLI output
|
||||||
|
and in the WebUI confirmation message.
|
||||||
|
|
||||||
|
Fixes: https://pagure.io/freeipa/issue/9320
|
||||||
|
Signed-off-by: Florence Blanc-Renaud <flo@redhat.com>
|
||||||
|
Reviewed-By: Francisco Trivino <ftrivino@redhat.com>
|
||||||
|
---
|
||||||
|
install/ui/test/data/i18n_messages.json | 2 +-
|
||||||
|
ipaclient/plugins/automember.py | 8 ++++++++
|
||||||
|
ipaserver/plugins/internal.py | 6 +++++-
|
||||||
|
3 files changed, 14 insertions(+), 2 deletions(-)
|
||||||
|
|
||||||
|
diff --git a/install/ui/test/data/i18n_messages.json b/install/ui/test/data/i18n_messages.json
|
||||||
|
index 49d288326d8cea192a16e93a274599805b0ea666..5b735487bf33805e8f0534d378d1497f05a11be8 100644
|
||||||
|
--- a/install/ui/test/data/i18n_messages.json
|
||||||
|
+++ b/install/ui/test/data/i18n_messages.json
|
||||||
|
@@ -7,7 +7,7 @@
|
||||||
|
"actions": {
|
||||||
|
"apply": "Apply",
|
||||||
|
"automember_rebuild": "Rebuild auto membership",
|
||||||
|
- "automember_rebuild_confirm": "Are you sure you want to rebuild auto membership?",
|
||||||
|
+ "automember_rebuild_confirm": "Are you sure you want to rebuild auto membership? In case of a high number of users, hosts or groups, the operation may require high CPU usage.",
|
||||||
|
"automember_rebuild_success": "Automember rebuild membership task completed",
|
||||||
|
"confirm": "Are you sure you want to proceed with the action?",
|
||||||
|
"delete_confirm": "Are you sure you want to delete ${object}?",
|
||||||
|
diff --git a/ipaclient/plugins/automember.py b/ipaclient/plugins/automember.py
|
||||||
|
index df4a2e5a01744e0ff22c74180e13c2e7dc33fbaa..7108dc948753b9f6a4439842bd75e7c5e064bda6 100644
|
||||||
|
--- a/ipaclient/plugins/automember.py
|
||||||
|
+++ b/ipaclient/plugins/automember.py
|
||||||
|
@@ -34,3 +34,11 @@ class automember_add_condition(MethodOverride):
|
||||||
|
flags=['suppress_empty'],
|
||||||
|
),
|
||||||
|
)
|
||||||
|
+
|
||||||
|
+
|
||||||
|
+@register(override=True, no_fail=True)
|
||||||
|
+class automember_rebuild(MethodOverride):
|
||||||
|
+ def interactive_prompt_callback(self, kw):
|
||||||
|
+ msg = _('IMPORTANT: In case of a high number of users, hosts or '
|
||||||
|
+ 'groups, the operation may require high CPU usage.')
|
||||||
|
+ self.Backend.textui.print_plain(msg)
|
||||||
|
diff --git a/ipaserver/plugins/internal.py b/ipaserver/plugins/internal.py
|
||||||
|
index 5ffa7a281548a0658386f8740dbddd96fd0bc7d6..e1e920f8bb49dd8ba8f30b727111bb1316f6a918 100644
|
||||||
|
--- a/ipaserver/plugins/internal.py
|
||||||
|
+++ b/ipaserver/plugins/internal.py
|
||||||
|
@@ -160,7 +160,11 @@ class i18n_messages(Command):
|
||||||
|
"actions": {
|
||||||
|
"apply": _("Apply"),
|
||||||
|
"automember_rebuild": _("Rebuild auto membership"),
|
||||||
|
- "automember_rebuild_confirm": _("Are you sure you want to rebuild auto membership?"),
|
||||||
|
+ "automember_rebuild_confirm": _(
|
||||||
|
+ "Are you sure you want to rebuild auto membership? In case of "
|
||||||
|
+ "a high number of users, hosts or groups, the operation "
|
||||||
|
+ "may require high CPU usage."
|
||||||
|
+ ),
|
||||||
|
"automember_rebuild_success": _("Automember rebuild membership task completed"),
|
||||||
|
"confirm": _("Are you sure you want to proceed with the action?"),
|
||||||
|
"delete_confirm": _("Are you sure you want to delete ${object}?"),
|
||||||
|
--
|
||||||
|
2.39.1
|
||||||
|
|
@ -1,89 +0,0 @@
|
|||||||
From bac601b7f35827236a106f7137f378e4888260da Mon Sep 17 00:00:00 2001
|
|
||||||
From: Julien Rische <jrische@redhat.com>
|
|
||||||
Date: Jan 30 2024 15:17:44 +0000
|
|
||||||
Subject: ipa-kdb: Fix memory leak during PAC verification
|
|
||||||
|
|
||||||
|
|
||||||
Commit 0022bd70d93708d325855d5271516d6cd894d6e8 introduced a memory leak
|
|
||||||
during the copy of some PAC buffers, because of an unfreed memory
|
|
||||||
allocation context.
|
|
||||||
|
|
||||||
Fixes: https://pagure.io/freeipa/issue/9520
|
|
||||||
|
|
||||||
Signed-off-by: Julien Rische <jrische@redhat.com>
|
|
||||||
Reviewed-By: Alexander Bokovoy <abokovoy@redhat.com>
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
diff --git a/daemons/ipa-kdb/ipa_kdb_mspac.c b/daemons/ipa-kdb/ipa_kdb_mspac.c
|
|
||||||
index a18beff..9e1431c 100644
|
|
||||||
--- a/daemons/ipa-kdb/ipa_kdb_mspac.c
|
|
||||||
+++ b/daemons/ipa-kdb/ipa_kdb_mspac.c
|
|
||||||
@@ -2316,6 +2316,7 @@ krb5_error_code ipadb_common_verify_pac(krb5_context context,
|
|
||||||
size_t i;
|
|
||||||
struct dom_sid *requester_sid = NULL;
|
|
||||||
struct dom_sid req_sid;
|
|
||||||
+ TALLOC_CTX *tmpctx = NULL;
|
|
||||||
|
|
||||||
if (signing_krbtgt != NULL &&
|
|
||||||
ipadb_is_cross_realm_krbtgt(signing_krbtgt->princ)) {
|
|
||||||
@@ -2371,6 +2372,12 @@ krb5_error_code ipadb_common_verify_pac(krb5_context context,
|
|
||||||
goto done;
|
|
||||||
}
|
|
||||||
|
|
||||||
+ tmpctx = talloc_new(NULL);
|
|
||||||
+ if (tmpctx == NULL) {
|
|
||||||
+ kerr = ENOMEM;
|
|
||||||
+ goto done;
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
for (i = 0; i < num_buffers; i++) {
|
|
||||||
if (types[i] == KRB5_PAC_SERVER_CHECKSUM ||
|
|
||||||
types[i] == KRB5_PAC_PRIVSVR_CHECKSUM ||
|
|
||||||
@@ -2395,32 +2402,21 @@ krb5_error_code ipadb_common_verify_pac(krb5_context context,
|
|
||||||
DATA_BLOB pac_attrs_data;
|
|
||||||
krb5_boolean pac_requested;
|
|
||||||
|
|
||||||
- TALLOC_CTX *tmpctx = talloc_new(NULL);
|
|
||||||
- if (tmpctx == NULL) {
|
|
||||||
- kerr = ENOMEM;
|
|
||||||
- goto done;
|
|
||||||
- }
|
|
||||||
-
|
|
||||||
kerr = ipadb_client_requested_pac(context, old_pac, tmpctx, &pac_requested);
|
|
||||||
- if (kerr != 0) {
|
|
||||||
- talloc_free(tmpctx);
|
|
||||||
+ if (kerr)
|
|
||||||
goto done;
|
|
||||||
- }
|
|
||||||
|
|
||||||
kerr = ipadb_get_pac_attrs_blob(tmpctx, &pac_requested, &pac_attrs_data);
|
|
||||||
- if (kerr) {
|
|
||||||
- talloc_free(tmpctx);
|
|
||||||
+ if (kerr)
|
|
||||||
goto done;
|
|
||||||
- }
|
|
||||||
+
|
|
||||||
data.magic = KV5M_DATA;
|
|
||||||
data.data = (char *)pac_attrs_data.data;
|
|
||||||
data.length = pac_attrs_data.length;
|
|
||||||
|
|
||||||
kerr = krb5_pac_add_buffer(context, new_pac, PAC_TYPE_ATTRIBUTES_INFO, &data);
|
|
||||||
- if (kerr) {
|
|
||||||
- talloc_free(tmpctx);
|
|
||||||
+ if (kerr)
|
|
||||||
goto done;
|
|
||||||
- }
|
|
||||||
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
@@ -2467,6 +2463,8 @@ done:
|
|
||||||
if (kerr != 0 && (new_pac != *pac)) {
|
|
||||||
krb5_pac_free(context, new_pac);
|
|
||||||
}
|
|
||||||
+ if (tmpctx)
|
|
||||||
+ talloc_free(tmpctx);
|
|
||||||
krb5_free_data_contents(context, &pac_blob);
|
|
||||||
free(types);
|
|
||||||
return kerr;
|
|
||||||
|
|
@ -1,238 +0,0 @@
|
|||||||
From 381af470779ea87335f57038dcbe72cd042ae6bb Mon Sep 17 00:00:00 2001
|
|
||||||
From: Stanislav Levin <slev@altlinux.org>
|
|
||||||
Date: Jan 30 2024 15:11:05 +0000
|
|
||||||
Subject: ipapython: Clean up krb5_error
|
|
||||||
|
|
||||||
|
|
||||||
`krb5_error` has different definition in MIT krb.
|
|
||||||
https://web.mit.edu/kerberos/krb5-latest/doc/appdev/refs/types/krb5_error.html
|
|
||||||
|
|
||||||
> Error message structure.
|
|
||||||
>
|
|
||||||
> Declaration:
|
|
||||||
> typedef struct _krb5_error krb5_error
|
|
||||||
|
|
||||||
While `krb5_error_code`
|
|
||||||
https://web.mit.edu/kerberos/www/krb5-latest/doc/appdev/refs/types/krb5_error_code.html#c.krb5_error_code
|
|
||||||
|
|
||||||
> krb5_error_code
|
|
||||||
> Used to convey an operation status.
|
|
||||||
>
|
|
||||||
> The value 0 indicates success; any other values are com_err codes. Use krb5_get_error_message() to obtain a string describing the error.
|
|
||||||
>
|
|
||||||
> Declaration
|
|
||||||
> typedef krb5_int32 krb5_error_code
|
|
||||||
|
|
||||||
And this is what was actually used.
|
|
||||||
|
|
||||||
To prevent confusion of types `krb5_error` was replaced with
|
|
||||||
`krb5_error_code`.
|
|
||||||
|
|
||||||
Fixes: https://pagure.io/freeipa/issue/9519
|
|
||||||
Signed-off-by: Stanislav Levin <slev@altlinux.org>
|
|
||||||
Reviewed-By: Alexander Bokovoy <abokovoy@redhat.com>
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
diff --git a/ipapython/session_storage.py b/ipapython/session_storage.py
|
|
||||||
index c43ef7d..371cf15 100644
|
|
||||||
--- a/ipapython/session_storage.py
|
|
||||||
+++ b/ipapython/session_storage.py
|
|
||||||
@@ -111,7 +111,7 @@ class KRB5Error(Exception):
|
|
||||||
|
|
||||||
|
|
||||||
def krb5_errcheck(result, func, arguments):
|
|
||||||
- """Error checker for krb5_error return value"""
|
|
||||||
+ """Error checker for krb5_error_code return value"""
|
|
||||||
if result != 0:
|
|
||||||
raise KRB5Error(result, func.__name__, arguments)
|
|
||||||
|
|
||||||
@@ -119,14 +119,13 @@ def krb5_errcheck(result, func, arguments):
|
|
||||||
krb5_context = ctypes.POINTER(_krb5_context)
|
|
||||||
krb5_ccache = ctypes.POINTER(_krb5_ccache)
|
|
||||||
krb5_data_p = ctypes.POINTER(_krb5_data)
|
|
||||||
-krb5_error = ctypes.c_int32
|
|
||||||
krb5_creds = _krb5_creds
|
|
||||||
krb5_pointer = ctypes.c_void_p
|
|
||||||
krb5_cc_cursor = krb5_pointer
|
|
||||||
|
|
||||||
krb5_init_context = LIBKRB5.krb5_init_context
|
|
||||||
krb5_init_context.argtypes = (ctypes.POINTER(krb5_context), )
|
|
||||||
-krb5_init_context.restype = krb5_error
|
|
||||||
+krb5_init_context.restype = krb5_error_code
|
|
||||||
krb5_init_context.errcheck = krb5_errcheck
|
|
||||||
|
|
||||||
krb5_free_context = LIBKRB5.krb5_free_context
|
|
||||||
@@ -143,30 +142,30 @@ krb5_free_data_contents.restype = None
|
|
||||||
|
|
||||||
krb5_cc_default = LIBKRB5.krb5_cc_default
|
|
||||||
krb5_cc_default.argtypes = (krb5_context, ctypes.POINTER(krb5_ccache), )
|
|
||||||
-krb5_cc_default.restype = krb5_error
|
|
||||||
+krb5_cc_default.restype = krb5_error_code
|
|
||||||
krb5_cc_default.errcheck = krb5_errcheck
|
|
||||||
|
|
||||||
krb5_cc_close = LIBKRB5.krb5_cc_close
|
|
||||||
krb5_cc_close.argtypes = (krb5_context, krb5_ccache, )
|
|
||||||
-krb5_cc_close.restype = krb5_error
|
|
||||||
+krb5_cc_close.restype = krb5_error_code
|
|
||||||
krb5_cc_close.errcheck = krb5_errcheck
|
|
||||||
|
|
||||||
krb5_parse_name = LIBKRB5.krb5_parse_name
|
|
||||||
krb5_parse_name.argtypes = (krb5_context, ctypes.c_char_p,
|
|
||||||
ctypes.POINTER(krb5_principal), )
|
|
||||||
-krb5_parse_name.restype = krb5_error
|
|
||||||
+krb5_parse_name.restype = krb5_error_code
|
|
||||||
krb5_parse_name.errcheck = krb5_errcheck
|
|
||||||
|
|
||||||
krb5_cc_set_config = LIBKRB5.krb5_cc_set_config
|
|
||||||
krb5_cc_set_config.argtypes = (krb5_context, krb5_ccache, krb5_principal,
|
|
||||||
ctypes.c_char_p, krb5_data_p, )
|
|
||||||
-krb5_cc_set_config.restype = krb5_error
|
|
||||||
+krb5_cc_set_config.restype = krb5_error_code
|
|
||||||
krb5_cc_set_config.errcheck = krb5_errcheck
|
|
||||||
|
|
||||||
krb5_cc_get_principal = LIBKRB5.krb5_cc_get_principal
|
|
||||||
krb5_cc_get_principal.argtypes = (krb5_context, krb5_ccache,
|
|
||||||
ctypes.POINTER(krb5_principal), )
|
|
||||||
-krb5_cc_get_principal.restype = krb5_error
|
|
||||||
+krb5_cc_get_principal.restype = krb5_error_code
|
|
||||||
krb5_cc_get_principal.errcheck = krb5_errcheck
|
|
||||||
|
|
||||||
# krb5_build_principal is a variadic function but that can't be expressed
|
|
||||||
@@ -177,26 +176,26 @@ krb5_build_principal.argtypes = (krb5_context, ctypes.POINTER(krb5_principal),
|
|
||||||
ctypes.c_uint, ctypes.c_char_p,
|
|
||||||
ctypes.c_char_p, ctypes.c_char_p,
|
|
||||||
ctypes.c_char_p, ctypes.c_char_p, )
|
|
||||||
-krb5_build_principal.restype = krb5_error
|
|
||||||
+krb5_build_principal.restype = krb5_error_code
|
|
||||||
krb5_build_principal.errcheck = krb5_errcheck
|
|
||||||
|
|
||||||
krb5_cc_start_seq_get = LIBKRB5.krb5_cc_start_seq_get
|
|
||||||
krb5_cc_start_seq_get.argtypes = (krb5_context, krb5_ccache,
|
|
||||||
ctypes.POINTER(krb5_cc_cursor), )
|
|
||||||
-krb5_cc_start_seq_get.restype = krb5_error
|
|
||||||
+krb5_cc_start_seq_get.restype = krb5_error_code
|
|
||||||
krb5_cc_start_seq_get.errcheck = krb5_errcheck
|
|
||||||
|
|
||||||
krb5_cc_next_cred = LIBKRB5.krb5_cc_next_cred
|
|
||||||
krb5_cc_next_cred.argtypes = (krb5_context, krb5_ccache,
|
|
||||||
ctypes.POINTER(krb5_cc_cursor),
|
|
||||||
ctypes.POINTER(krb5_creds), )
|
|
||||||
-krb5_cc_next_cred.restype = krb5_error
|
|
||||||
+krb5_cc_next_cred.restype = krb5_error_code
|
|
||||||
krb5_cc_next_cred.errcheck = krb5_errcheck
|
|
||||||
|
|
||||||
krb5_cc_end_seq_get = LIBKRB5.krb5_cc_end_seq_get
|
|
||||||
krb5_cc_end_seq_get.argtypes = (krb5_context, krb5_ccache,
|
|
||||||
ctypes.POINTER(krb5_cc_cursor), )
|
|
||||||
-krb5_cc_end_seq_get.restype = krb5_error
|
|
||||||
+krb5_cc_end_seq_get.restype = krb5_error_code
|
|
||||||
krb5_cc_end_seq_get.errcheck = krb5_errcheck
|
|
||||||
|
|
||||||
krb5_free_cred_contents = LIBKRB5.krb5_free_cred_contents
|
|
||||||
@@ -212,7 +211,7 @@ krb5_principal_compare.restype = krb5_boolean
|
|
||||||
krb5_unparse_name = LIBKRB5.krb5_unparse_name
|
|
||||||
krb5_unparse_name.argtypes = (krb5_context, krb5_principal,
|
|
||||||
ctypes.POINTER(ctypes.c_char_p), )
|
|
||||||
-krb5_unparse_name.restype = krb5_error
|
|
||||||
+krb5_unparse_name.restype = krb5_error_code
|
|
||||||
krb5_unparse_name.errcheck = krb5_errcheck
|
|
||||||
|
|
||||||
krb5_free_unparsed_name = LIBKRB5.krb5_free_unparsed_name
|
|
||||||
|
|
||||||
From 2a4bad8bb3295c5c0f5a760ecd41871c4c5a0c56 Mon Sep 17 00:00:00 2001
|
|
||||||
From: Stanislav Levin <slev@altlinux.org>
|
|
||||||
Date: Jan 30 2024 15:11:05 +0000
|
|
||||||
Subject: ipapython: Correct return type of krb5_free_cred_contents
|
|
||||||
|
|
||||||
|
|
||||||
According to https://web.mit.edu/kerberos/krb5-latest/doc/appdev/refs/api/krb5_free_cred_contents.html
|
|
||||||
|
|
||||||
> krb5_free_cred_contents - Free the contents of a krb5_creds structure.
|
|
||||||
>
|
|
||||||
> void krb5_free_cred_contents(krb5_context context, krb5_creds * val)
|
|
||||||
> param:
|
|
||||||
> [in] context - Library context
|
|
||||||
>
|
|
||||||
> [in] val - Credential structure to free contents of
|
|
||||||
>
|
|
||||||
> This function frees the contents of val , but not the structure itself.
|
|
||||||
|
|
||||||
https://github.com/krb5/krb5/blob/5b00197227231943bd2305328c8260dd0b0dbcf0/src/lib/krb5/krb/kfree.c#L166
|
|
||||||
|
|
||||||
This leads to undefined behavior and `krb5_free_cred_contents` can
|
|
||||||
raise KRB5Error (because of garbage data) while actually its foreign
|
|
||||||
function doesn't.
|
|
||||||
|
|
||||||
Fixes: https://pagure.io/freeipa/issue/9519
|
|
||||||
Signed-off-by: Stanislav Levin <slev@altlinux.org>
|
|
||||||
Reviewed-By: Alexander Bokovoy <abokovoy@redhat.com>
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
diff --git a/ipapython/session_storage.py b/ipapython/session_storage.py
|
|
||||||
index 371cf15..dc36f54 100644
|
|
||||||
--- a/ipapython/session_storage.py
|
|
||||||
+++ b/ipapython/session_storage.py
|
|
||||||
@@ -200,8 +200,7 @@ krb5_cc_end_seq_get.errcheck = krb5_errcheck
|
|
||||||
|
|
||||||
krb5_free_cred_contents = LIBKRB5.krb5_free_cred_contents
|
|
||||||
krb5_free_cred_contents.argtypes = (krb5_context, ctypes.POINTER(krb5_creds))
|
|
||||||
-krb5_free_cred_contents.restype = krb5_error
|
|
||||||
-krb5_free_cred_contents.errcheck = krb5_errcheck
|
|
||||||
+krb5_free_cred_contents.restype = None
|
|
||||||
|
|
||||||
krb5_principal_compare = LIBKRB5.krb5_principal_compare
|
|
||||||
krb5_principal_compare.argtypes = (krb5_context, krb5_principal,
|
|
||||||
|
|
||||||
From beb402afdbf32c01eed860e9416356f7b492ad74 Mon Sep 17 00:00:00 2001
|
|
||||||
From: Stanislav Levin <slev@altlinux.org>
|
|
||||||
Date: Jan 30 2024 15:11:05 +0000
|
|
||||||
Subject: ipapython: Propagate KRB5Error exceptions on iterating ccache
|
|
||||||
|
|
||||||
|
|
||||||
`ipapython.session_storage.get_data` iterates over
|
|
||||||
credentials in a credential cache till `krb5_cc_next_cred` returns
|
|
||||||
an error. This function doesn't expect any error on calling
|
|
||||||
other kerberos foreign functions during iteration. But that can
|
|
||||||
actually happen and KRB5Error exceptions stop an iteration while
|
|
||||||
they should be propagated.
|
|
||||||
|
|
||||||
With this change iteration will exactly stop on `krb5_cc_next_cred`
|
|
||||||
error as it was supposed to be.
|
|
||||||
|
|
||||||
Fixes: https://pagure.io/freeipa/issue/9519
|
|
||||||
Signed-off-by: Stanislav Levin <slev@altlinux.org>
|
|
||||||
Reviewed-By: Alexander Bokovoy <abokovoy@redhat.com>
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
diff --git a/ipapython/session_storage.py b/ipapython/session_storage.py
|
|
||||||
index dc36f54..e890dc9 100644
|
|
||||||
--- a/ipapython/session_storage.py
|
|
||||||
+++ b/ipapython/session_storage.py
|
|
||||||
@@ -312,8 +312,12 @@ def get_data(princ_name, key):
|
|
||||||
checkcreds = krb5_creds()
|
|
||||||
# the next function will throw an error and break out of the
|
|
||||||
# while loop when we try to access past the last cred
|
|
||||||
- krb5_cc_next_cred(context, ccache, ctypes.byref(cursor),
|
|
||||||
- ctypes.byref(checkcreds))
|
|
||||||
+ try:
|
|
||||||
+ krb5_cc_next_cred(context, ccache, ctypes.byref(cursor),
|
|
||||||
+ ctypes.byref(checkcreds))
|
|
||||||
+ except KRB5Error:
|
|
||||||
+ break
|
|
||||||
+
|
|
||||||
if (krb5_principal_compare(context, principal,
|
|
||||||
checkcreds.client) == 1 and
|
|
||||||
krb5_principal_compare(context, srv_princ,
|
|
||||||
@@ -328,8 +332,6 @@ def get_data(princ_name, key):
|
|
||||||
else:
|
|
||||||
krb5_free_cred_contents(context,
|
|
||||||
ctypes.byref(checkcreds))
|
|
||||||
- except KRB5Error:
|
|
||||||
- pass
|
|
||||||
finally:
|
|
||||||
krb5_cc_end_seq_get(context, ccache, ctypes.byref(cursor))
|
|
||||||
|
|
||||||
|
|
158
SOURCES/0011-Fix-setting-values-of-0-in-ACME-pruning.patch
Normal file
158
SOURCES/0011-Fix-setting-values-of-0-in-ACME-pruning.patch
Normal file
@ -0,0 +1,158 @@
|
|||||||
|
From 20ff7c16022793c707f6c2b8fb38a801870bc0e2 Mon Sep 17 00:00:00 2001
|
||||||
|
From: Rob Crittenden <rcritten@redhat.com>
|
||||||
|
Date: Wed, 8 Feb 2023 10:42:58 -0500
|
||||||
|
Subject: [PATCH] Fix setting values of 0 in ACME pruning
|
||||||
|
|
||||||
|
Replace comparisons of "if value" with "if value is not None"
|
||||||
|
in order to handle 0.
|
||||||
|
|
||||||
|
Add a short reference to the man page to indicat that a cert
|
||||||
|
or request retention time of 0 means remove at the next
|
||||||
|
execution.
|
||||||
|
|
||||||
|
Also indicate that the search time limit is in seconds.
|
||||||
|
|
||||||
|
Fixes: https://pagure.io/freeipa/issue/9325
|
||||||
|
|
||||||
|
Signed-off-by: Rob Crittenden <rcritten@redhat.com>
|
||||||
|
Reviewed-By: Francisco Trivino <ftrivino@redhat.com>
|
||||||
|
---
|
||||||
|
doc/designs/expired_certificate_pruning.md | 4 ++--
|
||||||
|
install/tools/man/ipa-acme-manage.1 | 8 +++----
|
||||||
|
ipaserver/install/ipa_acme_manage.py | 28 +++++++++++-----------
|
||||||
|
3 files changed, 20 insertions(+), 20 deletions(-)
|
||||||
|
|
||||||
|
diff --git a/doc/designs/expired_certificate_pruning.md b/doc/designs/expired_certificate_pruning.md
|
||||||
|
index a23e452696ba2a150c4ad5a3e57360ae0a16a338..35ead7b00145b5df44caf542cba277f0e6e08b6a 100644
|
||||||
|
--- a/doc/designs/expired_certificate_pruning.md
|
||||||
|
+++ b/doc/designs/expired_certificate_pruning.md
|
||||||
|
@@ -67,11 +67,11 @@ There are four values each that can be managed for pruning certificates and requ
|
||||||
|
* expired cert/incomplete request time
|
||||||
|
* time unit
|
||||||
|
* LDAP search size limit
|
||||||
|
-* LDAP search time limit
|
||||||
|
+* LDAP search time limit (in seconds)
|
||||||
|
|
||||||
|
The first two configure when an expired certificate or incomplete request will be deleted. The unit can be one of: minute, hour, day, year. By default it is 30 days.
|
||||||
|
|
||||||
|
-The LDAP limits control how many entries are returned and how long the search can take. By default it is 1000 entries and unlimited time.
|
||||||
|
+The LDAP limits control how many entries are returned and how long the search can take. By default it is 1000 entries and unlimited time (0 == unlimited, unit is seconds).
|
||||||
|
|
||||||
|
### Configuration settings
|
||||||
|
|
||||||
|
diff --git a/install/tools/man/ipa-acme-manage.1 b/install/tools/man/ipa-acme-manage.1
|
||||||
|
index e6cec4e4a7fd460c514a72456a2dc9a2e3682ebd..b8383c14f482698d2bcc8b08f0c0bf5882c3c298 100644
|
||||||
|
--- a/install/tools/man/ipa-acme-manage.1
|
||||||
|
+++ b/install/tools/man/ipa-acme-manage.1
|
||||||
|
@@ -79,7 +79,7 @@ For example, "0 0 1 * *" schedules the job to run at 12:00am on the first
|
||||||
|
day of each month.
|
||||||
|
.TP
|
||||||
|
\fB\-\-certretention=CERTRETENTION\fR
|
||||||
|
-Certificate retention time. The default is 30.
|
||||||
|
+Certificate retention time. The default is 30. A value of 0 will remove expired certificates with no delay.
|
||||||
|
.TP
|
||||||
|
\fB\-\-certretentionunit=CERTRETENTIONUNIT\fR
|
||||||
|
Certificate retention units. Valid units are: minute, hour, day, year.
|
||||||
|
@@ -89,10 +89,10 @@ The default is days.
|
||||||
|
LDAP search size limit searching for expired certificates. The default is 1000. This is a client-side limit. There may be additional server-side limitations.
|
||||||
|
.TP
|
||||||
|
\fB\-\-certsearchtimelimit=CERTSEARCHTIMELIMIT\fR
|
||||||
|
-LDAP search time limit searching for expired certificates. The default is 0, no limit. This is a client-side limit. There may be additional server-side limitations.
|
||||||
|
+LDAP search time limit (seconds) searching for expired certificates. The default is 0, no limit. This is a client-side limit. There may be additional server-side limitations.
|
||||||
|
.TP
|
||||||
|
\fB\-\-requestretention=REQUESTRETENTION\fR
|
||||||
|
-Request retention time. The default is 30.
|
||||||
|
+Request retention time. The default is 30. A value of 0 will remove expired requests with no delay.
|
||||||
|
.TP
|
||||||
|
\fB\-\-requestretentionunit=REQUESTRETENTIONUNIT\fR
|
||||||
|
Request retention units. Valid units are: minute, hour, day, year.
|
||||||
|
@@ -102,7 +102,7 @@ The default is days.
|
||||||
|
LDAP search size limit searching for unfulfilled requests. The default is 1000. There may be additional server-side limitations.
|
||||||
|
.TP
|
||||||
|
\fB\-\-requestsearchtimelimit=REQUESTSEARCHTIMELIMIT\fR
|
||||||
|
-LDAP search time limit searching for unfulfilled requests. The default is 0, no limit. There may be additional server-side limitations.
|
||||||
|
+LDAP search time limit (seconds) searching for unfulfilled requests. The default is 0, no limit. There may be additional server-side limitations.
|
||||||
|
.TP
|
||||||
|
\fB\-\-config\-show\fR
|
||||||
|
Show the current pruning configuration
|
||||||
|
diff --git a/ipaserver/install/ipa_acme_manage.py b/ipaserver/install/ipa_acme_manage.py
|
||||||
|
index b7b2111d9edcec2580aa4a485d7a7340146ff065..e7c35ff6fb5b7a30ac9e2c0c18f8db805cf06ee9 100644
|
||||||
|
--- a/ipaserver/install/ipa_acme_manage.py
|
||||||
|
+++ b/ipaserver/install/ipa_acme_manage.py
|
||||||
|
@@ -207,14 +207,14 @@ class IPAACMEManage(AdminTool):
|
||||||
|
self.options.enable,
|
||||||
|
self.options.disable,
|
||||||
|
self.options.cron,
|
||||||
|
- self.options.certretention,
|
||||||
|
+ self.options.certretention is not None,
|
||||||
|
self.options.certretentionunit,
|
||||||
|
- self.options.requestretention,
|
||||||
|
+ self.options.requestretention is not None,
|
||||||
|
self.options.requestretentionunit,
|
||||||
|
- self.options.certsearchsizelimit,
|
||||||
|
- self.options.certsearchtimelimit,
|
||||||
|
- self.options.requestsearchsizelimit,
|
||||||
|
- self.options.requestsearchtimelimit,
|
||||||
|
+ self.options.certsearchsizelimit is not None,
|
||||||
|
+ self.options.certsearchtimelimit is not None,
|
||||||
|
+ self.options.requestsearchsizelimit is not None,
|
||||||
|
+ self.options.requestsearchtimelimit is not None,
|
||||||
|
]
|
||||||
|
)
|
||||||
|
and (self.options.config_show or self.options.run)
|
||||||
|
@@ -226,7 +226,7 @@ class IPAACMEManage(AdminTool):
|
||||||
|
elif self.options.cron:
|
||||||
|
if len(self.options.cron.split()) != 5:
|
||||||
|
self.option_parser.error("Invalid format for --cron")
|
||||||
|
- # dogtag does no validation when setting an option so
|
||||||
|
+ # dogtag does no validation when setting this option so
|
||||||
|
# do the minimum. The dogtag cron is limited compared to
|
||||||
|
# crontab(5).
|
||||||
|
opt = self.options.cron.split()
|
||||||
|
@@ -255,7 +255,7 @@ class IPAACMEManage(AdminTool):
|
||||||
|
'pki-server', command,
|
||||||
|
f'{prefix}.{directive}'
|
||||||
|
]
|
||||||
|
- if value:
|
||||||
|
+ if value is not None:
|
||||||
|
args.extend([str(value)])
|
||||||
|
logger.debug(args)
|
||||||
|
result = run(args, raiseonerr=False, capture_output=True,
|
||||||
|
@@ -350,28 +350,28 @@ class IPAACMEManage(AdminTool):
|
||||||
|
|
||||||
|
# pki-server ca-config-set can only set one option at a time so
|
||||||
|
# loop through all the options and set what is there.
|
||||||
|
- if self.options.certretention:
|
||||||
|
+ if self.options.certretention is not None:
|
||||||
|
ca_config_set('certRetentionTime',
|
||||||
|
self.options.certretention)
|
||||||
|
if self.options.certretentionunit:
|
||||||
|
ca_config_set('certRetentionUnit',
|
||||||
|
self.options.certretentionunit)
|
||||||
|
- if self.options.certsearchtimelimit:
|
||||||
|
+ if self.options.certsearchtimelimit is not None:
|
||||||
|
ca_config_set('certSearchTimeLimit',
|
||||||
|
self.options.certsearchtimelimit)
|
||||||
|
- if self.options.certsearchsizelimit:
|
||||||
|
+ if self.options.certsearchsizelimit is not None:
|
||||||
|
ca_config_set('certSearchSizeLimit',
|
||||||
|
self.options.certsearchsizelimit)
|
||||||
|
- if self.options.requestretention:
|
||||||
|
+ if self.options.requestretention is not None:
|
||||||
|
ca_config_set('requestRetentionTime',
|
||||||
|
self.options.requestretention)
|
||||||
|
if self.options.requestretentionunit:
|
||||||
|
ca_config_set('requestRetentionUnit',
|
||||||
|
self.options.requestretentionunit)
|
||||||
|
- if self.options.requestsearchsizelimit:
|
||||||
|
+ if self.options.requestsearchsizelimit is not None:
|
||||||
|
ca_config_set('requestSearchSizeLimit',
|
||||||
|
self.options.requestsearchsizelimit)
|
||||||
|
- if self.options.requestsearchtimelimit:
|
||||||
|
+ if self.options.requestsearchtimelimit is not None:
|
||||||
|
ca_config_set('requestSearchTimeLimit',
|
||||||
|
self.options.requestsearchtimelimit)
|
||||||
|
if self.options.cron:
|
||||||
|
--
|
||||||
|
2.39.1
|
||||||
|
|
@ -1,109 +0,0 @@
|
|||||||
From b56a80581ef388e19d5761020454e51463036cd6 Mon Sep 17 00:00:00 2001
|
|
||||||
From: Alexander Bokovoy <abokovoy@redhat.com>
|
|
||||||
Date: Tue, 23 Jan 2024 14:47:50 +0200
|
|
||||||
Subject: [PATCH] sidgen: ignore staged users when generating SIDs
|
|
||||||
|
|
||||||
Staged users have
|
|
||||||
|
|
||||||
uidNumber: -1
|
|
||||||
gidNumber: -1
|
|
||||||
ipaUniqueID: autogenerate
|
|
||||||
|
|
||||||
We cannot generate ipaSecurityIdentifier based on those UID/GID numbers.
|
|
||||||
However, '-1' value will trigger an error
|
|
||||||
|
|
||||||
find_sid_for_ldap_entry - [file ipa_sidgen_common.c, line 483]: ID value too large.
|
|
||||||
|
|
||||||
And that, in turn, will cause stopping SID generation for all users.
|
|
||||||
|
|
||||||
Detect 'ipaUniqueID: autogenerate' situation and ignore these entries.
|
|
||||||
|
|
||||||
Fixes: https://pagure.io/freeipa/issue/9517
|
|
||||||
|
|
||||||
Signed-off-by: Alexander Bokovoy <abokovoy@redhat.com>
|
|
||||||
Reviewed-By: Florence Blanc-Renaud <flo@redhat.com>
|
|
||||||
Reviewed-By: Thierry Bordaz <tbordaz@redhat.com>
|
|
||||||
---
|
|
||||||
daemons/ipa-slapi-plugins/ipa-sidgen/ipa_sidgen.h | 2 ++
|
|
||||||
.../ipa-slapi-plugins/ipa-sidgen/ipa_sidgen_common.c | 12 ++++++++++++
|
|
||||||
2 files changed, 14 insertions(+)
|
|
||||||
|
|
||||||
diff --git a/daemons/ipa-slapi-plugins/ipa-sidgen/ipa_sidgen.h b/daemons/ipa-slapi-plugins/ipa-sidgen/ipa_sidgen.h
|
|
||||||
index 0feff7eec..bd46982d0 100644
|
|
||||||
--- a/daemons/ipa-slapi-plugins/ipa-sidgen/ipa_sidgen.h
|
|
||||||
+++ b/daemons/ipa-slapi-plugins/ipa-sidgen/ipa_sidgen.h
|
|
||||||
@@ -45,6 +45,8 @@
|
|
||||||
#define UID_NUMBER "uidnumber"
|
|
||||||
#define GID_NUMBER "gidnumber"
|
|
||||||
#define IPA_SID "ipantsecurityidentifier"
|
|
||||||
+#define IPA_UNIQUEID "ipauniqueid"
|
|
||||||
+#define IPA_UNIQUEID_AUTOGENERATE "autogenerate"
|
|
||||||
#define DOM_ATTRS_FILTER OBJECTCLASS"=ipantdomainattrs"
|
|
||||||
#define DOMAIN_ID_RANGE_FILTER OBJECTCLASS"=ipadomainidrange"
|
|
||||||
#define POSIX_ACCOUNT "posixaccount"
|
|
||||||
diff --git a/daemons/ipa-slapi-plugins/ipa-sidgen/ipa_sidgen_common.c b/daemons/ipa-slapi-plugins/ipa-sidgen/ipa_sidgen_common.c
|
|
||||||
index 6f784804c..cb763ebf8 100644
|
|
||||||
--- a/daemons/ipa-slapi-plugins/ipa-sidgen/ipa_sidgen_common.c
|
|
||||||
+++ b/daemons/ipa-slapi-plugins/ipa-sidgen/ipa_sidgen_common.c
|
|
||||||
@@ -454,6 +454,7 @@ int find_sid_for_ldap_entry(struct slapi_entry *entry,
|
|
||||||
uint32_t id;
|
|
||||||
char *sid = NULL;
|
|
||||||
char **objectclasses = NULL;
|
|
||||||
+ char *uniqueid = NULL;
|
|
||||||
Slapi_PBlock *mod_pb = NULL;
|
|
||||||
Slapi_Mods *smods = NULL;
|
|
||||||
int result;
|
|
||||||
@@ -479,6 +480,16 @@ int find_sid_for_ldap_entry(struct slapi_entry *entry,
|
|
||||||
goto done;
|
|
||||||
}
|
|
||||||
|
|
||||||
+ uniqueid = slapi_entry_attr_get_charptr(entry, IPA_UNIQUEID);
|
|
||||||
+ if (uniqueid != NULL &&
|
|
||||||
+ strncmp(IPA_UNIQUEID_AUTOGENERATE, uniqueid,
|
|
||||||
+ sizeof(IPA_UNIQUEID_AUTOGENERATE)) == 0) {
|
|
||||||
+ LOG("Staged entry [%s] does not have Posix IDs, nothing to do.\n",
|
|
||||||
+ dn_str);
|
|
||||||
+ ret = 0;
|
|
||||||
+ goto done;
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
if (uid_number >= UINT32_MAX || gid_number >= UINT32_MAX) {
|
|
||||||
LOG_FATAL("ID value too large.\n");
|
|
||||||
ret = LDAP_CONSTRAINT_VIOLATION;
|
|
||||||
@@ -554,6 +565,7 @@ int find_sid_for_ldap_entry(struct slapi_entry *entry,
|
|
||||||
}
|
|
||||||
|
|
||||||
done:
|
|
||||||
+ slapi_ch_free_string(&uniqueid);
|
|
||||||
slapi_ch_free_string(&sid);
|
|
||||||
slapi_pblock_destroy(mod_pb);
|
|
||||||
slapi_mods_free(&smods);
|
|
||||||
--
|
|
||||||
2.43.0
|
|
||||||
|
|
||||||
From 07150b71537744f491d022c737ef04775c72a10a Mon Sep 17 00:00:00 2001
|
|
||||||
From: Alexander Bokovoy <abokovoy@redhat.com>
|
|
||||||
Date: Tue, 23 Jan 2024 14:53:39 +0200
|
|
||||||
Subject: [PATCH] sidgen: fix missing prototypes
|
|
||||||
|
|
||||||
Signed-off-by: Alexander Bokovoy <abokovoy@redhat.com>
|
|
||||||
Reviewed-By: Florence Blanc-Renaud <flo@redhat.com>
|
|
||||||
Reviewed-By: Thierry Bordaz <tbordaz@redhat.com>
|
|
||||||
---
|
|
||||||
daemons/ipa-slapi-plugins/ipa-sidgen/ipa_sidgen.h | 3 +++
|
|
||||||
1 file changed, 3 insertions(+)
|
|
||||||
|
|
||||||
diff --git a/daemons/ipa-slapi-plugins/ipa-sidgen/ipa_sidgen.h b/daemons/ipa-slapi-plugins/ipa-sidgen/ipa_sidgen.h
|
|
||||||
index bd46982d0..aec862796 100644
|
|
||||||
--- a/daemons/ipa-slapi-plugins/ipa-sidgen/ipa_sidgen.h
|
|
||||||
+++ b/daemons/ipa-slapi-plugins/ipa-sidgen/ipa_sidgen.h
|
|
||||||
@@ -106,3 +106,6 @@ int find_sid_for_ldap_entry(struct slapi_entry *entry,
|
|
||||||
const char *base_dn,
|
|
||||||
const char *dom_sid,
|
|
||||||
struct range_info **ranges);
|
|
||||||
+
|
|
||||||
+int sidgen_task_init(Slapi_PBlock *pb);
|
|
||||||
+int ipa_sidgen_init(Slapi_PBlock *pb);
|
|
||||||
--
|
|
||||||
2.43.0
|
|
||||||
|
|
@ -0,0 +1,69 @@
|
|||||||
|
From 4e0ad96fbd9f438c884eeeaa60c2fb0c910a2b61 Mon Sep 17 00:00:00 2001
|
||||||
|
From: Rob Crittenden <rcritten@redhat.com>
|
||||||
|
Date: Mon, 11 Jul 2022 14:20:32 -0400
|
||||||
|
Subject: [PATCH] Wipe the ipa-ca DNS record when updating system records
|
||||||
|
|
||||||
|
If a server with a CA has been marked as hidden and
|
||||||
|
contains the last A or AAAA address then that address
|
||||||
|
would remain in the ipa-ca entry.
|
||||||
|
|
||||||
|
This is because update-dns-system-records did not delete
|
||||||
|
values, it just re-computed them. So if no A or AAAA
|
||||||
|
records were found then the existing value was left.
|
||||||
|
|
||||||
|
Fixes: https://pagure.io/freeipa/issue/9195
|
||||||
|
|
||||||
|
Signed-off-by: Rob Crittenden <rcritten@redhat.com>
|
||||||
|
Reviewed-By: Francisco Trivino <ftrivino@redhat.com>
|
||||||
|
Reviewed-By: Stanislav Levin <slev@altlinux.org>
|
||||||
|
---
|
||||||
|
ipaserver/dns_data_management.py | 12 +++++++++++-
|
||||||
|
1 file changed, 11 insertions(+), 1 deletion(-)
|
||||||
|
|
||||||
|
diff --git a/ipaserver/dns_data_management.py b/ipaserver/dns_data_management.py
|
||||||
|
index e2bc530ee8a8d7ade853652680c524ccd229205c..aaae5446856aba5e39ca9bb9c03decd434e4f71a 100644
|
||||||
|
--- a/ipaserver/dns_data_management.py
|
||||||
|
+++ b/ipaserver/dns_data_management.py
|
||||||
|
@@ -19,6 +19,7 @@ from dns import (
|
||||||
|
from time import sleep, time
|
||||||
|
|
||||||
|
from ipalib import errors
|
||||||
|
+from ipalib.constants import IPA_CA_RECORD
|
||||||
|
from ipalib.dns import record_name_format
|
||||||
|
from ipapython.dnsutil import DNSName
|
||||||
|
from ipaserver.install import installutils
|
||||||
|
@@ -187,7 +188,7 @@ class IPASystemRecords:
|
||||||
|
|
||||||
|
def __add_ca_records_from_hostname(self, zone_obj, hostname):
|
||||||
|
assert isinstance(hostname, DNSName) and hostname.is_absolute()
|
||||||
|
- r_name = DNSName('ipa-ca') + self.domain_abs
|
||||||
|
+ r_name = DNSName(IPA_CA_RECORD) + self.domain_abs
|
||||||
|
rrsets = None
|
||||||
|
end_time = time() + CA_RECORDS_DNS_TIMEOUT
|
||||||
|
while True:
|
||||||
|
@@ -210,6 +211,7 @@ class IPASystemRecords:
|
||||||
|
|
||||||
|
for rrset in rrsets:
|
||||||
|
for rd in rrset:
|
||||||
|
+ logger.debug("Adding CA IP %s for %s", rd.to_text(), hostname)
|
||||||
|
rdataset = zone_obj.get_rdataset(
|
||||||
|
r_name, rd.rdtype, create=True)
|
||||||
|
rdataset.add(rd, ttl=self.TTL)
|
||||||
|
@@ -461,6 +463,14 @@ class IPASystemRecords:
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
+ # Remove the ipa-ca record(s). They will be reconstructed in
|
||||||
|
+ # get_base_records().
|
||||||
|
+ r_name = DNSName(IPA_CA_RECORD) + self.domain_abs
|
||||||
|
+ try:
|
||||||
|
+ self.api_instance.Command.dnsrecord_del(
|
||||||
|
+ self.domain_abs, r_name, del_all=True)
|
||||||
|
+ except errors.NotFound:
|
||||||
|
+ pass
|
||||||
|
base_zone = self.get_base_records()
|
||||||
|
for record_name, node in base_zone.items():
|
||||||
|
set_cname_template = record_name in names_requiring_cname_templates
|
||||||
|
--
|
||||||
|
2.39.1
|
||||||
|
|
@ -1,310 +0,0 @@
|
|||||||
From 67ca47ba4092811029eec02f8af9c34ba7662924 Mon Sep 17 00:00:00 2001
|
|
||||||
From: Julien Rische <jrische@redhat.com>
|
|
||||||
Date: Mon, 9 Oct 2023 15:47:03 +0200
|
|
||||||
Subject: [PATCH] ipa-kdb: Ensure Bronze-Bit check can be enabled
|
|
||||||
|
|
||||||
MIT krb5 1.19 and older do not implement support for PAC ticket
|
|
||||||
signature to protect the encrypted part of tickets. This is the cause of
|
|
||||||
the Bronze-Bit vulnerability (CVE-2020-17043). The Bronze-Bit attack
|
|
||||||
detection mechanism introduced in a847e248 relies on the content of the
|
|
||||||
PAC.
|
|
||||||
|
|
||||||
However, since CVE-2022-37967, the content of the PAC can no longer be
|
|
||||||
trusted if the KDC does not support PAC extended KDC signature (aka.
|
|
||||||
PAC full checksum). This signature is supported in MIT krb5 since
|
|
||||||
version 1.21.
|
|
||||||
|
|
||||||
Support for the PAC extended KDC signature was backported downstream to
|
|
||||||
krb5 1.18.2 for CentOS 8 Stream (dist-git commit 7d215a54). This makes
|
|
||||||
the content of the PAC still trustworthy there.
|
|
||||||
|
|
||||||
This commit disables the Bronze-Bit attack detection mechanism at build
|
|
||||||
time in case krb5 does not provide the krb5_pac_full_sign_compat()
|
|
||||||
function.
|
|
||||||
|
|
||||||
Reviewed-By: Alexander Bokovoy <abokovoy@redhat.com>
|
|
||||||
---
|
|
||||||
daemons/ipa-kdb/ipa_kdb.h | 4 ++++
|
|
||||||
daemons/ipa-kdb/ipa_kdb_kdcpolicy.c | 7 +++++++
|
|
||||||
daemons/ipa-kdb/ipa_kdb_mspac.c | 4 ++++
|
|
||||||
3 files changed, 15 insertions(+)
|
|
||||||
|
|
||||||
diff --git a/daemons/ipa-kdb/ipa_kdb.h b/daemons/ipa-kdb/ipa_kdb.h
|
|
||||||
index 02b2cb631..c6926f7d5 100644
|
|
||||||
--- a/daemons/ipa-kdb/ipa_kdb.h
|
|
||||||
+++ b/daemons/ipa-kdb/ipa_kdb.h
|
|
||||||
@@ -367,6 +367,8 @@ krb5_error_code ipadb_is_princ_from_trusted_realm(krb5_context kcontext,
|
|
||||||
const char *test_realm, size_t size,
|
|
||||||
char **trusted_realm);
|
|
||||||
|
|
||||||
+#if KRB5_KDB_DAL_MAJOR_VERSION <= 8
|
|
||||||
+# ifdef HAVE_KRB5_PAC_FULL_SIGN_COMPAT
|
|
||||||
/* Try to detect a Bronze-Bit attack based on the content of the request and
|
|
||||||
* data from the KDB.
|
|
||||||
*
|
|
||||||
@@ -379,6 +381,8 @@ krb5_error_code ipadb_is_princ_from_trusted_realm(krb5_context kcontext,
|
|
||||||
krb5_error_code
|
|
||||||
ipadb_check_for_bronze_bit_attack(krb5_context context, krb5_kdc_req *request,
|
|
||||||
bool *detected, const char **status);
|
|
||||||
+# endif
|
|
||||||
+#endif
|
|
||||||
|
|
||||||
/* DELEGATION CHECKS */
|
|
||||||
|
|
||||||
diff --git a/daemons/ipa-kdb/ipa_kdb_kdcpolicy.c b/daemons/ipa-kdb/ipa_kdb_kdcpolicy.c
|
|
||||||
index 1032dff0b..ee0546c01 100644
|
|
||||||
--- a/daemons/ipa-kdb/ipa_kdb_kdcpolicy.c
|
|
||||||
+++ b/daemons/ipa-kdb/ipa_kdb_kdcpolicy.c
|
|
||||||
@@ -185,11 +185,18 @@ ipa_kdcpolicy_check_tgs(krb5_context context, krb5_kdcpolicy_moddata moddata,
|
|
||||||
const char **status, krb5_deltat *lifetime_out,
|
|
||||||
krb5_deltat *renew_lifetime_out)
|
|
||||||
{
|
|
||||||
+#if KRB5_KDB_DAL_MAJOR_VERSION <= 8
|
|
||||||
+# ifdef HAVE_KRB5_PAC_FULL_SIGN_COMPAT
|
|
||||||
krb5_error_code kerr;
|
|
||||||
|
|
||||||
kerr = ipadb_check_for_bronze_bit_attack(context, request, NULL, status);
|
|
||||||
if (kerr)
|
|
||||||
return KRB5KDC_ERR_POLICY;
|
|
||||||
+# else
|
|
||||||
+# warning Support for Kerberos PAC extended KDC signature is missing.\
|
|
||||||
+ This makes FreeIPA vulnerable to the Bronze-Bit exploit (CVE-2020-17049).
|
|
||||||
+# endif
|
|
||||||
+#endif
|
|
||||||
|
|
||||||
*status = NULL;
|
|
||||||
*lifetime_out = 0;
|
|
||||||
diff --git a/daemons/ipa-kdb/ipa_kdb_mspac.c b/daemons/ipa-kdb/ipa_kdb_mspac.c
|
|
||||||
index b4e22d431..05d5b407d 100644
|
|
||||||
--- a/daemons/ipa-kdb/ipa_kdb_mspac.c
|
|
||||||
+++ b/daemons/ipa-kdb/ipa_kdb_mspac.c
|
|
||||||
@@ -3299,6 +3299,8 @@ krb5_error_code ipadb_is_princ_from_trusted_realm(krb5_context kcontext,
|
|
||||||
return KRB5_KDB_NOENTRY;
|
|
||||||
}
|
|
||||||
|
|
||||||
+#if KRB5_KDB_DAL_MAJOR_VERSION <= 8
|
|
||||||
+# ifdef HAVE_KRB5_PAC_FULL_SIGN_COMPAT
|
|
||||||
krb5_error_code
|
|
||||||
ipadb_check_for_bronze_bit_attack(krb5_context context, krb5_kdc_req *request,
|
|
||||||
bool *detected, const char **status)
|
|
||||||
@@ -3471,3 +3473,5 @@ end:
|
|
||||||
ipadb_free_principal(context, proxy_entry);
|
|
||||||
return kerr;
|
|
||||||
}
|
|
||||||
+# endif
|
|
||||||
+#endif
|
|
||||||
--
|
|
||||||
2.43.0
|
|
||||||
|
|
||||||
From 27b96c17dd51d076e04d97662b7c788658a5094a Mon Sep 17 00:00:00 2001
|
|
||||||
From: Julien Rische <jrische@redhat.com>
|
|
||||||
Date: Jan 26 2024 09:35:57 +0000
|
|
||||||
Subject: ipa-kdb: Disable Bronze-Bit check if PAC not available
|
|
||||||
|
|
||||||
|
|
||||||
The Bronze-Bit check introduced in commit
|
|
||||||
a847e2483b4c4832ee5129901da169f4eb0d1392 requires the MS-PAC to be
|
|
||||||
present in the evidence ticket in order for S4U2Proxy requests to be
|
|
||||||
accepted. This actually requires SIDs to be set.
|
|
||||||
|
|
||||||
However, domains that were initialized before commit
|
|
||||||
e527857d000e558b3288a7a210400abaf2171237 may still not have SIDs
|
|
||||||
configured. This would results in all S4U2Proxy requests to fail
|
|
||||||
(including all the HTTP API requests).
|
|
||||||
|
|
||||||
This present commit disables the check for the Bronze-Bit exploit
|
|
||||||
(CVE-2020-17049) in case the domain is not able to generate PACs.
|
|
||||||
Instead, it prints a warning message in the KDC logs each time a
|
|
||||||
S4U2Proxy request is processed.
|
|
||||||
|
|
||||||
Fixes: https://pagure.io/freeipa/issue/9521
|
|
||||||
|
|
||||||
Signed-off-by: Julien Rische <jrische@redhat.com>
|
|
||||||
Reviewed-By: Alexander Bokovoy <abokovoy@redhat.com>
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
diff --git a/daemons/ipa-kdb/ipa_kdb.h b/daemons/ipa-kdb/ipa_kdb.h
|
|
||||||
index c6926f7..621c235 100644
|
|
||||||
--- a/daemons/ipa-kdb/ipa_kdb.h
|
|
||||||
+++ b/daemons/ipa-kdb/ipa_kdb.h
|
|
||||||
@@ -370,17 +370,21 @@ krb5_error_code ipadb_is_princ_from_trusted_realm(krb5_context kcontext,
|
|
||||||
#if KRB5_KDB_DAL_MAJOR_VERSION <= 8
|
|
||||||
# ifdef HAVE_KRB5_PAC_FULL_SIGN_COMPAT
|
|
||||||
/* Try to detect a Bronze-Bit attack based on the content of the request and
|
|
||||||
- * data from the KDB.
|
|
||||||
+ * data from the KDB. This check will work only if the domain supports MS-PAC.
|
|
||||||
*
|
|
||||||
* context krb5 context
|
|
||||||
* request KDB request
|
|
||||||
- * detected Set to "true" if a bronze bit attack is detected and the
|
|
||||||
- * pointer is not NULL. Remains unset otherwise.
|
|
||||||
+ * supported If not NULL, set to "false" in case the Bronze-Bit exploit
|
|
||||||
+ * detection process silently failed to complete because the
|
|
||||||
+ * domain does not meet requirements. Set to "true" otherwise.
|
|
||||||
+ * detected If not NULL, set to "true" if a Bronze-Bit attack is detected.
|
|
||||||
+ * Set to "false" otherwise.
|
|
||||||
* status If the call fails and the pointer is not NULL, set it with a
|
|
||||||
* message describing the cause of the failure. */
|
|
||||||
krb5_error_code
|
|
||||||
ipadb_check_for_bronze_bit_attack(krb5_context context, krb5_kdc_req *request,
|
|
||||||
- bool *detected, const char **status);
|
|
||||||
+ bool *supported, bool *detected,
|
|
||||||
+ const char **status);
|
|
||||||
# endif
|
|
||||||
#endif
|
|
||||||
|
|
||||||
diff --git a/daemons/ipa-kdb/ipa_kdb_kdcpolicy.c b/daemons/ipa-kdb/ipa_kdb_kdcpolicy.c
|
|
||||||
index ee0546c..713e9a0 100644
|
|
||||||
--- a/daemons/ipa-kdb/ipa_kdb_kdcpolicy.c
|
|
||||||
+++ b/daemons/ipa-kdb/ipa_kdb_kdcpolicy.c
|
|
||||||
@@ -188,10 +188,18 @@ ipa_kdcpolicy_check_tgs(krb5_context context, krb5_kdcpolicy_moddata moddata,
|
|
||||||
#if KRB5_KDB_DAL_MAJOR_VERSION <= 8
|
|
||||||
# ifdef HAVE_KRB5_PAC_FULL_SIGN_COMPAT
|
|
||||||
krb5_error_code kerr;
|
|
||||||
+ bool supported;
|
|
||||||
|
|
||||||
- kerr = ipadb_check_for_bronze_bit_attack(context, request, NULL, status);
|
|
||||||
+ kerr = ipadb_check_for_bronze_bit_attack(context, request, supported, NULL,
|
|
||||||
+ status);
|
|
||||||
if (kerr)
|
|
||||||
return KRB5KDC_ERR_POLICY;
|
|
||||||
+
|
|
||||||
+ if (!supported)
|
|
||||||
+ krb5_klog_syslog(LOG_WARNING, "MS-PAC not available. This makes "
|
|
||||||
+ "FreeIPA vulnerable to the Bronze-Bit exploit "
|
|
||||||
+ "(CVE-2020-17049). Please generate SIDs to enable "
|
|
||||||
+ "PAC support.");
|
|
||||||
# else
|
|
||||||
# warning Support for Kerberos PAC extended KDC signature is missing.\
|
|
||||||
This makes FreeIPA vulnerable to the Bronze-Bit exploit (CVE-2020-17049).
|
|
||||||
diff --git a/daemons/ipa-kdb/ipa_kdb_mspac.c b/daemons/ipa-kdb/ipa_kdb_mspac.c
|
|
||||||
index 05d5b40..a18beff 100644
|
|
||||||
--- a/daemons/ipa-kdb/ipa_kdb_mspac.c
|
|
||||||
+++ b/daemons/ipa-kdb/ipa_kdb_mspac.c
|
|
||||||
@@ -3303,11 +3303,14 @@ krb5_error_code ipadb_is_princ_from_trusted_realm(krb5_context kcontext,
|
|
||||||
# ifdef HAVE_KRB5_PAC_FULL_SIGN_COMPAT
|
|
||||||
krb5_error_code
|
|
||||||
ipadb_check_for_bronze_bit_attack(krb5_context context, krb5_kdc_req *request,
|
|
||||||
- bool *detected, const char **status)
|
|
||||||
+ bool *supported, bool *detected,
|
|
||||||
+ const char **status)
|
|
||||||
{
|
|
||||||
krb5_error_code kerr;
|
|
||||||
const char *st = NULL;
|
|
||||||
size_t i, j;
|
|
||||||
+ bool in_supported = true, in_detected = false;
|
|
||||||
+ struct ipadb_context *ipactx;
|
|
||||||
krb5_ticket *evidence_tkt;
|
|
||||||
krb5_authdata **authdata, **ifrel = NULL;
|
|
||||||
krb5_pac pac = NULL;
|
|
||||||
@@ -3327,6 +3330,21 @@ ipadb_check_for_bronze_bit_attack(krb5_context context, krb5_kdc_req *request,
|
|
||||||
goto end;
|
|
||||||
}
|
|
||||||
|
|
||||||
+ ipactx = ipadb_get_context(context);
|
|
||||||
+ if (!ipactx) {
|
|
||||||
+ kerr = KRB5_KDB_DBNOTINITED;
|
|
||||||
+ goto end;
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ /* Handle the case where the domain is not able to generate PACs (probably
|
|
||||||
+ * because SIDs are not set). In this case, we just skip the Bronze-Bit
|
|
||||||
+ * check. */
|
|
||||||
+ if (!ipactx->mspac) {
|
|
||||||
+ in_supported = false;
|
|
||||||
+ kerr = 0;
|
|
||||||
+ goto end;
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
evidence_tkt = request->second_ticket[0];
|
|
||||||
|
|
||||||
/* No need to check the Forwardable flag. If it was not set, this request
|
|
||||||
@@ -3451,8 +3469,7 @@ ipadb_check_for_bronze_bit_attack(krb5_context context, krb5_kdc_req *request,
|
|
||||||
/* This evidence ticket cannot be forwardable given the privileges
|
|
||||||
* of the proxy principal.
|
|
||||||
* This is a Bronze Bit attack. */
|
|
||||||
- if (detected)
|
|
||||||
- *detected = true;
|
|
||||||
+ in_detected = true;
|
|
||||||
st = "S4U2PROXY_BRONZE_BIT_ATTACK_DETECTED";
|
|
||||||
kerr = EBADE;
|
|
||||||
goto end;
|
|
||||||
@@ -3464,6 +3481,10 @@ ipadb_check_for_bronze_bit_attack(krb5_context context, krb5_kdc_req *request,
|
|
||||||
end:
|
|
||||||
if (st && status)
|
|
||||||
*status = st;
|
|
||||||
+ if (supported)
|
|
||||||
+ *supported = in_supported;
|
|
||||||
+ if (detected)
|
|
||||||
+ *detected = in_detected;
|
|
||||||
|
|
||||||
krb5_free_authdata(context, ifrel);
|
|
||||||
krb5_pac_free(context, pac);
|
|
||||||
|
|
||||||
From 81aa6ef695838a4b2fb5a53e773ea379a492913d Mon Sep 17 00:00:00 2001
|
|
||||||
From: Julien Rische <jrische@redhat.com>
|
|
||||||
Date: Fri, 9 Feb 2024 16:36:03 +0100
|
|
||||||
Subject: [PATCH] ipd-kdb: Fix some mistakes in
|
|
||||||
ipadb_check_for_bronze_bit_attack()
|
|
||||||
|
|
||||||
Fixes: https://pagure.io/freeipa/issue/9521
|
|
||||||
Signed-off-by: Julien Rische <jrische@redhat.com>
|
|
||||||
Reviewed-By: Alexander Bokovoy <abbra@users.noreply.github.com>
|
|
||||||
---
|
|
||||||
daemons/ipa-kdb/ipa_kdb.h | 3 ++-
|
|
||||||
daemons/ipa-kdb/ipa_kdb_kdcpolicy.c | 2 +-
|
|
||||||
daemons/ipa-kdb/ipa_kdb_mspac.c | 5 +++--
|
|
||||||
3 files changed, 6 insertions(+), 4 deletions(-)
|
|
||||||
|
|
||||||
diff --git a/daemons/ipa-kdb/ipa_kdb.h b/daemons/ipa-kdb/ipa_kdb.h
|
|
||||||
index 621c23591..5de5ea7a5 100644
|
|
||||||
--- a/daemons/ipa-kdb/ipa_kdb.h
|
|
||||||
+++ b/daemons/ipa-kdb/ipa_kdb.h
|
|
||||||
@@ -382,7 +382,8 @@ krb5_error_code ipadb_is_princ_from_trusted_realm(krb5_context kcontext,
|
|
||||||
* status If the call fails and the pointer is not NULL, set it with a
|
|
||||||
* message describing the cause of the failure. */
|
|
||||||
krb5_error_code
|
|
||||||
-ipadb_check_for_bronze_bit_attack(krb5_context context, krb5_kdc_req *request,
|
|
||||||
+ipadb_check_for_bronze_bit_attack(krb5_context context,
|
|
||||||
+ const krb5_kdc_req *request,
|
|
||||||
bool *supported, bool *detected,
|
|
||||||
const char **status);
|
|
||||||
# endif
|
|
||||||
diff --git a/daemons/ipa-kdb/ipa_kdb_kdcpolicy.c b/daemons/ipa-kdb/ipa_kdb_kdcpolicy.c
|
|
||||||
index 713e9a0c8..44959f3de 100644
|
|
||||||
--- a/daemons/ipa-kdb/ipa_kdb_kdcpolicy.c
|
|
||||||
+++ b/daemons/ipa-kdb/ipa_kdb_kdcpolicy.c
|
|
||||||
@@ -190,7 +190,7 @@ ipa_kdcpolicy_check_tgs(krb5_context context, krb5_kdcpolicy_moddata moddata,
|
|
||||||
krb5_error_code kerr;
|
|
||||||
bool supported;
|
|
||||||
|
|
||||||
- kerr = ipadb_check_for_bronze_bit_attack(context, request, supported, NULL,
|
|
||||||
+ kerr = ipadb_check_for_bronze_bit_attack(context, request, &supported, NULL,
|
|
||||||
status);
|
|
||||||
if (kerr)
|
|
||||||
return KRB5KDC_ERR_POLICY;
|
|
||||||
diff --git a/daemons/ipa-kdb/ipa_kdb_mspac.c b/daemons/ipa-kdb/ipa_kdb_mspac.c
|
|
||||||
index 80350364a..886ed7785 100644
|
|
||||||
--- a/daemons/ipa-kdb/ipa_kdb_mspac.c
|
|
||||||
+++ b/daemons/ipa-kdb/ipa_kdb_mspac.c
|
|
||||||
@@ -3308,13 +3308,14 @@ krb5_error_code ipadb_is_princ_from_trusted_realm(krb5_context kcontext,
|
|
||||||
#if KRB5_KDB_DAL_MAJOR_VERSION <= 8
|
|
||||||
# ifdef HAVE_KRB5_PAC_FULL_SIGN_COMPAT
|
|
||||||
krb5_error_code
|
|
||||||
-ipadb_check_for_bronze_bit_attack(krb5_context context, krb5_kdc_req *request,
|
|
||||||
+ipadb_check_for_bronze_bit_attack(krb5_context context,
|
|
||||||
+ const krb5_kdc_req *request,
|
|
||||||
bool *supported, bool *detected,
|
|
||||||
const char **status)
|
|
||||||
{
|
|
||||||
krb5_error_code kerr;
|
|
||||||
const char *st = NULL;
|
|
||||||
- size_t i, j;
|
|
||||||
+ size_t i, j = 0;
|
|
||||||
bool in_supported = true, in_detected = false;
|
|
||||||
struct ipadb_context *ipactx;
|
|
||||||
krb5_ticket *evidence_tkt;
|
|
||||||
--
|
|
||||||
2.43.0
|
|
||||||
|
|
@ -0,0 +1,111 @@
|
|||||||
|
From 0206369eec8530e96c66986c4ca501d8962193ce Mon Sep 17 00:00:00 2001
|
||||||
|
From: Alexander Bokovoy <abokovoy@redhat.com>
|
||||||
|
Date: Mon, 30 Jan 2023 14:22:30 +0200
|
||||||
|
Subject: [PATCH] ipa-kdb: PAC consistency checker needs to handle child
|
||||||
|
domains as well
|
||||||
|
|
||||||
|
When PAC check is performed, we might get a signing TGT instead of the
|
||||||
|
client DB entry. This means it is a principal from a trusted domain but
|
||||||
|
we don't know which one exactly because we only have a krbtgt for the
|
||||||
|
forest root. This happens in MIT Kerberos 1.20 or later where KDB's
|
||||||
|
issue_pac() callback never gets the original client principal directly.
|
||||||
|
|
||||||
|
Look into known child domains as well and make pass the check if both
|
||||||
|
NetBIOS name and SID correspond to one of the trusted domains under this
|
||||||
|
forest root. Move check for the SID before NetBIOS name check because we
|
||||||
|
can use SID of the domain in PAC to find out the right child domain in
|
||||||
|
our trusted domains' topology list.
|
||||||
|
|
||||||
|
Fixes: https://pagure.io/freeipa/issue/9316
|
||||||
|
|
||||||
|
Signed-off-by: Alexander Bokovoy <abokovoy@redhat.com>
|
||||||
|
Reviewed-By: Rafael Guterres Jeffman <rjeffman@redhat.com>
|
||||||
|
Reviewed-By: Rob Crittenden <rcritten@redhat.com>
|
||||||
|
---
|
||||||
|
daemons/ipa-kdb/ipa_kdb_mspac.c | 51 +++++++++++++++++++++------------
|
||||||
|
1 file changed, 32 insertions(+), 19 deletions(-)
|
||||||
|
|
||||||
|
diff --git a/daemons/ipa-kdb/ipa_kdb_mspac.c b/daemons/ipa-kdb/ipa_kdb_mspac.c
|
||||||
|
index a15050e2166f95c227d2e3c7d238e1ea2fe01235..476d1cb558a53420821ccfb1b794cb6bedce7794 100644
|
||||||
|
--- a/daemons/ipa-kdb/ipa_kdb_mspac.c
|
||||||
|
+++ b/daemons/ipa-kdb/ipa_kdb_mspac.c
|
||||||
|
@@ -1827,11 +1827,43 @@ krb5_error_code filter_logon_info(krb5_context context,
|
||||||
|
bool result;
|
||||||
|
char *domstr = NULL;
|
||||||
|
|
||||||
|
+ ipactx = ipadb_get_context(context);
|
||||||
|
+ if (!ipactx || !ipactx->mspac) {
|
||||||
|
+ return KRB5_KDB_DBNOTINITED;
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
domain = get_domain_from_realm_update(context, realm);
|
||||||
|
if (!domain) {
|
||||||
|
return EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
+ /* check exact sid */
|
||||||
|
+ result = dom_sid_check(&domain->domsid, info->info->info3.base.domain_sid, true);
|
||||||
|
+ if (!result) {
|
||||||
|
+ struct ipadb_mspac *mspac_ctx = ipactx->mspac;
|
||||||
|
+ result = FALSE;
|
||||||
|
+ /* Didn't match but perhaps the original PAC was issued by a child domain's DC? */
|
||||||
|
+ for (k = 0; k < mspac_ctx->num_trusts; k++) {
|
||||||
|
+ result = dom_sid_check(&mspac_ctx->trusts[k].domsid,
|
||||||
|
+ info->info->info3.base.domain_sid, true);
|
||||||
|
+ if (result) {
|
||||||
|
+ domain = &mspac_ctx->trusts[k];
|
||||||
|
+ break;
|
||||||
|
+ }
|
||||||
|
+ }
|
||||||
|
+ if (!result) {
|
||||||
|
+ domstr = dom_sid_string(NULL, info->info->info3.base.domain_sid);
|
||||||
|
+ krb5_klog_syslog(LOG_ERR, "PAC Info mismatch: domain = %s, "
|
||||||
|
+ "expected domain SID = %s, "
|
||||||
|
+ "found domain SID = %s",
|
||||||
|
+ domain->domain_name, domain->domain_sid,
|
||||||
|
+ domstr ? domstr : "<failed to display>");
|
||||||
|
+ talloc_free(domstr);
|
||||||
|
+ return EINVAL;
|
||||||
|
+ }
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ /* At this point we may have changed the domain we look at, */
|
||||||
|
/* check netbios/flat name */
|
||||||
|
if (strcasecmp(info->info->info3.base.logon_domain.string,
|
||||||
|
domain->flat_name) != 0) {
|
||||||
|
@@ -1843,21 +1875,6 @@ krb5_error_code filter_logon_info(krb5_context context,
|
||||||
|
return EINVAL;
|
||||||
|
}
|
||||||
|
|
||||||
|
- /* check exact sid */
|
||||||
|
- result = dom_sid_check(&domain->domsid, info->info->info3.base.domain_sid, true);
|
||||||
|
- if (!result) {
|
||||||
|
- domstr = dom_sid_string(NULL, info->info->info3.base.domain_sid);
|
||||||
|
- if (!domstr) {
|
||||||
|
- return EINVAL;
|
||||||
|
- }
|
||||||
|
- krb5_klog_syslog(LOG_ERR, "PAC Info mismatch: domain = %s, "
|
||||||
|
- "expected domain SID = %s, "
|
||||||
|
- "found domain SID = %s",
|
||||||
|
- domain->domain_name, domain->domain_sid, domstr);
|
||||||
|
- talloc_free(domstr);
|
||||||
|
- return EINVAL;
|
||||||
|
- }
|
||||||
|
-
|
||||||
|
/* Check if this domain has been filtered out by the trust itself*/
|
||||||
|
if (domain->parent != NULL) {
|
||||||
|
for(k = 0; k < domain->parent->len_sid_blocklist_incoming; k++) {
|
||||||
|
@@ -1944,10 +1961,6 @@ krb5_error_code filter_logon_info(krb5_context context,
|
||||||
|
* should include different possibilities into account
|
||||||
|
* */
|
||||||
|
if (info->info->info3.sidcount != 0) {
|
||||||
|
- ipactx = ipadb_get_context(context);
|
||||||
|
- if (!ipactx || !ipactx->mspac) {
|
||||||
|
- return KRB5_KDB_DBNOTINITED;
|
||||||
|
- }
|
||||||
|
count = info->info->info3.sidcount;
|
||||||
|
i = 0;
|
||||||
|
j = 0;
|
||||||
|
--
|
||||||
|
2.39.1
|
||||||
|
|
83
SOURCES/0014-Add-test-for-SSH-with-GSSAPI-auth.patch
Normal file
83
SOURCES/0014-Add-test-for-SSH-with-GSSAPI-auth.patch
Normal file
@ -0,0 +1,83 @@
|
|||||||
|
From a6cb905de74da38d62f9c3bd7957018924282521 Mon Sep 17 00:00:00 2001
|
||||||
|
From: Anuja More <amore@redhat.com>
|
||||||
|
Date: Mon, 30 Jan 2023 19:27:49 +0530
|
||||||
|
Subject: [PATCH] Add test for SSH with GSSAPI auth.
|
||||||
|
|
||||||
|
Added test for aduser with GSSAPI authentication.
|
||||||
|
|
||||||
|
Related : https://pagure.io/freeipa/issue/9316
|
||||||
|
|
||||||
|
Signed-off-by: Anuja More <amore@redhat.com>
|
||||||
|
Reviewed-By: Rafael Guterres Jeffman <rjeffman@redhat.com>
|
||||||
|
Reviewed-By: Rob Crittenden <rcritten@redhat.com>
|
||||||
|
---
|
||||||
|
ipatests/test_integration/test_trust.py | 46 +++++++++++++++++++++++++
|
||||||
|
1 file changed, 46 insertions(+)
|
||||||
|
|
||||||
|
diff --git a/ipatests/test_integration/test_trust.py b/ipatests/test_integration/test_trust.py
|
||||||
|
index c4b3b99ce1abbc16817b6530939fd9bae3f9500a..0d5b71cb0277a79eed7c34eb7e3d7eb6c09faa5e 100644
|
||||||
|
--- a/ipatests/test_integration/test_trust.py
|
||||||
|
+++ b/ipatests/test_integration/test_trust.py
|
||||||
|
@@ -527,6 +527,35 @@ class TestTrust(BaseTestTrust):
|
||||||
|
.format(self.ad_domain, subordinate_suffix))
|
||||||
|
self.ad.run_command(['powershell', '-c', cmd])
|
||||||
|
|
||||||
|
+ def test_ssh_aduser(self):
|
||||||
|
+ """Test ssh with GSSAPI is working with aduser
|
||||||
|
+
|
||||||
|
+ When kerberos ticket is obtained for child domain user
|
||||||
|
+ and ssh with this ticket should be successful
|
||||||
|
+ with no password prompt.
|
||||||
|
+
|
||||||
|
+ Related : https://pagure.io/freeipa/issue/9316
|
||||||
|
+ """
|
||||||
|
+ testuser = 'testuser@{0}'.format(self.ad_domain)
|
||||||
|
+ testusersub = 'subdomaintestuser@{0}'.format(self.ad_subdomain)
|
||||||
|
+
|
||||||
|
+ def sshuser(host, user):
|
||||||
|
+ tasks.kdestroy_all(host)
|
||||||
|
+ try:
|
||||||
|
+ tasks.kinit_as_user(host, user,
|
||||||
|
+ host.config.ad_admin_password
|
||||||
|
+ )
|
||||||
|
+ ssh_cmd = "ssh -q -K -l {user} {host} hostname"
|
||||||
|
+ valid_ssh = host.run_command(
|
||||||
|
+ ssh_cmd.format(user=user, host=host.hostname)
|
||||||
|
+ )
|
||||||
|
+ assert host.hostname in valid_ssh.stdout_text
|
||||||
|
+ finally:
|
||||||
|
+ tasks.kdestroy_all(host)
|
||||||
|
+
|
||||||
|
+ sshuser(self.master, testuser)
|
||||||
|
+ sshuser(self.master, testusersub)
|
||||||
|
+
|
||||||
|
def test_remove_nonposix_trust(self):
|
||||||
|
self.remove_trust(self.ad)
|
||||||
|
tasks.unconfigure_dns_for_trust(self.master, self.ad)
|
||||||
|
@@ -785,6 +814,23 @@ class TestTrust(BaseTestTrust):
|
||||||
|
assert re.search(
|
||||||
|
testuser_regex, result.stdout_text), result.stdout_text
|
||||||
|
|
||||||
|
+ def test_ssh_adtreeuser(self):
|
||||||
|
+ testuser = 'treetestuser@{0}'.format(self.ad_treedomain)
|
||||||
|
+ self.master.run_command(["id", testuser])
|
||||||
|
+ tasks.clear_sssd_cache(self.master)
|
||||||
|
+ tasks.kdestroy_all(self.master)
|
||||||
|
+ try:
|
||||||
|
+ tasks.kinit_as_user(self.master, testuser,
|
||||||
|
+ password="Secret123456"
|
||||||
|
+ )
|
||||||
|
+ ssh_cmd = "ssh -q -K -l {user} {host} hostname"
|
||||||
|
+ valid_ssh = self.master.run_command(
|
||||||
|
+ ssh_cmd.format(user=testuser, host=self.master.hostname)
|
||||||
|
+ )
|
||||||
|
+ assert self.master.hostname in valid_ssh.stdout_text
|
||||||
|
+ finally:
|
||||||
|
+ tasks.kdestroy_all(self.master)
|
||||||
|
+
|
||||||
|
def test_remove_external_treedomain_trust(self):
|
||||||
|
self.remove_trust(self.tree_ad)
|
||||||
|
tasks.unconfigure_dns_for_trust(self.master, self.ad, self.tree_ad)
|
||||||
|
--
|
||||||
|
2.39.1
|
||||||
|
|
@ -1,272 +0,0 @@
|
|||||||
From 00f8ddbfd2795228b343e1c39c1944b44d482c18 Mon Sep 17 00:00:00 2001
|
|
||||||
From: Alexander Bokovoy <abokovoy@redhat.com>
|
|
||||||
Date: Fri, 24 Nov 2023 11:46:19 +0200
|
|
||||||
Subject: [PATCH 1/4] ipa-kdb: add better detection of allowed user auth type
|
|
||||||
|
|
||||||
If default user authentication type is set to a list that does not
|
|
||||||
include a password or a hardened credential, the resulting configuration
|
|
||||||
might be incorrect for special service principals, including a krbtgt/..
|
|
||||||
one.
|
|
||||||
|
|
||||||
Add detection of special principals to avoid these situations and always
|
|
||||||
allow password or hardened for services.
|
|
||||||
|
|
||||||
Special handling is needed for the following principals:
|
|
||||||
|
|
||||||
- krbtgt/.. -- TGT service principals
|
|
||||||
- K/M -- master key principal
|
|
||||||
- kadmin/changepw -- service for changing passwords
|
|
||||||
- kadmin/kadmin -- kadmin service principal
|
|
||||||
- kadmin/history -- key used to encrypt history
|
|
||||||
|
|
||||||
Additionally, implicitly allow password or hardened credential use for
|
|
||||||
IPA services and IPA hosts since applications typically use keytabs for
|
|
||||||
that purpose.
|
|
||||||
|
|
||||||
Fixes: https://pagure.io/freeipa/issue/9485
|
|
||||||
|
|
||||||
Signed-off-by: Alexander Bokovoy <abokovoy@redhat.com>
|
|
||||||
Reviewed-By: Francisco Trivino <ftrivino@redhat.com>
|
|
||||||
---
|
|
||||||
daemons/ipa-kdb/ipa_kdb.c | 62 ++++++++++++++++++++++++++++++++++-----
|
|
||||||
1 file changed, 54 insertions(+), 8 deletions(-)
|
|
||||||
|
|
||||||
diff --git a/daemons/ipa-kdb/ipa_kdb.c b/daemons/ipa-kdb/ipa_kdb.c
|
|
||||||
index 06d511c76..dbb98dba6 100644
|
|
||||||
--- a/daemons/ipa-kdb/ipa_kdb.c
|
|
||||||
+++ b/daemons/ipa-kdb/ipa_kdb.c
|
|
||||||
@@ -26,6 +26,7 @@
|
|
||||||
#include "ipa_kdb.h"
|
|
||||||
#include "ipa_krb5.h"
|
|
||||||
#include "ipa_hostname.h"
|
|
||||||
+#include <kadm5/admin.h>
|
|
||||||
|
|
||||||
#define IPADB_GLOBAL_CONFIG_CACHE_TIME 60
|
|
||||||
|
|
||||||
@@ -207,6 +208,19 @@ static const struct {
|
|
||||||
{ "idp", IPADB_USER_AUTH_IDP },
|
|
||||||
{ "passkey", IPADB_USER_AUTH_PASSKEY },
|
|
||||||
{ }
|
|
||||||
+},
|
|
||||||
+ objclass_table[] = {
|
|
||||||
+ { "ipaservice", IPADB_USER_AUTH_PASSWORD },
|
|
||||||
+ { "ipahost", IPADB_USER_AUTH_PASSWORD },
|
|
||||||
+ { }
|
|
||||||
+},
|
|
||||||
+ princname_table[] = {
|
|
||||||
+ { KRB5_TGS_NAME, IPADB_USER_AUTH_PASSWORD },
|
|
||||||
+ { KRB5_KDB_M_NAME, IPADB_USER_AUTH_PASSWORD },
|
|
||||||
+ { KADM5_ADMIN_SERVICE, IPADB_USER_AUTH_PASSWORD },
|
|
||||||
+ { KADM5_CHANGEPW_SERVICE, IPADB_USER_AUTH_PASSWORD },
|
|
||||||
+ { KADM5_HIST_PRINCIPAL, IPADB_USER_AUTH_PASSWORD },
|
|
||||||
+ { }
|
|
||||||
};
|
|
||||||
|
|
||||||
void ipadb_parse_user_auth(LDAP *lcontext, LDAPMessage *le,
|
|
||||||
@@ -217,17 +231,49 @@ void ipadb_parse_user_auth(LDAP *lcontext, LDAPMessage *le,
|
|
||||||
|
|
||||||
*userauth = IPADB_USER_AUTH_NONE;
|
|
||||||
vals = ldap_get_values_len(lcontext, le, IPA_USER_AUTH_TYPE);
|
|
||||||
- if (!vals)
|
|
||||||
- return;
|
|
||||||
-
|
|
||||||
- for (i = 0; vals[i]; i++) {
|
|
||||||
- for (j = 0; userauth_table[j].name; j++) {
|
|
||||||
- if (strcasecmp(vals[i]->bv_val, userauth_table[j].name) == 0) {
|
|
||||||
- *userauth |= userauth_table[j].flag;
|
|
||||||
- break;
|
|
||||||
+ if (!vals) {
|
|
||||||
+ /* if there is no explicit ipaUserAuthType set, use objectclass */
|
|
||||||
+ vals = ldap_get_values_len(lcontext, le, "objectclass");
|
|
||||||
+ if (!vals)
|
|
||||||
+ return;
|
|
||||||
+
|
|
||||||
+ for (i = 0; vals[i]; i++) {
|
|
||||||
+ for (j = 0; objclass_table[j].name; j++) {
|
|
||||||
+ if (strcasecmp(vals[i]->bv_val, objclass_table[j].name) == 0) {
|
|
||||||
+ *userauth |= objclass_table[j].flag;
|
|
||||||
+ break;
|
|
||||||
+ }
|
|
||||||
+ }
|
|
||||||
+ }
|
|
||||||
+ } else {
|
|
||||||
+ for (i = 0; vals[i]; i++) {
|
|
||||||
+ for (j = 0; userauth_table[j].name; j++) {
|
|
||||||
+ if (strcasecmp(vals[i]->bv_val, userauth_table[j].name) == 0) {
|
|
||||||
+ *userauth |= userauth_table[j].flag;
|
|
||||||
+ break;
|
|
||||||
+ }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
+
|
|
||||||
+ /* If neither ipaUserAuthType nor objectClass were definitive,
|
|
||||||
+ * check the krbPrincipalName to see if it is krbtgt/ or K/M one */
|
|
||||||
+ if (*userauth == IPADB_USER_AUTH_NONE) {
|
|
||||||
+ ldap_value_free_len(vals);
|
|
||||||
+ vals = ldap_get_values_len(lcontext, le, "krbprincipalname");
|
|
||||||
+ if (!vals)
|
|
||||||
+ return;
|
|
||||||
+ for (i = 0; vals[i]; i++) {
|
|
||||||
+ for (j = 0; princname_table[j].name; j++) {
|
|
||||||
+ if (strncmp(vals[i]->bv_val, princname_table[j].name,
|
|
||||||
+ strlen(princname_table[j].name)) == 0) {
|
|
||||||
+ *userauth |= princname_table[j].flag;
|
|
||||||
+ break;
|
|
||||||
+ }
|
|
||||||
+ }
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ }
|
|
||||||
/* If password auth is enabled, enable hardened policy too. */
|
|
||||||
if (*userauth & IPADB_USER_AUTH_PASSWORD) {
|
|
||||||
*userauth |= IPADB_USER_AUTH_HARDENED;
|
|
||||||
--
|
|
||||||
2.43.0
|
|
||||||
|
|
||||||
|
|
||||||
From 69ae9febfb4462766b3bfe3e07e76550ece97b42 Mon Sep 17 00:00:00 2001
|
|
||||||
From: Alexander Bokovoy <abokovoy@redhat.com>
|
|
||||||
Date: Fri, 24 Nov 2023 11:54:04 +0200
|
|
||||||
Subject: [PATCH 2/4] ipa-kdb: when applying ticket policy, do not deny PKINIT
|
|
||||||
|
|
||||||
PKINIT differs from other pre-authentication methods by the fact that it
|
|
||||||
can be matched indepedently of the user authentication types via certmap
|
|
||||||
plugin in KDC.
|
|
||||||
|
|
||||||
Since PKINIT is a strong authentication method, allow its authentication
|
|
||||||
indicator and only apply the ticket policy.
|
|
||||||
|
|
||||||
Fixes: https://pagure.io/freeipa/issue/9485
|
|
||||||
|
|
||||||
Signed-off-by: Alexander Bokovoy <abokovoy@redhat.com>
|
|
||||||
Reviewed-By: Francisco Trivino <ftrivino@redhat.com>
|
|
||||||
---
|
|
||||||
daemons/ipa-kdb/ipa_kdb_kdcpolicy.c | 7 ++-----
|
|
||||||
1 file changed, 2 insertions(+), 5 deletions(-)
|
|
||||||
|
|
||||||
diff --git a/daemons/ipa-kdb/ipa_kdb_kdcpolicy.c b/daemons/ipa-kdb/ipa_kdb_kdcpolicy.c
|
|
||||||
index 436ee0e62..2802221c7 100644
|
|
||||||
--- a/daemons/ipa-kdb/ipa_kdb_kdcpolicy.c
|
|
||||||
+++ b/daemons/ipa-kdb/ipa_kdb_kdcpolicy.c
|
|
||||||
@@ -119,11 +119,8 @@ ipa_kdcpolicy_check_as(krb5_context context, krb5_kdcpolicy_moddata moddata,
|
|
||||||
pol_limits = &(ied->pol_limits[IPADB_USER_AUTH_IDX_RADIUS]);
|
|
||||||
} else if (strcmp(auth_indicator, "pkinit") == 0) {
|
|
||||||
valid_auth_indicators++;
|
|
||||||
- if (!(ua & IPADB_USER_AUTH_PKINIT)) {
|
|
||||||
- *status = "PKINIT pre-authentication not allowed for this user.";
|
|
||||||
- kerr = KRB5KDC_ERR_POLICY;
|
|
||||||
- goto done;
|
|
||||||
- }
|
|
||||||
+ /* allow PKINIT unconditionally -- it has passed already at this
|
|
||||||
+ * point so some certificate was useful, only apply the limits */
|
|
||||||
pol_limits = &(ied->pol_limits[IPADB_USER_AUTH_IDX_PKINIT]);
|
|
||||||
} else if (strcmp(auth_indicator, "hardened") == 0) {
|
|
||||||
valid_auth_indicators++;
|
|
||||||
--
|
|
||||||
2.43.0
|
|
||||||
|
|
||||||
|
|
||||||
From 62c44c9e69aa2721990ca3628434713e1af6f59b Mon Sep 17 00:00:00 2001
|
|
||||||
From: Alexander Bokovoy <abokovoy@redhat.com>
|
|
||||||
Date: Fri, 24 Nov 2023 12:20:55 +0200
|
|
||||||
Subject: [PATCH 3/4] ipa-kdb: clarify user auth table mapping use of
|
|
||||||
_AUTH_PASSWORD
|
|
||||||
|
|
||||||
Related: https://pagure.io/freeipa/issue/9485
|
|
||||||
|
|
||||||
Signed-off-by: Alexander Bokovoy <abokovoy@redhat.com>
|
|
||||||
Reviewed-By: Francisco Trivino <ftrivino@redhat.com>
|
|
||||||
---
|
|
||||||
daemons/ipa-kdb/ipa_kdb.c | 3 +++
|
|
||||||
1 file changed, 3 insertions(+)
|
|
||||||
|
|
||||||
diff --git a/daemons/ipa-kdb/ipa_kdb.c b/daemons/ipa-kdb/ipa_kdb.c
|
|
||||||
index dbb98dba6..4e6cacf24 100644
|
|
||||||
--- a/daemons/ipa-kdb/ipa_kdb.c
|
|
||||||
+++ b/daemons/ipa-kdb/ipa_kdb.c
|
|
||||||
@@ -195,6 +195,9 @@ done:
|
|
||||||
return base;
|
|
||||||
}
|
|
||||||
|
|
||||||
+/* In this table all _AUTH_PASSWORD entries will be
|
|
||||||
+ * expanded to include _AUTH_HARDENED in ipadb_parse_user_auth()
|
|
||||||
+ * which means there is no need to explicitly add it here */
|
|
||||||
static const struct {
|
|
||||||
const char *name;
|
|
||||||
enum ipadb_user_auth flag;
|
|
||||||
--
|
|
||||||
2.43.0
|
|
||||||
|
|
||||||
|
|
||||||
From c3bc938650b19a51706d8ccd98cdf8deaa26dc28 Mon Sep 17 00:00:00 2001
|
|
||||||
From: Alexander Bokovoy <abokovoy@redhat.com>
|
|
||||||
Date: Fri, 24 Nov 2023 13:00:48 +0200
|
|
||||||
Subject: [PATCH 4/4] ipatests: make sure PKINIT enrollment works with a strict
|
|
||||||
policy
|
|
||||||
|
|
||||||
Previously, for a global policy which does not include
|
|
||||||
'password', krb5kdc restart was failing. Now it should succeed.
|
|
||||||
|
|
||||||
We set admin user authentication type to PASSWORD to simplify
|
|
||||||
configuration in the test.
|
|
||||||
|
|
||||||
What matters here is that global policy does not include PKINIT and that
|
|
||||||
means a code in the ticket policy check will allow PKINIT implicitly
|
|
||||||
rather than explicitly.
|
|
||||||
|
|
||||||
Related: https://pagure.io/freeipa/issue/9485
|
|
||||||
|
|
||||||
Signed-off-by: Alexander Bokovoy <abokovoy@redhat.com>
|
|
||||||
Reviewed-By: Francisco Trivino <ftrivino@redhat.com>
|
|
||||||
---
|
|
||||||
.../test_integration/test_pkinit_install.py | 26 +++++++++++++++++++
|
|
||||||
1 file changed, 26 insertions(+)
|
|
||||||
|
|
||||||
diff --git a/ipatests/test_integration/test_pkinit_install.py b/ipatests/test_integration/test_pkinit_install.py
|
|
||||||
index caa0e6a34..5c2e7af02 100644
|
|
||||||
--- a/ipatests/test_integration/test_pkinit_install.py
|
|
||||||
+++ b/ipatests/test_integration/test_pkinit_install.py
|
|
||||||
@@ -23,6 +23,24 @@ class TestPkinitClientInstall(IntegrationTest):
|
|
||||||
def install(cls, mh):
|
|
||||||
tasks.install_master(cls.master)
|
|
||||||
|
|
||||||
+ def enforce_password_and_otp(self):
|
|
||||||
+ """enforce otp by default and password for admin """
|
|
||||||
+ self.master.run_command(
|
|
||||||
+ [
|
|
||||||
+ "ipa",
|
|
||||||
+ "config-mod",
|
|
||||||
+ "--user-auth-type=otp",
|
|
||||||
+ ]
|
|
||||||
+ )
|
|
||||||
+ self.master.run_command(
|
|
||||||
+ [
|
|
||||||
+ "ipa",
|
|
||||||
+ "user-mod",
|
|
||||||
+ "admin",
|
|
||||||
+ "--user-auth-type=password",
|
|
||||||
+ ]
|
|
||||||
+ )
|
|
||||||
+
|
|
||||||
def add_certmaperule(self):
|
|
||||||
"""add certmap rule to map SAN dNSName to host entry"""
|
|
||||||
self.master.run_command(
|
|
||||||
@@ -86,6 +104,14 @@ class TestPkinitClientInstall(IntegrationTest):
|
|
||||||
cabundle = self.master.get_file_contents(paths.KDC_CA_BUNDLE_PEM)
|
|
||||||
client.put_file_contents(self.tmpbundle, cabundle)
|
|
||||||
|
|
||||||
+ def test_restart_krb5kdc(self):
|
|
||||||
+ tasks.kinit_admin(self.master)
|
|
||||||
+ self.enforce_password_and_otp()
|
|
||||||
+ self.master.run_command(['systemctl', 'stop', 'krb5kdc.service'])
|
|
||||||
+ self.master.run_command(['systemctl', 'start', 'krb5kdc.service'])
|
|
||||||
+ self.master.run_command(['systemctl', 'stop', 'kadmin.service'])
|
|
||||||
+ self.master.run_command(['systemctl', 'start', 'kadmin.service'])
|
|
||||||
+
|
|
||||||
def test_client_install_pkinit(self):
|
|
||||||
tasks.kinit_admin(self.master)
|
|
||||||
self.add_certmaperule()
|
|
||||||
--
|
|
||||||
2.43.0
|
|
||||||
|
|
@ -1,139 +0,0 @@
|
|||||||
From 48846e98e5e988d600ddf81c937f353fcecdea1a Mon Sep 17 00:00:00 2001
|
|
||||||
From: Rob Crittenden <rcritten@redhat.com>
|
|
||||||
Date: Mon, 27 Nov 2023 16:11:08 -0500
|
|
||||||
Subject: [PATCH 1/2] hbactest was not collecting or returning messages
|
|
||||||
|
|
||||||
hbactest does a number of internal searches, one of which
|
|
||||||
can exceed the configured sizelimit: hbacrule-find
|
|
||||||
|
|
||||||
Collect any messages returned from thsi call and display them
|
|
||||||
to the user on the cli.
|
|
||||||
|
|
||||||
Fixes: https://pagure.io/freeipa/issue/9486
|
|
||||||
|
|
||||||
Signed-off-by: Rob Crittenden <rcritten@redhat.com>
|
|
||||||
Reviewed-By: Florence Blanc-Renaud <flo@redhat.com>
|
|
||||||
---
|
|
||||||
ipaclient/plugins/hbactest.py | 2 ++
|
|
||||||
ipaserver/plugins/hbactest.py | 14 +++++++++++---
|
|
||||||
2 files changed, 13 insertions(+), 3 deletions(-)
|
|
||||||
|
|
||||||
diff --git a/ipaclient/plugins/hbactest.py b/ipaclient/plugins/hbactest.py
|
|
||||||
index 1b54530b2..e0f93b9c2 100644
|
|
||||||
--- a/ipaclient/plugins/hbactest.py
|
|
||||||
+++ b/ipaclient/plugins/hbactest.py
|
|
||||||
@@ -38,6 +38,8 @@ class hbactest(CommandOverride):
|
|
||||||
# Note that we don't actually use --detail below to see if details need
|
|
||||||
# to be printed as our execute() method will return None for corresponding
|
|
||||||
# entries and None entries will be skipped.
|
|
||||||
+ self.log_messages(output)
|
|
||||||
+
|
|
||||||
for o in self.output:
|
|
||||||
if o == 'value':
|
|
||||||
continue
|
|
||||||
diff --git a/ipaserver/plugins/hbactest.py b/ipaserver/plugins/hbactest.py
|
|
||||||
index 887a35b7e..568c13174 100644
|
|
||||||
--- a/ipaserver/plugins/hbactest.py
|
|
||||||
+++ b/ipaserver/plugins/hbactest.py
|
|
||||||
@@ -24,6 +24,8 @@ from ipalib import Command, Str, Flag, Int
|
|
||||||
from ipalib import _
|
|
||||||
from ipapython.dn import DN
|
|
||||||
from ipalib.plugable import Registry
|
|
||||||
+from ipalib.messages import VersionMissing
|
|
||||||
+
|
|
||||||
if api.env.in_server:
|
|
||||||
try:
|
|
||||||
import ipaserver.dcerpc
|
|
||||||
@@ -323,6 +325,9 @@ class hbactest(Command):
|
|
||||||
# 2. Required options are (user, target host, service)
|
|
||||||
# 3. Options: rules to test (--rules, --enabled, --disabled), request for detail output
|
|
||||||
rules = []
|
|
||||||
+ result = {
|
|
||||||
+ 'warning':None, 'matched':None, 'notmatched':None, 'error':None
|
|
||||||
+ }
|
|
||||||
|
|
||||||
# Use all enabled IPA rules by default
|
|
||||||
all_enabled = True
|
|
||||||
@@ -351,8 +356,12 @@ class hbactest(Command):
|
|
||||||
|
|
||||||
hbacset = []
|
|
||||||
if len(testrules) == 0:
|
|
||||||
- hbacset = self.api.Command.hbacrule_find(
|
|
||||||
- sizelimit=sizelimit, no_members=False)['result']
|
|
||||||
+ hbacrules = self.api.Command.hbacrule_find(
|
|
||||||
+ sizelimit=sizelimit, no_members=False)
|
|
||||||
+ hbacset = hbacrules['result']
|
|
||||||
+ for message in hbacrules['messages']:
|
|
||||||
+ if message['code'] != VersionMissing.errno:
|
|
||||||
+ result.setdefault('messages', []).append(message)
|
|
||||||
else:
|
|
||||||
for rule in testrules:
|
|
||||||
try:
|
|
||||||
@@ -469,7 +478,6 @@ class hbactest(Command):
|
|
||||||
error_rules = []
|
|
||||||
warning_rules = []
|
|
||||||
|
|
||||||
- result = {'warning':None, 'matched':None, 'notmatched':None, 'error':None}
|
|
||||||
if not options['nodetail']:
|
|
||||||
# Validate runs rules one-by-one and reports failed ones
|
|
||||||
for ipa_rule in rules:
|
|
||||||
--
|
|
||||||
2.43.0
|
|
||||||
|
|
||||||
|
|
||||||
From d1e09c68af8ac77f656dd639af5d9a7f07c41f9d Mon Sep 17 00:00:00 2001
|
|
||||||
From: Rob Crittenden <rcritten@redhat.com>
|
|
||||||
Date: Tue, 28 Nov 2023 13:35:13 -0500
|
|
||||||
Subject: [PATCH 2/2] ipatests: Verify that hbactest will return messages
|
|
||||||
|
|
||||||
Limit the sizelimit of the hbactest request to confirm that
|
|
||||||
the output includes a SearchResultTruncated message.
|
|
||||||
|
|
||||||
Fixes: https://pagure.io/freeipa/issue/9486
|
|
||||||
|
|
||||||
Signed-off-by: Rob Crittenden <rcritten@redhat.com>
|
|
||||||
Reviewed-By: Florence Blanc-Renaud <flo@redhat.com>
|
|
||||||
---
|
|
||||||
ipatests/test_xmlrpc/test_hbactest_plugin.py | 19 ++++++++++++++++++-
|
|
||||||
1 file changed, 18 insertions(+), 1 deletion(-)
|
|
||||||
|
|
||||||
diff --git a/ipatests/test_xmlrpc/test_hbactest_plugin.py b/ipatests/test_xmlrpc/test_hbactest_plugin.py
|
|
||||||
index 73c4ce232..e2e66c759 100644
|
|
||||||
--- a/ipatests/test_xmlrpc/test_hbactest_plugin.py
|
|
||||||
+++ b/ipatests/test_xmlrpc/test_hbactest_plugin.py
|
|
||||||
@@ -134,6 +134,7 @@ class test_hbactest(XMLRPC_test):
|
|
||||||
assert ret['value']
|
|
||||||
assert ret['error'] is None
|
|
||||||
assert ret['matched'] is None
|
|
||||||
+ assert 'messages' not in ret
|
|
||||||
assert ret['notmatched'] is None
|
|
||||||
|
|
||||||
def test_c_hbactest_check_rules_enabled_detail(self):
|
|
||||||
@@ -200,7 +201,23 @@ class test_hbactest(XMLRPC_test):
|
|
||||||
nodetail=True
|
|
||||||
)
|
|
||||||
|
|
||||||
- def test_g_hbactest_clear_testing_data(self):
|
|
||||||
+ def test_g_hbactest_searchlimit_message(self):
|
|
||||||
+ """
|
|
||||||
+ Test running 'ipa hbactest' with limited --sizelimit
|
|
||||||
+
|
|
||||||
+ We know there are at least 6 rules, 4 created here + 2 default.
|
|
||||||
+ """
|
|
||||||
+ ret = api.Command['hbactest'](
|
|
||||||
+ user=self.test_user,
|
|
||||||
+ targethost=self.test_host,
|
|
||||||
+ service=self.test_service,
|
|
||||||
+ nodetail=True,
|
|
||||||
+ sizelimit=2,
|
|
||||||
+ )
|
|
||||||
+
|
|
||||||
+ assert ret['messages'] is not None
|
|
||||||
+
|
|
||||||
+ def test_h_hbactest_clear_testing_data(self):
|
|
||||||
"""
|
|
||||||
Clear data for HBAC test plugin testing.
|
|
||||||
"""
|
|
||||||
--
|
|
||||||
2.43.0
|
|
||||||
|
|
@ -0,0 +1,36 @@
|
|||||||
|
From c411c2e7b2e400829ffac250db81609ef3c56faa Mon Sep 17 00:00:00 2001
|
||||||
|
From: Florence Blanc-Renaud <flo@redhat.com>
|
||||||
|
Date: Tue, 29 Nov 2022 10:04:41 +0100
|
||||||
|
Subject: [PATCH] webui tests: fix assertion in test_subid.py
|
||||||
|
|
||||||
|
The test wants to check the error related to an
|
||||||
|
exception obtained inside a "with pytest.raises" instruction.
|
||||||
|
The object is an ExceptionInfo and offers a match method
|
||||||
|
to check the content of the string representation.
|
||||||
|
Use this match() method instead of str(excinfo) which now
|
||||||
|
returns
|
||||||
|
'<ExceptionInfo NoSuchElementException() tblen=10>'
|
||||||
|
|
||||||
|
Fixes: https://pagure.io/freeipa/issue/9282
|
||||||
|
|
||||||
|
Signed-off-by: Florence Blanc-Renaud <flo@redhat.com>
|
||||||
|
Reviewed-By: Mohammad Rizwan Yusuf <myusuf@redhat.com>
|
||||||
|
---
|
||||||
|
ipatests/test_webui/test_subid.py | 4 ++--
|
||||||
|
1 file changed, 2 insertions(+), 2 deletions(-)
|
||||||
|
|
||||||
|
diff --git a/ipatests/test_webui/test_subid.py b/ipatests/test_webui/test_subid.py
|
||||||
|
index 104b5692da94437880e638c0b2bc8efd41bd969e..3aaf80ac885fea08d0bac7e2f46645fe207f2cb0 100644
|
||||||
|
--- a/ipatests/test_webui/test_subid.py
|
||||||
|
+++ b/ipatests/test_webui/test_subid.py
|
||||||
|
@@ -146,5 +146,5 @@ class test_subid(UI_driver):
|
||||||
|
with pytest.raises(NoSuchElementException) as excinfo:
|
||||||
|
self.delete_record(admin_uid, table_name="ipauniqueid")
|
||||||
|
# Ensure that the exception is really related to missing remove button
|
||||||
|
- msg = "Unable to locate element: .facet-controls button[name=remove]"
|
||||||
|
- assert msg in str(excinfo)
|
||||||
|
+ msg = r"Unable to locate element: .facet-controls button\[name=remove\]"
|
||||||
|
+ assert excinfo.match(msg)
|
||||||
|
--
|
||||||
|
2.39.1
|
||||||
|
|
29
SOURCES/0016-ipatests-mark-test_smb-as-xfail.patch
Normal file
29
SOURCES/0016-ipatests-mark-test_smb-as-xfail.patch
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
From b5f2b0b1b213149b5bfe2653c9e40de98249dc73 Mon Sep 17 00:00:00 2001
|
||||||
|
From: Florence Blanc-Renaud <flo@redhat.com>
|
||||||
|
Date: Tue, 10 Jan 2023 11:45:17 +0100
|
||||||
|
Subject: [PATCH] ipatests: mark test_smb as xfail
|
||||||
|
|
||||||
|
Mark the test test_smb.py::TestSMB::test_smb_service_s4u2self as xfail.
|
||||||
|
|
||||||
|
Related: https://pagure.io/freeipa/issue/9124
|
||||||
|
Signed-off-by: Florence Blanc-Renaud <flo@redhat.com>
|
||||||
|
Reviewed-By: Alexander Bokovoy <abokovoy@redhat.com>
|
||||||
|
---
|
||||||
|
ipatests/test_integration/test_smb.py | 1 +
|
||||||
|
1 file changed, 1 insertion(+)
|
||||||
|
|
||||||
|
diff --git a/ipatests/test_integration/test_smb.py b/ipatests/test_integration/test_smb.py
|
||||||
|
index eb3981bddb7ca9f72a0d2cb6c46e5c73de8623ac..30f8d5901afbcda95f27cd966ac03d47205dbb26 100644
|
||||||
|
--- a/ipatests/test_integration/test_smb.py
|
||||||
|
+++ b/ipatests/test_integration/test_smb.py
|
||||||
|
@@ -349,6 +349,7 @@ class TestSMB(IntegrationTest):
|
||||||
|
@pytest.mark.skipif(
|
||||||
|
osinfo.id == 'fedora' and osinfo.version_number <= (31,),
|
||||||
|
reason='Test requires krb 1.18')
|
||||||
|
+ @pytest.mark.xfail(reason="Pagure ticket 9124", strict=True)
|
||||||
|
def test_smb_service_s4u2self(self):
|
||||||
|
"""Test S4U2Self operation by IPA service
|
||||||
|
against both AD and IPA users
|
||||||
|
--
|
||||||
|
2.39.1
|
||||||
|
|
@ -1,43 +0,0 @@
|
|||||||
From 16a739e0260f97705827f972d53c828809dbfdb2 Mon Sep 17 00:00:00 2001
|
|
||||||
From: Masahiro Matsuya <mmatsuya@redhat.com>
|
|
||||||
Date: Tue, 9 Jan 2024 23:12:11 +0900
|
|
||||||
Subject: [PATCH] ipatests: wait for replica update in test_dns_locations
|
|
||||||
|
|
||||||
test_ipa_ca_records and test_adtrust_system_records can fail with
|
|
||||||
NXDOMAIN, because it doesn't wait enough for the update on replica.
|
|
||||||
It can be resolved by waiting for the update with wait_for_replication.
|
|
||||||
|
|
||||||
Fixes: https://pagure.io/freeipa/issue/9504
|
|
||||||
Reviewed-By: Florence Blanc-Renaud <flo@redhat.com>
|
|
||||||
(cherry picked from commit 905a55a4ef926068630ebd2ab375f58c24dedcd1)
|
|
||||||
---
|
|
||||||
ipatests/test_integration/test_dns_locations.py | 6 ++++++
|
|
||||||
1 file changed, 6 insertions(+)
|
|
||||||
|
|
||||||
diff --git a/ipatests/test_integration/test_dns_locations.py b/ipatests/test_integration/test_dns_locations.py
|
|
||||||
index 44900af80..89a310892 100644
|
|
||||||
--- a/ipatests/test_integration/test_dns_locations.py
|
|
||||||
+++ b/ipatests/test_integration/test_dns_locations.py
|
|
||||||
@@ -534,6 +534,9 @@ class TestDNSLocations(IntegrationTest):
|
|
||||||
|
|
||||||
expected_servers = (self.master.ip, self.replicas[1].ip)
|
|
||||||
|
|
||||||
+ ldap = self.master.ldap_connect()
|
|
||||||
+ tasks.wait_for_replication(ldap)
|
|
||||||
+
|
|
||||||
for ip in (self.master.ip, self.replicas[0].ip, self.replicas[1].ip):
|
|
||||||
self._test_A_rec_against_server(ip, self.domain, expected_servers)
|
|
||||||
|
|
||||||
@@ -557,6 +560,9 @@ class TestDNSLocations(IntegrationTest):
|
|
||||||
(self.PRIO_HIGH, self.WEIGHT, DNSName(self.master.hostname)),
|
|
||||||
)
|
|
||||||
|
|
||||||
+ ldap = self.master.ldap_connect()
|
|
||||||
+ tasks.wait_for_replication(ldap)
|
|
||||||
+
|
|
||||||
for ip in (self.master.ip, self.replicas[0].ip, self.replicas[1].ip):
|
|
||||||
self._test_SRV_rec_against_server(
|
|
||||||
ip, self.domain, expected_servers,
|
|
||||||
--
|
|
||||||
2.43.0
|
|
||||||
|
|
43
SOURCES/0017-Tests-force-key-type-in-ACME-tests.patch
Normal file
43
SOURCES/0017-Tests-force-key-type-in-ACME-tests.patch
Normal file
@ -0,0 +1,43 @@
|
|||||||
|
From 36cba23f3f671886f5e7fa310c25a6e500c76e0b Mon Sep 17 00:00:00 2001
|
||||||
|
From: Florence Blanc-Renaud <flo@redhat.com>
|
||||||
|
Date: Mon, 16 Jan 2023 09:31:57 +0100
|
||||||
|
Subject: [PATCH] Tests: force key type in ACME tests
|
||||||
|
|
||||||
|
PKI can issue ACME certs only when the key type is rsa.
|
||||||
|
|
||||||
|
With version 2.0.0, certbot defaults to ecdsa key type,
|
||||||
|
and this causes test failures.
|
||||||
|
For now, force rsa when requesting an ACME certificate.
|
||||||
|
This change can be reverted when PKI fixes the issue
|
||||||
|
on their side (https://github.com/dogtagpki/pki/issues/4273)
|
||||||
|
|
||||||
|
Related: https://pagure.io/freeipa/issue/9298
|
||||||
|
Signed-off-by: Florence Blanc-Renaud <flo@redhat.com>
|
||||||
|
Reviewed-By: Rob Crittenden <rcritten@redhat.com>
|
||||||
|
---
|
||||||
|
ipatests/test_integration/test_acme.py | 2 ++
|
||||||
|
1 file changed, 2 insertions(+)
|
||||||
|
|
||||||
|
diff --git a/ipatests/test_integration/test_acme.py b/ipatests/test_integration/test_acme.py
|
||||||
|
index a30f2fc756783c0a5c28ecf32c1e40f422c47a19..15d7543cfb0fa0fcb921166f7cd8f13d0535a41d 100644
|
||||||
|
--- a/ipatests/test_integration/test_acme.py
|
||||||
|
+++ b/ipatests/test_integration/test_acme.py
|
||||||
|
@@ -131,6 +131,7 @@ def certbot_standalone_cert(host, acme_server):
|
||||||
|
'certonly',
|
||||||
|
'--domain', host.hostname,
|
||||||
|
'--standalone',
|
||||||
|
+ '--key-type', 'rsa',
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|
||||||
|
@@ -305,6 +306,7 @@ class TestACME(CALessBase):
|
||||||
|
'--manual-public-ip-logging-ok',
|
||||||
|
'--manual-auth-hook', CERTBOT_DNS_IPA_SCRIPT,
|
||||||
|
'--manual-cleanup-hook', CERTBOT_DNS_IPA_SCRIPT,
|
||||||
|
+ '--key-type', 'rsa',
|
||||||
|
])
|
||||||
|
|
||||||
|
##############
|
||||||
|
--
|
||||||
|
2.39.1
|
||||||
|
|
@ -1,707 +0,0 @@
|
|||||||
From c3ac69e9cf8dfcc31ed11fc988c37bd99d3ec3cf Mon Sep 17 00:00:00 2001
|
|
||||||
From: Julien Rische <jrische@redhat.com>
|
|
||||||
Date: Wed, 14 Feb 2024 17:47:00 +0100
|
|
||||||
Subject: [PATCH] ipa-kdb: Rework ipadb_reinit_mspac()
|
|
||||||
|
|
||||||
Modify ipadb_reinit_mspac() to allocate and initialize ipactx->mspac
|
|
||||||
only if all its attributes can be set. If not, ipactx->mspac is set to
|
|
||||||
NULL. This makes easier to determine if the KDC is able to generate PACs
|
|
||||||
or not.
|
|
||||||
|
|
||||||
Also ipadb_reinit_mspac() is now able to return a status message
|
|
||||||
explaining why initialization of the PAC generator failed. This message
|
|
||||||
is printed in KDC logs.
|
|
||||||
|
|
||||||
Fixes: https://pagure.io/freeipa/issue/9535
|
|
||||||
|
|
||||||
Signed-off-by: Julien Rische <jrische@redhat.com>
|
|
||||||
Reviewed-By: Alexander Bokovoy <abbra@users.noreply.github.com>
|
|
||||||
(cherry picked from commit 7f072e348d318e928f6270a182ca04dee8716677)
|
|
||||||
---
|
|
||||||
daemons/ipa-kdb/ipa_kdb.c | 14 +-
|
|
||||||
daemons/ipa-kdb/ipa_kdb.h | 4 +-
|
|
||||||
daemons/ipa-kdb/ipa_kdb_mspac.c | 340 +++++++++++++-----------
|
|
||||||
daemons/ipa-kdb/ipa_kdb_mspac_private.h | 2 +-
|
|
||||||
daemons/ipa-kdb/ipa_kdb_mspac_v6.c | 5 +-
|
|
||||||
daemons/ipa-kdb/ipa_kdb_mspac_v9.c | 16 +-
|
|
||||||
daemons/ipa-kdb/ipa_kdb_principals.c | 6 +-
|
|
||||||
7 files changed, 218 insertions(+), 169 deletions(-)
|
|
||||||
|
|
||||||
diff --git a/daemons/ipa-kdb/ipa_kdb.c b/daemons/ipa-kdb/ipa_kdb.c
|
|
||||||
index 0c6325df9..fcadb8ee7 100644
|
|
||||||
--- a/daemons/ipa-kdb/ipa_kdb.c
|
|
||||||
+++ b/daemons/ipa-kdb/ipa_kdb.c
|
|
||||||
@@ -443,6 +443,7 @@ int ipadb_get_connection(struct ipadb_context *ipactx)
|
|
||||||
struct timeval tv = { 5, 0 };
|
|
||||||
LDAPMessage *res = NULL;
|
|
||||||
LDAPMessage *first;
|
|
||||||
+ const char *stmsg;
|
|
||||||
int ret;
|
|
||||||
int v3;
|
|
||||||
|
|
||||||
@@ -522,16 +523,9 @@ int ipadb_get_connection(struct ipadb_context *ipactx)
|
|
||||||
}
|
|
||||||
|
|
||||||
/* get adtrust options using default refresh interval */
|
|
||||||
- ret = ipadb_reinit_mspac(ipactx, false);
|
|
||||||
- if (ret && ret != ENOENT) {
|
|
||||||
- /* TODO: log that there is an issue with adtrust settings */
|
|
||||||
- if (ipactx->lcontext == NULL) {
|
|
||||||
- /* for some reason ldap connection was reset in ipadb_reinit_mspac
|
|
||||||
- * and is no longer established => failure of ipadb_get_connection
|
|
||||||
- */
|
|
||||||
- goto done;
|
|
||||||
- }
|
|
||||||
- }
|
|
||||||
+ ret = ipadb_reinit_mspac(ipactx, false, &stmsg);
|
|
||||||
+ if (ret && stmsg)
|
|
||||||
+ krb5_klog_syslog(LOG_WARNING, "MS-PAC generator: %s", stmsg);
|
|
||||||
|
|
||||||
ret = 0;
|
|
||||||
|
|
||||||
diff --git a/daemons/ipa-kdb/ipa_kdb.h b/daemons/ipa-kdb/ipa_kdb.h
|
|
||||||
index 5de5ea7a5..7baf4697f 100644
|
|
||||||
--- a/daemons/ipa-kdb/ipa_kdb.h
|
|
||||||
+++ b/daemons/ipa-kdb/ipa_kdb.h
|
|
||||||
@@ -352,7 +352,9 @@ krb5_error_code ipadb_v9_issue_pac(krb5_context context, unsigned int flags,
|
|
||||||
krb5_data ***auth_indicators);
|
|
||||||
#endif
|
|
||||||
|
|
||||||
-krb5_error_code ipadb_reinit_mspac(struct ipadb_context *ipactx, bool force_reinit);
|
|
||||||
+krb5_error_code ipadb_reinit_mspac(struct ipadb_context *ipactx,
|
|
||||||
+ bool force_reinit,
|
|
||||||
+ const char **stmsg);
|
|
||||||
|
|
||||||
void ipadb_mspac_struct_free(struct ipadb_mspac **mspac);
|
|
||||||
krb5_error_code ipadb_check_transited_realms(krb5_context kcontext,
|
|
||||||
diff --git a/daemons/ipa-kdb/ipa_kdb_mspac.c b/daemons/ipa-kdb/ipa_kdb_mspac.c
|
|
||||||
index 886ed7785..deed513b9 100644
|
|
||||||
--- a/daemons/ipa-kdb/ipa_kdb_mspac.c
|
|
||||||
+++ b/daemons/ipa-kdb/ipa_kdb_mspac.c
|
|
||||||
@@ -793,16 +793,16 @@ static krb5_error_code ipadb_fill_info3(struct ipadb_context *ipactx,
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
+ if (!ipactx->mspac) {
|
|
||||||
+ /* can't give a PAC without server NetBIOS name or primary group RID */
|
|
||||||
+ return ENOENT;
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
if (info3->base.primary_gid == 0) {
|
|
||||||
if (is_host || is_service) {
|
|
||||||
info3->base.primary_gid = 515; /* Well known RID for domain computers group */
|
|
||||||
} else {
|
|
||||||
- if (ipactx->mspac->fallback_rid) {
|
|
||||||
- info3->base.primary_gid = ipactx->mspac->fallback_rid;
|
|
||||||
- } else {
|
|
||||||
- /* can't give a pack without a primary group rid */
|
|
||||||
- return ENOENT;
|
|
||||||
- }
|
|
||||||
+ info3->base.primary_gid = ipactx->mspac->fallback_rid;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@@ -812,26 +812,16 @@ static krb5_error_code ipadb_fill_info3(struct ipadb_context *ipactx,
|
|
||||||
/* always zero out, not used for Krb, only NTLM */
|
|
||||||
memset(&info3->base.key, '\0', sizeof(info3->base.key));
|
|
||||||
|
|
||||||
- if (ipactx->mspac->flat_server_name) {
|
|
||||||
- info3->base.logon_server.string =
|
|
||||||
- talloc_strdup(memctx, ipactx->mspac->flat_server_name);
|
|
||||||
- if (!info3->base.logon_server.string) {
|
|
||||||
- return ENOMEM;
|
|
||||||
- }
|
|
||||||
- } else {
|
|
||||||
- /* can't give a pack without Server NetBIOS Name :-| */
|
|
||||||
- return ENOENT;
|
|
||||||
+ info3->base.logon_server.string =
|
|
||||||
+ talloc_strdup(memctx, ipactx->mspac->flat_server_name);
|
|
||||||
+ if (!info3->base.logon_server.string) {
|
|
||||||
+ return ENOMEM;
|
|
||||||
}
|
|
||||||
|
|
||||||
- if (ipactx->mspac->flat_domain_name) {
|
|
||||||
- info3->base.logon_domain.string =
|
|
||||||
- talloc_strdup(memctx, ipactx->mspac->flat_domain_name);
|
|
||||||
- if (!info3->base.logon_domain.string) {
|
|
||||||
- return ENOMEM;
|
|
||||||
- }
|
|
||||||
- } else {
|
|
||||||
- /* can't give a pack without Domain NetBIOS Name :-| */
|
|
||||||
- return ENOENT;
|
|
||||||
+ info3->base.logon_domain.string =
|
|
||||||
+ talloc_strdup(memctx, ipactx->mspac->flat_domain_name);
|
|
||||||
+ if (!info3->base.logon_domain.string) {
|
|
||||||
+ return ENOMEM;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (is_host || is_service) {
|
|
||||||
@@ -1044,6 +1034,11 @@ krb5_error_code ipadb_get_pac(krb5_context kcontext,
|
|
||||||
return KRB5_KDB_DBNOTINITED;
|
|
||||||
}
|
|
||||||
|
|
||||||
+ /* Check if PAC generator is initialized */
|
|
||||||
+ if (!ipactx->mspac) {
|
|
||||||
+ return ENOENT;
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
ied = (struct ipadb_e_data *)client->e_data;
|
|
||||||
if (ied->magic != IPA_E_DATA_MAGIC) {
|
|
||||||
return EINVAL;
|
|
||||||
@@ -1626,14 +1621,14 @@ static struct ipadb_adtrusts *get_domain_from_realm(krb5_context context,
|
|
||||||
{
|
|
||||||
struct ipadb_context *ipactx;
|
|
||||||
struct ipadb_adtrusts *domain;
|
|
||||||
- int i;
|
|
||||||
+ size_t i;
|
|
||||||
|
|
||||||
ipactx = ipadb_get_context(context);
|
|
||||||
if (!ipactx) {
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
- if (ipactx->mspac == NULL) {
|
|
||||||
+ if (!ipactx->mspac) {
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
|
|
||||||
@@ -1655,6 +1650,7 @@ static struct ipadb_adtrusts *get_domain_from_realm_update(krb5_context context,
|
|
||||||
{
|
|
||||||
struct ipadb_context *ipactx;
|
|
||||||
struct ipadb_adtrusts *domain;
|
|
||||||
+ const char *stmsg = NULL;
|
|
||||||
krb5_error_code kerr;
|
|
||||||
|
|
||||||
ipactx = ipadb_get_context(context);
|
|
||||||
@@ -1663,8 +1659,10 @@ static struct ipadb_adtrusts *get_domain_from_realm_update(krb5_context context,
|
|
||||||
}
|
|
||||||
|
|
||||||
/* re-init MS-PAC info using default update interval */
|
|
||||||
- kerr = ipadb_reinit_mspac(ipactx, false);
|
|
||||||
+ kerr = ipadb_reinit_mspac(ipactx, false, &stmsg);
|
|
||||||
if (kerr != 0) {
|
|
||||||
+ if (stmsg)
|
|
||||||
+ krb5_klog_syslog(LOG_WARNING, "MS-PAC generator: %s", stmsg);
|
|
||||||
return NULL;
|
|
||||||
}
|
|
||||||
domain = get_domain_from_realm(context, realm);
|
|
||||||
@@ -1717,6 +1715,7 @@ static krb5_error_code check_logon_info_consistent(krb5_context context,
|
|
||||||
struct ipadb_e_data *ied = NULL;
|
|
||||||
int flags = 0;
|
|
||||||
struct dom_sid client_sid;
|
|
||||||
+ const char *stmsg = NULL;
|
|
||||||
#ifdef KRB5_KDB_FLAG_ALIAS_OK
|
|
||||||
flags = KRB5_KDB_FLAG_ALIAS_OK;
|
|
||||||
#endif
|
|
||||||
@@ -1730,10 +1729,14 @@ static krb5_error_code check_logon_info_consistent(krb5_context context,
|
|
||||||
* check that our own view on the PAC details is up to date */
|
|
||||||
if (ipactx->mspac->domsid.num_auths == 0) {
|
|
||||||
/* Force re-init of KDB's view on our domain */
|
|
||||||
- kerr = ipadb_reinit_mspac(ipactx, true);
|
|
||||||
+ kerr = ipadb_reinit_mspac(ipactx, true, &stmsg);
|
|
||||||
if (kerr != 0) {
|
|
||||||
- krb5_klog_syslog(LOG_ERR,
|
|
||||||
- "PAC issue: unable to update realm's view on PAC info");
|
|
||||||
+ if (stmsg) {
|
|
||||||
+ krb5_klog_syslog(LOG_ERR, "MS-PAC generator: %s", stmsg);
|
|
||||||
+ } else {
|
|
||||||
+ krb5_klog_syslog(LOG_ERR, "PAC issue: unable to update " \
|
|
||||||
+ "realm's view on PAC info");
|
|
||||||
+ }
|
|
||||||
return KRB5KDC_ERR_POLICY;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -1746,7 +1749,7 @@ static krb5_error_code check_logon_info_consistent(krb5_context context,
|
|
||||||
if (is_s4u && (ipactx->mspac->trusts != NULL)) {
|
|
||||||
/* Iterate through list of trusts and check if this SID belongs to
|
|
||||||
* one of the domains we trust */
|
|
||||||
- for(int i = 0 ; i < ipactx->mspac->num_trusts ; i++) {
|
|
||||||
+ for(size_t i = 0 ; i < ipactx->mspac->num_trusts ; i++) {
|
|
||||||
result = dom_sid_check(&ipactx->mspac->trusts[i].domsid,
|
|
||||||
info->info->info3.base.domain_sid, true);
|
|
||||||
if (result) {
|
|
||||||
@@ -1858,11 +1861,11 @@ krb5_error_code filter_logon_info(krb5_context context,
|
|
||||||
struct ipadb_mspac *mspac_ctx = ipactx->mspac;
|
|
||||||
result = FALSE;
|
|
||||||
/* Didn't match but perhaps the original PAC was issued by a child domain's DC? */
|
|
||||||
- for (k = 0; k < mspac_ctx->num_trusts; k++) {
|
|
||||||
- result = dom_sid_check(&mspac_ctx->trusts[k].domsid,
|
|
||||||
+ for (size_t m = 0; m < mspac_ctx->num_trusts; m++) {
|
|
||||||
+ result = dom_sid_check(&mspac_ctx->trusts[m].domsid,
|
|
||||||
info->info->info3.base.domain_sid, true);
|
|
||||||
if (result) {
|
|
||||||
- domain = &mspac_ctx->trusts[k];
|
|
||||||
+ domain = &mspac_ctx->trusts[m];
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -2091,10 +2094,10 @@ static krb5_error_code ipadb_check_logon_info(krb5_context context,
|
|
||||||
return KRB5_KDB_DBNOTINITED;
|
|
||||||
}
|
|
||||||
/* In S4U case we might be dealing with the PAC issued by the trusted domain */
|
|
||||||
- if ((ipactx->mspac->trusts != NULL)) {
|
|
||||||
+ if (ipactx->mspac->trusts) {
|
|
||||||
/* Iterate through list of trusts and check if this SID belongs to
|
|
||||||
* one of the domains we trust */
|
|
||||||
- for(int i = 0 ; i < ipactx->mspac->num_trusts ; i++) {
|
|
||||||
+ for(size_t i = 0 ; i < ipactx->mspac->num_trusts ; i++) {
|
|
||||||
result = dom_sid_check(&ipactx->mspac->trusts[i].domsid,
|
|
||||||
&client_sid, false);
|
|
||||||
if (result) {
|
|
||||||
@@ -2631,7 +2634,7 @@ static char *get_server_netbios_name(struct ipadb_context *ipactx)
|
|
||||||
|
|
||||||
void ipadb_mspac_struct_free(struct ipadb_mspac **mspac)
|
|
||||||
{
|
|
||||||
- int i, j;
|
|
||||||
+ size_t i, j;
|
|
||||||
|
|
||||||
if (!*mspac) return;
|
|
||||||
|
|
||||||
@@ -2786,7 +2789,8 @@ ipadb_mspac_get_trusted_domains(struct ipadb_context *ipactx)
|
|
||||||
LDAPDN dn = NULL;
|
|
||||||
char **sid_blocklist_incoming = NULL;
|
|
||||||
char **sid_blocklist_outgoing = NULL;
|
|
||||||
- int ret, n, i;
|
|
||||||
+ size_t i, n;
|
|
||||||
+ int ret;
|
|
||||||
|
|
||||||
ret = asprintf(&base, "cn=ad,cn=trusts,%s", ipactx->base);
|
|
||||||
if (ret == -1) {
|
|
||||||
@@ -2871,7 +2875,7 @@ ipadb_mspac_get_trusted_domains(struct ipadb_context *ipactx)
|
|
||||||
|
|
||||||
t[n].upn_suffixes_len = NULL;
|
|
||||||
if (t[n].upn_suffixes != NULL) {
|
|
||||||
- int len = 0;
|
|
||||||
+ size_t len = 0;
|
|
||||||
|
|
||||||
for (; t[n].upn_suffixes[len] != NULL; len++);
|
|
||||||
|
|
||||||
@@ -2986,108 +2990,114 @@ done:
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
-krb5_error_code ipadb_reinit_mspac(struct ipadb_context *ipactx, bool force_reinit)
|
|
||||||
+krb5_error_code
|
|
||||||
+ipadb_reinit_mspac(struct ipadb_context *ipactx, bool force_reinit,
|
|
||||||
+ const char **stmsg)
|
|
||||||
{
|
|
||||||
char *dom_attrs[] = { "ipaNTFlatName",
|
|
||||||
"ipaNTFallbackPrimaryGroup",
|
|
||||||
"ipaNTSecurityIdentifier",
|
|
||||||
NULL };
|
|
||||||
char *grp_attrs[] = { "ipaNTSecurityIdentifier", NULL };
|
|
||||||
- krb5_error_code kerr;
|
|
||||||
LDAPMessage *result = NULL;
|
|
||||||
LDAPMessage *lentry;
|
|
||||||
- struct dom_sid gsid;
|
|
||||||
- char *resstr;
|
|
||||||
- int ret;
|
|
||||||
+ struct dom_sid gsid, domsid;
|
|
||||||
+ char *resstr = NULL;
|
|
||||||
+ char *flat_domain_name = NULL;
|
|
||||||
+ char *flat_server_name = NULL;
|
|
||||||
+ char *fallback_group = NULL;
|
|
||||||
+ uint32_t fallback_rid;
|
|
||||||
time_t now;
|
|
||||||
+ const char *in_stmsg = NULL;
|
|
||||||
+ int err;
|
|
||||||
+ krb5_error_code trust_kerr = 0;
|
|
||||||
+
|
|
||||||
|
|
||||||
/* Do not update the mspac struct more than once a minute. This would
|
|
||||||
* avoid heavy load on the directory server if there are lots of requests
|
|
||||||
* from domains which we do not trust. */
|
|
||||||
now = time(NULL);
|
|
||||||
|
|
||||||
- if (ipactx->mspac != NULL &&
|
|
||||||
- (force_reinit == false) &&
|
|
||||||
- (now > ipactx->mspac->last_update) &&
|
|
||||||
- (now - ipactx->mspac->last_update) < 60) {
|
|
||||||
- return 0;
|
|
||||||
- }
|
|
||||||
-
|
|
||||||
- if (ipactx->mspac && ipactx->mspac->num_trusts == 0) {
|
|
||||||
- /* Check if there is any trust configured. If not, just return
|
|
||||||
- * and do not re-initialize the MS-PAC structure. */
|
|
||||||
- kerr = ipadb_mspac_check_trusted_domains(ipactx);
|
|
||||||
- if (kerr == KRB5_KDB_NOENTRY) {
|
|
||||||
- kerr = 0;
|
|
||||||
- goto done;
|
|
||||||
- } else if (kerr != 0) {
|
|
||||||
- goto done;
|
|
||||||
+ if (ipactx->mspac) {
|
|
||||||
+ if (!force_reinit &&
|
|
||||||
+ (now > ipactx->mspac->last_update) &&
|
|
||||||
+ (now - ipactx->mspac->last_update) < 60) {
|
|
||||||
+ /* SKIP */
|
|
||||||
+ err = 0;
|
|
||||||
+ goto end;
|
|
||||||
}
|
|
||||||
- }
|
|
||||||
-
|
|
||||||
- /* clean up in case we had old values around */
|
|
||||||
- ipadb_mspac_struct_free(&ipactx->mspac);
|
|
||||||
|
|
||||||
- ipactx->mspac = calloc(1, sizeof(struct ipadb_mspac));
|
|
||||||
- if (!ipactx->mspac) {
|
|
||||||
- kerr = ENOMEM;
|
|
||||||
- goto done;
|
|
||||||
+ if (ipactx->mspac->num_trusts == 0) {
|
|
||||||
+ /* Check if there is any trust configured. If not, just return
|
|
||||||
+ * and do not re-initialize the MS-PAC structure. */
|
|
||||||
+ err = ipadb_mspac_check_trusted_domains(ipactx);
|
|
||||||
+ if (err) {
|
|
||||||
+ if (err == KRB5_KDB_NOENTRY) {
|
|
||||||
+ /* SKIP */
|
|
||||||
+ err = 0;
|
|
||||||
+ } else {
|
|
||||||
+ in_stmsg = "Failed to fetch trusted domains information";
|
|
||||||
+ }
|
|
||||||
+ goto end;
|
|
||||||
+ }
|
|
||||||
+ }
|
|
||||||
}
|
|
||||||
|
|
||||||
- ipactx->mspac->last_update = now;
|
|
||||||
-
|
|
||||||
- kerr = ipadb_simple_search(ipactx, ipactx->base, LDAP_SCOPE_SUBTREE,
|
|
||||||
- "(objectclass=ipaNTDomainAttrs)", dom_attrs,
|
|
||||||
- &result);
|
|
||||||
- if (kerr == KRB5_KDB_NOENTRY) {
|
|
||||||
- return ENOENT;
|
|
||||||
- } else if (kerr != 0) {
|
|
||||||
- return EIO;
|
|
||||||
+ err = ipadb_simple_search(ipactx, ipactx->base, LDAP_SCOPE_SUBTREE,
|
|
||||||
+ "(objectclass=ipaNTDomainAttrs)", dom_attrs,
|
|
||||||
+ &result);
|
|
||||||
+ if (err == KRB5_KDB_NOENTRY) {
|
|
||||||
+ err = ENOENT;
|
|
||||||
+ in_stmsg = "Local domain NT attributes not configured";
|
|
||||||
+ goto end;
|
|
||||||
+ } else if (err) {
|
|
||||||
+ err = EIO;
|
|
||||||
+ in_stmsg = "Failed to fetch local domain NT attributes";
|
|
||||||
+ goto end;
|
|
||||||
}
|
|
||||||
|
|
||||||
lentry = ldap_first_entry(ipactx->lcontext, result);
|
|
||||||
if (!lentry) {
|
|
||||||
- kerr = ENOENT;
|
|
||||||
- goto done;
|
|
||||||
+ err = ENOENT;
|
|
||||||
+ in_stmsg = "Local domain NT attributes not configured";
|
|
||||||
+ goto end;
|
|
||||||
}
|
|
||||||
|
|
||||||
- ret = ipadb_ldap_attr_to_str(ipactx->lcontext, lentry,
|
|
||||||
- "ipaNTFlatName",
|
|
||||||
- &ipactx->mspac->flat_domain_name);
|
|
||||||
- if (ret) {
|
|
||||||
- kerr = ret;
|
|
||||||
- goto done;
|
|
||||||
+ err = ipadb_ldap_attr_to_str(ipactx->lcontext, lentry, "ipaNTFlatName",
|
|
||||||
+ &flat_domain_name);
|
|
||||||
+ if (err) {
|
|
||||||
+ in_stmsg = "Local domain NT flat name not configured";
|
|
||||||
+ goto end;
|
|
||||||
}
|
|
||||||
|
|
||||||
- ret = ipadb_ldap_attr_to_str(ipactx->lcontext, lentry,
|
|
||||||
- "ipaNTSecurityIdentifier",
|
|
||||||
- &resstr);
|
|
||||||
- if (ret) {
|
|
||||||
- kerr = ret;
|
|
||||||
- goto done;
|
|
||||||
+ err = ipadb_ldap_attr_to_str(ipactx->lcontext, lentry,
|
|
||||||
+ "ipaNTSecurityIdentifier", &resstr);
|
|
||||||
+ if (err) {
|
|
||||||
+ in_stmsg = "Local domain SID not configured";
|
|
||||||
+ goto end;
|
|
||||||
}
|
|
||||||
|
|
||||||
- ret = ipadb_string_to_sid(resstr, &ipactx->mspac->domsid);
|
|
||||||
- if (ret) {
|
|
||||||
- kerr = ret;
|
|
||||||
- free(resstr);
|
|
||||||
- goto done;
|
|
||||||
+ err = ipadb_string_to_sid(resstr, &domsid);
|
|
||||||
+ if (err) {
|
|
||||||
+ in_stmsg = "Malformed local domain SID";
|
|
||||||
+ goto end;
|
|
||||||
}
|
|
||||||
+
|
|
||||||
free(resstr);
|
|
||||||
|
|
||||||
- free(ipactx->mspac->flat_server_name);
|
|
||||||
- ipactx->mspac->flat_server_name = get_server_netbios_name(ipactx);
|
|
||||||
- if (!ipactx->mspac->flat_server_name) {
|
|
||||||
- kerr = ENOMEM;
|
|
||||||
- goto done;
|
|
||||||
+ flat_server_name = get_server_netbios_name(ipactx);
|
|
||||||
+ if (!flat_server_name) {
|
|
||||||
+ err = ENOMEM;
|
|
||||||
+ goto end;
|
|
||||||
}
|
|
||||||
|
|
||||||
- ret = ipadb_ldap_attr_to_str(ipactx->lcontext, lentry,
|
|
||||||
- "ipaNTFallbackPrimaryGroup",
|
|
||||||
- &ipactx->mspac->fallback_group);
|
|
||||||
- if (ret && ret != ENOENT) {
|
|
||||||
- kerr = ret;
|
|
||||||
- goto done;
|
|
||||||
+ err = ipadb_ldap_attr_to_str(ipactx->lcontext, lentry,
|
|
||||||
+ "ipaNTFallbackPrimaryGroup", &fallback_group);
|
|
||||||
+ if (err) {
|
|
||||||
+ in_stmsg = (err == ENOENT)
|
|
||||||
+ ? "Local fallback primary group not configured"
|
|
||||||
+ : "Failed to fetch local fallback primary group";
|
|
||||||
+ goto end;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* result and lentry not valid any more from here on */
|
|
||||||
@@ -3095,53 +3105,81 @@ krb5_error_code ipadb_reinit_mspac(struct ipadb_context *ipactx, bool force_rein
|
|
||||||
result = NULL;
|
|
||||||
lentry = NULL;
|
|
||||||
|
|
||||||
- if (ret != ENOENT) {
|
|
||||||
- kerr = ipadb_simple_search(ipactx, ipactx->mspac->fallback_group,
|
|
||||||
- LDAP_SCOPE_BASE,
|
|
||||||
- "(objectclass=posixGroup)",
|
|
||||||
- grp_attrs, &result);
|
|
||||||
- if (kerr && kerr != KRB5_KDB_NOENTRY) {
|
|
||||||
- kerr = ret;
|
|
||||||
- goto done;
|
|
||||||
- }
|
|
||||||
+ err = ipadb_simple_search(ipactx, fallback_group, LDAP_SCOPE_BASE,
|
|
||||||
+ "(objectclass=posixGroup)", grp_attrs, &result);
|
|
||||||
+ if (err) {
|
|
||||||
+ in_stmsg = (err == KRB5_KDB_NOENTRY)
|
|
||||||
+ ? "Local fallback primary group has no POSIX definition"
|
|
||||||
+ : "Failed to fetch SID of POSIX group mapped as local fallback " \
|
|
||||||
+ "primary group";
|
|
||||||
+ goto end;
|
|
||||||
+ }
|
|
||||||
|
|
||||||
- lentry = ldap_first_entry(ipactx->lcontext, result);
|
|
||||||
- if (!lentry) {
|
|
||||||
- kerr = ENOENT;
|
|
||||||
- goto done;
|
|
||||||
- }
|
|
||||||
+ lentry = ldap_first_entry(ipactx->lcontext, result);
|
|
||||||
+ if (!lentry) {
|
|
||||||
+ err = ENOENT;
|
|
||||||
+ goto end;
|
|
||||||
+ }
|
|
||||||
|
|
||||||
- if (kerr == 0) {
|
|
||||||
- ret = ipadb_ldap_attr_to_str(ipactx->lcontext, lentry,
|
|
||||||
- "ipaNTSecurityIdentifier",
|
|
||||||
- &resstr);
|
|
||||||
- if (ret && ret != ENOENT) {
|
|
||||||
- kerr = ret;
|
|
||||||
- goto done;
|
|
||||||
- }
|
|
||||||
- if (ret == 0) {
|
|
||||||
- ret = ipadb_string_to_sid(resstr, &gsid);
|
|
||||||
- if (ret) {
|
|
||||||
- free(resstr);
|
|
||||||
- kerr = ret;
|
|
||||||
- goto done;
|
|
||||||
- }
|
|
||||||
- ret = sid_split_rid(&gsid, &ipactx->mspac->fallback_rid);
|
|
||||||
- if (ret) {
|
|
||||||
- free(resstr);
|
|
||||||
- kerr = ret;
|
|
||||||
- goto done;
|
|
||||||
- }
|
|
||||||
- free(resstr);
|
|
||||||
- }
|
|
||||||
- }
|
|
||||||
+ err = ipadb_ldap_attr_to_str(ipactx->lcontext, lentry,
|
|
||||||
+ "ipaNTSecurityIdentifier", &resstr);
|
|
||||||
+ if (err) {
|
|
||||||
+ in_stmsg = (err == ENOENT)
|
|
||||||
+ ? "The POSIX group set as fallback primary group has no SID " \
|
|
||||||
+ "configured"
|
|
||||||
+ : "Failed to fetch SID of POSIX group set as local fallback " \
|
|
||||||
+ "primary group";
|
|
||||||
+ goto end;
|
|
||||||
}
|
|
||||||
|
|
||||||
- kerr = ipadb_mspac_get_trusted_domains(ipactx);
|
|
||||||
+ err = ipadb_string_to_sid(resstr, &gsid);
|
|
||||||
+ if (err) {
|
|
||||||
+ in_stmsg = "Malformed SID of POSIX group set as local fallback " \
|
|
||||||
+ "primary group";
|
|
||||||
+ goto end;
|
|
||||||
+ }
|
|
||||||
|
|
||||||
-done:
|
|
||||||
+ err = sid_split_rid(&gsid, &fallback_rid);
|
|
||||||
+ if (err) {
|
|
||||||
+ in_stmsg = "Malformed SID of POSIX group mapped as local fallback " \
|
|
||||||
+ "primary group";
|
|
||||||
+ goto end;
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ /* clean up in case we had old values around */
|
|
||||||
+ ipadb_mspac_struct_free(&ipactx->mspac);
|
|
||||||
+
|
|
||||||
+ ipactx->mspac = calloc(1, sizeof(struct ipadb_mspac));
|
|
||||||
+ if (!ipactx->mspac) {
|
|
||||||
+ err = ENOMEM;
|
|
||||||
+ goto end;
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ ipactx->mspac->last_update = now;
|
|
||||||
+ ipactx->mspac->flat_domain_name = flat_domain_name;
|
|
||||||
+ ipactx->mspac->flat_server_name = flat_server_name;
|
|
||||||
+ ipactx->mspac->domsid = domsid;
|
|
||||||
+ ipactx->mspac->fallback_group = fallback_group;
|
|
||||||
+ ipactx->mspac->fallback_rid = fallback_rid;
|
|
||||||
+
|
|
||||||
+ trust_kerr = ipadb_mspac_get_trusted_domains(ipactx);
|
|
||||||
+ if (trust_kerr)
|
|
||||||
+ in_stmsg = "Failed to assemble trusted domains information";
|
|
||||||
+
|
|
||||||
+end:
|
|
||||||
+ if (stmsg)
|
|
||||||
+ *stmsg = in_stmsg;
|
|
||||||
+
|
|
||||||
+ if (resstr) free(resstr);
|
|
||||||
ldap_msgfree(result);
|
|
||||||
- return kerr;
|
|
||||||
+
|
|
||||||
+ if (err) {
|
|
||||||
+ if (flat_domain_name) free(flat_domain_name);
|
|
||||||
+ if (flat_server_name) free(flat_server_name);
|
|
||||||
+ if (fallback_group) free(fallback_group);
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ return err ? (krb5_error_code)err : trust_kerr;
|
|
||||||
}
|
|
||||||
|
|
||||||
krb5_error_code ipadb_check_transited_realms(krb5_context kcontext,
|
|
||||||
@@ -3151,11 +3189,11 @@ krb5_error_code ipadb_check_transited_realms(krb5_context kcontext,
|
|
||||||
{
|
|
||||||
struct ipadb_context *ipactx;
|
|
||||||
bool has_transited_contents, has_client_realm, has_server_realm;
|
|
||||||
- int i;
|
|
||||||
+ size_t i;
|
|
||||||
krb5_error_code ret;
|
|
||||||
|
|
||||||
ipactx = ipadb_get_context(kcontext);
|
|
||||||
- if (!ipactx || !ipactx->mspac) {
|
|
||||||
+ if (!ipactx) {
|
|
||||||
return KRB5_KDB_DBNOTINITED;
|
|
||||||
}
|
|
||||||
|
|
||||||
@@ -3217,7 +3255,7 @@ krb5_error_code ipadb_is_princ_from_trusted_realm(krb5_context kcontext,
|
|
||||||
char **trusted_realm)
|
|
||||||
{
|
|
||||||
struct ipadb_context *ipactx;
|
|
||||||
- int i, j, length;
|
|
||||||
+ size_t i, j, length;
|
|
||||||
const char *name;
|
|
||||||
bool result = false;
|
|
||||||
|
|
||||||
diff --git a/daemons/ipa-kdb/ipa_kdb_mspac_private.h b/daemons/ipa-kdb/ipa_kdb_mspac_private.h
|
|
||||||
index 7f0ca7a79..e650cfa73 100644
|
|
||||||
--- a/daemons/ipa-kdb/ipa_kdb_mspac_private.h
|
|
||||||
+++ b/daemons/ipa-kdb/ipa_kdb_mspac_private.h
|
|
||||||
@@ -31,7 +31,7 @@ struct ipadb_mspac {
|
|
||||||
char *fallback_group;
|
|
||||||
uint32_t fallback_rid;
|
|
||||||
|
|
||||||
- int num_trusts;
|
|
||||||
+ size_t num_trusts;
|
|
||||||
struct ipadb_adtrusts *trusts;
|
|
||||||
time_t last_update;
|
|
||||||
};
|
|
||||||
diff --git a/daemons/ipa-kdb/ipa_kdb_mspac_v6.c b/daemons/ipa-kdb/ipa_kdb_mspac_v6.c
|
|
||||||
index faf47ad1b..96cd50e4c 100644
|
|
||||||
--- a/daemons/ipa-kdb/ipa_kdb_mspac_v6.c
|
|
||||||
+++ b/daemons/ipa-kdb/ipa_kdb_mspac_v6.c
|
|
||||||
@@ -233,6 +233,7 @@ krb5_error_code ipadb_sign_authdata(krb5_context context,
|
|
||||||
krb5_db_entry *client_entry = NULL;
|
|
||||||
krb5_boolean is_equal;
|
|
||||||
bool force_reinit_mspac = false;
|
|
||||||
+ const char *stmsg = NULL;
|
|
||||||
|
|
||||||
|
|
||||||
is_as_req = ((flags & KRB5_KDB_FLAG_CLIENT_REFERRALS_ONLY) != 0);
|
|
||||||
@@ -309,7 +310,9 @@ krb5_error_code ipadb_sign_authdata(krb5_context context,
|
|
||||||
force_reinit_mspac = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
- (void)ipadb_reinit_mspac(ipactx, force_reinit_mspac);
|
|
||||||
+ kerr = ipadb_reinit_mspac(ipactx, force_reinit_mspac, &stmsg);
|
|
||||||
+ if (kerr && stmsg)
|
|
||||||
+ krb5_klog_syslog(LOG_WARNING, "MS-PAC generator: %s", stmsg);
|
|
||||||
|
|
||||||
kerr = ipadb_get_pac(context, flags, client, server, NULL, authtime, &pac);
|
|
||||||
if (kerr != 0 && kerr != ENOENT) {
|
|
||||||
diff --git a/daemons/ipa-kdb/ipa_kdb_mspac_v9.c b/daemons/ipa-kdb/ipa_kdb_mspac_v9.c
|
|
||||||
index 3badd5b08..60db048e1 100644
|
|
||||||
--- a/daemons/ipa-kdb/ipa_kdb_mspac_v9.c
|
|
||||||
+++ b/daemons/ipa-kdb/ipa_kdb_mspac_v9.c
|
|
||||||
@@ -46,6 +46,7 @@ ipadb_v9_issue_pac(krb5_context context, unsigned int flags,
|
|
||||||
bool with_pad;
|
|
||||||
krb5_error_code kerr = 0;
|
|
||||||
bool is_as_req = flags & CLIENT_REFERRALS_FLAGS;
|
|
||||||
+ const char *stmsg = NULL;
|
|
||||||
|
|
||||||
if (is_as_req) {
|
|
||||||
get_authz_data_types(context, client, &with_pac, &with_pad);
|
|
||||||
@@ -110,12 +111,19 @@ ipadb_v9_issue_pac(krb5_context context, unsigned int flags,
|
|
||||||
force_reinit_mspac = TRUE;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
- (void)ipadb_reinit_mspac(ipactx, force_reinit_mspac);
|
|
||||||
|
|
||||||
- /* MS-PAC needs proper configuration and if it is missing, we simply skip issuing one */
|
|
||||||
- if (ipactx->mspac->flat_server_name == NULL) {
|
|
||||||
+ /* MS-PAC generator has to be initalized */
|
|
||||||
+ kerr = ipadb_reinit_mspac(ipactx, force_reinit_mspac, &stmsg);
|
|
||||||
+ if (kerr && stmsg)
|
|
||||||
+ krb5_klog_syslog(LOG_ERR, "MS-PAC generator: %s", stmsg);
|
|
||||||
+
|
|
||||||
+ /* Continue even if initilization of PAC generator failed.
|
|
||||||
+ * It may caused by the trust objects part only. */
|
|
||||||
+
|
|
||||||
+ /* At least the core part of the PAC generator is required. */
|
|
||||||
+ if (!ipactx->mspac)
|
|
||||||
return KRB5_PLUGIN_OP_NOTSUPP;
|
|
||||||
- }
|
|
||||||
+
|
|
||||||
kerr = ipadb_get_pac(context, flags,
|
|
||||||
client, server, replaced_reply_key,
|
|
||||||
authtime, &new_pac);
|
|
||||||
diff --git a/daemons/ipa-kdb/ipa_kdb_principals.c b/daemons/ipa-kdb/ipa_kdb_principals.c
|
|
||||||
index fadb132ed..07cc87746 100644
|
|
||||||
--- a/daemons/ipa-kdb/ipa_kdb_principals.c
|
|
||||||
+++ b/daemons/ipa-kdb/ipa_kdb_principals.c
|
|
||||||
@@ -1495,6 +1495,7 @@ static krb5_error_code dbget_alias(krb5_context kcontext,
|
|
||||||
krb5_db_entry *kentry = NULL;
|
|
||||||
krb5_data *realm;
|
|
||||||
krb5_boolean check = FALSE;
|
|
||||||
+ const char *stmsg = NULL;
|
|
||||||
|
|
||||||
/* TODO: also support hostbased aliases */
|
|
||||||
|
|
||||||
@@ -1562,8 +1563,11 @@ static krb5_error_code dbget_alias(krb5_context kcontext,
|
|
||||||
if (kerr == KRB5_KDB_NOENTRY) {
|
|
||||||
/* If no trusted realm found, refresh trusted domain data and try again
|
|
||||||
* because it might be a freshly added trust to AD */
|
|
||||||
- kerr = ipadb_reinit_mspac(ipactx, false);
|
|
||||||
+ kerr = ipadb_reinit_mspac(ipactx, false, &stmsg);
|
|
||||||
if (kerr != 0) {
|
|
||||||
+ if (stmsg)
|
|
||||||
+ krb5_klog_syslog(LOG_WARNING, "MS-PAC generator: %s",
|
|
||||||
+ stmsg);
|
|
||||||
kerr = KRB5_KDB_NOENTRY;
|
|
||||||
goto done;
|
|
||||||
}
|
|
||||||
--
|
|
||||||
2.43.0
|
|
||||||
|
|
@ -1,34 +0,0 @@
|
|||||||
From 44a762413c83f9637399afeb61b1e4b1ac111260 Mon Sep 17 00:00:00 2001
|
|
||||||
From: Florence Blanc-Renaud <flo@redhat.com>
|
|
||||||
Date: Feb 14 2024 12:24:48 +0000
|
|
||||||
Subject: ipatests: fix tasks.wait_for_replication method
|
|
||||||
|
|
||||||
|
|
||||||
With the fix for https://pagure.io/freeipa/issue/9171, the
|
|
||||||
method entry.single_value['nsds5replicaupdateinprogress'] now
|
|
||||||
returns a Boolean instead of a string "TRUE"/"FALSE".
|
|
||||||
|
|
||||||
The method tasks.wait_for_replication needs to be fixed so that
|
|
||||||
it properly detects when replication is not done.
|
|
||||||
|
|
||||||
Fixes: https://pagure.io/freeipa/issue/9530
|
|
||||||
|
|
||||||
Signed-off-by: Florence Blanc-Renaud <flo@redhat.com>
|
|
||||||
Reviewed-By: Rob Crittenden <rcritten@redhat.com>
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
diff --git a/ipatests/pytest_ipa/integration/tasks.py b/ipatests/pytest_ipa/integration/tasks.py
|
|
||||||
index 9068ba6..952c9e6 100755
|
|
||||||
--- a/ipatests/pytest_ipa/integration/tasks.py
|
|
||||||
+++ b/ipatests/pytest_ipa/integration/tasks.py
|
|
||||||
@@ -1510,7 +1510,7 @@ def wait_for_replication(ldap, timeout=30,
|
|
||||||
statuses = [entry.single_value[status_attr] for entry in entries]
|
|
||||||
wrong_statuses = [s for s in statuses
|
|
||||||
if not re.match(target_status_re, s)]
|
|
||||||
- if any(e.single_value[progress_attr] == 'TRUE' for e in entries):
|
|
||||||
+ if any(e.single_value[progress_attr] for e in entries):
|
|
||||||
msg = 'Replication not finished'
|
|
||||||
logger.debug(msg)
|
|
||||||
elif wrong_statuses:
|
|
||||||
|
|
@ -0,0 +1,36 @@
|
|||||||
|
From ff31b0c40cc5e046f839b98b80bd16bb649205ac Mon Sep 17 00:00:00 2001
|
||||||
|
From: Rob Crittenden <rcritten@redhat.com>
|
||||||
|
Date: Mon, 30 Jan 2023 11:54:36 -0500
|
||||||
|
Subject: [PATCH] tests: Add ipa_ca_name checking to DNS system records
|
||||||
|
|
||||||
|
freeipa-healthcheck 0.12 includes a SUCCESS message if the
|
||||||
|
ipa-ca records are as expected so a user will know they
|
||||||
|
were checked. For that version and beyond test that it
|
||||||
|
is included.
|
||||||
|
|
||||||
|
Related: https://pagure.io/freeipa/issue/9291
|
||||||
|
|
||||||
|
Signed-off-by: Rob Crittenden <rcritten@redhat.com>
|
||||||
|
Reviewed-By: Florence Blanc-Renaud <flo@redhat.com>
|
||||||
|
---
|
||||||
|
ipatests/test_integration/test_ipahealthcheck.py | 4 +++-
|
||||||
|
1 file changed, 3 insertions(+), 1 deletion(-)
|
||||||
|
|
||||||
|
diff --git a/ipatests/test_integration/test_ipahealthcheck.py b/ipatests/test_integration/test_ipahealthcheck.py
|
||||||
|
index 49a5779307ef05617fe9ae200f7149d120977355..94b0db0b7869e722955e232e1dddb26a2dc3d41e 100644
|
||||||
|
--- a/ipatests/test_integration/test_ipahealthcheck.py
|
||||||
|
+++ b/ipatests/test_integration/test_ipahealthcheck.py
|
||||||
|
@@ -810,7 +810,9 @@ class TestIpaHealthCheck(IntegrationTest):
|
||||||
|
+ [str(ip) for ip in resolve_ip_addresses_nss(h.external_hostname)]
|
||||||
|
]
|
||||||
|
SYSTEM_RECORDS.append(f'"{self.master.domain.realm.upper()}"')
|
||||||
|
-
|
||||||
|
+ version = tasks.get_healthcheck_version(self.master)
|
||||||
|
+ if parse_version(version) >= parse_version("0.12"):
|
||||||
|
+ SYSTEM_RECORDS.append('ipa_ca_check')
|
||||||
|
|
||||||
|
returncode, data = run_healthcheck(
|
||||||
|
self.master,
|
||||||
|
--
|
||||||
|
2.39.1
|
||||||
|
|
@ -1,127 +0,0 @@
|
|||||||
From 163f06cab678d517ab30ab6da59ae339f39ee7cf Mon Sep 17 00:00:00 2001
|
|
||||||
From: Francisco Trivino <ftrivino@redhat.com>
|
|
||||||
Date: Fri, 27 May 2022 17:31:40 +0200
|
|
||||||
Subject: [PATCH] Vault: add support for RSA-OAEP wrapping algo
|
|
||||||
|
|
||||||
None of the FIPS certified modules in RHEL support PKCS#1 v1.5 as FIPS
|
|
||||||
approved mechanism. This commit adds support for RSA-OAEP padding as a
|
|
||||||
fallback.
|
|
||||||
|
|
||||||
Fixes: https://pagure.io/freeipa/issue/9191
|
|
||||||
|
|
||||||
Signed-off-by: Francisco Trivino <ftrivino@redhat.com>
|
|
||||||
Reviewed-By: Rob Crittenden <rcritten@redhat.com>
|
|
||||||
(cherry picked from commit b1fb31fd20c900c9ff1d5d28dfe136439f6bf605)
|
|
||||||
---
|
|
||||||
ipaclient/plugins/vault.py | 57 ++++++++++++++++++++++++++++++--------
|
|
||||||
1 file changed, 45 insertions(+), 12 deletions(-)
|
|
||||||
|
|
||||||
diff --git a/ipaclient/plugins/vault.py b/ipaclient/plugins/vault.py
|
|
||||||
index d4c84eb6b..ed16c73ae 100644
|
|
||||||
--- a/ipaclient/plugins/vault.py
|
|
||||||
+++ b/ipaclient/plugins/vault.py
|
|
||||||
@@ -119,8 +119,8 @@ def encrypt(data, symmetric_key=None, public_key=None):
|
|
||||||
return public_key_obj.encrypt(
|
|
||||||
data,
|
|
||||||
padding.OAEP(
|
|
||||||
- mgf=padding.MGF1(algorithm=hashes.SHA1()),
|
|
||||||
- algorithm=hashes.SHA1(),
|
|
||||||
+ mgf=padding.MGF1(algorithm=hashes.SHA256()),
|
|
||||||
+ algorithm=hashes.SHA256(),
|
|
||||||
label=None
|
|
||||||
)
|
|
||||||
)
|
|
||||||
@@ -154,8 +154,8 @@ def decrypt(data, symmetric_key=None, private_key=None):
|
|
||||||
return private_key_obj.decrypt(
|
|
||||||
data,
|
|
||||||
padding.OAEP(
|
|
||||||
- mgf=padding.MGF1(algorithm=hashes.SHA1()),
|
|
||||||
- algorithm=hashes.SHA1(),
|
|
||||||
+ mgf=padding.MGF1(algorithm=hashes.SHA256()),
|
|
||||||
+ algorithm=hashes.SHA256(),
|
|
||||||
label=None
|
|
||||||
)
|
|
||||||
)
|
|
||||||
@@ -705,14 +705,39 @@ class ModVaultData(Local):
|
|
||||||
return transport_cert, wrapping_algo
|
|
||||||
|
|
||||||
def _do_internal(self, algo, transport_cert, raise_unexpected,
|
|
||||||
- *args, **options):
|
|
||||||
+ use_oaep=False, *args, **options):
|
|
||||||
public_key = transport_cert.public_key()
|
|
||||||
|
|
||||||
# wrap session key with transport certificate
|
|
||||||
- wrapped_session_key = public_key.encrypt(
|
|
||||||
- algo.key,
|
|
||||||
- padding.PKCS1v15()
|
|
||||||
- )
|
|
||||||
+ # KRA may be configured using either the default PKCS1v15 or RSA-OAEP.
|
|
||||||
+ # there is no way to query this info using the REST interface.
|
|
||||||
+ if not use_oaep:
|
|
||||||
+ # PKCS1v15() causes an OpenSSL exception when FIPS is enabled
|
|
||||||
+ # if so, we fallback to RSA-OAEP
|
|
||||||
+ try:
|
|
||||||
+ wrapped_session_key = public_key.encrypt(
|
|
||||||
+ algo.key,
|
|
||||||
+ padding.PKCS1v15()
|
|
||||||
+ )
|
|
||||||
+ except ValueError:
|
|
||||||
+ wrapped_session_key = public_key.encrypt(
|
|
||||||
+ algo.key,
|
|
||||||
+ padding.OAEP(
|
|
||||||
+ mgf=padding.MGF1(algorithm=hashes.SHA256()),
|
|
||||||
+ algorithm=hashes.SHA256(),
|
|
||||||
+ label=None
|
|
||||||
+ )
|
|
||||||
+ )
|
|
||||||
+ else:
|
|
||||||
+ wrapped_session_key = public_key.encrypt(
|
|
||||||
+ algo.key,
|
|
||||||
+ padding.OAEP(
|
|
||||||
+ mgf=padding.MGF1(algorithm=hashes.SHA256()),
|
|
||||||
+ algorithm=hashes.SHA256(),
|
|
||||||
+ label=None
|
|
||||||
+ )
|
|
||||||
+ )
|
|
||||||
+
|
|
||||||
options['session_key'] = wrapped_session_key
|
|
||||||
|
|
||||||
name = self.name + '_internal'
|
|
||||||
@@ -723,7 +748,7 @@ class ModVaultData(Local):
|
|
||||||
errors.ExecutionError,
|
|
||||||
errors.GenericError):
|
|
||||||
_kra_config_cache.remove(self.api.env.domain)
|
|
||||||
- if raise_unexpected:
|
|
||||||
+ if raise_unexpected and use_oaep:
|
|
||||||
raise
|
|
||||||
return None
|
|
||||||
|
|
||||||
@@ -733,15 +758,23 @@ class ModVaultData(Local):
|
|
||||||
"""
|
|
||||||
# try call with cached transport certificate
|
|
||||||
result = self._do_internal(algo, transport_cert, False,
|
|
||||||
- *args, **options)
|
|
||||||
+ False, *args, **options)
|
|
||||||
if result is not None:
|
|
||||||
return result
|
|
||||||
|
|
||||||
# retrieve transport certificate (cached by vaultconfig_show)
|
|
||||||
transport_cert = self._get_vaultconfig(force_refresh=True)[0]
|
|
||||||
+
|
|
||||||
# call with the retrieved transport certificate
|
|
||||||
+ result = self._do_internal(algo, transport_cert, True,
|
|
||||||
+ False, *args, **options)
|
|
||||||
+
|
|
||||||
+ if result is not None:
|
|
||||||
+ return result
|
|
||||||
+
|
|
||||||
+ # call and use_oaep this time, last attempt
|
|
||||||
return self._do_internal(algo, transport_cert, True,
|
|
||||||
- *args, **options)
|
|
||||||
+ True, *args, **options)
|
|
||||||
|
|
||||||
|
|
||||||
@register(no_fail=True)
|
|
||||||
--
|
|
||||||
2.43.0
|
|
||||||
|
|
@ -0,0 +1,53 @@
|
|||||||
|
From 6ca119686aadfa72c0474f72758b63cd671952d4 Mon Sep 17 00:00:00 2001
|
||||||
|
From: Rob Crittenden <rcritten@redhat.com>
|
||||||
|
Date: Mon, 30 Jan 2023 12:00:03 -0500
|
||||||
|
Subject: [PATCH] tests: Add new ipa-ca error messages to
|
||||||
|
IPADNSSystemRecordsCheck
|
||||||
|
|
||||||
|
freeipa-healthcheck changed some messages related to ipa-ca
|
||||||
|
DNS record validation in IPADNSSystemRecordsCheck. Include support
|
||||||
|
for it and retain backwards compatibility.
|
||||||
|
|
||||||
|
Fixes: https://pagure.io/freeipa/issue/9291
|
||||||
|
|
||||||
|
Signed-off-by: Rob Crittenden <rcritten@redhat.com>
|
||||||
|
Reviewed-By: Florence Blanc-Renaud <flo@redhat.com>
|
||||||
|
---
|
||||||
|
.../test_integration/test_ipahealthcheck.py | 21 +++++++++++++------
|
||||||
|
1 file changed, 15 insertions(+), 6 deletions(-)
|
||||||
|
|
||||||
|
diff --git a/ipatests/test_integration/test_ipahealthcheck.py b/ipatests/test_integration/test_ipahealthcheck.py
|
||||||
|
index 94b0db0b7869e722955e232e1dddb26a2dc3d41e..47f64f2cb36904ef61211423de7cf33d21a199c3 100644
|
||||||
|
--- a/ipatests/test_integration/test_ipahealthcheck.py
|
||||||
|
+++ b/ipatests/test_integration/test_ipahealthcheck.py
|
||||||
|
@@ -1614,12 +1614,21 @@ class TestIpaHealthCheckWithoutDNS(IntegrationTest):
|
||||||
|
Test checks the result of IPADNSSystemRecordsCheck
|
||||||
|
when ipa-server is configured without DNS.
|
||||||
|
"""
|
||||||
|
- expected_msgs = {
|
||||||
|
- "Expected SRV record missing",
|
||||||
|
- "Got {count} ipa-ca A records, expected {expected}",
|
||||||
|
- "Got {count} ipa-ca AAAA records, expected {expected}",
|
||||||
|
- "Expected URI record missing",
|
||||||
|
- }
|
||||||
|
+ version = tasks.get_healthcheck_version(self.master)
|
||||||
|
+ if (parse_version(version) < parse_version('0.12')):
|
||||||
|
+ expected_msgs = {
|
||||||
|
+ "Expected SRV record missing",
|
||||||
|
+ "Got {count} ipa-ca A records, expected {expected}",
|
||||||
|
+ "Got {count} ipa-ca AAAA records, expected {expected}",
|
||||||
|
+ "Expected URI record missing",
|
||||||
|
+ }
|
||||||
|
+ else:
|
||||||
|
+ expected_msgs = {
|
||||||
|
+ "Expected SRV record missing",
|
||||||
|
+ "Unexpected ipa-ca address {ipaddr}",
|
||||||
|
+ "expected ipa-ca to contain {ipaddr} for {server}",
|
||||||
|
+ "Expected URI record missing",
|
||||||
|
+ }
|
||||||
|
|
||||||
|
tasks.install_packages(self.master, HEALTHCHECK_PKG)
|
||||||
|
returncode, data = run_healthcheck(
|
||||||
|
--
|
||||||
|
2.39.1
|
||||||
|
|
@ -1,88 +0,0 @@
|
|||||||
From 84798137fabf75fe79aebbd97e4b8418de8ab0f2 Mon Sep 17 00:00:00 2001
|
|
||||||
From: Francisco Trivino <ftrivino@redhat.com>
|
|
||||||
Date: Fri, 19 Jan 2024 18:15:28 +0100
|
|
||||||
Subject: [PATCH] Vault: improve vault server archival/retrieval calls
|
|
||||||
error handling
|
|
||||||
|
|
||||||
If a vault operation fails, the error message just says "InternalError". This commit
|
|
||||||
improves error handling of key archival and retrieval calls by catching the PKIException
|
|
||||||
error and raising it as an IPA error.
|
|
||||||
|
|
||||||
Related: https://pagure.io/freeipa/issue/9191
|
|
||||||
|
|
||||||
Signed-off-by: Francisco Trivino <ftrivino@redhat.com>
|
|
||||||
Reviewed-By: Rob Crittenden <rcritten@redhat.com>
|
|
||||||
(cherry picked from commit dc1ab53f0aa0398d493f7440b5ec4d70d9c7d663)
|
|
||||||
---
|
|
||||||
ipaserver/plugins/vault.py | 40 +++++++++++++++++++++++++-------------
|
|
||||||
1 file changed, 26 insertions(+), 14 deletions(-)
|
|
||||||
|
|
||||||
diff --git a/ipaserver/plugins/vault.py b/ipaserver/plugins/vault.py
|
|
||||||
index 574c83a9a..13c4fac9a 100644
|
|
||||||
--- a/ipaserver/plugins/vault.py
|
|
||||||
+++ b/ipaserver/plugins/vault.py
|
|
||||||
@@ -45,6 +45,7 @@ if api.env.in_server:
|
|
||||||
import pki.key
|
|
||||||
from pki.crypto import DES_EDE3_CBC_OID
|
|
||||||
from pki.crypto import AES_128_CBC_OID
|
|
||||||
+ from pki import PKIException
|
|
||||||
|
|
||||||
if six.PY3:
|
|
||||||
unicode = str
|
|
||||||
@@ -1094,16 +1095,21 @@ class vault_archive_internal(PKQuery):
|
|
||||||
pki.key.KeyClient.KEY_STATUS_INACTIVE)
|
|
||||||
|
|
||||||
# forward wrapped data to KRA
|
|
||||||
- kra_client.keys.archive_encrypted_data(
|
|
||||||
- client_key_id,
|
|
||||||
- pki.key.KeyClient.PASS_PHRASE_TYPE,
|
|
||||||
- wrapped_vault_data,
|
|
||||||
- wrapped_session_key,
|
|
||||||
- algorithm_oid=algorithm_oid,
|
|
||||||
- nonce_iv=nonce,
|
|
||||||
- )
|
|
||||||
-
|
|
||||||
- kra_account.logout()
|
|
||||||
+ try:
|
|
||||||
+ kra_client.keys.archive_encrypted_data(
|
|
||||||
+ client_key_id,
|
|
||||||
+ pki.key.KeyClient.PASS_PHRASE_TYPE,
|
|
||||||
+ wrapped_vault_data,
|
|
||||||
+ wrapped_session_key,
|
|
||||||
+ algorithm_oid=algorithm_oid,
|
|
||||||
+ nonce_iv=nonce,
|
|
||||||
+ )
|
|
||||||
+ except PKIException as e:
|
|
||||||
+ kra_account.logout()
|
|
||||||
+ raise errors.EncodingError(
|
|
||||||
+ message=_("Unable to archive key: %s") % e)
|
|
||||||
+ finally:
|
|
||||||
+ kra_account.logout()
|
|
||||||
|
|
||||||
response = {
|
|
||||||
'value': args[-1],
|
|
||||||
@@ -1174,11 +1180,17 @@ class vault_retrieve_internal(PKQuery):
|
|
||||||
kra_client.keys.encrypt_alg_oid = algorithm_oid
|
|
||||||
|
|
||||||
# retrieve encrypted data from KRA
|
|
||||||
- key = kra_client.keys.retrieve_key(
|
|
||||||
- key_info.get_key_id(),
|
|
||||||
- wrapped_session_key)
|
|
||||||
+ try:
|
|
||||||
|
|
||||||
- kra_account.logout()
|
|
||||||
+ key = kra_client.keys.retrieve_key(
|
|
||||||
+ key_info.get_key_id(),
|
|
||||||
+ wrapped_session_key)
|
|
||||||
+ except PKIException as e:
|
|
||||||
+ kra_account.logout()
|
|
||||||
+ raise errors.EncodingError(
|
|
||||||
+ message=_("Unable to retrieve key: %s") % e)
|
|
||||||
+ finally:
|
|
||||||
+ kra_account.logout()
|
|
||||||
|
|
||||||
response = {
|
|
||||||
'value': args[-1],
|
|
||||||
--
|
|
||||||
2.43.0
|
|
||||||
|
|
445
SOURCES/0020-ipatests-tests-for-certificate-pruning.patch
Normal file
445
SOURCES/0020-ipatests-tests-for-certificate-pruning.patch
Normal file
@ -0,0 +1,445 @@
|
|||||||
|
From 0f77b359e241fc4055fb8d785e18f96338451ebf Mon Sep 17 00:00:00 2001
|
||||||
|
From: Mohammad Rizwan <myusuf@redhat.com>
|
||||||
|
Date: Mon, 6 Feb 2023 15:31:27 +0530
|
||||||
|
Subject: [PATCH] ipatests: tests for certificate pruning
|
||||||
|
|
||||||
|
1. Test to prune the expired certificate by manual run
|
||||||
|
2. Test to prune expired certificate by cron job
|
||||||
|
3. Test to prune expired certificate with retention unit option
|
||||||
|
4. Test to prune expired certificate with search size limit option
|
||||||
|
5. Test to check config-show command shows set param
|
||||||
|
6. Test prune command shows proper status after disabling the pruning
|
||||||
|
|
||||||
|
related: https://pagure.io/freeipa/issue/9294
|
||||||
|
|
||||||
|
Signed-off-by: Mohammad Rizwan <myusuf@redhat.com>
|
||||||
|
Reviewed-By: Rob Crittenden <rcritten@redhat.com>
|
||||||
|
---
|
||||||
|
ipatests/test_integration/test_acme.py | 306 +++++++++++++++++++++----
|
||||||
|
1 file changed, 260 insertions(+), 46 deletions(-)
|
||||||
|
|
||||||
|
diff --git a/ipatests/test_integration/test_acme.py b/ipatests/test_integration/test_acme.py
|
||||||
|
index 5ceba05976059de69414a79634d98045c3ab68bb..1334be52f4530dd8b2a4207744146cd0eb5477a3 100644
|
||||||
|
--- a/ipatests/test_integration/test_acme.py
|
||||||
|
+++ b/ipatests/test_integration/test_acme.py
|
||||||
|
@@ -122,21 +122,23 @@ def certbot_register(host, acme_server):
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
-def certbot_standalone_cert(host, acme_server):
|
||||||
|
+def certbot_standalone_cert(host, acme_server, no_of_cert=1):
|
||||||
|
"""method to issue a certbot's certonly standalone cert"""
|
||||||
|
# Get a cert from ACME service using HTTP challenge and Certbot's
|
||||||
|
# standalone HTTP server mode
|
||||||
|
host.run_command(['systemctl', 'stop', 'httpd'])
|
||||||
|
- host.run_command(
|
||||||
|
- [
|
||||||
|
- 'certbot',
|
||||||
|
- '--server', acme_server,
|
||||||
|
- 'certonly',
|
||||||
|
- '--domain', host.hostname,
|
||||||
|
- '--standalone',
|
||||||
|
- '--key-type', 'rsa',
|
||||||
|
- ]
|
||||||
|
- )
|
||||||
|
+ for _i in range(0, no_of_cert):
|
||||||
|
+ host.run_command(
|
||||||
|
+ [
|
||||||
|
+ 'certbot',
|
||||||
|
+ '--server', acme_server,
|
||||||
|
+ 'certonly',
|
||||||
|
+ '--domain', host.hostname,
|
||||||
|
+ '--standalone',
|
||||||
|
+ '--key-type', 'rsa',
|
||||||
|
+ '--force-renewal'
|
||||||
|
+ ]
|
||||||
|
+ )
|
||||||
|
|
||||||
|
|
||||||
|
class TestACME(CALessBase):
|
||||||
|
@@ -573,43 +575,41 @@ class TestACMEwithExternalCA(TestACME):
|
||||||
|
tasks.install_replica(cls.master, cls.replicas[0])
|
||||||
|
|
||||||
|
|
||||||
|
-class TestACMERenew(IntegrationTest):
|
||||||
|
-
|
||||||
|
- num_clients = 1
|
||||||
|
+@pytest.fixture
|
||||||
|
+def issue_and_expire_acme_cert():
|
||||||
|
+ """Fixture to expire cert by moving date past expiry of acme cert"""
|
||||||
|
+ hosts = []
|
||||||
|
|
||||||
|
- @classmethod
|
||||||
|
- def install(cls, mh):
|
||||||
|
-
|
||||||
|
- # install packages before client install in case of IPA DNS problems
|
||||||
|
- cls.acme_server = prepare_acme_client(cls.master, cls.clients[0])
|
||||||
|
+ def _issue_and_expire_acme_cert(
|
||||||
|
+ master, client,
|
||||||
|
+ acme_server_url, no_of_cert=1
|
||||||
|
+ ):
|
||||||
|
|
||||||
|
- tasks.install_master(cls.master, setup_dns=True)
|
||||||
|
- tasks.install_client(cls.master, cls.clients[0])
|
||||||
|
+ hosts.append(master)
|
||||||
|
+ hosts.append(client)
|
||||||
|
|
||||||
|
- @pytest.fixture
|
||||||
|
- def issue_and_expire_cert(self):
|
||||||
|
- """Fixture to expire cert by moving date past expiry of acme cert"""
|
||||||
|
# enable the ACME service on master
|
||||||
|
- self.master.run_command(['ipa-acme-manage', 'enable'])
|
||||||
|
+ master.run_command(['ipa-acme-manage', 'enable'])
|
||||||
|
|
||||||
|
# register the account with certbot
|
||||||
|
- certbot_register(self.clients[0], self.acme_server)
|
||||||
|
+ certbot_register(client, acme_server_url)
|
||||||
|
|
||||||
|
# request a standalone acme cert
|
||||||
|
- certbot_standalone_cert(self.clients[0], self.acme_server)
|
||||||
|
+ certbot_standalone_cert(client, acme_server_url, no_of_cert)
|
||||||
|
|
||||||
|
# move system date to expire acme cert
|
||||||
|
- for host in self.clients[0], self.master:
|
||||||
|
+ for host in hosts:
|
||||||
|
tasks.kdestroy_all(host)
|
||||||
|
tasks.move_date(host, 'stop', '+90days')
|
||||||
|
|
||||||
|
+ time.sleep(10)
|
||||||
|
tasks.get_kdcinfo(host)
|
||||||
|
# Note raiseonerr=False:
|
||||||
|
# the assert is located after kdcinfo retrieval.
|
||||||
|
- result = host.run_command(
|
||||||
|
+ result = master.run_command(
|
||||||
|
"KRB5_TRACE=/dev/stdout kinit admin",
|
||||||
|
stdin_text='{0}\n{0}\n{0}\n'.format(
|
||||||
|
- self.clients[0].config.admin_password
|
||||||
|
+ master.config.admin_password
|
||||||
|
),
|
||||||
|
raiseonerr=False
|
||||||
|
)
|
||||||
|
@@ -618,16 +618,28 @@ class TestACMERenew(IntegrationTest):
|
||||||
|
tasks.get_kdcinfo(host)
|
||||||
|
assert result.returncode == 0
|
||||||
|
|
||||||
|
- yield
|
||||||
|
+ yield _issue_and_expire_acme_cert
|
||||||
|
|
||||||
|
- # move back date
|
||||||
|
- for host in self.clients[0], self.master:
|
||||||
|
- tasks.kdestroy_all(host)
|
||||||
|
- tasks.move_date(host, 'start', '-90days')
|
||||||
|
- tasks.kinit_admin(host)
|
||||||
|
+ # move back date
|
||||||
|
+ for host in hosts:
|
||||||
|
+ tasks.move_date(host, 'start', '-90days')
|
||||||
|
+
|
||||||
|
+
|
||||||
|
+class TestACMERenew(IntegrationTest):
|
||||||
|
+
|
||||||
|
+ num_clients = 1
|
||||||
|
+
|
||||||
|
+ @classmethod
|
||||||
|
+ def install(cls, mh):
|
||||||
|
+
|
||||||
|
+ # install packages before client install in case of IPA DNS problems
|
||||||
|
+ cls.acme_server = prepare_acme_client(cls.master, cls.clients[0])
|
||||||
|
+
|
||||||
|
+ tasks.install_master(cls.master, setup_dns=True)
|
||||||
|
+ tasks.install_client(cls.master, cls.clients[0])
|
||||||
|
|
||||||
|
@pytest.mark.skipif(skip_certbot_tests, reason='certbot not available')
|
||||||
|
- def test_renew(self, issue_and_expire_cert):
|
||||||
|
+ def test_renew(self, issue_and_expire_acme_cert):
|
||||||
|
"""Test if ACME renews the issued cert with cerbot
|
||||||
|
|
||||||
|
This test is to check if ACME certificate renews upon
|
||||||
|
@@ -635,6 +647,8 @@ class TestACMERenew(IntegrationTest):
|
||||||
|
|
||||||
|
related: https://pagure.io/freeipa/issue/4751
|
||||||
|
"""
|
||||||
|
+ issue_and_expire_acme_cert(
|
||||||
|
+ self.master, self.clients[0], self.acme_server)
|
||||||
|
data = self.clients[0].get_file_contents(
|
||||||
|
f'/etc/letsencrypt/live/{self.clients[0].hostname}/cert.pem'
|
||||||
|
)
|
||||||
|
@@ -656,6 +670,7 @@ class TestACMEPrune(IntegrationTest):
|
||||||
|
"""Validate that ipa-acme-manage configures dogtag for pruning"""
|
||||||
|
|
||||||
|
random_serial = True
|
||||||
|
+ num_clients = 1
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def install(cls, mh):
|
||||||
|
@@ -663,6 +678,8 @@ class TestACMEPrune(IntegrationTest):
|
||||||
|
raise pytest.skip("RNSv3 not supported")
|
||||||
|
tasks.install_master(cls.master, setup_dns=True,
|
||||||
|
random_serial=True)
|
||||||
|
+ cls.acme_server = prepare_acme_client(cls.master, cls.clients[0])
|
||||||
|
+ tasks.install_client(cls.master, cls.clients[0])
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def uninstall(cls, mh):
|
||||||
|
@@ -718,7 +735,7 @@ class TestACMEPrune(IntegrationTest):
|
||||||
|
['ipa-acme-manage', 'pruning',
|
||||||
|
'--requestretention=60',
|
||||||
|
'--requestretentionunit=minute',
|
||||||
|
- '--requestresearchsizelimit=2000',
|
||||||
|
+ '--requestsearchsizelimit=2000',
|
||||||
|
'--requestsearchtimelimit=5',]
|
||||||
|
)
|
||||||
|
cs_cfg = self.master.get_file_contents(paths.CA_CS_CFG_PATH)
|
||||||
|
@@ -741,7 +758,7 @@ class TestACMEPrune(IntegrationTest):
|
||||||
|
|
||||||
|
self.master.run_command(
|
||||||
|
['ipa-acme-manage', 'pruning',
|
||||||
|
- '--cron="0 23 1 * *',]
|
||||||
|
+ '--cron=0 23 1 * *',]
|
||||||
|
)
|
||||||
|
cs_cfg = self.master.get_file_contents(paths.CA_CS_CFG_PATH)
|
||||||
|
assert (
|
||||||
|
@@ -760,7 +777,7 @@ class TestACMEPrune(IntegrationTest):
|
||||||
|
'--enable', '--disable'],
|
||||||
|
raiseonerr=False
|
||||||
|
)
|
||||||
|
- assert result.returncode == 1
|
||||||
|
+ assert result.returncode == 2
|
||||||
|
assert "Cannot both enable and disable" in result.stderr_text
|
||||||
|
|
||||||
|
for cmd in ('--config-show', '--run'):
|
||||||
|
@@ -769,20 +786,20 @@ class TestACMEPrune(IntegrationTest):
|
||||||
|
cmd, '--enable'],
|
||||||
|
raiseonerr=False
|
||||||
|
)
|
||||||
|
- assert result.returncode == 1
|
||||||
|
+ assert result.returncode == 2
|
||||||
|
assert "Cannot change and show config" in result.stderr_text
|
||||||
|
|
||||||
|
result = self.master.run_command(
|
||||||
|
['ipa-acme-manage', 'pruning',
|
||||||
|
- '--cron="* *"'],
|
||||||
|
+ '--cron=* *'],
|
||||||
|
raiseonerr=False
|
||||||
|
)
|
||||||
|
- assert result.returncode == 1
|
||||||
|
- assert "Invalid format format --cron" in result.stderr_text
|
||||||
|
+ assert result.returncode == 2
|
||||||
|
+ assert "Invalid format for --cron" in result.stderr_text
|
||||||
|
|
||||||
|
result = self.master.run_command(
|
||||||
|
['ipa-acme-manage', 'pruning',
|
||||||
|
- '--cron="100 * * * *"'],
|
||||||
|
+ '--cron=100 * * * *'],
|
||||||
|
raiseonerr=False
|
||||||
|
)
|
||||||
|
assert result.returncode == 1
|
||||||
|
@@ -790,8 +807,205 @@ class TestACMEPrune(IntegrationTest):
|
||||||
|
|
||||||
|
result = self.master.run_command(
|
||||||
|
['ipa-acme-manage', 'pruning',
|
||||||
|
- '--cron="10 1-5 * * *"'],
|
||||||
|
+ '--cron=10 1-5 * * *'],
|
||||||
|
raiseonerr=False
|
||||||
|
)
|
||||||
|
assert result.returncode == 1
|
||||||
|
assert "1-5 ranges are not supported" in result.stderr_text
|
||||||
|
+
|
||||||
|
+ def test_prune_cert_manual(self, issue_and_expire_acme_cert):
|
||||||
|
+ """Test to prune expired certificate by manual run"""
|
||||||
|
+ if (tasks.get_pki_version(self.master)
|
||||||
|
+ < tasks.parse_version('11.3.0')):
|
||||||
|
+ raise pytest.skip("Certificate pruning is not available")
|
||||||
|
+
|
||||||
|
+ issue_and_expire_acme_cert(
|
||||||
|
+ self.master, self.clients[0], self.acme_server)
|
||||||
|
+
|
||||||
|
+ # check that the certificate issued for the client
|
||||||
|
+ result = self.master.run_command(
|
||||||
|
+ ['ipa', 'cert-find', '--subject', self.clients[0].hostname]
|
||||||
|
+ )
|
||||||
|
+ assert f'CN={self.clients[0].hostname}' in result.stdout_text
|
||||||
|
+
|
||||||
|
+ # run prune command manually
|
||||||
|
+ self.master.run_command(['ipa-acme-manage', 'pruning', '--enable'])
|
||||||
|
+ self.master.run_command(['ipactl', 'restart'])
|
||||||
|
+ self.master.run_command(['ipa-acme-manage', 'pruning', '--run'])
|
||||||
|
+ # wait for cert to get prune
|
||||||
|
+ time.sleep(50)
|
||||||
|
+
|
||||||
|
+ # check if client cert is removed
|
||||||
|
+ result = self.master.run_command(
|
||||||
|
+ ['ipa', 'cert-find', '--subject', self.clients[0].hostname],
|
||||||
|
+ raiseonerr=False
|
||||||
|
+ )
|
||||||
|
+ assert f'CN={self.clients[0].hostname}' not in result.stdout_text
|
||||||
|
+
|
||||||
|
+ def test_prune_cert_cron(self, issue_and_expire_acme_cert):
|
||||||
|
+ """Test to prune expired certificate by cron job"""
|
||||||
|
+ if (tasks.get_pki_version(self.master)
|
||||||
|
+ < tasks.parse_version('11.3.0')):
|
||||||
|
+ raise pytest.skip("Certificate pruning is not available")
|
||||||
|
+
|
||||||
|
+ issue_and_expire_acme_cert(
|
||||||
|
+ self.master, self.clients[0], self.acme_server)
|
||||||
|
+
|
||||||
|
+ # check that the certificate issued for the client
|
||||||
|
+ result = self.master.run_command(
|
||||||
|
+ ['ipa', 'cert-find', '--subject', self.clients[0].hostname]
|
||||||
|
+ )
|
||||||
|
+ assert f'CN={self.clients[0].hostname}' in result.stdout_text
|
||||||
|
+
|
||||||
|
+ # enable pruning
|
||||||
|
+ self.master.run_command(['ipa-acme-manage', 'pruning', '--enable'])
|
||||||
|
+
|
||||||
|
+ # cron would be set to run the next minute
|
||||||
|
+ cron_minute = self.master.run_command(
|
||||||
|
+ [
|
||||||
|
+ "python3",
|
||||||
|
+ "-c",
|
||||||
|
+ (
|
||||||
|
+ "from datetime import datetime; "
|
||||||
|
+ "print(int(datetime.now().strftime('%M')) + 5)"
|
||||||
|
+ ),
|
||||||
|
+ ]
|
||||||
|
+ ).stdout_text.strip()
|
||||||
|
+ self.master.run_command(
|
||||||
|
+ ['ipa-acme-manage', 'pruning',
|
||||||
|
+ f'--cron={cron_minute} * * * *']
|
||||||
|
+ )
|
||||||
|
+ self.master.run_command(['ipactl', 'restart'])
|
||||||
|
+ # wait for 5 minutes to cron to execute and 20 sec for just in case
|
||||||
|
+ time.sleep(320)
|
||||||
|
+
|
||||||
|
+ # check if client cert is removed
|
||||||
|
+ result = self.master.run_command(
|
||||||
|
+ ['ipa', 'cert-find', '--subject', self.clients[0].hostname],
|
||||||
|
+ raiseonerr=False
|
||||||
|
+ )
|
||||||
|
+ assert f'CN={self.clients[0].hostname}' not in result.stdout_text
|
||||||
|
+
|
||||||
|
+ def test_prune_cert_retention_unit(self, issue_and_expire_acme_cert):
|
||||||
|
+ """Test to prune expired certificate with retention unit option"""
|
||||||
|
+ if (tasks.get_pki_version(self.master)
|
||||||
|
+ < tasks.parse_version('11.3.0')):
|
||||||
|
+ raise pytest.skip("Certificate pruning is not available")
|
||||||
|
+ issue_and_expire_acme_cert(
|
||||||
|
+ self.master, self.clients[0], self.acme_server)
|
||||||
|
+
|
||||||
|
+ # check that the certificate issued for the client
|
||||||
|
+ result = self.master.run_command(
|
||||||
|
+ ['ipa', 'cert-find', '--subject', self.clients[0].hostname]
|
||||||
|
+ )
|
||||||
|
+ assert f'CN={self.clients[0].hostname}' in result.stdout_text
|
||||||
|
+
|
||||||
|
+ # enable pruning
|
||||||
|
+ self.master.run_command(['ipa-acme-manage', 'pruning', '--enable'])
|
||||||
|
+
|
||||||
|
+ # certretention set to 5 min
|
||||||
|
+ self.master.run_command(
|
||||||
|
+ ['ipa-acme-manage', 'pruning',
|
||||||
|
+ '--certretention=5', '--certretentionunit=minute']
|
||||||
|
+ )
|
||||||
|
+ self.master.run_command(['ipactl', 'restart'])
|
||||||
|
+
|
||||||
|
+ # wait for 5 min and check if expired cert is removed
|
||||||
|
+ time.sleep(310)
|
||||||
|
+ self.master.run_command(['ipa-acme-manage', 'pruning', '--run'])
|
||||||
|
+ result = self.master.run_command(
|
||||||
|
+ ['ipa', 'cert-find', '--subject', self.clients[0].hostname],
|
||||||
|
+ raiseonerr=False
|
||||||
|
+ )
|
||||||
|
+ assert f'CN={self.clients[0].hostname}' not in result.stdout_text
|
||||||
|
+
|
||||||
|
+ def test_prune_cert_search_size_limit(self, issue_and_expire_acme_cert):
|
||||||
|
+ """Test to prune expired certificate with search size limit option"""
|
||||||
|
+ if (tasks.get_pki_version(self.master)
|
||||||
|
+ < tasks.parse_version('11.3.0')):
|
||||||
|
+ raise pytest.skip("Certificate pruning is not available")
|
||||||
|
+ no_of_cert = 10
|
||||||
|
+ search_size_limit = 5
|
||||||
|
+ issue_and_expire_acme_cert(
|
||||||
|
+ self.master, self.clients[0], self.acme_server, no_of_cert)
|
||||||
|
+
|
||||||
|
+ # check that the certificate issued for the client
|
||||||
|
+ result = self.master.run_command(
|
||||||
|
+ ['ipa', 'cert-find', '--subject', self.clients[0].hostname]
|
||||||
|
+ )
|
||||||
|
+ assert f'CN={self.clients[0].hostname}' in result.stdout_text
|
||||||
|
+ assert f'Number of entries returned {no_of_cert}'
|
||||||
|
+
|
||||||
|
+ # enable pruning
|
||||||
|
+ self.master.run_command(['ipa-acme-manage', 'pruning', '--enable'])
|
||||||
|
+
|
||||||
|
+ # certretention set to 5 min
|
||||||
|
+ self.master.run_command(
|
||||||
|
+ ['ipa-acme-manage', 'pruning',
|
||||||
|
+ f'--certsearchsizelimit={search_size_limit}',
|
||||||
|
+ '--certsearchtimelimit=100']
|
||||||
|
+ )
|
||||||
|
+ self.master.run_command(['ipactl', 'restart'])
|
||||||
|
+
|
||||||
|
+ # prune the certificates
|
||||||
|
+ self.master.run_command(['ipa-acme-manage', 'pruning', '--run'])
|
||||||
|
+
|
||||||
|
+ # check if 5 expired cert is removed
|
||||||
|
+ result = self.master.run_command(
|
||||||
|
+ ['ipa', 'cert-find', '--subject', self.clients[0].hostname]
|
||||||
|
+ )
|
||||||
|
+ assert f'Number of entries returned {no_of_cert - search_size_limit}'
|
||||||
|
+
|
||||||
|
+ def test_prune_config_show(self, issue_and_expire_acme_cert):
|
||||||
|
+ """Test to check config-show command shows set param"""
|
||||||
|
+ if (tasks.get_pki_version(self.master)
|
||||||
|
+ < tasks.parse_version('11.3.0')):
|
||||||
|
+ raise pytest.skip("Certificate pruning is not available")
|
||||||
|
+
|
||||||
|
+ self.master.run_command(['ipa-acme-manage', 'pruning', '--enable'])
|
||||||
|
+ self.master.run_command(
|
||||||
|
+ ['ipa-acme-manage', 'pruning',
|
||||||
|
+ '--cron=0 0 1 * *']
|
||||||
|
+ )
|
||||||
|
+ self.master.run_command(
|
||||||
|
+ ['ipa-acme-manage', 'pruning',
|
||||||
|
+ '--certretention=30', '--certretentionunit=day']
|
||||||
|
+ )
|
||||||
|
+ self.master.run_command(
|
||||||
|
+ ['ipa-acme-manage', 'pruning',
|
||||||
|
+ '--certsearchsizelimit=1000', '--certsearchtimelimit=0']
|
||||||
|
+ )
|
||||||
|
+ self.master.run_command(
|
||||||
|
+ ['ipa-acme-manage', 'pruning',
|
||||||
|
+ '--requestretention=30', '--requestretentionunit=day']
|
||||||
|
+ )
|
||||||
|
+ self.master.run_command(
|
||||||
|
+ ['ipa-acme-manage', 'pruning',
|
||||||
|
+ '--requestsearchsizelimit=1000', '--requestsearchtimelimit=0']
|
||||||
|
+ )
|
||||||
|
+ result = self.master.run_command(
|
||||||
|
+ ['ipa-acme-manage', 'pruning', '--config-show']
|
||||||
|
+ )
|
||||||
|
+ assert 'Status: enabled' in result.stdout_text
|
||||||
|
+ assert 'Certificate Retention Time: 30' in result.stdout_text
|
||||||
|
+ assert 'Certificate Retention Unit: day' in result.stdout_text
|
||||||
|
+ assert 'Certificate Search Size Limit: 1000' in result.stdout_text
|
||||||
|
+ assert 'Certificate Search Time Limit: 100' in result.stdout_text
|
||||||
|
+ assert 'Request Retention Time: 30' in result.stdout_text
|
||||||
|
+ assert 'Request Retention Unit: day' in result.stdout_text
|
||||||
|
+ assert 'Request Search Size Limit' in result.stdout_text
|
||||||
|
+ assert 'Request Search Time Limit: 100' in result.stdout_text
|
||||||
|
+ assert 'cron Schedule: 0 0 1 * *' in result.stdout_text
|
||||||
|
+
|
||||||
|
+ def test_prune_disable(self, issue_and_expire_acme_cert):
|
||||||
|
+ """Test prune command throw error after disabling the pruning"""
|
||||||
|
+ if (tasks.get_pki_version(self.master)
|
||||||
|
+ < tasks.parse_version('11.3.0')):
|
||||||
|
+ raise pytest.skip("Certificate pruning is not available")
|
||||||
|
+
|
||||||
|
+ self.master.run_command(['ipa-acme-manage', 'pruning', '--disable'])
|
||||||
|
+ result = self.master.run_command(
|
||||||
|
+ ['ipa-acme-manage', 'pruning',
|
||||||
|
+ '--cron=0 0 1 * *']
|
||||||
|
+ )
|
||||||
|
+ assert 'Status: disabled' in result.stdout_text
|
||||||
|
--
|
||||||
|
2.39.1
|
||||||
|
|
@ -0,0 +1,65 @@
|
|||||||
|
From 88b9be29036a3580a8bccd31986fc30faa9852df Mon Sep 17 00:00:00 2001
|
||||||
|
From: mbhalodi <mbhalodi@redhat.com>
|
||||||
|
Date: Tue, 14 Feb 2023 15:04:58 +0530
|
||||||
|
Subject: [PATCH] ipatests: ensure that ipa automember-rebuild prints a warning
|
||||||
|
|
||||||
|
ipa automember-rebuild now prints a warning about CPU usage.
|
||||||
|
Ensure that the warning is properly displayed.
|
||||||
|
|
||||||
|
Related: https://pagure.io/freeipa/issue/9320
|
||||||
|
|
||||||
|
Signed-off-by: mbhalodi <mbhalodi@redhat.com>
|
||||||
|
Reviewed-By: Florence Blanc-Renaud <flo@redhat.com>
|
||||||
|
---
|
||||||
|
ipatests/test_integration/test_automember.py | 13 ++++++++++---
|
||||||
|
1 file changed, 10 insertions(+), 3 deletions(-)
|
||||||
|
|
||||||
|
diff --git a/ipatests/test_integration/test_automember.py b/ipatests/test_integration/test_automember.py
|
||||||
|
index f013964140714db046a1aa6a92409244b2137727..7acd0d7bf895fec970f2bda8b54f4496280525b6 100644
|
||||||
|
--- a/ipatests/test_integration/test_automember.py
|
||||||
|
+++ b/ipatests/test_integration/test_automember.py
|
||||||
|
@@ -10,6 +10,9 @@ from ipapython.dn import DN
|
||||||
|
from ipatests.pytest_ipa.integration import tasks
|
||||||
|
from ipatests.test_integration.base import IntegrationTest
|
||||||
|
|
||||||
|
+msg = ('IMPORTANT: In case of a high number of users, hosts or '
|
||||||
|
+ 'groups, the operation may require high CPU usage.')
|
||||||
|
+
|
||||||
|
|
||||||
|
class TestAutounmembership(IntegrationTest):
|
||||||
|
"""Tests for autounmembership feature.
|
||||||
|
@@ -206,11 +209,13 @@ class TestAutounmembership(IntegrationTest):
|
||||||
|
assert self.is_user_member_of_group(user2, group1)
|
||||||
|
|
||||||
|
# Running automember-build so that user is part of correct group
|
||||||
|
- self.master.run_command(['ipa', 'automember-rebuild',
|
||||||
|
- '--users=%s' % user2])
|
||||||
|
+ result = self.master.run_command(['ipa', 'automember-rebuild',
|
||||||
|
+ '--users=%s' % user2])
|
||||||
|
assert self.is_user_member_of_group(user2, group2)
|
||||||
|
assert not self.is_user_member_of_group(user2, group1)
|
||||||
|
|
||||||
|
+ assert msg in result.stdout_text
|
||||||
|
+
|
||||||
|
finally:
|
||||||
|
# testcase cleanup
|
||||||
|
self.remove_user_automember(user2, raiseonerr=False)
|
||||||
|
@@ -240,12 +245,14 @@ class TestAutounmembership(IntegrationTest):
|
||||||
|
assert self.is_host_member_of_hostgroup(host2, hostgroup1)
|
||||||
|
|
||||||
|
# Running the automember-build so host is part of correct hostgroup
|
||||||
|
- self.master.run_command(
|
||||||
|
+ result = self.master.run_command(
|
||||||
|
['ipa', 'automember-rebuild', '--hosts=%s' % host2]
|
||||||
|
)
|
||||||
|
assert self.is_host_member_of_hostgroup(host2, hostgroup2)
|
||||||
|
assert not self.is_host_member_of_hostgroup(host2, hostgroup1)
|
||||||
|
|
||||||
|
+ assert msg in result.stdout_text
|
||||||
|
+
|
||||||
|
finally:
|
||||||
|
# testcase cleanup
|
||||||
|
self.remove_host_automember(host2, raiseonerr=False)
|
||||||
|
--
|
||||||
|
2.39.1
|
||||||
|
|
@ -1,98 +0,0 @@
|
|||||||
From a406fd9aec7d053c044e73f16b05489bebd84bc8 Mon Sep 17 00:00:00 2001
|
|
||||||
From: Francisco Trivino <ftrivino@redhat.com>
|
|
||||||
Date: Fri, 19 Jan 2024 17:12:07 +0100
|
|
||||||
Subject: [PATCH] kra: set RSA-OAEP as default wrapping algo when FIPS is
|
|
||||||
enabled
|
|
||||||
|
|
||||||
Vault uses PKCS1v15 as default padding wrapping algo, which is not an approved
|
|
||||||
FIPS algorithm. This commit ensures that KRA is installed with RSA-OAEP if FIPS
|
|
||||||
is enabled. It also handles upgrade path.
|
|
||||||
|
|
||||||
Fixes: https://pagure.io/freeipa/issue/9191
|
|
||||||
|
|
||||||
Signed-off-by: Francisco Trivino <ftrivino@redhat.com>
|
|
||||||
Reviewed-By: Rob Crittenden <rcritten@redhat.com>
|
|
||||||
(cherry picked from commit f2eec9eb208e62f923375b9eaf34fcc491046a0d)
|
|
||||||
---
|
|
||||||
install/share/ipaca_default.ini | 3 +++
|
|
||||||
ipaserver/install/dogtaginstance.py | 4 +++-
|
|
||||||
ipaserver/install/krainstance.py | 12 ++++++++++++
|
|
||||||
ipaserver/install/server/upgrade.py | 12 ++++++++++++
|
|
||||||
4 files changed, 30 insertions(+), 1 deletion(-)
|
|
||||||
|
|
||||||
diff --git a/install/share/ipaca_default.ini b/install/share/ipaca_default.ini
|
|
||||||
index 082f507b2..691f1e1b7 100644
|
|
||||||
--- a/install/share/ipaca_default.ini
|
|
||||||
+++ b/install/share/ipaca_default.ini
|
|
||||||
@@ -166,3 +166,6 @@ pki_audit_signing_subject_dn=cn=KRA Audit,%(ipa_subject_base)s
|
|
||||||
# We will use the dbuser created for the CA.
|
|
||||||
pki_share_db=True
|
|
||||||
pki_share_dbuser_dn=uid=pkidbuser,ou=people,o=ipaca
|
|
||||||
+
|
|
||||||
+# KRA padding, set RSA-OAEP in FIPS mode
|
|
||||||
+pki_use_oaep_rsa_keywrap=%(fips_use_oaep_rsa_keywrap)s
|
|
||||||
\ No newline at end of file
|
|
||||||
diff --git a/ipaserver/install/dogtaginstance.py b/ipaserver/install/dogtaginstance.py
|
|
||||||
index c2c6b3f49..c3c726f68 100644
|
|
||||||
--- a/ipaserver/install/dogtaginstance.py
|
|
||||||
+++ b/ipaserver/install/dogtaginstance.py
|
|
||||||
@@ -1020,7 +1020,9 @@ class PKIIniLoader:
|
|
||||||
# for softhsm2 testing
|
|
||||||
softhsm2_so=paths.LIBSOFTHSM2_SO,
|
|
||||||
# Configure a more secure AJP password by default
|
|
||||||
- ipa_ajp_secret=ipautil.ipa_generate_password(special=None)
|
|
||||||
+ ipa_ajp_secret=ipautil.ipa_generate_password(special=None),
|
|
||||||
+ # in FIPS mode use RSA-OAEP wrapping padding algo as default
|
|
||||||
+ fips_use_oaep_rsa_keywrap=tasks.is_fips_enabled()
|
|
||||||
)
|
|
||||||
|
|
||||||
@classmethod
|
|
||||||
diff --git a/ipaserver/install/krainstance.py b/ipaserver/install/krainstance.py
|
|
||||||
index 13cb2dcaa..0e04840a1 100644
|
|
||||||
--- a/ipaserver/install/krainstance.py
|
|
||||||
+++ b/ipaserver/install/krainstance.py
|
|
||||||
@@ -277,6 +277,18 @@ class KRAInstance(DogtagInstance):
|
|
||||||
|
|
||||||
# A restart is required
|
|
||||||
|
|
||||||
+ def enable_oaep_wrap_algo(self):
|
|
||||||
+ """
|
|
||||||
+ Enable KRA OAEP key wrap algorithm
|
|
||||||
+ """
|
|
||||||
+ with installutils.stopped_service('pki-tomcatd', 'pki-tomcat'):
|
|
||||||
+ directivesetter.set_directive(
|
|
||||||
+ self.config,
|
|
||||||
+ 'keyWrap.useOAEP',
|
|
||||||
+ 'true', quotes=False, separator='=')
|
|
||||||
+
|
|
||||||
+ # A restart is required
|
|
||||||
+
|
|
||||||
def update_cert_config(self, nickname, cert):
|
|
||||||
"""
|
|
||||||
When renewing a KRA subsystem certificate the configuration file
|
|
||||||
diff --git a/ipaserver/install/server/upgrade.py b/ipaserver/install/server/upgrade.py
|
|
||||||
index e4dc7ae73..c84516b56 100644
|
|
||||||
--- a/ipaserver/install/server/upgrade.py
|
|
||||||
+++ b/ipaserver/install/server/upgrade.py
|
|
||||||
@@ -1780,6 +1780,18 @@ def upgrade_configuration():
|
|
||||||
else:
|
|
||||||
logger.info('ephemeralRequest is already enabled')
|
|
||||||
|
|
||||||
+ if tasks.is_fips_enabled():
|
|
||||||
+ logger.info('[Ensuring KRA OAEP wrap algo is enabled in FIPS]')
|
|
||||||
+ value = directivesetter.get_directive(
|
|
||||||
+ paths.KRA_CS_CFG_PATH,
|
|
||||||
+ 'keyWrap.useOAEP',
|
|
||||||
+ separator='=')
|
|
||||||
+ if value is None or value.lower() != 'true':
|
|
||||||
+ logger.info('Use the OAEP key wrap algo')
|
|
||||||
+ kra.enable_oaep_wrap_algo()
|
|
||||||
+ else:
|
|
||||||
+ logger.info('OAEP key wrap algo is already enabled')
|
|
||||||
+
|
|
||||||
# several upgrade steps require running CA. If CA is configured,
|
|
||||||
# always run ca.start() because we need to wait until CA is really ready
|
|
||||||
# by checking status using http
|
|
||||||
--
|
|
||||||
2.43.0
|
|
||||||
|
|
@ -1,29 +0,0 @@
|
|||||||
From a8e433f7c8d844d9f337a34db09b0197f2dbc5af Mon Sep 17 00:00:00 2001
|
|
||||||
From: Julien Rische <jrische@redhat.com>
|
|
||||||
Date: Tue, 20 Feb 2024 15:14:24 +0100
|
|
||||||
Subject: [PATCH] ipa-kdb: Fix double free in ipadb_reinit_mspac()
|
|
||||||
|
|
||||||
Fixes: https://pagure.io/freeipa/issue/9535
|
|
||||||
|
|
||||||
Signed-off-by: Julien Rische <jrische@redhat.com>
|
|
||||||
Reviewed-By: Florence Blanc-Renaud <flo@redhat.com>
|
|
||||||
(cherry picked from commit dd27d225524aa81c038a970961a4f878cf742e2a)
|
|
||||||
---
|
|
||||||
daemons/ipa-kdb/ipa_kdb_mspac.c | 1 +
|
|
||||||
1 file changed, 1 insertion(+)
|
|
||||||
|
|
||||||
diff --git a/daemons/ipa-kdb/ipa_kdb_mspac.c b/daemons/ipa-kdb/ipa_kdb_mspac.c
|
|
||||||
index deed513b9..0964d112a 100644
|
|
||||||
--- a/daemons/ipa-kdb/ipa_kdb_mspac.c
|
|
||||||
+++ b/daemons/ipa-kdb/ipa_kdb_mspac.c
|
|
||||||
@@ -3084,6 +3084,7 @@ ipadb_reinit_mspac(struct ipadb_context *ipactx, bool force_reinit,
|
|
||||||
}
|
|
||||||
|
|
||||||
free(resstr);
|
|
||||||
+ resstr = NULL;
|
|
||||||
|
|
||||||
flat_server_name = get_server_netbios_name(ipactx);
|
|
||||||
if (!flat_server_name) {
|
|
||||||
--
|
|
||||||
2.43.0
|
|
||||||
|
|
74
SOURCES/0022-ipatests-fix-tests-in-TestACMEPrune.patch
Normal file
74
SOURCES/0022-ipatests-fix-tests-in-TestACMEPrune.patch
Normal file
@ -0,0 +1,74 @@
|
|||||||
|
From e76b219c21d53b6bccce4ea3d18e2b61ac835e1f Mon Sep 17 00:00:00 2001
|
||||||
|
From: Mohammad Rizwan <myusuf@redhat.com>
|
||||||
|
Date: Mon, 20 Feb 2023 15:33:09 +0530
|
||||||
|
Subject: [PATCH] ipatests: fix tests in TestACMEPrune
|
||||||
|
|
||||||
|
When cron_minute + 5 > 59, cron job throwing error for it.
|
||||||
|
i.e 58 + 5 = 63 which is not acceptable value for cron minute.
|
||||||
|
|
||||||
|
Second fix is related to mismatch of confing setting and corresponding
|
||||||
|
assert.
|
||||||
|
|
||||||
|
Third fix is related to extending time by 60 minutes to properly
|
||||||
|
expire the certs.
|
||||||
|
|
||||||
|
related: https://pagure.io/freeipa/issue/9294
|
||||||
|
|
||||||
|
Signed-off-by: Mohammad Rizwan <myusuf@redhat.com>
|
||||||
|
Reviewed-By: Florence Blanc-Renaud <flo@redhat.com>
|
||||||
|
---
|
||||||
|
ipatests/test_integration/test_acme.py | 15 ++++++++-------
|
||||||
|
1 file changed, 8 insertions(+), 7 deletions(-)
|
||||||
|
|
||||||
|
diff --git a/ipatests/test_integration/test_acme.py b/ipatests/test_integration/test_acme.py
|
||||||
|
index 1334be52f4530dd8b2a4207744146cd0eb5477a3..49b173060f88d4b8e876d8e3461a935938518b44 100644
|
||||||
|
--- a/ipatests/test_integration/test_acme.py
|
||||||
|
+++ b/ipatests/test_integration/test_acme.py
|
||||||
|
@@ -600,7 +600,7 @@ def issue_and_expire_acme_cert():
|
||||||
|
# move system date to expire acme cert
|
||||||
|
for host in hosts:
|
||||||
|
tasks.kdestroy_all(host)
|
||||||
|
- tasks.move_date(host, 'stop', '+90days')
|
||||||
|
+ tasks.move_date(host, 'stop', '+90days+60minutes')
|
||||||
|
|
||||||
|
time.sleep(10)
|
||||||
|
tasks.get_kdcinfo(host)
|
||||||
|
@@ -622,7 +622,7 @@ def issue_and_expire_acme_cert():
|
||||||
|
|
||||||
|
# move back date
|
||||||
|
for host in hosts:
|
||||||
|
- tasks.move_date(host, 'start', '-90days')
|
||||||
|
+ tasks.move_date(host, 'start', '-90days-60minutes')
|
||||||
|
|
||||||
|
|
||||||
|
class TestACMERenew(IntegrationTest):
|
||||||
|
@@ -866,8 +866,9 @@ class TestACMEPrune(IntegrationTest):
|
||||||
|
"python3",
|
||||||
|
"-c",
|
||||||
|
(
|
||||||
|
- "from datetime import datetime; "
|
||||||
|
- "print(int(datetime.now().strftime('%M')) + 5)"
|
||||||
|
+ "from datetime import datetime, timedelta; "
|
||||||
|
+ "print(int((datetime.now() + "
|
||||||
|
+ "timedelta(minutes=5)).strftime('%M')))"
|
||||||
|
),
|
||||||
|
]
|
||||||
|
).stdout_text.strip()
|
||||||
|
@@ -990,11 +991,11 @@ class TestACMEPrune(IntegrationTest):
|
||||||
|
assert 'Certificate Retention Time: 30' in result.stdout_text
|
||||||
|
assert 'Certificate Retention Unit: day' in result.stdout_text
|
||||||
|
assert 'Certificate Search Size Limit: 1000' in result.stdout_text
|
||||||
|
- assert 'Certificate Search Time Limit: 100' in result.stdout_text
|
||||||
|
+ assert 'Certificate Search Time Limit: 0' in result.stdout_text
|
||||||
|
assert 'Request Retention Time: 30' in result.stdout_text
|
||||||
|
assert 'Request Retention Unit: day' in result.stdout_text
|
||||||
|
- assert 'Request Search Size Limit' in result.stdout_text
|
||||||
|
- assert 'Request Search Time Limit: 100' in result.stdout_text
|
||||||
|
+ assert 'Request Search Size Limit: 1000' in result.stdout_text
|
||||||
|
+ assert 'Request Search Time Limit: 0' in result.stdout_text
|
||||||
|
assert 'cron Schedule: 0 0 1 * *' in result.stdout_text
|
||||||
|
|
||||||
|
def test_prune_disable(self, issue_and_expire_acme_cert):
|
||||||
|
--
|
||||||
|
2.39.1
|
||||||
|
|
@ -0,0 +1,574 @@
|
|||||||
|
From 51c378f66fcf59322a0774a6d9b37e7e9ac55a17 Mon Sep 17 00:00:00 2001
|
||||||
|
From: Julien Rische <jrische@redhat.com>
|
||||||
|
Date: Fri, 7 Apr 2023 17:04:06 +0200
|
||||||
|
Subject: [PATCH] Tolerate absence of PAC ticket signature depending of server
|
||||||
|
capabilities
|
||||||
|
|
||||||
|
Since November 2020, Active Directory KDC generates a new type of
|
||||||
|
signature as part of the PAC. It is called "ticket signature", and is
|
||||||
|
generated based on the encrypted part of the ticket. The presence of
|
||||||
|
this signature is not mandatory in order for the PAC to be accepted for
|
||||||
|
S4U requests.
|
||||||
|
|
||||||
|
However, the behavior is different for MIT krb5. Support was added as
|
||||||
|
part of the 1.20 release, and this signature is required in order to
|
||||||
|
process S4U requests. Contrary to the PAC extended KDC signature, the
|
||||||
|
code generating this signature cannot be isolated and backported to
|
||||||
|
older krb5 versions because this version of the KDB API does not allow
|
||||||
|
passing the content of the ticket's encrypted part to IPA.
|
||||||
|
|
||||||
|
This is an issue in gradual upgrade scenarios where some IPA servers
|
||||||
|
rely on 1.19 and older versions of MIT krb5, while others use version
|
||||||
|
1.20 or newer. A service ticket that was provided by 1.19- IPA KDC will
|
||||||
|
be rejected when used by a service against a 1.20+ IPA KDC for S4U
|
||||||
|
requests.
|
||||||
|
|
||||||
|
On Fedora, CentOS 9 Stream, and RHEL 9, when the krb5 version is 1.20 or
|
||||||
|
newer, it will include a downstream-only update adding the
|
||||||
|
"optional_pac_tkt_chksum" KDB string attribute allowing to tolerate the
|
||||||
|
absence of PAC ticket signatures, if necessary.
|
||||||
|
|
||||||
|
This commit adds an extra step during the installation and update
|
||||||
|
processes where it adds a "pacTktSignSupported" ipaConfigString
|
||||||
|
attribute in "cn=KDC,cn=[server],cn=masters,cn=ipa,cn=etc,[basedn]" if
|
||||||
|
the MIT krb5 version IPA what built with was 1.20 or newer.
|
||||||
|
|
||||||
|
This commit also set "optional_pac_tkt_chksum" as a virtual KDB entry
|
||||||
|
attribute. This means the value of the attribute is not actually stored
|
||||||
|
in the database (to avoid race conditions), but its value is determined
|
||||||
|
at the KDC starting time by search the "pacTktSignSupported"
|
||||||
|
ipaConfigString in the server list. If this value is missing for at
|
||||||
|
least of them is missing, enforcement of the PAC ticket signature is
|
||||||
|
disabled by setting "optional_pac_tkt_chksum" to true for the local
|
||||||
|
realm TGS KDB entry.
|
||||||
|
|
||||||
|
For foreign realm TGS KDB entries, the "optional_pac_tkt_chksum" virtual
|
||||||
|
string attribute is set to true systematically, because, at least for
|
||||||
|
now, trusted AD domains can still have PAC ticket signature support
|
||||||
|
disabled.
|
||||||
|
|
||||||
|
Given the fact the "pacTktSignSupported" ipaConfigString for a single
|
||||||
|
server is added when this server is updated, and that the value of
|
||||||
|
"optional_pac_tkt_chksum" is determined at KDC starting time based on
|
||||||
|
the ipaConfigString attributes of all the KDCs in the domain, this
|
||||||
|
requires to restart all the KDCs in the domain after all IPA servers
|
||||||
|
were updated in order for PAC ticket signature enforcement to actually
|
||||||
|
take effect.
|
||||||
|
|
||||||
|
Fixes: https://pagure.io/freeipa/issue/9371
|
||||||
|
Signed-off-by: Julien Rische <jrische@redhat.com>
|
||||||
|
Reviewed-By: Rob Crittenden <rcritten@redhat.com>
|
||||||
|
Reviewed-By: Alexander Bokovoy <abokovoy@redhat.com>
|
||||||
|
(cherry picked from commit bbe545ff9feb972e549c743025e4a26b14ef8f89)
|
||||||
|
---
|
||||||
|
VERSION.m4 | 6 ++
|
||||||
|
configure.ac | 1 +
|
||||||
|
daemons/ipa-kdb/ipa_kdb.c | 55 +++++++++++
|
||||||
|
daemons/ipa-kdb/ipa_kdb.h | 1 +
|
||||||
|
daemons/ipa-kdb/ipa_kdb_principals.c | 139 +++++++++++++++++++++++----
|
||||||
|
ipapython/Makefile.am | 15 +--
|
||||||
|
ipapython/version.py.in | 4 +
|
||||||
|
ipaserver/install/krbinstance.py | 25 ++++-
|
||||||
|
ipaserver/install/server/upgrade.py | 5 +
|
||||||
|
ipaserver/masters.py | 2 +
|
||||||
|
10 files changed, 225 insertions(+), 28 deletions(-)
|
||||||
|
|
||||||
|
diff --git a/VERSION.m4 b/VERSION.m4
|
||||||
|
index e5d60c4c3..9b727feca 100644
|
||||||
|
--- a/VERSION.m4
|
||||||
|
+++ b/VERSION.m4
|
||||||
|
@@ -137,6 +137,11 @@ ifelse(IPA_VERSION_IS_GIT_SNAPSHOT, yes,
|
||||||
|
IPA_GIT_VERSION),
|
||||||
|
NEWLINE)) dnl IPA_VERSION end
|
||||||
|
|
||||||
|
+########################################################
|
||||||
|
+# Version of MIT krb5 used to build IPA
|
||||||
|
+########################################################
|
||||||
|
+define(IPA_KRB5_BUILD_VERSION, translit(esyscmd(krb5-config --version | awk '{ print $NF }'), NEWLINE))
|
||||||
|
+
|
||||||
|
dnl DEBUG: uncomment following lines and run command m4 VERSION.m4
|
||||||
|
dnl `IPA_VERSION: ''IPA_VERSION'
|
||||||
|
dnl `IPA_GIT_VERSION: ''IPA_GIT_VERSION'
|
||||||
|
@@ -144,3 +149,4 @@ dnl `IPA_GIT_BRANCH: ''IPA_GIT_BRANCH'
|
||||||
|
dnl `IPA_API_VERSION: ''IPA_API_VERSION'
|
||||||
|
dnl `IPA_DATA_VERSION: ''IPA_DATA_VERSION'
|
||||||
|
dnl `IPA_NUM_VERSION: ''IPA_NUM_VERSION'
|
||||||
|
+dnl `IPA_KRB5_BUILD_VERSION: ''IPA_KRB5_BUILD_VERSION'
|
||||||
|
diff --git a/configure.ac b/configure.ac
|
||||||
|
index 140045821..973cba33c 100644
|
||||||
|
--- a/configure.ac
|
||||||
|
+++ b/configure.ac
|
||||||
|
@@ -460,6 +460,7 @@ AC_SUBST(VENDOR_SUFFIX)
|
||||||
|
AC_SUBST([VERSION], [IPA_VERSION])
|
||||||
|
AC_SUBST([GIT_VERSION], [IPA_GIT_VERSION])
|
||||||
|
AC_SUBST([GIT_BRANCH], [IPA_GIT_BRANCH])
|
||||||
|
+AC_SUBST([KRB5_BUILD_VERSION], [IPA_KRB5_BUILD_VERSION])
|
||||||
|
# used by Makefile.am for files depending on templates
|
||||||
|
AC_SUBST([CONFIG_STATUS])
|
||||||
|
|
||||||
|
diff --git a/daemons/ipa-kdb/ipa_kdb.c b/daemons/ipa-kdb/ipa_kdb.c
|
||||||
|
index 93563536c..9a56640ff 100644
|
||||||
|
--- a/daemons/ipa-kdb/ipa_kdb.c
|
||||||
|
+++ b/daemons/ipa-kdb/ipa_kdb.c
|
||||||
|
@@ -524,6 +524,52 @@ static krb5_principal ipadb_create_local_tgs(krb5_context kcontext,
|
||||||
|
return tgtp;
|
||||||
|
}
|
||||||
|
|
||||||
|
+static char *no_attrs[] = {
|
||||||
|
+ LDAP_NO_ATTRS,
|
||||||
|
+
|
||||||
|
+ NULL
|
||||||
|
+};
|
||||||
|
+
|
||||||
|
+static krb5_error_code
|
||||||
|
+should_support_pac_tkt_sign(krb5_context kcontext, bool *result)
|
||||||
|
+{
|
||||||
|
+ struct ipadb_context *ipactx;
|
||||||
|
+ krb5_error_code kerr;
|
||||||
|
+ LDAPMessage *res = NULL;
|
||||||
|
+ char *masters_dn = NULL;
|
||||||
|
+ int count;
|
||||||
|
+
|
||||||
|
+ char *kdc_filter = "(&(cn=KDC)(objectClass=ipaConfigObject)"
|
||||||
|
+ "(!(ipaConfigString=pacTktSignSupported)))";
|
||||||
|
+
|
||||||
|
+ ipactx = ipadb_get_context(kcontext);
|
||||||
|
+ if (!ipactx) {
|
||||||
|
+ kerr = KRB5_KDB_DBNOTINITED;
|
||||||
|
+ goto done;
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ count = asprintf(&masters_dn, "cn=masters,cn=ipa,cn=etc,%s", ipactx->base);
|
||||||
|
+ if (count < 0) {
|
||||||
|
+ kerr = ENOMEM;
|
||||||
|
+ goto done;
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ kerr = ipadb_simple_search(ipactx, masters_dn, LDAP_SCOPE_SUBTREE,
|
||||||
|
+ kdc_filter, no_attrs, &res);
|
||||||
|
+ if (kerr)
|
||||||
|
+ goto done;
|
||||||
|
+
|
||||||
|
+ count = ldap_count_entries(ipactx->lcontext, res);
|
||||||
|
+
|
||||||
|
+ if (result)
|
||||||
|
+ *result = (count == 0);
|
||||||
|
+
|
||||||
|
+done:
|
||||||
|
+ free(masters_dn);
|
||||||
|
+ ldap_msgfree(res);
|
||||||
|
+ return kerr;
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
/* INTERFACE */
|
||||||
|
|
||||||
|
static krb5_error_code ipadb_init_library(void)
|
||||||
|
@@ -544,6 +590,7 @@ static krb5_error_code ipadb_init_module(krb5_context kcontext,
|
||||||
|
krb5_error_code kerr;
|
||||||
|
int ret;
|
||||||
|
int i;
|
||||||
|
+ bool pac_tkt_sign_supported;
|
||||||
|
|
||||||
|
/* make sure the context is freed to avoid leaking it */
|
||||||
|
ipactx = ipadb_get_context(kcontext);
|
||||||
|
@@ -628,6 +675,14 @@ static krb5_error_code ipadb_init_module(krb5_context kcontext,
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
|
||||||
|
+ /* Enforce PAC ticket signature verification if supported by all KDCs */
|
||||||
|
+ kerr = should_support_pac_tkt_sign(kcontext, &pac_tkt_sign_supported);
|
||||||
|
+ if (kerr) {
|
||||||
|
+ ret = kerr;
|
||||||
|
+ goto fail;
|
||||||
|
+ }
|
||||||
|
+ ipactx->optional_pac_tkt_chksum = !pac_tkt_sign_supported;
|
||||||
|
+
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
fail:
|
||||||
|
diff --git a/daemons/ipa-kdb/ipa_kdb.h b/daemons/ipa-kdb/ipa_kdb.h
|
||||||
|
index 7aa5be494..0f4d3e431 100644
|
||||||
|
--- a/daemons/ipa-kdb/ipa_kdb.h
|
||||||
|
+++ b/daemons/ipa-kdb/ipa_kdb.h
|
||||||
|
@@ -143,6 +143,7 @@ struct ipadb_context {
|
||||||
|
krb5_key_salt_tuple *def_encs;
|
||||||
|
int n_def_encs;
|
||||||
|
struct ipadb_mspac *mspac;
|
||||||
|
+ bool optional_pac_tkt_chksum;
|
||||||
|
#ifdef HAVE_KRB5_CERTAUTH_PLUGIN
|
||||||
|
krb5_certauth_moddata certauth_moddata;
|
||||||
|
#endif
|
||||||
|
diff --git a/daemons/ipa-kdb/ipa_kdb_principals.c b/daemons/ipa-kdb/ipa_kdb_principals.c
|
||||||
|
index e95cb453c..e6c3fba21 100644
|
||||||
|
--- a/daemons/ipa-kdb/ipa_kdb_principals.c
|
||||||
|
+++ b/daemons/ipa-kdb/ipa_kdb_principals.c
|
||||||
|
@@ -113,6 +113,8 @@ static char *std_principal_obj_classes[] = {
|
||||||
|
|
||||||
|
#define DEFAULT_TL_DATA_CONTENT "\x00\x00\x00\x00principal@UNINITIALIZED"
|
||||||
|
|
||||||
|
+#define OPT_PAC_TKT_CHKSUM_STR_ATTR_NAME "optional_pac_tkt_chksum"
|
||||||
|
+
|
||||||
|
static int ipadb_ldap_attr_to_tl_data(LDAP *lcontext, LDAPMessage *le,
|
||||||
|
char *attrname,
|
||||||
|
krb5_tl_data **result, int *num)
|
||||||
|
@@ -178,10 +180,56 @@ done:
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
-static krb5_error_code ipadb_set_tl_data(krb5_db_entry *entry,
|
||||||
|
- krb5_int16 type,
|
||||||
|
- krb5_ui_2 length,
|
||||||
|
- const krb5_octet *data)
|
||||||
|
+static bool
|
||||||
|
+is_tgs_princ(krb5_context kcontext, krb5_const_principal princ)
|
||||||
|
+{
|
||||||
|
+ krb5_data *primary;
|
||||||
|
+ size_t l_tgs_name;
|
||||||
|
+
|
||||||
|
+ if (2 != krb5_princ_size(kcontext, princ))
|
||||||
|
+ return false;
|
||||||
|
+
|
||||||
|
+ primary = krb5_princ_component(kcontext, princ, 0);
|
||||||
|
+
|
||||||
|
+ l_tgs_name = strlen(KRB5_TGS_NAME);
|
||||||
|
+
|
||||||
|
+ if (l_tgs_name != primary->length)
|
||||||
|
+ return false;
|
||||||
|
+
|
||||||
|
+ return 0 == memcmp(primary->data, KRB5_TGS_NAME, l_tgs_name);
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+static krb5_error_code
|
||||||
|
+cmp_local_tgs_princ(krb5_context kcontext, const char *local_realm,
|
||||||
|
+ krb5_const_principal princ, bool *result)
|
||||||
|
+{
|
||||||
|
+ krb5_principal local_tgs_princ;
|
||||||
|
+ size_t l_local_realm;
|
||||||
|
+ krb5_error_code kerr;
|
||||||
|
+ bool res;
|
||||||
|
+
|
||||||
|
+ l_local_realm = strlen(local_realm);
|
||||||
|
+
|
||||||
|
+ kerr = krb5_build_principal(kcontext, &local_tgs_princ,
|
||||||
|
+ l_local_realm, local_realm,
|
||||||
|
+ KRB5_TGS_NAME, local_realm, NULL);
|
||||||
|
+ if (kerr)
|
||||||
|
+ goto end;
|
||||||
|
+
|
||||||
|
+ res = (bool) krb5_principal_compare(kcontext, local_tgs_princ, princ);
|
||||||
|
+
|
||||||
|
+ if (result)
|
||||||
|
+ *result = res;
|
||||||
|
+
|
||||||
|
+end:
|
||||||
|
+ krb5_free_principal(kcontext, local_tgs_princ);
|
||||||
|
+ return kerr;
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
+krb5_error_code ipadb_set_tl_data(krb5_db_entry *entry,
|
||||||
|
+ krb5_int16 type,
|
||||||
|
+ krb5_ui_2 length,
|
||||||
|
+ const krb5_octet *data)
|
||||||
|
{
|
||||||
|
krb5_error_code kerr;
|
||||||
|
krb5_tl_data *new_td = NULL;
|
||||||
|
@@ -1632,6 +1680,8 @@ krb5_error_code ipadb_get_principal(krb5_context kcontext,
|
||||||
|
krb5_db_entry **entry)
|
||||||
|
{
|
||||||
|
struct ipadb_context *ipactx;
|
||||||
|
+ bool is_local_tgs_princ;
|
||||||
|
+ const char *opt_pac_tkt_chksum_val;
|
||||||
|
krb5_error_code kerr;
|
||||||
|
|
||||||
|
*entry = NULL;
|
||||||
|
@@ -1647,11 +1697,33 @@ krb5_error_code ipadb_get_principal(krb5_context kcontext,
|
||||||
|
|
||||||
|
/* Lookup local names and aliases first. */
|
||||||
|
kerr = dbget_princ(kcontext, ipactx, search_for, flags, entry);
|
||||||
|
- if (kerr != KRB5_KDB_NOENTRY) {
|
||||||
|
+ if (kerr == KRB5_KDB_NOENTRY) {
|
||||||
|
+ kerr = dbget_alias(kcontext, ipactx, search_for, flags, entry);
|
||||||
|
+ }
|
||||||
|
+ if (kerr)
|
||||||
|
return kerr;
|
||||||
|
+
|
||||||
|
+ /* If TGS principal, some virtual attributes may be added */
|
||||||
|
+ if (is_tgs_princ(kcontext, (*entry)->princ)) {
|
||||||
|
+ kerr = cmp_local_tgs_princ(kcontext, ipactx->realm, (*entry)->princ,
|
||||||
|
+ &is_local_tgs_princ);
|
||||||
|
+ if (kerr)
|
||||||
|
+ return kerr;
|
||||||
|
+
|
||||||
|
+ /* PAC ticket signature should be optional for foreign realms, and local
|
||||||
|
+ * realm if not supported by all servers
|
||||||
|
+ */
|
||||||
|
+ if (!is_local_tgs_princ || ipactx->optional_pac_tkt_chksum)
|
||||||
|
+ opt_pac_tkt_chksum_val = "true";
|
||||||
|
+ else
|
||||||
|
+ opt_pac_tkt_chksum_val = "false";
|
||||||
|
+
|
||||||
|
+ kerr = krb5_dbe_set_string(kcontext, *entry,
|
||||||
|
+ OPT_PAC_TKT_CHKSUM_STR_ATTR_NAME,
|
||||||
|
+ opt_pac_tkt_chksum_val);
|
||||||
|
}
|
||||||
|
|
||||||
|
- return dbget_alias(kcontext, ipactx, search_for, flags, entry);
|
||||||
|
+ return kerr;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ipadb_free_principal_e_data(krb5_context kcontext, krb5_octet *e_data)
|
||||||
|
@@ -1954,6 +2026,20 @@ done:
|
||||||
|
return kerr;
|
||||||
|
}
|
||||||
|
|
||||||
|
+static bool should_filter_out_attr(krb5_tl_data *data)
|
||||||
|
+{
|
||||||
|
+ switch (data->tl_data_type) {
|
||||||
|
+ case KRB5_TL_DB_ARGS:
|
||||||
|
+ case KRB5_TL_KADM_DATA:
|
||||||
|
+ case KRB5_TL_LAST_ADMIN_UNLOCK:
|
||||||
|
+ case KRB5_TL_LAST_PWD_CHANGE:
|
||||||
|
+ case KRB5_TL_MKVNO:
|
||||||
|
+ return true;
|
||||||
|
+ default:
|
||||||
|
+ return false;
|
||||||
|
+ }
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
static krb5_error_code ipadb_get_ldap_mod_extra_data(struct ipadb_mods *imods,
|
||||||
|
krb5_tl_data *tl_data,
|
||||||
|
int mod_op)
|
||||||
|
@@ -1965,13 +2051,8 @@ static krb5_error_code ipadb_get_ldap_mod_extra_data(struct ipadb_mods *imods,
|
||||||
|
int n, i;
|
||||||
|
|
||||||
|
for (n = 0, data = tl_data; data; data = data->tl_data_next) {
|
||||||
|
- if (data->tl_data_type == KRB5_TL_LAST_PWD_CHANGE ||
|
||||||
|
- data->tl_data_type == KRB5_TL_KADM_DATA ||
|
||||||
|
- data->tl_data_type == KRB5_TL_DB_ARGS ||
|
||||||
|
- data->tl_data_type == KRB5_TL_MKVNO ||
|
||||||
|
- data->tl_data_type == KRB5_TL_LAST_ADMIN_UNLOCK) {
|
||||||
|
+ if (should_filter_out_attr(data))
|
||||||
|
continue;
|
||||||
|
- }
|
||||||
|
n++;
|
||||||
|
}
|
||||||
|
|
||||||
|
@@ -1987,13 +2068,8 @@ static krb5_error_code ipadb_get_ldap_mod_extra_data(struct ipadb_mods *imods,
|
||||||
|
|
||||||
|
for (i = 0, data = tl_data; data; data = data->tl_data_next) {
|
||||||
|
|
||||||
|
- if (data->tl_data_type == KRB5_TL_LAST_PWD_CHANGE ||
|
||||||
|
- data->tl_data_type == KRB5_TL_KADM_DATA ||
|
||||||
|
- data->tl_data_type == KRB5_TL_DB_ARGS ||
|
||||||
|
- data->tl_data_type == KRB5_TL_MKVNO ||
|
||||||
|
- data->tl_data_type == KRB5_TL_LAST_ADMIN_UNLOCK) {
|
||||||
|
+ if (should_filter_out_attr(data))
|
||||||
|
continue;
|
||||||
|
- }
|
||||||
|
|
||||||
|
be_type = htons(data->tl_data_type);
|
||||||
|
|
||||||
|
@@ -2745,10 +2821,37 @@ done:
|
||||||
|
return kerr;
|
||||||
|
}
|
||||||
|
|
||||||
|
+static krb5_error_code
|
||||||
|
+remove_virtual_str_attrs(krb5_context kcontext, krb5_db_entry *entry)
|
||||||
|
+{
|
||||||
|
+ char *str_attr_val;
|
||||||
|
+ krb5_error_code kerr;
|
||||||
|
+
|
||||||
|
+ kerr = krb5_dbe_get_string(kcontext, entry,
|
||||||
|
+ OPT_PAC_TKT_CHKSUM_STR_ATTR_NAME,
|
||||||
|
+ &str_attr_val);
|
||||||
|
+ if (kerr)
|
||||||
|
+ return kerr;
|
||||||
|
+
|
||||||
|
+ if (str_attr_val)
|
||||||
|
+ kerr = krb5_dbe_set_string(kcontext, entry,
|
||||||
|
+ OPT_PAC_TKT_CHKSUM_STR_ATTR_NAME,
|
||||||
|
+ NULL);
|
||||||
|
+
|
||||||
|
+ krb5_dbe_free_string(kcontext, str_attr_val);
|
||||||
|
+ return kerr;
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
krb5_error_code ipadb_put_principal(krb5_context kcontext,
|
||||||
|
krb5_db_entry *entry,
|
||||||
|
char **db_args)
|
||||||
|
{
|
||||||
|
+ krb5_error_code kerr;
|
||||||
|
+
|
||||||
|
+ kerr = remove_virtual_str_attrs(kcontext, entry);
|
||||||
|
+ if (kerr)
|
||||||
|
+ return kerr;
|
||||||
|
+
|
||||||
|
if (entry->mask & KMASK_PRINCIPAL) {
|
||||||
|
return ipadb_add_principal(kcontext, entry);
|
||||||
|
} else {
|
||||||
|
diff --git a/ipapython/Makefile.am b/ipapython/Makefile.am
|
||||||
|
index 7038e8b57..6b336d8fe 100644
|
||||||
|
--- a/ipapython/Makefile.am
|
||||||
|
+++ b/ipapython/Makefile.am
|
||||||
|
@@ -13,11 +13,12 @@ bdist_wheel: version.py
|
||||||
|
$(AM_V_GEN)awk '$$1 == "default:" { print $$2 }' $< >$@
|
||||||
|
|
||||||
|
version.py: version.py.in .DEFAULT_PLUGINS $(top_builddir)/$(CONFIG_STATUS)
|
||||||
|
- $(AM_V_GEN)sed \
|
||||||
|
- -e 's|@API_VERSION[@]|$(API_VERSION)|g' \
|
||||||
|
- -e 's|@NUM_VERSION[@]|$(NUM_VERSION)|g' \
|
||||||
|
- -e 's|@VERSION[@]|$(VERSION)|g' \
|
||||||
|
- -e 's|@VENDOR_SUFFIX[@]|$(VENDOR_SUFFIX)|g' \
|
||||||
|
- -e '/@DEFAULT_PLUGINS[@]/r .DEFAULT_PLUGINS' \
|
||||||
|
- -e '/@DEFAULT_PLUGINS[@]/d' \
|
||||||
|
+ $(AM_V_GEN)sed \
|
||||||
|
+ -e 's|@API_VERSION[@]|$(API_VERSION)|g' \
|
||||||
|
+ -e 's|@NUM_VERSION[@]|$(NUM_VERSION)|g' \
|
||||||
|
+ -e 's|@VERSION[@]|$(VERSION)|g' \
|
||||||
|
+ -e 's|@VENDOR_SUFFIX[@]|$(VENDOR_SUFFIX)|g' \
|
||||||
|
+ -e 's|@KRB5_BUILD_VERSION[@]|$(KRB5_BUILD_VERSION)|g' \
|
||||||
|
+ -e '/@DEFAULT_PLUGINS[@]/r .DEFAULT_PLUGINS' \
|
||||||
|
+ -e '/@DEFAULT_PLUGINS[@]/d' \
|
||||||
|
$< > $@
|
||||||
|
diff --git a/ipapython/version.py.in b/ipapython/version.py.in
|
||||||
|
index 5a71fb8cf..a8f4218a7 100644
|
||||||
|
--- a/ipapython/version.py.in
|
||||||
|
+++ b/ipapython/version.py.in
|
||||||
|
@@ -17,6 +17,8 @@
|
||||||
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
#
|
||||||
|
|
||||||
|
+from pkg_resources import parse_version
|
||||||
|
+
|
||||||
|
# The full version including strings
|
||||||
|
VERSION = "@VERSION@"
|
||||||
|
|
||||||
|
@@ -51,3 +53,5 @@ API_VERSION = "@API_VERSION@"
|
||||||
|
DEFAULT_PLUGINS = frozenset(l.strip() for l in """
|
||||||
|
@DEFAULT_PLUGINS@
|
||||||
|
""".strip().splitlines())
|
||||||
|
+
|
||||||
|
+KRB5_BUILD_VERSION = parse_version("@KRB5_BUILD_VERSION@")
|
||||||
|
diff --git a/ipaserver/install/krbinstance.py b/ipaserver/install/krbinstance.py
|
||||||
|
index a5eaa7b17..acb7419d6 100644
|
||||||
|
--- a/ipaserver/install/krbinstance.py
|
||||||
|
+++ b/ipaserver/install/krbinstance.py
|
||||||
|
@@ -26,6 +26,7 @@ import socket
|
||||||
|
import dbus
|
||||||
|
|
||||||
|
import dns.name
|
||||||
|
+from pkg_resources import parse_version
|
||||||
|
|
||||||
|
from ipalib import x509
|
||||||
|
from ipalib.install import certstore
|
||||||
|
@@ -34,6 +35,7 @@ from ipaserver.install import installutils
|
||||||
|
from ipapython import ipaldap
|
||||||
|
from ipapython import ipautil
|
||||||
|
from ipapython import kernel_keyring
|
||||||
|
+from ipapython.version import KRB5_BUILD_VERSION
|
||||||
|
from ipalib import api, errors
|
||||||
|
from ipalib.constants import ANON_USER
|
||||||
|
from ipalib.install import certmonger
|
||||||
|
@@ -42,15 +44,17 @@ from ipapython.dogtag import KDC_PROFILE
|
||||||
|
|
||||||
|
from ipaserver.install import replication
|
||||||
|
from ipaserver.install import certs
|
||||||
|
-from ipaserver.masters import find_providing_servers
|
||||||
|
+from ipaserver.masters import (
|
||||||
|
+ find_providing_servers,
|
||||||
|
+ PAC_TKT_SIGN_SUPPORTED,
|
||||||
|
+ PKINIT_ENABLED,
|
||||||
|
+)
|
||||||
|
from ipaplatform.constants import constants
|
||||||
|
from ipaplatform.tasks import tasks
|
||||||
|
from ipaplatform.paths import paths
|
||||||
|
|
||||||
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
-PKINIT_ENABLED = 'pkinitEnabled'
|
||||||
|
-
|
||||||
|
MASTER_KEY_TYPE = 'aes256-sha2'
|
||||||
|
SUPPORTED_ENCTYPES = ('aes256-sha2:special', 'aes128-sha2:special',
|
||||||
|
'aes256-sha2:normal', 'aes128-sha2:normal',
|
||||||
|
@@ -169,6 +173,13 @@ class KrbInstance(service.Service):
|
||||||
|
# Add the host to the ipaserver host group
|
||||||
|
self._ldap_update(['20-ipaservers_hostgroup.update'])
|
||||||
|
|
||||||
|
+ def pac_tkt_sign_support_enable(self):
|
||||||
|
+ """
|
||||||
|
+ Advertise PAC ticket signature support in master's KDC entry in LDAP
|
||||||
|
+ """
|
||||||
|
+ service.set_service_entry_config(
|
||||||
|
+ 'KDC', self.fqdn, [PAC_TKT_SIGN_SUPPORTED], self.suffix)
|
||||||
|
+
|
||||||
|
def __common_setup(self, realm_name, host_name, domain_name, admin_password):
|
||||||
|
self.fqdn = host_name
|
||||||
|
self.realm = realm_name.upper()
|
||||||
|
@@ -212,6 +223,10 @@ class KrbInstance(service.Service):
|
||||||
|
|
||||||
|
self.__common_post_setup()
|
||||||
|
|
||||||
|
+ if KRB5_BUILD_VERSION >= parse_version('1.20'):
|
||||||
|
+ self.step("enable PAC ticket signature support",
|
||||||
|
+ self.pac_tkt_sign_support_enable)
|
||||||
|
+
|
||||||
|
self.start_creation()
|
||||||
|
|
||||||
|
self.kpasswd = KpasswdInstance()
|
||||||
|
@@ -235,6 +250,10 @@ class KrbInstance(service.Service):
|
||||||
|
|
||||||
|
self.__common_post_setup()
|
||||||
|
|
||||||
|
+ if KRB5_BUILD_VERSION >= parse_version('1.20'):
|
||||||
|
+ self.step("enable PAC ticket signature support",
|
||||||
|
+ self.pac_tkt_sign_support_enable)
|
||||||
|
+
|
||||||
|
self.start_creation()
|
||||||
|
|
||||||
|
self.kpasswd = KpasswdInstance()
|
||||||
|
diff --git a/ipaserver/install/server/upgrade.py b/ipaserver/install/server/upgrade.py
|
||||||
|
index 5f5a60d10..f8701c8a0 100644
|
||||||
|
--- a/ipaserver/install/server/upgrade.py
|
||||||
|
+++ b/ipaserver/install/server/upgrade.py
|
||||||
|
@@ -18,6 +18,7 @@ import sys
|
||||||
|
import tempfile
|
||||||
|
from contextlib import contextmanager
|
||||||
|
from augeas import Augeas
|
||||||
|
+from pkg_resources import parse_version
|
||||||
|
|
||||||
|
from ipalib import api, x509
|
||||||
|
from ipalib.constants import RENEWAL_CA_NAME, RA_AGENT_PROFILE, IPA_CA_RECORD
|
||||||
|
@@ -36,6 +37,7 @@ from ipapython import ipautil, version
|
||||||
|
from ipapython import ipaldap
|
||||||
|
from ipapython import directivesetter
|
||||||
|
from ipapython.dn import DN
|
||||||
|
+from ipapython.version import KRB5_BUILD_VERSION
|
||||||
|
from ipaplatform.constants import constants
|
||||||
|
from ipaplatform.paths import paths
|
||||||
|
from ipaserver import servroles
|
||||||
|
@@ -1961,6 +1963,9 @@ def upgrade_configuration():
|
||||||
|
enable_server_snippet()
|
||||||
|
setup_kpasswd_server(krb)
|
||||||
|
|
||||||
|
+ if KRB5_BUILD_VERSION >= parse_version('1.20'):
|
||||||
|
+ krb.pac_tkt_sign_support_enable()
|
||||||
|
+
|
||||||
|
# Must be executed after certificate_renewal_update
|
||||||
|
# (see function docstring for details)
|
||||||
|
http_certificate_ensure_ipa_ca_dnsname(http)
|
||||||
|
diff --git a/ipaserver/masters.py b/ipaserver/masters.py
|
||||||
|
index b532f2b72..c9b57b2a5 100644
|
||||||
|
--- a/ipaserver/masters.py
|
||||||
|
+++ b/ipaserver/masters.py
|
||||||
|
@@ -20,6 +20,8 @@ logger = logging.getLogger(__name__)
|
||||||
|
CONFIGURED_SERVICE = u'configuredService'
|
||||||
|
ENABLED_SERVICE = u'enabledService'
|
||||||
|
HIDDEN_SERVICE = u'hiddenService'
|
||||||
|
+PAC_TKT_SIGN_SUPPORTED = u'pacTktSignSupported'
|
||||||
|
+PKINIT_ENABLED = u'pkinitEnabled'
|
||||||
|
|
||||||
|
# The service name as stored in cn=masters,cn=ipa,cn=etc. The values are:
|
||||||
|
# 0: systemd service name
|
||||||
|
--
|
||||||
|
2.39.2
|
||||||
|
|
@ -1,392 +0,0 @@
|
|||||||
From b039f3087a13de3f34b230dbe29a7cfb1965700d Mon Sep 17 00:00:00 2001
|
|
||||||
From: Alexander Bokovoy <abokovoy@redhat.com>
|
|
||||||
Date: Feb 23 2024 09:49:27 +0000
|
|
||||||
Subject: rpcserver: validate Kerberos principal name before running kinit
|
|
||||||
|
|
||||||
|
|
||||||
Do minimal validation of the Kerberos principal name when passing it to
|
|
||||||
kinit command line tool. Also pass it as the final argument to prevent
|
|
||||||
option injection.
|
|
||||||
|
|
||||||
Accepted Kerberos principals are:
|
|
||||||
- user names, using the following regexp
|
|
||||||
(username with optional @realm, no spaces or slashes in the name):
|
|
||||||
"(?!^[0-9]+$)^[a-zA-Z0-9_.][a-zA-Z0-9_.-]*[a-zA-Z0-9_.$-]?@?[a-zA-Z0-9.-]*$"
|
|
||||||
|
|
||||||
- service names (with slash in the name but no spaces). Validation of
|
|
||||||
the hostname is done. There is no validation of the service name.
|
|
||||||
|
|
||||||
The regular expression above also covers cases where a principal name
|
|
||||||
starts with '-'. This prevents option injection as well.
|
|
||||||
|
|
||||||
This fixes CVE-2024-1481
|
|
||||||
|
|
||||||
Fixes: https://pagure.io/freeipa/issue/9541
|
|
||||||
|
|
||||||
Signed-off-by: Alexander Bokovoy <abokovoy@redhat.com>
|
|
||||||
Signed-off-by: Rob Crittenden <rcritten@redhat.com>
|
|
||||||
Reviewed-By: Florence Blanc-Renaud <frenaud@redhat.com>
|
|
||||||
Reviewed-By: Florence Blanc-Renaud <frenaud@redhat.com>
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
diff --git a/ipalib/install/kinit.py b/ipalib/install/kinit.py
|
|
||||||
index cc839ec..4ad4eaa 100644
|
|
||||||
--- a/ipalib/install/kinit.py
|
|
||||||
+++ b/ipalib/install/kinit.py
|
|
||||||
@@ -6,12 +6,16 @@ from __future__ import absolute_import
|
|
||||||
|
|
||||||
import logging
|
|
||||||
import os
|
|
||||||
+import re
|
|
||||||
import time
|
|
||||||
|
|
||||||
import gssapi
|
|
||||||
|
|
||||||
from ipaplatform.paths import paths
|
|
||||||
from ipapython.ipautil import run
|
|
||||||
+from ipalib.constants import PATTERN_GROUPUSER_NAME
|
|
||||||
+from ipalib.util import validate_hostname
|
|
||||||
+from ipalib import api
|
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
|
||||||
|
|
||||||
@@ -21,6 +25,40 @@ KRB5_KDC_UNREACH = 2529639068
|
|
||||||
# A service is not available that s required to process the request
|
|
||||||
KRB5KDC_ERR_SVC_UNAVAILABLE = 2529638941
|
|
||||||
|
|
||||||
+PATTERN_REALM = '@?([a-zA-Z0-9.-]*)$'
|
|
||||||
+PATTERN_PRINCIPAL = '(' + PATTERN_GROUPUSER_NAME[:-1] + ')' + PATTERN_REALM
|
|
||||||
+PATTERN_SERVICE = '([a-zA-Z0-9.-]+)/([a-zA-Z0-9.-]+)' + PATTERN_REALM
|
|
||||||
+
|
|
||||||
+user_pattern = re.compile(PATTERN_PRINCIPAL)
|
|
||||||
+service_pattern = re.compile(PATTERN_SERVICE)
|
|
||||||
+
|
|
||||||
+
|
|
||||||
+def validate_principal(principal):
|
|
||||||
+ if not isinstance(principal, str):
|
|
||||||
+ raise RuntimeError('Invalid principal: not a string')
|
|
||||||
+ if ('/' in principal) and (' ' in principal):
|
|
||||||
+ raise RuntimeError('Invalid principal: bad spacing')
|
|
||||||
+ else:
|
|
||||||
+ realm = None
|
|
||||||
+ match = user_pattern.match(principal)
|
|
||||||
+ if match is None:
|
|
||||||
+ match = service_pattern.match(principal)
|
|
||||||
+ if match is None:
|
|
||||||
+ raise RuntimeError('Invalid principal: cannot parse')
|
|
||||||
+ else:
|
|
||||||
+ # service = match[1]
|
|
||||||
+ hostname = match[2]
|
|
||||||
+ realm = match[3]
|
|
||||||
+ try:
|
|
||||||
+ validate_hostname(hostname)
|
|
||||||
+ except ValueError as e:
|
|
||||||
+ raise RuntimeError(str(e))
|
|
||||||
+ else: # user match, validate realm
|
|
||||||
+ # username = match[1]
|
|
||||||
+ realm = match[2]
|
|
||||||
+ if realm and 'realm' in api.env and realm != api.env.realm:
|
|
||||||
+ raise RuntimeError('Invalid principal: realm mismatch')
|
|
||||||
+
|
|
||||||
|
|
||||||
def kinit_keytab(principal, keytab, ccache_name, config=None, attempts=1):
|
|
||||||
"""
|
|
||||||
@@ -29,6 +67,7 @@ def kinit_keytab(principal, keytab, ccache_name, config=None, attempts=1):
|
|
||||||
The optional parameter 'attempts' specifies how many times the credential
|
|
||||||
initialization should be attempted in case of non-responsive KDC.
|
|
||||||
"""
|
|
||||||
+ validate_principal(principal)
|
|
||||||
errors_to_retry = {KRB5KDC_ERR_SVC_UNAVAILABLE,
|
|
||||||
KRB5_KDC_UNREACH}
|
|
||||||
logger.debug("Initializing principal %s using keytab %s",
|
|
||||||
@@ -65,6 +104,7 @@ def kinit_keytab(principal, keytab, ccache_name, config=None, attempts=1):
|
|
||||||
|
|
||||||
return None
|
|
||||||
|
|
||||||
+
|
|
||||||
def kinit_password(principal, password, ccache_name, config=None,
|
|
||||||
armor_ccache_name=None, canonicalize=False,
|
|
||||||
enterprise=False, lifetime=None):
|
|
||||||
@@ -73,8 +113,9 @@ def kinit_password(principal, password, ccache_name, config=None,
|
|
||||||
web-based authentication, use armor_ccache_path to specify http service
|
|
||||||
ccache.
|
|
||||||
"""
|
|
||||||
+ validate_principal(principal)
|
|
||||||
logger.debug("Initializing principal %s using password", principal)
|
|
||||||
- args = [paths.KINIT, principal, '-c', ccache_name]
|
|
||||||
+ args = [paths.KINIT, '-c', ccache_name]
|
|
||||||
if armor_ccache_name is not None:
|
|
||||||
logger.debug("Using armor ccache %s for FAST webauth",
|
|
||||||
armor_ccache_name)
|
|
||||||
@@ -91,6 +132,7 @@ def kinit_password(principal, password, ccache_name, config=None,
|
|
||||||
logger.debug("Using enterprise principal")
|
|
||||||
args.append('-E')
|
|
||||||
|
|
||||||
+ args.extend(['--', principal])
|
|
||||||
env = {'LC_ALL': 'C'}
|
|
||||||
if config is not None:
|
|
||||||
env['KRB5_CONFIG'] = config
|
|
||||||
@@ -154,6 +196,7 @@ def kinit_pkinit(
|
|
||||||
|
|
||||||
:raises: CalledProcessError if PKINIT fails
|
|
||||||
"""
|
|
||||||
+ validate_principal(principal)
|
|
||||||
logger.debug(
|
|
||||||
"Initializing principal %s using PKINIT %s", principal, user_identity
|
|
||||||
)
|
|
||||||
@@ -168,7 +211,7 @@ def kinit_pkinit(
|
|
||||||
assert pkinit_anchor.startswith(("FILE:", "DIR:", "ENV:"))
|
|
||||||
args.extend(["-X", f"X509_anchors={pkinit_anchor}"])
|
|
||||||
args.extend(["-X", f"X509_user_identity={user_identity}"])
|
|
||||||
- args.append(principal)
|
|
||||||
+ args.extend(['--', principal])
|
|
||||||
|
|
||||||
# this workaround enables us to capture stderr and put it
|
|
||||||
# into the raised exception in case of unsuccessful authentication
|
|
||||||
diff --git a/ipaserver/rpcserver.py b/ipaserver/rpcserver.py
|
|
||||||
index 3555014..60bfa61 100644
|
|
||||||
--- a/ipaserver/rpcserver.py
|
|
||||||
+++ b/ipaserver/rpcserver.py
|
|
||||||
@@ -1134,10 +1134,6 @@ class login_password(Backend, KerberosSession):
|
|
||||||
canonicalize=True,
|
|
||||||
lifetime=self.api.env.kinit_lifetime)
|
|
||||||
|
|
||||||
- if armor_path:
|
|
||||||
- logger.debug('Cleanup the armor ccache')
|
|
||||||
- ipautil.run([paths.KDESTROY, '-A', '-c', armor_path],
|
|
||||||
- env={'KRB5CCNAME': armor_path}, raiseonerr=False)
|
|
||||||
except RuntimeError as e:
|
|
||||||
if ('kinit: Cannot read password while '
|
|
||||||
'getting initial credentials') in str(e):
|
|
||||||
@@ -1155,6 +1151,11 @@ class login_password(Backend, KerberosSession):
|
|
||||||
raise KrbPrincipalWrongFAST(principal=principal)
|
|
||||||
raise InvalidSessionPassword(principal=principal,
|
|
||||||
message=unicode(e))
|
|
||||||
+ finally:
|
|
||||||
+ if armor_path:
|
|
||||||
+ logger.debug('Cleanup the armor ccache')
|
|
||||||
+ ipautil.run([paths.KDESTROY, '-A', '-c', armor_path],
|
|
||||||
+ env={'KRB5CCNAME': armor_path}, raiseonerr=False)
|
|
||||||
|
|
||||||
|
|
||||||
class change_password(Backend, HTTP_Status):
|
|
||||||
diff --git a/ipatests/prci_definitions/gating.yaml b/ipatests/prci_definitions/gating.yaml
|
|
||||||
index 91be057..400a248 100644
|
|
||||||
--- a/ipatests/prci_definitions/gating.yaml
|
|
||||||
+++ b/ipatests/prci_definitions/gating.yaml
|
|
||||||
@@ -310,3 +310,15 @@ jobs:
|
|
||||||
template: *ci-ipa-4-9-latest
|
|
||||||
timeout: 3600
|
|
||||||
topology: *master_1repl_1client
|
|
||||||
+
|
|
||||||
+ fedora-latest-ipa-4-9/test_ipalib_install:
|
|
||||||
+ requires: [fedora-latest-ipa-4-9/build]
|
|
||||||
+ priority: 100
|
|
||||||
+ job:
|
|
||||||
+ class: RunPytest
|
|
||||||
+ args:
|
|
||||||
+ build_url: '{fedora-latest-ipa-4-9/build_url}'
|
|
||||||
+ test_suite: test_ipalib_install/test_kinit.py
|
|
||||||
+ template: *ci-ipa-4-9-latest
|
|
||||||
+ timeout: 600
|
|
||||||
+ topology: *master_1repl
|
|
||||||
diff --git a/ipatests/prci_definitions/nightly_ipa-4-9_latest.yaml b/ipatests/prci_definitions/nightly_ipa-4-9_latest.yaml
|
|
||||||
index b2ab765..7c03a48 100644
|
|
||||||
--- a/ipatests/prci_definitions/nightly_ipa-4-9_latest.yaml
|
|
||||||
+++ b/ipatests/prci_definitions/nightly_ipa-4-9_latest.yaml
|
|
||||||
@@ -1801,3 +1801,15 @@ jobs:
|
|
||||||
template: *ci-ipa-4-9-latest
|
|
||||||
timeout: 5000
|
|
||||||
topology: *master_2repl_1client
|
|
||||||
+
|
|
||||||
+ fedora-latest-ipa-4-9/test_ipalib_install:
|
|
||||||
+ requires: [fedora-latest-ipa-4-9/build]
|
|
||||||
+ priority: 50
|
|
||||||
+ job:
|
|
||||||
+ class: RunPytest
|
|
||||||
+ args:
|
|
||||||
+ build_url: '{fedora-latest-ipa-4-9/build_url}'
|
|
||||||
+ test_suite: test_ipalib_install/test_kinit.py
|
|
||||||
+ template: *ci-ipa-4-9-latest
|
|
||||||
+ timeout: 600
|
|
||||||
+ topology: *master_1repl
|
|
||||||
diff --git a/ipatests/prci_definitions/nightly_ipa-4-9_latest_selinux.yaml b/ipatests/prci_definitions/nightly_ipa-4-9_latest_selinux.yaml
|
|
||||||
index b7b3d3b..802bd2a 100644
|
|
||||||
--- a/ipatests/prci_definitions/nightly_ipa-4-9_latest_selinux.yaml
|
|
||||||
+++ b/ipatests/prci_definitions/nightly_ipa-4-9_latest_selinux.yaml
|
|
||||||
@@ -1944,3 +1944,16 @@ jobs:
|
|
||||||
template: *ci-ipa-4-9-latest
|
|
||||||
timeout: 5000
|
|
||||||
topology: *master_2repl_1client
|
|
||||||
+
|
|
||||||
+ fedora-latest-ipa-4-9/test_ipalib_install:
|
|
||||||
+ requires: [fedora-latest-ipa-4-9/build]
|
|
||||||
+ priority: 50
|
|
||||||
+ job:
|
|
||||||
+ class: RunPytest
|
|
||||||
+ args:
|
|
||||||
+ build_url: '{fedora-latest-ipa-4-9/build_url}'
|
|
||||||
+ selinux_enforcing: True
|
|
||||||
+ test_suite: test_ipalib_install/test_kinit.py
|
|
||||||
+ template: *ci-ipa-4-9-latest
|
|
||||||
+ timeout: 600
|
|
||||||
+ topology: *master_1repl
|
|
||||||
diff --git a/ipatests/prci_definitions/nightly_ipa-4-9_previous.yaml b/ipatests/prci_definitions/nightly_ipa-4-9_previous.yaml
|
|
||||||
index eb3849e..1e1adb8 100644
|
|
||||||
--- a/ipatests/prci_definitions/nightly_ipa-4-9_previous.yaml
|
|
||||||
+++ b/ipatests/prci_definitions/nightly_ipa-4-9_previous.yaml
|
|
||||||
@@ -1801,3 +1801,15 @@ jobs:
|
|
||||||
template: *ci-ipa-4-9-previous
|
|
||||||
timeout: 5000
|
|
||||||
topology: *master_2repl_1client
|
|
||||||
+
|
|
||||||
+ fedora-previous-ipa-4-9/test_ipalib_install:
|
|
||||||
+ requires: [fedora-previous-ipa-4-9/build]
|
|
||||||
+ priority: 50
|
|
||||||
+ job:
|
|
||||||
+ class: RunPytest
|
|
||||||
+ args:
|
|
||||||
+ build_url: '{fedora-previous-ipa-4-9/build_url}'
|
|
||||||
+ test_suite: test_ipalib_install/test_kinit.py
|
|
||||||
+ template: *ci-ipa-4-9-previous
|
|
||||||
+ timeout: 600
|
|
||||||
+ topology: *master_1repl
|
|
||||||
diff --git a/ipatests/setup.py b/ipatests/setup.py
|
|
||||||
index 6217a1b..0aec4a7 100644
|
|
||||||
--- a/ipatests/setup.py
|
|
||||||
+++ b/ipatests/setup.py
|
|
||||||
@@ -41,6 +41,7 @@ if __name__ == '__main__':
|
|
||||||
"ipatests.test_integration",
|
|
||||||
"ipatests.test_ipaclient",
|
|
||||||
"ipatests.test_ipalib",
|
|
||||||
+ "ipatests.test_ipalib_install",
|
|
||||||
"ipatests.test_ipaplatform",
|
|
||||||
"ipatests.test_ipapython",
|
|
||||||
"ipatests.test_ipaserver",
|
|
||||||
diff --git a/ipatests/test_ipalib_install/__init__.py b/ipatests/test_ipalib_install/__init__.py
|
|
||||||
new file mode 100644
|
|
||||||
index 0000000..e69de29
|
|
||||||
--- /dev/null
|
|
||||||
+++ b/ipatests/test_ipalib_install/__init__.py
|
|
||||||
diff --git a/ipatests/test_ipalib_install/test_kinit.py b/ipatests/test_ipalib_install/test_kinit.py
|
|
||||||
new file mode 100644
|
|
||||||
index 0000000..f89ea17
|
|
||||||
--- /dev/null
|
|
||||||
+++ b/ipatests/test_ipalib_install/test_kinit.py
|
|
||||||
@@ -0,0 +1,29 @@
|
|
||||||
+#
|
|
||||||
+# Copyright (C) 2024 FreeIPA Contributors see COPYING for license
|
|
||||||
+#
|
|
||||||
+"""Tests for ipalib.install.kinit module
|
|
||||||
+"""
|
|
||||||
+
|
|
||||||
+import pytest
|
|
||||||
+
|
|
||||||
+from ipalib.install.kinit import validate_principal
|
|
||||||
+
|
|
||||||
+
|
|
||||||
+# None means no exception is expected
|
|
||||||
+@pytest.mark.parametrize('principal, exception', [
|
|
||||||
+ ('testuser', None),
|
|
||||||
+ ('testuser@EXAMPLE.TEST', None),
|
|
||||||
+ ('test/ipa.example.test', None),
|
|
||||||
+ ('test/ipa.example.test@EXAMPLE.TEST', None),
|
|
||||||
+ ('test/ipa@EXAMPLE.TEST', RuntimeError),
|
|
||||||
+ ('test/-ipa.example.test@EXAMPLE.TEST', RuntimeError),
|
|
||||||
+ ('test/ipa.1example.test@EXAMPLE.TEST', RuntimeError),
|
|
||||||
+ ('test /ipa.example,test', RuntimeError),
|
|
||||||
+ ('testuser@OTHER.TEST', RuntimeError),
|
|
||||||
+ ('test/ipa.example.test@OTHER.TEST', RuntimeError),
|
|
||||||
+])
|
|
||||||
+def test_validate_principal(principal, exception):
|
|
||||||
+ try:
|
|
||||||
+ validate_principal(principal)
|
|
||||||
+ except Exception as e:
|
|
||||||
+ assert e.__class__ == exception
|
|
||||||
|
|
||||||
From 96a478bbedd49c31e0f078f00f2d1cb55bb952fd Mon Sep 17 00:00:00 2001
|
|
||||||
From: Rob Crittenden <rcritten@redhat.com>
|
|
||||||
Date: Feb 23 2024 09:49:27 +0000
|
|
||||||
Subject: validate_principal: Don't try to verify that the realm is known
|
|
||||||
|
|
||||||
|
|
||||||
The actual value is less important than whether it matches the
|
|
||||||
regular expression. A number of legal but difficult to know in
|
|
||||||
context realms could be passed in here (trust for example).
|
|
||||||
|
|
||||||
This fixes CVE-2024-1481
|
|
||||||
|
|
||||||
Fixes: https://pagure.io/freeipa/issue/9541
|
|
||||||
|
|
||||||
Signed-off-by: Rob Crittenden <rcritten@redhat.com>
|
|
||||||
Reviewed-By: Florence Blanc-Renaud <frenaud@redhat.com>
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
diff --git a/ipalib/install/kinit.py b/ipalib/install/kinit.py
|
|
||||||
index 4ad4eaa..d5fb56b 100644
|
|
||||||
--- a/ipalib/install/kinit.py
|
|
||||||
+++ b/ipalib/install/kinit.py
|
|
||||||
@@ -15,7 +15,6 @@ from ipaplatform.paths import paths
|
|
||||||
from ipapython.ipautil import run
|
|
||||||
from ipalib.constants import PATTERN_GROUPUSER_NAME
|
|
||||||
from ipalib.util import validate_hostname
|
|
||||||
-from ipalib import api
|
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
|
||||||
|
|
||||||
@@ -39,7 +38,9 @@ def validate_principal(principal):
|
|
||||||
if ('/' in principal) and (' ' in principal):
|
|
||||||
raise RuntimeError('Invalid principal: bad spacing')
|
|
||||||
else:
|
|
||||||
- realm = None
|
|
||||||
+ # For a user match in the regex
|
|
||||||
+ # username = match[1]
|
|
||||||
+ # realm = match[2]
|
|
||||||
match = user_pattern.match(principal)
|
|
||||||
if match is None:
|
|
||||||
match = service_pattern.match(principal)
|
|
||||||
@@ -48,16 +49,11 @@ def validate_principal(principal):
|
|
||||||
else:
|
|
||||||
# service = match[1]
|
|
||||||
hostname = match[2]
|
|
||||||
- realm = match[3]
|
|
||||||
+ # realm = match[3]
|
|
||||||
try:
|
|
||||||
validate_hostname(hostname)
|
|
||||||
except ValueError as e:
|
|
||||||
raise RuntimeError(str(e))
|
|
||||||
- else: # user match, validate realm
|
|
||||||
- # username = match[1]
|
|
||||||
- realm = match[2]
|
|
||||||
- if realm and 'realm' in api.env and realm != api.env.realm:
|
|
||||||
- raise RuntimeError('Invalid principal: realm mismatch')
|
|
||||||
|
|
||||||
|
|
||||||
def kinit_keytab(principal, keytab, ccache_name, config=None, attempts=1):
|
|
||||||
diff --git a/ipatests/test_ipalib_install/test_kinit.py b/ipatests/test_ipalib_install/test_kinit.py
|
|
||||||
index f89ea17..8289c4b 100644
|
|
||||||
--- a/ipatests/test_ipalib_install/test_kinit.py
|
|
||||||
+++ b/ipatests/test_ipalib_install/test_kinit.py
|
|
||||||
@@ -17,13 +17,16 @@ from ipalib.install.kinit import validate_principal
|
|
||||||
('test/ipa.example.test@EXAMPLE.TEST', None),
|
|
||||||
('test/ipa@EXAMPLE.TEST', RuntimeError),
|
|
||||||
('test/-ipa.example.test@EXAMPLE.TEST', RuntimeError),
|
|
||||||
- ('test/ipa.1example.test@EXAMPLE.TEST', RuntimeError),
|
|
||||||
+ ('test/ipa.1example.test@EXAMPLE.TEST', None),
|
|
||||||
('test /ipa.example,test', RuntimeError),
|
|
||||||
- ('testuser@OTHER.TEST', RuntimeError),
|
|
||||||
- ('test/ipa.example.test@OTHER.TEST', RuntimeError),
|
|
||||||
+ ('testuser@OTHER.TEST', None),
|
|
||||||
+ ('test/ipa.example.test@OTHER.TEST', None)
|
|
||||||
])
|
|
||||||
def test_validate_principal(principal, exception):
|
|
||||||
try:
|
|
||||||
validate_principal(principal)
|
|
||||||
except Exception as e:
|
|
||||||
assert e.__class__ == exception
|
|
||||||
+ else:
|
|
||||||
+ if exception is not None:
|
|
||||||
+ raise RuntimeError('Test should have failed')
|
|
||||||
|
|
@ -1,43 +0,0 @@
|
|||||||
From d7c1ba0672fc8964f7674a526f3019429a551372 Mon Sep 17 00:00:00 2001
|
|
||||||
From: Rob Crittenden <rcritten@redhat.com>
|
|
||||||
Date: Mar 06 2024 08:34:57 +0000
|
|
||||||
Subject: Vault: add additional fallback to RSA-OAEP wrapping algo
|
|
||||||
|
|
||||||
|
|
||||||
There is a fallback when creating the wrapping key but one was missing
|
|
||||||
when trying to use the cached transport_cert.
|
|
||||||
|
|
||||||
This allows, along with forcing keyWrap.useOAEP=true, vault creation
|
|
||||||
on an nCipher HSM.
|
|
||||||
|
|
||||||
This can be seen in HSMs where the device doesn't support the
|
|
||||||
PKCS#1 v1.5 mechanism. It will error out with either "invalid
|
|
||||||
algorithm" or CKR_FUNCTION_FAILED.
|
|
||||||
|
|
||||||
Related: https://pagure.io/freeipa/issue/9191
|
|
||||||
|
|
||||||
Signed-off-by: Rob Crittenden <rcritten@redhat.com>
|
|
||||||
Reviewed-By: Florence Blanc-Renaud <frenaud@redhat.com>
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
diff --git a/ipaclient/plugins/vault.py b/ipaclient/plugins/vault.py
|
|
||||||
index ed16c73..1523187 100644
|
|
||||||
--- a/ipaclient/plugins/vault.py
|
|
||||||
+++ b/ipaclient/plugins/vault.py
|
|
||||||
@@ -757,8 +757,12 @@ class ModVaultData(Local):
|
|
||||||
Calls the internal counterpart of the command.
|
|
||||||
"""
|
|
||||||
# try call with cached transport certificate
|
|
||||||
- result = self._do_internal(algo, transport_cert, False,
|
|
||||||
- False, *args, **options)
|
|
||||||
+ try:
|
|
||||||
+ result = self._do_internal(algo, transport_cert, False,
|
|
||||||
+ False, *args, **options)
|
|
||||||
+ except errors.EncodingError:
|
|
||||||
+ result = self._do_internal(algo, transport_cert, False,
|
|
||||||
+ True, *args, **options)
|
|
||||||
if result is not None:
|
|
||||||
return result
|
|
||||||
|
|
||||||
|
|
@ -0,0 +1,269 @@
|
|||||||
|
From 33242a967011b9cbce74b6b3c39a7247d66eda19 Mon Sep 17 00:00:00 2001
|
||||||
|
From: Alexander Bokovoy <abokovoy@redhat.com>
|
||||||
|
Date: Thu, 25 May 2023 09:19:57 +0300
|
||||||
|
Subject: [PATCH] ipa-kdb: postpone ticket checksum configuration
|
||||||
|
|
||||||
|
Postpone ticket checksum configuration after KDB module was initialized.
|
||||||
|
This, in practice, should now happen when a master key is retrieved.
|
||||||
|
|
||||||
|
Signed-off-by: Alexander Bokovoy <abokovoy@redhat.com>
|
||||||
|
Reviewed-By: Julien Rische <jrische@redhat.com>
|
||||||
|
(cherry picked from commit fefa0248296413b6ee5ad2543d8feb1b31840aee)
|
||||||
|
---
|
||||||
|
daemons/ipa-kdb/ipa_kdb.c | 56 +----------------------
|
||||||
|
daemons/ipa-kdb/ipa_kdb.h | 8 +++-
|
||||||
|
daemons/ipa-kdb/ipa_kdb_common.c | 67 +++++++++++++++++++++++++++-
|
||||||
|
daemons/ipa-kdb/ipa_kdb_principals.c | 14 ++++--
|
||||||
|
4 files changed, 84 insertions(+), 61 deletions(-)
|
||||||
|
|
||||||
|
diff --git a/daemons/ipa-kdb/ipa_kdb.c b/daemons/ipa-kdb/ipa_kdb.c
|
||||||
|
index 9a56640ff..a3c3746c2 100644
|
||||||
|
--- a/daemons/ipa-kdb/ipa_kdb.c
|
||||||
|
+++ b/daemons/ipa-kdb/ipa_kdb.c
|
||||||
|
@@ -524,52 +524,6 @@ static krb5_principal ipadb_create_local_tgs(krb5_context kcontext,
|
||||||
|
return tgtp;
|
||||||
|
}
|
||||||
|
|
||||||
|
-static char *no_attrs[] = {
|
||||||
|
- LDAP_NO_ATTRS,
|
||||||
|
-
|
||||||
|
- NULL
|
||||||
|
-};
|
||||||
|
-
|
||||||
|
-static krb5_error_code
|
||||||
|
-should_support_pac_tkt_sign(krb5_context kcontext, bool *result)
|
||||||
|
-{
|
||||||
|
- struct ipadb_context *ipactx;
|
||||||
|
- krb5_error_code kerr;
|
||||||
|
- LDAPMessage *res = NULL;
|
||||||
|
- char *masters_dn = NULL;
|
||||||
|
- int count;
|
||||||
|
-
|
||||||
|
- char *kdc_filter = "(&(cn=KDC)(objectClass=ipaConfigObject)"
|
||||||
|
- "(!(ipaConfigString=pacTktSignSupported)))";
|
||||||
|
-
|
||||||
|
- ipactx = ipadb_get_context(kcontext);
|
||||||
|
- if (!ipactx) {
|
||||||
|
- kerr = KRB5_KDB_DBNOTINITED;
|
||||||
|
- goto done;
|
||||||
|
- }
|
||||||
|
-
|
||||||
|
- count = asprintf(&masters_dn, "cn=masters,cn=ipa,cn=etc,%s", ipactx->base);
|
||||||
|
- if (count < 0) {
|
||||||
|
- kerr = ENOMEM;
|
||||||
|
- goto done;
|
||||||
|
- }
|
||||||
|
-
|
||||||
|
- kerr = ipadb_simple_search(ipactx, masters_dn, LDAP_SCOPE_SUBTREE,
|
||||||
|
- kdc_filter, no_attrs, &res);
|
||||||
|
- if (kerr)
|
||||||
|
- goto done;
|
||||||
|
-
|
||||||
|
- count = ldap_count_entries(ipactx->lcontext, res);
|
||||||
|
-
|
||||||
|
- if (result)
|
||||||
|
- *result = (count == 0);
|
||||||
|
-
|
||||||
|
-done:
|
||||||
|
- free(masters_dn);
|
||||||
|
- ldap_msgfree(res);
|
||||||
|
- return kerr;
|
||||||
|
-}
|
||||||
|
-
|
||||||
|
/* INTERFACE */
|
||||||
|
|
||||||
|
static krb5_error_code ipadb_init_library(void)
|
||||||
|
@@ -590,7 +544,6 @@ static krb5_error_code ipadb_init_module(krb5_context kcontext,
|
||||||
|
krb5_error_code kerr;
|
||||||
|
int ret;
|
||||||
|
int i;
|
||||||
|
- bool pac_tkt_sign_supported;
|
||||||
|
|
||||||
|
/* make sure the context is freed to avoid leaking it */
|
||||||
|
ipactx = ipadb_get_context(kcontext);
|
||||||
|
@@ -662,6 +615,8 @@ static krb5_error_code ipadb_init_module(krb5_context kcontext,
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
|
||||||
|
+ ipactx->optional_pac_tkt_chksum = IPADB_TRISTATE_UNDEFINED;
|
||||||
|
+
|
||||||
|
ret = ipadb_get_connection(ipactx);
|
||||||
|
if (ret != 0) {
|
||||||
|
/* Not a fatal failure, as the LDAP server may be temporarily down. */
|
||||||
|
@@ -675,13 +630,6 @@ static krb5_error_code ipadb_init_module(krb5_context kcontext,
|
||||||
|
goto fail;
|
||||||
|
}
|
||||||
|
|
||||||
|
- /* Enforce PAC ticket signature verification if supported by all KDCs */
|
||||||
|
- kerr = should_support_pac_tkt_sign(kcontext, &pac_tkt_sign_supported);
|
||||||
|
- if (kerr) {
|
||||||
|
- ret = kerr;
|
||||||
|
- goto fail;
|
||||||
|
- }
|
||||||
|
- ipactx->optional_pac_tkt_chksum = !pac_tkt_sign_supported;
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
diff --git a/daemons/ipa-kdb/ipa_kdb.h b/daemons/ipa-kdb/ipa_kdb.h
|
||||||
|
index 0f4d3e431..edf3b0dfc 100644
|
||||||
|
--- a/daemons/ipa-kdb/ipa_kdb.h
|
||||||
|
+++ b/daemons/ipa-kdb/ipa_kdb.h
|
||||||
|
@@ -126,6 +126,12 @@ struct ipadb_global_config {
|
||||||
|
bool disable_preauth_for_spns;
|
||||||
|
};
|
||||||
|
|
||||||
|
+enum ipadb_tristate_option {
|
||||||
|
+ IPADB_TRISTATE_FALSE = FALSE,
|
||||||
|
+ IPADB_TRISTATE_TRUE = TRUE,
|
||||||
|
+ IPADB_TRISTATE_UNDEFINED,
|
||||||
|
+};
|
||||||
|
+
|
||||||
|
#define IPA_CONTEXT_MAGIC 0x0c027ea7
|
||||||
|
struct ipadb_context {
|
||||||
|
int magic;
|
||||||
|
@@ -143,7 +149,7 @@ struct ipadb_context {
|
||||||
|
krb5_key_salt_tuple *def_encs;
|
||||||
|
int n_def_encs;
|
||||||
|
struct ipadb_mspac *mspac;
|
||||||
|
- bool optional_pac_tkt_chksum;
|
||||||
|
+ enum ipadb_tristate_option optional_pac_tkt_chksum;
|
||||||
|
#ifdef HAVE_KRB5_CERTAUTH_PLUGIN
|
||||||
|
krb5_certauth_moddata certauth_moddata;
|
||||||
|
#endif
|
||||||
|
diff --git a/daemons/ipa-kdb/ipa_kdb_common.c b/daemons/ipa-kdb/ipa_kdb_common.c
|
||||||
|
index 42e0856d0..ae7742a32 100644
|
||||||
|
--- a/daemons/ipa-kdb/ipa_kdb_common.c
|
||||||
|
+++ b/daemons/ipa-kdb/ipa_kdb_common.c
|
||||||
|
@@ -158,12 +158,75 @@ static bool ipadb_need_retry(struct ipadb_context *ipactx, int error)
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
+static char *no_attrs[] = {
|
||||||
|
+ LDAP_NO_ATTRS,
|
||||||
|
+
|
||||||
|
+ NULL
|
||||||
|
+};
|
||||||
|
+
|
||||||
|
+static int
|
||||||
|
+should_support_pac_tkt_sign(struct ipadb_context *ipactx, bool *result)
|
||||||
|
+{
|
||||||
|
+ int ret;
|
||||||
|
+ LDAPMessage *res = NULL;
|
||||||
|
+ char *masters_dn = NULL;
|
||||||
|
+ int count;
|
||||||
|
+
|
||||||
|
+ char *kdc_filter = "(&(cn=KDC)(objectClass=ipaConfigObject)"
|
||||||
|
+ "(!(ipaConfigString=pacTktSignSupported)))";
|
||||||
|
+
|
||||||
|
+ if (!ipactx) {
|
||||||
|
+ ret = KRB5_KDB_DBNOTINITED;
|
||||||
|
+ goto done;
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ count = asprintf(&masters_dn, "cn=masters,cn=ipa,cn=etc,%s", ipactx->base);
|
||||||
|
+ if (count < 0) {
|
||||||
|
+ ret = ENOMEM;
|
||||||
|
+ goto done;
|
||||||
|
+ }
|
||||||
|
+
|
||||||
|
+ ret = ipadb_simple_search(ipactx, masters_dn, LDAP_SCOPE_SUBTREE,
|
||||||
|
+ kdc_filter, no_attrs, &res);
|
||||||
|
+ if (ret)
|
||||||
|
+ goto done;
|
||||||
|
+
|
||||||
|
+ count = ldap_count_entries(ipactx->lcontext, res);
|
||||||
|
+
|
||||||
|
+ if (result)
|
||||||
|
+ *result = (count == 0);
|
||||||
|
+
|
||||||
|
+done:
|
||||||
|
+ free(masters_dn);
|
||||||
|
+ ldap_msgfree(res);
|
||||||
|
+ return ret;
|
||||||
|
+}
|
||||||
|
+
|
||||||
|
static int ipadb_check_connection(struct ipadb_context *ipactx)
|
||||||
|
{
|
||||||
|
+ int ret = 0;
|
||||||
|
+
|
||||||
|
if (ipactx->lcontext == NULL) {
|
||||||
|
- return ipadb_get_connection(ipactx);
|
||||||
|
+ ret = ipadb_get_connection(ipactx);
|
||||||
|
+ }
|
||||||
|
+ if ((ret == 0) && (ipactx->optional_pac_tkt_chksum == IPADB_TRISTATE_UNDEFINED)) {
|
||||||
|
+ bool pac_tkt_sign_supported;
|
||||||
|
+
|
||||||
|
+ /* Enforce PAC ticket signature verification if supported by all KDCs
|
||||||
|
+ * To avoid loops as all search functions call into
|
||||||
|
+ * ipadb_check_connection(), mark that the init is complete at this
|
||||||
|
+ * point. Default to not issuing PAC to be safe.
|
||||||
|
+ */
|
||||||
|
+ ipactx->optional_pac_tkt_chksum = IPADB_TRISTATE_FALSE;
|
||||||
|
+ ret = should_support_pac_tkt_sign(ipactx,
|
||||||
|
+ &pac_tkt_sign_supported);
|
||||||
|
+ if (ret == 0) {
|
||||||
|
+ ipactx->optional_pac_tkt_chksum = !pac_tkt_sign_supported;
|
||||||
|
+ } else {
|
||||||
|
+ ipactx->optional_pac_tkt_chksum = IPADB_TRISTATE_UNDEFINED;
|
||||||
|
+ }
|
||||||
|
}
|
||||||
|
- return 0;
|
||||||
|
+ return ret;
|
||||||
|
}
|
||||||
|
|
||||||
|
krb5_error_code ipadb_simple_search(struct ipadb_context *ipactx,
|
||||||
|
diff --git a/daemons/ipa-kdb/ipa_kdb_principals.c b/daemons/ipa-kdb/ipa_kdb_principals.c
|
||||||
|
index e6c3fba21..d35cec2e0 100644
|
||||||
|
--- a/daemons/ipa-kdb/ipa_kdb_principals.c
|
||||||
|
+++ b/daemons/ipa-kdb/ipa_kdb_principals.c
|
||||||
|
@@ -113,7 +113,9 @@ static char *std_principal_obj_classes[] = {
|
||||||
|
|
||||||
|
#define DEFAULT_TL_DATA_CONTENT "\x00\x00\x00\x00principal@UNINITIALIZED"
|
||||||
|
|
||||||
|
-#define OPT_PAC_TKT_CHKSUM_STR_ATTR_NAME "optional_pac_tkt_chksum"
|
||||||
|
+#ifndef KRB5_KDB_SK_OPTIONAL_PAC_TKT_CHKSUM
|
||||||
|
+#define KRB5_KDB_SK_OPTIONAL_PAC_TKT_CHKSUM "optional_pac_tkt_chksum"
|
||||||
|
+#endif
|
||||||
|
|
||||||
|
static int ipadb_ldap_attr_to_tl_data(LDAP *lcontext, LDAPMessage *le,
|
||||||
|
char *attrname,
|
||||||
|
@@ -1710,6 +1712,10 @@ krb5_error_code ipadb_get_principal(krb5_context kcontext,
|
||||||
|
if (kerr)
|
||||||
|
return kerr;
|
||||||
|
|
||||||
|
+ /* We should have been initialized at this point already */
|
||||||
|
+ if (ipactx->optional_pac_tkt_chksum == IPADB_TRISTATE_UNDEFINED) {
|
||||||
|
+ return KRB5_KDB_SERVER_INTERNAL_ERR;
|
||||||
|
+ }
|
||||||
|
/* PAC ticket signature should be optional for foreign realms, and local
|
||||||
|
* realm if not supported by all servers
|
||||||
|
*/
|
||||||
|
@@ -1719,7 +1725,7 @@ krb5_error_code ipadb_get_principal(krb5_context kcontext,
|
||||||
|
opt_pac_tkt_chksum_val = "false";
|
||||||
|
|
||||||
|
kerr = krb5_dbe_set_string(kcontext, *entry,
|
||||||
|
- OPT_PAC_TKT_CHKSUM_STR_ATTR_NAME,
|
||||||
|
+ KRB5_KDB_SK_OPTIONAL_PAC_TKT_CHKSUM,
|
||||||
|
opt_pac_tkt_chksum_val);
|
||||||
|
}
|
||||||
|
|
||||||
|
@@ -2828,14 +2834,14 @@ remove_virtual_str_attrs(krb5_context kcontext, krb5_db_entry *entry)
|
||||||
|
krb5_error_code kerr;
|
||||||
|
|
||||||
|
kerr = krb5_dbe_get_string(kcontext, entry,
|
||||||
|
- OPT_PAC_TKT_CHKSUM_STR_ATTR_NAME,
|
||||||
|
+ KRB5_KDB_SK_OPTIONAL_PAC_TKT_CHKSUM,
|
||||||
|
&str_attr_val);
|
||||||
|
if (kerr)
|
||||||
|
return kerr;
|
||||||
|
|
||||||
|
if (str_attr_val)
|
||||||
|
kerr = krb5_dbe_set_string(kcontext, entry,
|
||||||
|
- OPT_PAC_TKT_CHKSUM_STR_ATTR_NAME,
|
||||||
|
+ KRB5_KDB_SK_OPTIONAL_PAC_TKT_CHKSUM,
|
||||||
|
NULL);
|
||||||
|
|
||||||
|
krb5_dbe_free_string(kcontext, str_attr_val);
|
||||||
|
--
|
||||||
|
2.39.2
|
||||||
|
|
51
SOURCES/0025-Fix-OTP-on-s390x.patch
Normal file
51
SOURCES/0025-Fix-OTP-on-s390x.patch
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
From 7060e3a031fb4e4cdf85f616f1e1a3435d61e696 Mon Sep 17 00:00:00 2001
|
||||||
|
From: Florence Blanc-Renaud <flo@redhat.com>
|
||||||
|
Date: Jun 28 2023 15:28:41 +0000
|
||||||
|
Subject: OTP: fix data type to avoid endianness issue
|
||||||
|
|
||||||
|
|
||||||
|
When 389-ds process an OTP authentication, the ipa-pwd-extop
|
||||||
|
plugin reads a buffer to extract the authentication type.
|
||||||
|
The type is stored in an int but the data is a ber_tag_t.
|
||||||
|
|
||||||
|
On big endian machines the type cast does not cause any issue
|
||||||
|
but on s390x the buffer that should return 128 is seen as 0.
|
||||||
|
|
||||||
|
As a consequence, the plugin considers that the method is not
|
||||||
|
LDAP_AUTH_SIMPLE and exits early, without processing the OTP.
|
||||||
|
|
||||||
|
The fix is simple and consists in using the right type
|
||||||
|
(ber_tag_t is an unsigned long).
|
||||||
|
|
||||||
|
Fixes: https://pagure.io/freeipa/issue/9402
|
||||||
|
|
||||||
|
Signed-off-by: Florence Blanc-Renaud <flo@redhat.com>
|
||||||
|
Reviewed-By: Rob Crittenden <rcritten@redhat.com>
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
diff --git a/daemons/ipa-slapi-plugins/ipa-pwd-extop/prepost.c b/daemons/ipa-slapi-plugins/ipa-pwd-extop/prepost.c
|
||||||
|
index 9375941..4562652 100644
|
||||||
|
--- a/daemons/ipa-slapi-plugins/ipa-pwd-extop/prepost.c
|
||||||
|
+++ b/daemons/ipa-slapi-plugins/ipa-pwd-extop/prepost.c
|
||||||
|
@@ -1433,7 +1433,7 @@ static int ipapwd_pre_bind(Slapi_PBlock *pb)
|
||||||
|
Slapi_DN *target_sdn = NULL;
|
||||||
|
Slapi_DN *sdn = NULL;
|
||||||
|
const char *dn = NULL;
|
||||||
|
- int method = 0;
|
||||||
|
+ ber_tag_t method = 0;
|
||||||
|
bool syncreq;
|
||||||
|
bool otpreq;
|
||||||
|
int ret = 0;
|
||||||
|
@@ -1454,8 +1454,10 @@ static int ipapwd_pre_bind(Slapi_PBlock *pb)
|
||||||
|
}
|
||||||
|
|
||||||
|
/* We're only interested in simple authentication. */
|
||||||
|
- if (method != LDAP_AUTH_SIMPLE || credentials->bv_len == 0)
|
||||||
|
+ if (method != LDAP_AUTH_SIMPLE || credentials->bv_len == 0) {
|
||||||
|
+ LOG("Not handled (not simple bind or NULL dn/credentials)\n");
|
||||||
|
return 0;
|
||||||
|
+ }
|
||||||
|
|
||||||
|
/* Retrieve the user's entry. */
|
||||||
|
sdn = slapi_sdn_dup(target_sdn);
|
@ -1,50 +0,0 @@
|
|||||||
From 656a11ae961f8d1afad54567cfe8ccb53e084a67 Mon Sep 17 00:00:00 2001
|
|
||||||
From: Alexander Bokovoy <abokovoy@redhat.com>
|
|
||||||
Date: Mar 20 2024 10:06:07 +0000
|
|
||||||
Subject: dcerpc: invalidate forest trust info cache when filtering out realm domains
|
|
||||||
|
|
||||||
|
|
||||||
When get_realmdomains() method is called, it will filter out subdomains
|
|
||||||
of the IPA primary domain. This is required because Active Directory
|
|
||||||
domain controllers are assuming subdomains already covered by the main
|
|
||||||
domain namespace.
|
|
||||||
|
|
||||||
[MS-LSAD] 3.1.4.7.16.1, 'Forest Trust Collision Generation' defines the
|
|
||||||
method of validating the forest trust information. They are the same as
|
|
||||||
rules in [MS-ADTS] section 6.1.6. Specifically,
|
|
||||||
|
|
||||||
- A top-level name must not be superior to an enabled top-level name
|
|
||||||
for another trusted domain object, unless the current trusted domain
|
|
||||||
object has a corresponding exclusion record.
|
|
||||||
|
|
||||||
In practice, we filtered those subdomains already but the code wasn't
|
|
||||||
invalidating a previously retrieved forest trust information.
|
|
||||||
|
|
||||||
Fixes: https://pagure.io/freeipa/issue/9551
|
|
||||||
|
|
||||||
Signed-off-by: Alexander Bokovoy <abokovoy@redhat.com>
|
|
||||||
Reviewed-By: Florence Blanc-Renaud <flo@redhat.com>
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
diff --git a/ipaserver/dcerpc.py b/ipaserver/dcerpc.py
|
|
||||||
index b6139db..7ee553d 100644
|
|
||||||
--- a/ipaserver/dcerpc.py
|
|
||||||
+++ b/ipaserver/dcerpc.py
|
|
||||||
@@ -1103,6 +1103,7 @@ class TrustDomainInstance:
|
|
||||||
|
|
||||||
info.count = len(ftinfo_records)
|
|
||||||
info.entries = ftinfo_records
|
|
||||||
+ another_domain.ftinfo_data = info
|
|
||||||
return info
|
|
||||||
|
|
||||||
def clear_ftinfo_conflict(self, another_domain, cinfo):
|
|
||||||
@@ -1778,6 +1779,7 @@ class TrustDomainJoins:
|
|
||||||
return
|
|
||||||
|
|
||||||
self.local_domain.ftinfo_records = []
|
|
||||||
+ self.local_domain.ftinfo_data = None
|
|
||||||
|
|
||||||
realm_domains = self.api.Command.realmdomains_show()['result']
|
|
||||||
# Use realmdomains' modification timestamp
|
|
||||||
|
|
@ -1,335 +0,0 @@
|
|||||||
From 3bba254ccdcf9b62fdd8a6d71baecf37c97c300c Mon Sep 17 00:00:00 2001
|
|
||||||
From: Florence Blanc-Renaud <flo@redhat.com>
|
|
||||||
Date: Mon, 3 Apr 2023 08:37:28 +0200
|
|
||||||
Subject: [PATCH] ipatests: mark known failures for autoprivategroup
|
|
||||||
|
|
||||||
Two tests have known issues in test_trust.py with sssd 2.8.2+:
|
|
||||||
- TestNonPosixAutoPrivateGroup::test_idoverride_with_auto_private_group
|
|
||||||
(when called with the "hybrid" parameter)
|
|
||||||
- TestPosixAutoPrivateGroup::test_only_uid_number_auto_private_group_default
|
|
||||||
(when called with the "true" parameter)
|
|
||||||
|
|
||||||
Related: https://pagure.io/freeipa/issue/9295
|
|
||||||
Signed-off-by: Florence Blanc-Renaud <flo@redhat.com>
|
|
||||||
Reviewed-By: Rob Crittenden <rcritten@redhat.com>
|
|
||||||
Reviewed-By: Alexander Bokovoy <abokovoy@redhat.com>
|
|
||||||
---
|
|
||||||
ipatests/test_integration/test_trust.py | 17 ++++++++++++-----
|
|
||||||
1 file changed, 12 insertions(+), 5 deletions(-)
|
|
||||||
|
|
||||||
diff --git a/ipatests/test_integration/test_trust.py b/ipatests/test_integration/test_trust.py
|
|
||||||
index 0d5b71cb0..12f000c1a 100644
|
|
||||||
--- a/ipatests/test_integration/test_trust.py
|
|
||||||
+++ b/ipatests/test_integration/test_trust.py
|
|
||||||
@@ -1154,11 +1154,15 @@ class TestNonPosixAutoPrivateGroup(BaseTestTrust):
|
|
||||||
self.gid_override
|
|
||||||
):
|
|
||||||
self.mod_idrange_auto_private_group(type)
|
|
||||||
- (uid, gid) = self.get_user_id(self.clients[0], nonposixuser)
|
|
||||||
- assert (uid == self.uid_override and gid == self.gid_override)
|
|
||||||
+ sssd_version = tasks.get_sssd_version(self.clients[0])
|
|
||||||
+ bad_version = sssd_version >= tasks.parse_version("2.8.2")
|
|
||||||
+ cond = (type == 'hybrid') and bad_version
|
|
||||||
+ with xfail_context(condition=cond,
|
|
||||||
+ reason="https://pagure.io/freeipa/issue/9295"):
|
|
||||||
+ (uid, gid) = self.get_user_id(self.clients[0], nonposixuser)
|
|
||||||
+ assert (uid == self.uid_override and gid == self.gid_override)
|
|
||||||
test_group = self.clients[0].run_command(
|
|
||||||
["id", nonposixuser]).stdout_text
|
|
||||||
- # version = tasks.get_sssd_version(self.clients[0])
|
|
||||||
with xfail_context(type == "hybrid",
|
|
||||||
'https://github.com/SSSD/sssd/issues/5989'):
|
|
||||||
assert "domain users@{0}".format(self.ad_domain) in test_group
|
|
||||||
@@ -1232,8 +1236,11 @@ class TestPosixAutoPrivateGroup(BaseTestTrust):
|
|
||||||
posixuser = "testuser1@%s" % self.ad_domain
|
|
||||||
self.mod_idrange_auto_private_group(type)
|
|
||||||
if type == "true":
|
|
||||||
- (uid, gid) = self.get_user_id(self.clients[0], posixuser)
|
|
||||||
- assert uid == gid
|
|
||||||
+ sssd_version = tasks.get_sssd_version(self.clients[0])
|
|
||||||
+ with xfail_context(sssd_version >= tasks.parse_version("2.8.2"),
|
|
||||||
+ "https://pagure.io/freeipa/issue/9295"):
|
|
||||||
+ (uid, gid) = self.get_user_id(self.clients[0], posixuser)
|
|
||||||
+ assert uid == gid
|
|
||||||
else:
|
|
||||||
for host in [self.master, self.clients[0]]:
|
|
||||||
result = host.run_command(['id', posixuser], raiseonerr=False)
|
|
||||||
--
|
|
||||||
2.44.0
|
|
||||||
|
|
||||||
From ed2a8eb0cefadfe0544074114facfef381349ae0 Mon Sep 17 00:00:00 2001
|
|
||||||
From: Florence Blanc-Renaud <flo@redhat.com>
|
|
||||||
Date: Feb 12 2024 10:43:39 +0000
|
|
||||||
Subject: ipatests: add xfail for autoprivate group test with override
|
|
||||||
|
|
||||||
|
|
||||||
Because of SSSD issue 7169, secondary groups are not
|
|
||||||
retrieved when autoprivate group is set and an idoverride
|
|
||||||
replaces the user's primary group.
|
|
||||||
Mark the known issues as xfail.
|
|
||||||
|
|
||||||
Related: https://github.com/SSSD/sssd/issues/7169
|
|
||||||
|
|
||||||
Signed-off-by: Florence Blanc-Renaud <flo@redhat.com>
|
|
||||||
Reviewed-By: Anuja More <amore@redhat.com>
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
diff --git a/ipatests/test_integration/test_trust.py b/ipatests/test_integration/test_trust.py
|
|
||||||
index 3b9f0fb..2b94514 100644
|
|
||||||
--- a/ipatests/test_integration/test_trust.py
|
|
||||||
+++ b/ipatests/test_integration/test_trust.py
|
|
||||||
@@ -1164,8 +1164,12 @@ class TestNonPosixAutoPrivateGroup(BaseTestTrust):
|
|
||||||
assert (uid == self.uid_override and gid == self.gid_override)
|
|
||||||
test_group = self.clients[0].run_command(
|
|
||||||
["id", nonposixuser]).stdout_text
|
|
||||||
- with xfail_context(type == "hybrid",
|
|
||||||
- 'https://github.com/SSSD/sssd/issues/5989'):
|
|
||||||
+ cond2 = ((type == 'false'
|
|
||||||
+ and sssd_version >= tasks.parse_version("2.9.4"))
|
|
||||||
+ or type == 'hybrid')
|
|
||||||
+ with xfail_context(cond2,
|
|
||||||
+ 'https://github.com/SSSD/sssd/issues/5989 '
|
|
||||||
+ 'and 7169'):
|
|
||||||
assert "domain users@{0}".format(self.ad_domain) in test_group
|
|
||||||
|
|
||||||
@pytest.mark.parametrize('type', ['hybrid', 'true', "false"])
|
|
||||||
@@ -1287,5 +1291,9 @@ class TestPosixAutoPrivateGroup(BaseTestTrust):
|
|
||||||
assert(uid == self.uid_override
|
|
||||||
and gid == self.gid_override)
|
|
||||||
result = self.clients[0].run_command(['id', posixuser])
|
|
||||||
- assert "10047(testgroup@{0})".format(
|
|
||||||
- self.ad_domain) in result.stdout_text
|
|
||||||
+ sssd_version = tasks.get_sssd_version(self.clients[0])
|
|
||||||
+ bad_version = sssd_version >= tasks.parse_version("2.9.4")
|
|
||||||
+ with xfail_context(bad_version and type in ('false', 'hybrid'),
|
|
||||||
+ "https://github.com/SSSD/sssd/issues/7169"):
|
|
||||||
+ assert "10047(testgroup@{0})".format(
|
|
||||||
+ self.ad_domain) in result.stdout_text
|
|
||||||
|
|
||||||
From d5392300d77170ea3202ee80690ada8bf81b60b5 Mon Sep 17 00:00:00 2001
|
|
||||||
From: Florence Blanc-Renaud <flo@redhat.com>
|
|
||||||
Date: Feb 12 2024 10:44:47 +0000
|
|
||||||
Subject: ipatests: remove xfail thanks to sssd 2.9.4
|
|
||||||
|
|
||||||
|
|
||||||
SSSD 2.9.4 fixes some issues related to auto-private-group
|
|
||||||
|
|
||||||
Related: https://pagure.io/freeipa/issue/9295
|
|
||||||
Signed-off-by: Florence Blanc-Renaud <flo@redhat.com>
|
|
||||||
Reviewed-By: Anuja More <amore@redhat.com>
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
diff --git a/ipatests/test_integration/test_trust.py b/ipatests/test_integration/test_trust.py
|
|
||||||
index 12f000c..3b9f0fb 100644
|
|
||||||
--- a/ipatests/test_integration/test_trust.py
|
|
||||||
+++ b/ipatests/test_integration/test_trust.py
|
|
||||||
@@ -1155,7 +1155,8 @@ class TestNonPosixAutoPrivateGroup(BaseTestTrust):
|
|
||||||
):
|
|
||||||
self.mod_idrange_auto_private_group(type)
|
|
||||||
sssd_version = tasks.get_sssd_version(self.clients[0])
|
|
||||||
- bad_version = sssd_version >= tasks.parse_version("2.8.2")
|
|
||||||
+ bad_version = (tasks.parse_version("2.8.2") <= sssd_version
|
|
||||||
+ < tasks.parse_version("2.9.4"))
|
|
||||||
cond = (type == 'hybrid') and bad_version
|
|
||||||
with xfail_context(condition=cond,
|
|
||||||
reason="https://pagure.io/freeipa/issue/9295"):
|
|
||||||
@@ -1237,7 +1238,9 @@ class TestPosixAutoPrivateGroup(BaseTestTrust):
|
|
||||||
self.mod_idrange_auto_private_group(type)
|
|
||||||
if type == "true":
|
|
||||||
sssd_version = tasks.get_sssd_version(self.clients[0])
|
|
||||||
- with xfail_context(sssd_version >= tasks.parse_version("2.8.2"),
|
|
||||||
+ bad_version = (tasks.parse_version("2.8.2") <= sssd_version
|
|
||||||
+ < tasks.parse_version("2.9.4"))
|
|
||||||
+ with xfail_context(bad_version,
|
|
||||||
"https://pagure.io/freeipa/issue/9295"):
|
|
||||||
(uid, gid) = self.get_user_id(self.clients[0], posixuser)
|
|
||||||
assert uid == gid
|
|
||||||
|
|
||||||
From 34d048ede0c439b3a53e02f8ace96ff91aa1609d Mon Sep 17 00:00:00 2001
|
|
||||||
From: Florence Blanc-Renaud <flo@redhat.com>
|
|
||||||
Date: Mar 14 2023 16:50:25 +0000
|
|
||||||
Subject: ipatests: adapt for new automembership fixup behavior
|
|
||||||
|
|
||||||
|
|
||||||
The automembership fixup task now needs to be called
|
|
||||||
with --cleanup argument when the user expects automember
|
|
||||||
to remove user/hosts from automember groups.
|
|
||||||
Update the test to call create a cleanup task equivalent to
|
|
||||||
dsconf plugin automember fixup --cleanup
|
|
||||||
when it is needed.
|
|
||||||
|
|
||||||
Fixes: https://pagure.io/freeipa/issue/9313
|
|
||||||
Signed-off-by: Florence Blanc-Renaud <flo@redhat.com>
|
|
||||||
Reviewed-By: Alexander Bokovoy <abokovoy@redhat.com>
|
|
||||||
Reviewed-By: Rob Crittenden <rcritten@redhat.com>
|
|
||||||
|
|
||||||
---
|
|
||||||
|
|
||||||
diff --git a/ipatests/test_integration/test_automember.py b/ipatests/test_integration/test_automember.py
|
|
||||||
index 7acd0d7..8b27f4d 100644
|
|
||||||
--- a/ipatests/test_integration/test_automember.py
|
|
||||||
+++ b/ipatests/test_integration/test_automember.py
|
|
||||||
@@ -4,6 +4,7 @@
|
|
||||||
"""This covers tests for automemberfeature."""
|
|
||||||
|
|
||||||
from __future__ import absolute_import
|
|
||||||
+import uuid
|
|
||||||
|
|
||||||
from ipapython.dn import DN
|
|
||||||
|
|
||||||
@@ -211,11 +212,27 @@ class TestAutounmembership(IntegrationTest):
|
|
||||||
# Running automember-build so that user is part of correct group
|
|
||||||
result = self.master.run_command(['ipa', 'automember-rebuild',
|
|
||||||
'--users=%s' % user2])
|
|
||||||
+ assert msg in result.stdout_text
|
|
||||||
+
|
|
||||||
+ # The additional --cleanup argument is required
|
|
||||||
+ cleanup_ldif = (
|
|
||||||
+ "dn: cn={cn},cn=automember rebuild membership,"
|
|
||||||
+ "cn=tasks,cn=config\n"
|
|
||||||
+ "changetype: add\n"
|
|
||||||
+ "objectclass: top\n"
|
|
||||||
+ "objectclass: extensibleObject\n"
|
|
||||||
+ "basedn: cn=users,cn=accounts,{suffix}\n"
|
|
||||||
+ "filter: (uid={user})\n"
|
|
||||||
+ "cleanup: yes\n"
|
|
||||||
+ "scope: sub"
|
|
||||||
+ ).format(cn=str(uuid.uuid4()),
|
|
||||||
+ suffix=str(self.master.domain.basedn),
|
|
||||||
+ user=user2)
|
|
||||||
+ tasks.ldapmodify_dm(self.master, cleanup_ldif)
|
|
||||||
+
|
|
||||||
assert self.is_user_member_of_group(user2, group2)
|
|
||||||
assert not self.is_user_member_of_group(user2, group1)
|
|
||||||
|
|
||||||
- assert msg in result.stdout_text
|
|
||||||
-
|
|
||||||
finally:
|
|
||||||
# testcase cleanup
|
|
||||||
self.remove_user_automember(user2, raiseonerr=False)
|
|
||||||
@@ -248,11 +265,27 @@ class TestAutounmembership(IntegrationTest):
|
|
||||||
result = self.master.run_command(
|
|
||||||
['ipa', 'automember-rebuild', '--hosts=%s' % host2]
|
|
||||||
)
|
|
||||||
+ assert msg in result.stdout_text
|
|
||||||
+
|
|
||||||
+ # The additional --cleanup argument is required
|
|
||||||
+ cleanup_ldif = (
|
|
||||||
+ "dn: cn={cn},cn=automember rebuild membership,"
|
|
||||||
+ "cn=tasks,cn=config\n"
|
|
||||||
+ "changetype: add\n"
|
|
||||||
+ "objectclass: top\n"
|
|
||||||
+ "objectclass: extensibleObject\n"
|
|
||||||
+ "basedn: cn=computers,cn=accounts,{suffix}\n"
|
|
||||||
+ "filter: (fqdn={fqdn})\n"
|
|
||||||
+ "cleanup: yes\n"
|
|
||||||
+ "scope: sub"
|
|
||||||
+ ).format(cn=str(uuid.uuid4()),
|
|
||||||
+ suffix=str(self.master.domain.basedn),
|
|
||||||
+ fqdn=host2)
|
|
||||||
+ tasks.ldapmodify_dm(self.master, cleanup_ldif)
|
|
||||||
+
|
|
||||||
assert self.is_host_member_of_hostgroup(host2, hostgroup2)
|
|
||||||
assert not self.is_host_member_of_hostgroup(host2, hostgroup1)
|
|
||||||
|
|
||||||
- assert msg in result.stdout_text
|
|
||||||
-
|
|
||||||
finally:
|
|
||||||
# testcase cleanup
|
|
||||||
self.remove_host_automember(host2, raiseonerr=False)
|
|
||||||
|
|
||||||
From 9b777390fbb6d4c683bf7d3e5f74d5443209b1d5 Mon Sep 17 00:00:00 2001
|
|
||||||
From: Alexander Bokovoy <abokovoy@redhat.com>
|
|
||||||
Date: Fri, 24 Mar 2023 08:15:00 +0200
|
|
||||||
Subject: [PATCH] test_xmlrpc: adopt to automember plugin message changes in
|
|
||||||
389-ds
|
|
||||||
|
|
||||||
Another change in automember plugin messaging that breaks FreeIPA tests.
|
|
||||||
Use common substring to match.
|
|
||||||
|
|
||||||
Signed-off-by: Alexander Bokovoy <abokovoy@redhat.com>
|
|
||||||
Reviewed-By: Rob Crittenden <rcritten@redhat.com>
|
|
||||||
---
|
|
||||||
ipatests/test_xmlrpc/xmlrpc_test.py | 2 +-
|
|
||||||
1 file changed, 1 insertion(+), 1 deletion(-)
|
|
||||||
|
|
||||||
diff --git a/ipatests/test_xmlrpc/xmlrpc_test.py b/ipatests/test_xmlrpc/xmlrpc_test.py
|
|
||||||
index cf11721bfca..5fe1245dc65 100644
|
|
||||||
--- a/ipatests/test_xmlrpc/xmlrpc_test.py
|
|
||||||
+++ b/ipatests/test_xmlrpc/xmlrpc_test.py
|
|
||||||
@@ -64,7 +64,7 @@ def test(xs):
|
|
||||||
|
|
||||||
# Matches an automember task finish message
|
|
||||||
fuzzy_automember_message = Fuzzy(
|
|
||||||
- r'^Automember rebuild task finished\. Processed \(\d+\) entries\.$'
|
|
||||||
+ r'^Automember rebuild task finished\. Processed \(\d+\) entries'
|
|
||||||
)
|
|
||||||
|
|
||||||
# Matches trusted domain GUID, like u'463bf2be-3456-4a57-979e-120304f2a0eb'
|
|
||||||
From 8e8b97a2251329aec9633a5c7c644bc5034bc8c2 Mon Sep 17 00:00:00 2001
|
|
||||||
From: Sudhir Menon <sumenon@redhat.com>
|
|
||||||
Date: Wed, 20 Mar 2024 14:29:46 +0530
|
|
||||||
Subject: [PATCH] ipatests: Fixes for test_ipahealthcheck_ipansschainvalidation
|
|
||||||
testcases.
|
|
||||||
|
|
||||||
Currently the test is using IPA_NSSDB_PWDFILE_TXT which is /etc/ipa/nssdb/pwdfile.txt
|
|
||||||
which causes error in STIG mode.
|
|
||||||
|
|
||||||
[root@master slapd-TESTRELM-TEST]# certutil -M -n 'TESTRELM.TEST IPA CA' -t ',,' -d . -f /etc/ipa/nssdb/pwdfile.txt
|
|
||||||
Incorrect password/PIN entered.
|
|
||||||
|
|
||||||
Hence modified the test to include paths.ETC_DIRSRV_SLAPD_INSTANCE_TEMPLATE/pwd.txt.
|
|
||||||
|
|
||||||
Signed-off-by: Sudhir Menon <sumenon@redhat.com>
|
|
||||||
Reviewed-By: Florence Blanc-Renaud <flo@redhat.com>
|
|
||||||
Reviewed-By: Rob Crittenden <rcritten@redhat.com>
|
|
||||||
---
|
|
||||||
ipatests/test_integration/test_ipahealthcheck.py | 11 ++++++-----
|
|
||||||
1 file changed, 6 insertions(+), 5 deletions(-)
|
|
||||||
|
|
||||||
diff --git a/ipatests/test_integration/test_ipahealthcheck.py b/ipatests/test_integration/test_ipahealthcheck.py
|
|
||||||
index 8aae9fad776..a96de7088aa 100644
|
|
||||||
--- a/ipatests/test_integration/test_ipahealthcheck.py
|
|
||||||
+++ b/ipatests/test_integration/test_ipahealthcheck.py
|
|
||||||
@@ -2731,17 +2731,18 @@ def remove_server_cert(self):
|
|
||||||
Fixture to remove Server cert and revert the change.
|
|
||||||
"""
|
|
||||||
instance = realm_to_serverid(self.master.domain.realm)
|
|
||||||
+ instance_dir = paths.ETC_DIRSRV_SLAPD_INSTANCE_TEMPLATE % instance
|
|
||||||
self.master.run_command(
|
|
||||||
[
|
|
||||||
"certutil",
|
|
||||||
"-L",
|
|
||||||
"-d",
|
|
||||||
- paths.ETC_DIRSRV_SLAPD_INSTANCE_TEMPLATE % instance,
|
|
||||||
+ instance_dir,
|
|
||||||
"-n",
|
|
||||||
"Server-Cert",
|
|
||||||
"-a",
|
|
||||||
"-o",
|
|
||||||
- paths.ETC_DIRSRV_SLAPD_INSTANCE_TEMPLATE % instance
|
|
||||||
+ instance_dir
|
|
||||||
+ "/Server-Cert.pem",
|
|
||||||
]
|
|
||||||
)
|
|
||||||
@@ -2760,15 +2761,15 @@ def remove_server_cert(self):
|
|
||||||
[
|
|
||||||
"certutil",
|
|
||||||
"-d",
|
|
||||||
- paths.ETC_DIRSRV_SLAPD_INSTANCE_TEMPLATE % instance,
|
|
||||||
+ instance_dir,
|
|
||||||
"-A",
|
|
||||||
"-i",
|
|
||||||
- paths.ETC_DIRSRV_SLAPD_INSTANCE_TEMPLATE % instance
|
|
||||||
+ instance_dir
|
|
||||||
+ "/Server-Cert.pem",
|
|
||||||
"-t",
|
|
||||||
"u,u,u",
|
|
||||||
"-f",
|
|
||||||
- paths.IPA_NSSDB_PWDFILE_TXT,
|
|
||||||
+ "%s/pwdfile.txt" % instance_dir,
|
|
||||||
"-n",
|
|
||||||
"Server-Cert",
|
|
||||||
]
|
|
@ -0,0 +1,85 @@
|
|||||||
|
From c84c59c66f1b22ebc671960cae90088a024d2d62 Mon Sep 17 00:00:00 2001
|
||||||
|
From: Julien Rische <jrische@redhat.com>
|
||||||
|
Date: Aug 01 2023 11:31:09 +0000
|
||||||
|
Subject: ipa-kdb: fix error handling of is_master_host()
|
||||||
|
|
||||||
|
|
||||||
|
Adding proper error handling to the is_master_host() function to allow
|
||||||
|
it to make the difference between the absence of a master host object
|
||||||
|
and a connection failure. This will keep the krb5kdc daemon from
|
||||||
|
continuing to run with a NULL LDAP context.
|
||||||
|
|
||||||
|
Fixes: https://pagure.io/freeipa/issue/9422
|
||||||
|
|
||||||
|
Signed-off-by: Julien Rische <jrische@redhat.com>
|
||||||
|
Reviewed-By: Alexander Bokovoy <abokovoy@redhat.com>
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
diff --git a/daemons/ipa-kdb/ipa_kdb_mspac.c b/daemons/ipa-kdb/ipa_kdb_mspac.c
|
||||||
|
index 83b507c..1558e2b 100644
|
||||||
|
--- a/daemons/ipa-kdb/ipa_kdb_mspac.c
|
||||||
|
+++ b/daemons/ipa-kdb/ipa_kdb_mspac.c
|
||||||
|
@@ -401,27 +401,29 @@ static krb5_error_code ipadb_add_asserted_identity(struct ipadb_context *ipactx,
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
-static bool is_master_host(struct ipadb_context *ipactx, const char *fqdn)
|
||||||
|
+static krb5_error_code
|
||||||
|
+is_master_host(struct ipadb_context *ipactx, const char *fqdn, bool *result)
|
||||||
|
{
|
||||||
|
- int ret;
|
||||||
|
+ int err;
|
||||||
|
char *master_host_base = NULL;
|
||||||
|
- LDAPMessage *result = NULL;
|
||||||
|
- krb5_error_code err;
|
||||||
|
+ LDAPMessage *ldap_res = NULL;
|
||||||
|
|
||||||
|
- ret = asprintf(&master_host_base, "cn=%s,cn=masters,cn=ipa,cn=etc,%s",
|
||||||
|
+ err = asprintf(&master_host_base, "cn=%s,cn=masters,cn=ipa,cn=etc,%s",
|
||||||
|
fqdn, ipactx->base);
|
||||||
|
- if (ret == -1) {
|
||||||
|
- return false;
|
||||||
|
- }
|
||||||
|
+ if (err == -1)
|
||||||
|
+ return ENOMEM;
|
||||||
|
+
|
||||||
|
err = ipadb_simple_search(ipactx, master_host_base, LDAP_SCOPE_BASE,
|
||||||
|
- NULL, NULL, &result);
|
||||||
|
+ NULL, NULL, &ldap_res);
|
||||||
|
free(master_host_base);
|
||||||
|
- ldap_msgfree(result);
|
||||||
|
- if (err == 0) {
|
||||||
|
- return true;
|
||||||
|
- }
|
||||||
|
+ ldap_msgfree(ldap_res);
|
||||||
|
+ if (err != KRB5_KDB_NOENTRY && err != 0)
|
||||||
|
+ return err;
|
||||||
|
+
|
||||||
|
+ if (result)
|
||||||
|
+ *result = err != KRB5_KDB_NOENTRY;
|
||||||
|
|
||||||
|
- return false;
|
||||||
|
+ return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static krb5_error_code ipadb_fill_info3(struct ipadb_context *ipactx,
|
||||||
|
@@ -692,9 +694,14 @@ static krb5_error_code ipadb_fill_info3(struct ipadb_context *ipactx,
|
||||||
|
if ((is_host || is_service)) {
|
||||||
|
/* it is either host or service, so get the hostname first */
|
||||||
|
char *sep = strchr(info3->base.account_name.string, '/');
|
||||||
|
- bool is_master = is_master_host(
|
||||||
|
- ipactx,
|
||||||
|
- sep ? sep + 1 : info3->base.account_name.string);
|
||||||
|
+ bool is_master;
|
||||||
|
+
|
||||||
|
+ ret = is_master_host(ipactx,
|
||||||
|
+ sep ? sep + 1 : info3->base.account_name.string,
|
||||||
|
+ &is_master);
|
||||||
|
+ if (ret)
|
||||||
|
+ return ret;
|
||||||
|
+
|
||||||
|
if (is_master) {
|
||||||
|
/* Well known RID of domain controllers group */
|
||||||
|
if (info3->base.rid == 0) {
|
||||||
|
|
@ -1,341 +0,0 @@
|
|||||||
From 0a48726e104282fb40d8f471ebb306bc9134cb0c Mon Sep 17 00:00:00 2001
|
|
||||||
From: Julien Rische <jrische@redhat.com>
|
|
||||||
Date: Tue, 19 Mar 2024 12:24:40 +0100
|
|
||||||
Subject: [PATCH] kdb: fix vulnerability in GCD rules handling
|
|
||||||
|
|
||||||
The initial implementation of MS-SFU by MIT Kerberos was missing some
|
|
||||||
a condition for granting the "forwardable" flag on S4U2Self tickets.
|
|
||||||
Fixing this mistake required to add a special case for the
|
|
||||||
check_allowed_to_delegate() function: if the target service argument is
|
|
||||||
NULL, then it means the KDC is probing for general constrained
|
|
||||||
delegation rules, not actually checking a specific S4U2Proxy request.
|
|
||||||
|
|
||||||
In commit e86807b5, the behavior of ipadb_match_acl() was modified to
|
|
||||||
match the changes from upstream MIT Kerberos a441fbe3. However, a
|
|
||||||
mistake resulted in this mechanism to apply in cases where target
|
|
||||||
service argument is set AND unset. This results in S4U2Proxy requests to
|
|
||||||
be accepted regardless of the fact there is a matching service
|
|
||||||
delegation rule or not.
|
|
||||||
|
|
||||||
This vulnerability does not affect services having RBCD (resource-based
|
|
||||||
constrained delegation) rules.
|
|
||||||
|
|
||||||
This fixes CVE-2024-2698
|
|
||||||
|
|
||||||
Signed-off-by: Julien Rische <jrische@redhat.com>
|
|
||||||
---
|
|
||||||
daemons/ipa-kdb/README.s4u2proxy.txt | 19 ++-
|
|
||||||
daemons/ipa-kdb/ipa_kdb_delegation.c | 191 +++++++++++++++------------
|
|
||||||
2 files changed, 118 insertions(+), 92 deletions(-)
|
|
||||||
|
|
||||||
diff --git a/daemons/ipa-kdb/README.s4u2proxy.txt b/daemons/ipa-kdb/README.s4u2proxy.txt
|
|
||||||
index 254fcc4d1..ab34aff36 100644
|
|
||||||
--- a/daemons/ipa-kdb/README.s4u2proxy.txt
|
|
||||||
+++ b/daemons/ipa-kdb/README.s4u2proxy.txt
|
|
||||||
@@ -12,9 +12,7 @@ much more easily managed.
|
|
||||||
|
|
||||||
The grouping mechanism has been built so that lookup is highly optimized
|
|
||||||
and is basically reduced to a single search that uses the derefernce
|
|
||||||
-control. Speed is very important in this case because KDC operations
|
|
||||||
-time out very quickly and unless we add a caching layer in ipa-kdb we
|
|
||||||
-must keep the number of searches down to avoid client timeouts.
|
|
||||||
+control.
|
|
||||||
|
|
||||||
The grouping mechanism is very simple a groupOfPrincipals object is
|
|
||||||
introduced, this Auxiliary class have a single optional attribute called
|
|
||||||
@@ -112,8 +110,7 @@ kinit -kt /etc/httpd/conf/ipa.keytab HTTP/ipaserver.example.com
|
|
||||||
kvno -U admin HTTP/ipaserver.example.com
|
|
||||||
|
|
||||||
# Perform S4U2Proxy
|
|
||||||
-kvno -k /etc/httpd/conf/ipa.keytab -U admin -P HTTP/ipaserver.example.com
|
|
||||||
-ldap/ipaserver.example.com
|
|
||||||
+kvno -U admin -P ldap/ipaserver.example.com
|
|
||||||
|
|
||||||
|
|
||||||
If this works it means you successfully impersonated the admin user with
|
|
||||||
@@ -125,6 +122,18 @@ modprinc -ok_to_auth_as_delegate HTTP/ipaserver.example.com
|
|
||||||
Simo.
|
|
||||||
|
|
||||||
|
|
||||||
+If IPA is compiled with krb5 1.20 and newer (KDB DAL >= 9), then the
|
|
||||||
+behavior of S4U2Self changes: S4U2Self TGS-REQs produce forwardable
|
|
||||||
+tickets for all requesters, except if the requester principal is set as
|
|
||||||
+the proxy (impersonating service) in at least one `servicedelegation`
|
|
||||||
+rule. In this case, even if the rule has no target, the KDC will
|
|
||||||
+response to S4U2Self requests with a non-forwardable ticket. Hence,
|
|
||||||
+granting the `ok_to_auth_as_delegate` permission to the proxy service
|
|
||||||
+remains the only way for this service to obtain the evidence ticket
|
|
||||||
+required for general constrained delegation requests if this ticket is
|
|
||||||
+not provided by the client.
|
|
||||||
+
|
|
||||||
+
|
|
||||||
[1]
|
|
||||||
Note that here I use the term proxy in a different way than it is used in
|
|
||||||
the krb interfaces. It may seem a bit confusing but I think people will
|
|
||||||
diff --git a/daemons/ipa-kdb/ipa_kdb_delegation.c b/daemons/ipa-kdb/ipa_kdb_delegation.c
|
|
||||||
index de82174ad..3581f3c79 100644
|
|
||||||
--- a/daemons/ipa-kdb/ipa_kdb_delegation.c
|
|
||||||
+++ b/daemons/ipa-kdb/ipa_kdb_delegation.c
|
|
||||||
@@ -91,120 +91,110 @@ static bool ipadb_match_member(char *princ, LDAPDerefRes *dres)
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
-static krb5_error_code ipadb_match_acl(krb5_context kcontext,
|
|
||||||
- LDAPMessage *results,
|
|
||||||
- krb5_const_principal client,
|
|
||||||
- krb5_const_principal target)
|
|
||||||
+#if KRB5_KDB_DAL_MAJOR_VERSION >= 9
|
|
||||||
+static krb5_error_code
|
|
||||||
+ipadb_has_acl(krb5_context kcontext, LDAPMessage *ldap_acl, bool *res)
|
|
||||||
{
|
|
||||||
struct ipadb_context *ipactx;
|
|
||||||
- krb5_error_code kerr;
|
|
||||||
- LDAPMessage *lentry;
|
|
||||||
- LDAPDerefRes *deref_results;
|
|
||||||
- LDAPDerefRes *dres;
|
|
||||||
- char *client_princ = NULL;
|
|
||||||
- char *target_princ = NULL;
|
|
||||||
- bool client_missing;
|
|
||||||
- bool client_found;
|
|
||||||
- bool target_found;
|
|
||||||
- bool is_constraint_delegation = false;
|
|
||||||
- size_t nrules = 0;
|
|
||||||
- int ret;
|
|
||||||
+ bool in_res = false;
|
|
||||||
+ krb5_error_code kerr = 0;
|
|
||||||
|
|
||||||
ipactx = ipadb_get_context(kcontext);
|
|
||||||
- if (!ipactx) {
|
|
||||||
+ if (!ipactx)
|
|
||||||
return KRB5_KDB_DBNOTINITED;
|
|
||||||
- }
|
|
||||||
|
|
||||||
- if ((client != NULL) && (target != NULL)) {
|
|
||||||
- kerr = krb5_unparse_name(kcontext, client, &client_princ);
|
|
||||||
- if (kerr != 0) {
|
|
||||||
- goto done;
|
|
||||||
- }
|
|
||||||
- kerr = krb5_unparse_name(kcontext, target, &target_princ);
|
|
||||||
- if (kerr != 0) {
|
|
||||||
- goto done;
|
|
||||||
- }
|
|
||||||
- } else {
|
|
||||||
- is_constraint_delegation = true;
|
|
||||||
+ switch (ldap_count_entries(ipactx->lcontext, ldap_acl)) {
|
|
||||||
+ case 0:
|
|
||||||
+ break;
|
|
||||||
+ case -1:
|
|
||||||
+ kerr = EINVAL;
|
|
||||||
+ goto end;
|
|
||||||
+ default:
|
|
||||||
+ in_res = true;
|
|
||||||
+ goto end;
|
|
||||||
}
|
|
||||||
|
|
||||||
- lentry = ldap_first_entry(ipactx->lcontext, results);
|
|
||||||
- if (!lentry) {
|
|
||||||
- kerr = ENOENT;
|
|
||||||
- goto done;
|
|
||||||
- }
|
|
||||||
+end:
|
|
||||||
+ if (res)
|
|
||||||
+ *res = in_res;
|
|
||||||
+
|
|
||||||
+ return kerr;
|
|
||||||
+}
|
|
||||||
+#endif
|
|
||||||
+
|
|
||||||
+static krb5_error_code
|
|
||||||
+ipadb_match_acl(krb5_context kcontext, LDAPMessage *ldap_acl,
|
|
||||||
+ krb5_const_principal client, krb5_const_principal target)
|
|
||||||
+{
|
|
||||||
+ struct ipadb_context *ipactx;
|
|
||||||
+ LDAPMessage *rule;
|
|
||||||
+ LDAPDerefRes *acis, *aci;
|
|
||||||
+ char *client_princ = NULL, *target_princ= NULL;
|
|
||||||
+ bool client_missing, client_found, target_found;
|
|
||||||
+ int lerr;
|
|
||||||
+ krb5_error_code kerr;
|
|
||||||
+
|
|
||||||
+ ipactx = ipadb_get_context(kcontext);
|
|
||||||
+ if (!ipactx)
|
|
||||||
+ return KRB5_KDB_DBNOTINITED;
|
|
||||||
+
|
|
||||||
+ kerr = krb5_unparse_name(kcontext, client, &client_princ);
|
|
||||||
+ if (kerr)
|
|
||||||
+ goto end;
|
|
||||||
+
|
|
||||||
+ kerr = krb5_unparse_name(kcontext, target, &target_princ);
|
|
||||||
+ if (kerr)
|
|
||||||
+ goto end;
|
|
||||||
|
|
||||||
/* the default is that we fail */
|
|
||||||
- kerr = ENOENT;
|
|
||||||
+ kerr = KRB5KDC_ERR_BADOPTION;
|
|
||||||
|
|
||||||
- while (lentry) {
|
|
||||||
+ for (rule = ldap_first_entry(ipactx->lcontext, ldap_acl);
|
|
||||||
+ rule;
|
|
||||||
+ rule = ldap_next_entry(ipactx->lcontext, rule))
|
|
||||||
+ {
|
|
||||||
/* both client and target must be found in the same ACI */
|
|
||||||
client_missing = true;
|
|
||||||
client_found = false;
|
|
||||||
target_found = false;
|
|
||||||
|
|
||||||
- ret = ipadb_ldap_deref_results(ipactx->lcontext, lentry,
|
|
||||||
- &deref_results);
|
|
||||||
- switch (ret) {
|
|
||||||
+ lerr = ipadb_ldap_deref_results(ipactx->lcontext, rule, &acis);
|
|
||||||
+ switch (lerr) {
|
|
||||||
case 0:
|
|
||||||
- for (dres = deref_results; dres; dres = dres->next) {
|
|
||||||
- nrules++;
|
|
||||||
- if (is_constraint_delegation) {
|
|
||||||
- /*
|
|
||||||
- Microsoft revised the S4U2Proxy rules for forwardable
|
|
||||||
- tickets. All S4U2Proxy operations require forwardable
|
|
||||||
- evidence tickets, but S4U2Self should issue a
|
|
||||||
- forwardable ticket if the requesting service has no
|
|
||||||
- ok-to-auth-as-delegate bit but also no constrained
|
|
||||||
- delegation privileges for traditional S4U2Proxy.
|
|
||||||
- Implement these rules, extending the
|
|
||||||
- check_allowed_to_delegate() DAL method so that the KDC
|
|
||||||
- can ask if a principal has any delegation privileges.
|
|
||||||
-
|
|
||||||
- Since target principal is NULL and client principal is
|
|
||||||
- NULL in this case, we simply calculate number of rules associated
|
|
||||||
- with the server principal to decide whether to deny forwardable bit
|
|
||||||
- */
|
|
||||||
- continue;
|
|
||||||
- }
|
|
||||||
- if (client_found == false &&
|
|
||||||
- strcasecmp(dres->derefAttr, "ipaAllowToImpersonate") == 0) {
|
|
||||||
+ for (aci = acis; aci; aci = aci->next) {
|
|
||||||
+ if (!client_found &&
|
|
||||||
+ 0 == strcasecmp(aci->derefAttr, "ipaAllowToImpersonate"))
|
|
||||||
+ {
|
|
||||||
/* NOTE: client_missing is used to signal that the
|
|
||||||
* attribute was completely missing. This signals that
|
|
||||||
* ANY client is allowed to be impersonated.
|
|
||||||
* This logic is valid only for clients, not for targets */
|
|
||||||
client_missing = false;
|
|
||||||
- client_found = ipadb_match_member(client_princ, dres);
|
|
||||||
+ client_found = ipadb_match_member(client_princ, aci);
|
|
||||||
}
|
|
||||||
- if (target_found == false &&
|
|
||||||
- strcasecmp(dres->derefAttr, "ipaAllowedTarget") == 0) {
|
|
||||||
- target_found = ipadb_match_member(target_princ, dres);
|
|
||||||
+ if (!target_found &&
|
|
||||||
+ 0 == strcasecmp(aci->derefAttr, "ipaAllowedTarget"))
|
|
||||||
+ {
|
|
||||||
+ target_found = ipadb_match_member(target_princ, aci);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
- ldap_derefresponse_free(deref_results);
|
|
||||||
+ ldap_derefresponse_free(acis);
|
|
||||||
break;
|
|
||||||
case ENOENT:
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
- kerr = ret;
|
|
||||||
- goto done;
|
|
||||||
+ kerr = lerr;
|
|
||||||
+ goto end;
|
|
||||||
}
|
|
||||||
|
|
||||||
- if ((client_found == true || client_missing == true) &&
|
|
||||||
- target_found == true) {
|
|
||||||
+ if ((client_found || client_missing) && target_found) {
|
|
||||||
kerr = 0;
|
|
||||||
- goto done;
|
|
||||||
+ goto end;
|
|
||||||
}
|
|
||||||
-
|
|
||||||
- lentry = ldap_next_entry(ipactx->lcontext, lentry);
|
|
||||||
- }
|
|
||||||
-
|
|
||||||
- if (nrules > 0) {
|
|
||||||
- kerr = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
-done:
|
|
||||||
+end:
|
|
||||||
krb5_free_unparsed_name(kcontext, client_princ);
|
|
||||||
krb5_free_unparsed_name(kcontext, target_princ);
|
|
||||||
return kerr;
|
|
||||||
@@ -223,7 +213,7 @@ krb5_error_code ipadb_check_allowed_to_delegate(krb5_context kcontext,
|
|
||||||
char *srv_principal = NULL;
|
|
||||||
krb5_db_entry *proxy_entry = NULL;
|
|
||||||
struct ipadb_e_data *ied_server, *ied_proxy;
|
|
||||||
- LDAPMessage *res = NULL;
|
|
||||||
+ LDAPMessage *ldap_gcd_acl = NULL;
|
|
||||||
|
|
||||||
if (proxy != NULL) {
|
|
||||||
/* Handle the case where server == proxy, this is allowed in S4U */
|
|
||||||
@@ -261,26 +251,53 @@ krb5_error_code ipadb_check_allowed_to_delegate(krb5_context kcontext,
|
|
||||||
goto done;
|
|
||||||
}
|
|
||||||
|
|
||||||
- kerr = ipadb_get_delegation_acl(kcontext, srv_principal, &res);
|
|
||||||
+ /* Load general constrained delegation rules */
|
|
||||||
+ kerr = ipadb_get_delegation_acl(kcontext, srv_principal, &ldap_gcd_acl);
|
|
||||||
if (kerr) {
|
|
||||||
goto done;
|
|
||||||
}
|
|
||||||
|
|
||||||
- kerr = ipadb_match_acl(kcontext, res, client, proxy);
|
|
||||||
- if (kerr) {
|
|
||||||
- goto done;
|
|
||||||
+#if KRB5_KDB_DAL_MAJOR_VERSION >= 9
|
|
||||||
+ /*
|
|
||||||
+ * Microsoft revised the S4U2Proxy rules for forwardable tickets. All
|
|
||||||
+ * S4U2Proxy operations require forwardable evidence tickets, but
|
|
||||||
+ * S4U2Self should issue a forwardable ticket if the requesting service
|
|
||||||
+ * has no ok-to-auth-as-delegate bit but also no constrained delegation
|
|
||||||
+ * privileges for traditional S4U2Proxy. Implement these rules,
|
|
||||||
+ * extending the check_allowed_to_delegate() DAL method so that the KDC
|
|
||||||
+ * can ask if a principal has any delegation privileges.
|
|
||||||
+ *
|
|
||||||
+ * If target service principal is NULL, and the impersonating service has
|
|
||||||
+ * at least one GCD rule, then succeed.
|
|
||||||
+ */
|
|
||||||
+ if (!proxy) {
|
|
||||||
+ bool has_gcd_rules;
|
|
||||||
+
|
|
||||||
+ kerr = ipadb_has_acl(kcontext, ldap_gcd_acl, &has_gcd_rules);
|
|
||||||
+ if (!kerr)
|
|
||||||
+ kerr = has_gcd_rules ? 0 : KRB5KDC_ERR_BADOPTION;
|
|
||||||
+ } else if (client) {
|
|
||||||
+#else
|
|
||||||
+ if (client && proxy) {
|
|
||||||
+#endif
|
|
||||||
+ kerr = ipadb_match_acl(kcontext, ldap_gcd_acl, client, proxy);
|
|
||||||
+ } else {
|
|
||||||
+ /* client and/or proxy is missing */
|
|
||||||
+ kerr = KRB5KDC_ERR_BADOPTION;
|
|
||||||
}
|
|
||||||
+ if (kerr)
|
|
||||||
+ goto done;
|
|
||||||
|
|
||||||
done:
|
|
||||||
if (kerr) {
|
|
||||||
-#if KRB5_KDB_DAL_MAJOR_VERSION < 9
|
|
||||||
- kerr = KRB5KDC_ERR_POLICY;
|
|
||||||
-#else
|
|
||||||
+#if KRB5_KDB_DAL_MAJOR_VERSION >= 9
|
|
||||||
kerr = KRB5KDC_ERR_BADOPTION;
|
|
||||||
+#else
|
|
||||||
+ kerr = KRB5KDC_ERR_POLICY;
|
|
||||||
#endif
|
|
||||||
}
|
|
||||||
ipadb_free_principal(kcontext, proxy_entry);
|
|
||||||
krb5_free_unparsed_name(kcontext, srv_principal);
|
|
||||||
- ldap_msgfree(res);
|
|
||||||
+ ldap_msgfree(ldap_gcd_acl);
|
|
||||||
return kerr;
|
|
||||||
}
|
|
||||||
--
|
|
||||||
2.44.0
|
|
||||||
|
|
@ -1,615 +0,0 @@
|
|||||||
From 542e12325afc2f64298f90296760235bfdcef04a Mon Sep 17 00:00:00 2001
|
|
||||||
From: Julien Rische <jrische@redhat.com>
|
|
||||||
Date: Mon, 25 Mar 2024 18:25:52 +0200
|
|
||||||
Subject: [PATCH] kdb: apply combinatorial logic for ticket flags
|
|
||||||
|
|
||||||
The initial design for ticket flags was implementing this logic:
|
|
||||||
* If a ticket policy is defined for the principal entry, use flags from
|
|
||||||
this policy if they are set. Otherwise, use default ticket flags.
|
|
||||||
* If no ticket policy is defined for the principal entry, but there is a
|
|
||||||
global one, use flags from the global ticket policy if they are set.
|
|
||||||
Otherwise, use default ticket flags.
|
|
||||||
* If no policy (principal nor global) is defined, use default ticket
|
|
||||||
flags.
|
|
||||||
|
|
||||||
However, this logic was broken by a1165ffb which introduced creation of
|
|
||||||
a principal-level ticket policy in case the ticket flag set is modified.
|
|
||||||
This was typically the case for the -allow_tix flag, which was set
|
|
||||||
virtually by the KDB driver when a user was locked until they initialize
|
|
||||||
their password on first kinit pre-authentication.
|
|
||||||
|
|
||||||
This was causing multiple issues, which are mitigated by the new
|
|
||||||
approach:
|
|
||||||
|
|
||||||
Now flags from each level are combined together. There flags like
|
|
||||||
+requires_preauth which are set systematically by the KDB diver, as
|
|
||||||
well as -allow_tix which is set based on the value of "nsAccountLock".
|
|
||||||
This commit also adds the implicit -allow_svr ticket flag for user
|
|
||||||
principals to protect users against Kerberoast-type attacks. None of
|
|
||||||
these flags are stored in the LDAP database, they are hard-coded in the
|
|
||||||
KDB driver.
|
|
||||||
|
|
||||||
In addition to these "virtual" ticket flags, flags from both global and
|
|
||||||
principal ticket policies are applied (if these policies exist).
|
|
||||||
|
|
||||||
Principal ticket policies are not supported for hosts and services, but
|
|
||||||
this is only an HTTP API limitation. The "krbTicketPolicyAux" object
|
|
||||||
class is supported for all account types. This is required for ticket
|
|
||||||
flags like +ok_to_auth_as_delegate. Such flags can be set using "ipa
|
|
||||||
host-mod" and "ipa serivce-mod", or using kadmin's "modprinc".
|
|
||||||
|
|
||||||
It is possible to ignore flags from the global ticket policy or default
|
|
||||||
flags like -allow_svr for a user principal by setting the
|
|
||||||
"final_user_tkt_flags" string attribute to "true" in kadmin. In this
|
|
||||||
case, any ticket flag can be configured in the principal ticket policy,
|
|
||||||
except requires_preauth and allow_tix.
|
|
||||||
|
|
||||||
When in IPA setup mode (using the "ipa-setup-override-restrictions" KDB
|
|
||||||
argument), all the system described above is disabled and ticket flags
|
|
||||||
are written in the principal ticket policy as they are provided. This is
|
|
||||||
required to initialize the Kerberos LDAP container during IPA server
|
|
||||||
installation.
|
|
||||||
|
|
||||||
This fixes CVE-2024-3183
|
|
||||||
|
|
||||||
Signed-off-by: Julien Rische <jrische@redhat.com>
|
|
||||||
---
|
|
||||||
daemons/ipa-kdb/ipa_kdb.h | 43 ++++
|
|
||||||
daemons/ipa-kdb/ipa_kdb_principals.c | 353 +++++++++++++++++++++++----
|
|
||||||
util/ipa_krb5.c | 18 ++
|
|
||||||
util/ipa_krb5.h | 4 +
|
|
||||||
4 files changed, 365 insertions(+), 53 deletions(-)
|
|
||||||
|
|
||||||
diff --git a/daemons/ipa-kdb/ipa_kdb.h b/daemons/ipa-kdb/ipa_kdb.h
|
|
||||||
index 7baf4697f..85cabe142 100644
|
|
||||||
--- a/daemons/ipa-kdb/ipa_kdb.h
|
|
||||||
+++ b/daemons/ipa-kdb/ipa_kdb.h
|
|
||||||
@@ -94,6 +94,34 @@
|
|
||||||
#define IPA_KRB_AUTHZ_DATA_ATTR "ipaKrbAuthzData"
|
|
||||||
#define IPA_USER_AUTH_TYPE "ipaUserAuthType"
|
|
||||||
|
|
||||||
+/* Virtual managed ticket flags like "-allow_tix", are always controlled by the
|
|
||||||
+ * "nsAccountLock" attribute, such flags should never be set in the database.
|
|
||||||
+ * The following expression combine all of them, and is used to filter them
|
|
||||||
+ * out. */
|
|
||||||
+#define IPA_KDB_TKTFLAGS_VIRTUAL_MANAGED_ALL (KRB5_KDB_DISALLOW_ALL_TIX)
|
|
||||||
+
|
|
||||||
+/* Virtual static ticket flags are hard-coded in the KDB driver. */
|
|
||||||
+/* Virtual static mandatory flags are set systematically and implicitly for all
|
|
||||||
+ * principals. They are filtered out from database ticket flags updates.
|
|
||||||
+ * (However, "KRB5_KDB_REQUIRES_PRE_AUTH" can still be unset by the
|
|
||||||
+ * "KDC:Disable Default Preauth for SPNs" global setting) */
|
|
||||||
+#define IPA_KDB_TKTFLAGS_VIRTUAL_STATIC_MANDATORY (KRB5_KDB_REQUIRES_PRE_AUTH)
|
|
||||||
+/* Virtual static default ticket flags are implicitly set for user and non-user
|
|
||||||
+ * (SPN) principals, and not stored in the database.
|
|
||||||
+ * (Except if the "IPA_KDB_STRATTR_FINAL_TKTFLAGS" string attribute is "true"
|
|
||||||
+ * the principal) */
|
|
||||||
+/* Virtual static default user ticket flags are set for users only. The
|
|
||||||
+ * "-allow_svr" flag is set to protect them from CVE-2024-3183. */
|
|
||||||
+#define IPA_KDB_TKTFLAGS_VIRTUAL_STATIC_DEFAULTS_USER (KRB5_KDB_DISALLOW_SVR)
|
|
||||||
+#define IPA_KDB_TKTFLAGS_VIRTUAL_STATIC_DEFAULTS_SPN (0)
|
|
||||||
+
|
|
||||||
+/* If this string attribute is set to "true", then only the virtual managed and
|
|
||||||
+ * virtual static mandatory ticket flags are applied and filtered out from
|
|
||||||
+ * database read and write operations for the concerned user principal.
|
|
||||||
+ * Configurable principal ticket flags are applied, but not the configurable
|
|
||||||
+ * global ticket policy flags. */
|
|
||||||
+#define IPA_KDB_STRATTR_FINAL_USER_TKTFLAGS "final_user_tkt_flags"
|
|
||||||
+
|
|
||||||
struct ipadb_mspac;
|
|
||||||
struct dom_sid;
|
|
||||||
|
|
||||||
@@ -178,6 +206,21 @@ struct ipadb_e_data {
|
|
||||||
struct dom_sid *sid;
|
|
||||||
};
|
|
||||||
|
|
||||||
+inline static krb5_error_code
|
|
||||||
+ipadb_get_edata(krb5_db_entry *entry, struct ipadb_e_data **ied)
|
|
||||||
+{
|
|
||||||
+ struct ipadb_e_data *in_ied;
|
|
||||||
+
|
|
||||||
+ in_ied = (struct ipadb_e_data *)entry->e_data;
|
|
||||||
+ if (!in_ied || in_ied->magic != IPA_E_DATA_MAGIC)
|
|
||||||
+ return EINVAL;
|
|
||||||
+
|
|
||||||
+ if (ied)
|
|
||||||
+ *ied = in_ied;
|
|
||||||
+
|
|
||||||
+ return 0;
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
struct ipadb_context *ipadb_get_context(krb5_context kcontext);
|
|
||||||
int ipadb_get_connection(struct ipadb_context *ipactx);
|
|
||||||
|
|
||||||
diff --git a/daemons/ipa-kdb/ipa_kdb_principals.c b/daemons/ipa-kdb/ipa_kdb_principals.c
|
|
||||||
index 07cc87746..6eb542d4f 100644
|
|
||||||
--- a/daemons/ipa-kdb/ipa_kdb_principals.c
|
|
||||||
+++ b/daemons/ipa-kdb/ipa_kdb_principals.c
|
|
||||||
@@ -706,9 +706,12 @@ static krb5_error_code ipadb_parse_ldap_entry(krb5_context kcontext,
|
|
||||||
"krbTicketFlags", &result);
|
|
||||||
if (ret == 0) {
|
|
||||||
entry->attributes = result;
|
|
||||||
- } else {
|
|
||||||
- *polmask |= TKTFLAGS_BIT;
|
|
||||||
}
|
|
||||||
+ /* Since principal, global policy, and virtual ticket flags are combined,
|
|
||||||
+ * they must always be resolved, except if we are in IPA setup mode (because
|
|
||||||
+ * ticket policies and virtual ticket flags are irrelevant in this case). */
|
|
||||||
+ if (!ipactx->override_restrictions)
|
|
||||||
+ *polmask |= TKTFLAGS_BIT;
|
|
||||||
|
|
||||||
ret = ipadb_ldap_attr_to_int(lcontext, lentry,
|
|
||||||
"krbMaxTicketLife", &result);
|
|
||||||
@@ -912,7 +915,12 @@ static krb5_error_code ipadb_parse_ldap_entry(krb5_context kcontext,
|
|
||||||
goto done;
|
|
||||||
}
|
|
||||||
if (ret == 0) {
|
|
||||||
- ied->ipa_user = true;
|
|
||||||
+ if (1 == krb5_princ_size(kcontext, entry->princ)) {
|
|
||||||
+ /* A principal must be a POSIX account AND have only one element to
|
|
||||||
+ * be considered a user (this is to filter out CIFS principals). */
|
|
||||||
+ ied->ipa_user = true;
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
ret = ipadb_ldap_attr_to_str(lcontext, lentry,
|
|
||||||
"uid", &uidstring);
|
|
||||||
if (ret != 0 && ret != ENOENT) {
|
|
||||||
@@ -1251,23 +1259,150 @@ done:
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
-static krb5_flags maybe_require_preauth(struct ipadb_context *ipactx,
|
|
||||||
- krb5_db_entry *entry)
|
|
||||||
+static krb5_error_code
|
|
||||||
+are_final_tktflags(struct ipadb_context *ipactx, krb5_db_entry *entry,
|
|
||||||
+ bool *final_tktflags)
|
|
||||||
{
|
|
||||||
- const struct ipadb_global_config *config;
|
|
||||||
+ krb5_error_code kerr;
|
|
||||||
struct ipadb_e_data *ied;
|
|
||||||
+ char *str = NULL;
|
|
||||||
+ bool in_final_tktflags = false;
|
|
||||||
|
|
||||||
- config = ipadb_get_global_config(ipactx);
|
|
||||||
- if (config && config->disable_preauth_for_spns) {
|
|
||||||
- ied = (struct ipadb_e_data *)entry->e_data;
|
|
||||||
- if (ied && ied->ipa_user != true) {
|
|
||||||
- /* not a user, assume SPN */
|
|
||||||
- return 0;
|
|
||||||
- }
|
|
||||||
+ kerr = ipadb_get_edata(entry, &ied);
|
|
||||||
+ if (kerr)
|
|
||||||
+ goto end;
|
|
||||||
+
|
|
||||||
+ if (!ied->ipa_user) {
|
|
||||||
+ kerr = 0;
|
|
||||||
+ goto end;
|
|
||||||
}
|
|
||||||
|
|
||||||
- /* By default require preauth for all principals */
|
|
||||||
- return KRB5_KDB_REQUIRES_PRE_AUTH;
|
|
||||||
+ kerr = krb5_dbe_get_string(ipactx->kcontext, entry,
|
|
||||||
+ IPA_KDB_STRATTR_FINAL_USER_TKTFLAGS, &str);
|
|
||||||
+ if (kerr)
|
|
||||||
+ goto end;
|
|
||||||
+
|
|
||||||
+ in_final_tktflags = str && ipa_krb5_parse_bool(str);
|
|
||||||
+
|
|
||||||
+end:
|
|
||||||
+ if (final_tktflags)
|
|
||||||
+ *final_tktflags = in_final_tktflags;
|
|
||||||
+
|
|
||||||
+ krb5_dbe_free_string(ipactx->kcontext, str);
|
|
||||||
+ return kerr;
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
+static krb5_error_code
|
|
||||||
+add_virtual_static_tktflags(struct ipadb_context *ipactx, krb5_db_entry *entry,
|
|
||||||
+ krb5_flags *tktflags)
|
|
||||||
+{
|
|
||||||
+ krb5_error_code kerr;
|
|
||||||
+ krb5_flags vsflg;
|
|
||||||
+ bool final_tktflags;
|
|
||||||
+ const struct ipadb_global_config *gcfg;
|
|
||||||
+ struct ipadb_e_data *ied;
|
|
||||||
+
|
|
||||||
+ vsflg = IPA_KDB_TKTFLAGS_VIRTUAL_STATIC_MANDATORY;
|
|
||||||
+
|
|
||||||
+ kerr = ipadb_get_edata(entry, &ied);
|
|
||||||
+ if (kerr)
|
|
||||||
+ goto end;
|
|
||||||
+
|
|
||||||
+ kerr = are_final_tktflags(ipactx, entry, &final_tktflags);
|
|
||||||
+ if (kerr)
|
|
||||||
+ goto end;
|
|
||||||
+
|
|
||||||
+ /* In practice, principal ticket flags cannot be final for SPNs. */
|
|
||||||
+ if (!final_tktflags)
|
|
||||||
+ vsflg |= ied->ipa_user ? IPA_KDB_TKTFLAGS_VIRTUAL_STATIC_DEFAULTS_USER
|
|
||||||
+ : IPA_KDB_TKTFLAGS_VIRTUAL_STATIC_DEFAULTS_SPN;
|
|
||||||
+
|
|
||||||
+ if (!ied->ipa_user) {
|
|
||||||
+ gcfg = ipadb_get_global_config(ipactx);
|
|
||||||
+ if (gcfg && gcfg->disable_preauth_for_spns)
|
|
||||||
+ vsflg &= ~KRB5_KDB_REQUIRES_PRE_AUTH;
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ if (tktflags)
|
|
||||||
+ *tktflags |= vsflg;
|
|
||||||
+
|
|
||||||
+end:
|
|
||||||
+ return kerr;
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
+static krb5_error_code
|
|
||||||
+get_virtual_static_tktflags_mask(struct ipadb_context *ipactx,
|
|
||||||
+ krb5_db_entry *entry, krb5_flags *mask)
|
|
||||||
+{
|
|
||||||
+ krb5_error_code kerr;
|
|
||||||
+ krb5_flags flags = IPA_KDB_TKTFLAGS_VIRTUAL_MANAGED_ALL;
|
|
||||||
+
|
|
||||||
+ kerr = add_virtual_static_tktflags(ipactx, entry, &flags);
|
|
||||||
+ if (kerr)
|
|
||||||
+ goto end;
|
|
||||||
+
|
|
||||||
+ if (mask)
|
|
||||||
+ *mask = ~flags;
|
|
||||||
+
|
|
||||||
+ kerr = 0;
|
|
||||||
+
|
|
||||||
+end:
|
|
||||||
+ return kerr;
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
+/* Add ticket flags from the global ticket policy if it exists, otherwise
|
|
||||||
+ * succeed. If the global ticket policy is set, the "exists" parameter is set to
|
|
||||||
+ * true. */
|
|
||||||
+static krb5_error_code
|
|
||||||
+add_global_ticket_policy_flags(struct ipadb_context *ipactx,
|
|
||||||
+ bool *gtpol_exists, krb5_flags *tktflags)
|
|
||||||
+{
|
|
||||||
+ krb5_error_code kerr;
|
|
||||||
+ char *policy_dn;
|
|
||||||
+ char *tktflags_attr[] = { "krbticketflags", NULL };
|
|
||||||
+ LDAPMessage *res = NULL, *first;
|
|
||||||
+ int ec, ldap_tktflags;
|
|
||||||
+ bool in_gtpol_exists = false;
|
|
||||||
+
|
|
||||||
+ ec = asprintf(&policy_dn, "cn=%s,cn=kerberos,%s", ipactx->realm,
|
|
||||||
+ ipactx->base);
|
|
||||||
+ if (-1 == ec) {
|
|
||||||
+ kerr = ENOMEM;
|
|
||||||
+ goto end;
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ kerr = ipadb_simple_search(ipactx, policy_dn, LDAP_SCOPE_BASE,
|
|
||||||
+ "(objectclass=krbticketpolicyaux)",
|
|
||||||
+ tktflags_attr, &res);
|
|
||||||
+ if (kerr) {
|
|
||||||
+ if (KRB5_KDB_NOENTRY == kerr)
|
|
||||||
+ kerr = 0;
|
|
||||||
+ goto end;
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ first = ldap_first_entry(ipactx->lcontext, res);
|
|
||||||
+ if (!first) {
|
|
||||||
+ kerr = 0;
|
|
||||||
+ goto end;
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ in_gtpol_exists = true;
|
|
||||||
+
|
|
||||||
+ ec = ipadb_ldap_attr_to_int(ipactx->lcontext, first, "krbticketflags",
|
|
||||||
+ &ldap_tktflags);
|
|
||||||
+ if (0 == ec && tktflags) {
|
|
||||||
+ *tktflags |= (krb5_flags)ldap_tktflags;
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ kerr = 0;
|
|
||||||
+
|
|
||||||
+end:
|
|
||||||
+ if (gtpol_exists)
|
|
||||||
+ *gtpol_exists = in_gtpol_exists;
|
|
||||||
+
|
|
||||||
+ ldap_msgfree(res);
|
|
||||||
+ free(policy_dn);
|
|
||||||
+ return kerr;
|
|
||||||
}
|
|
||||||
|
|
||||||
static krb5_error_code ipadb_fetch_tktpolicy(krb5_context kcontext,
|
|
||||||
@@ -1280,6 +1415,7 @@ static krb5_error_code ipadb_fetch_tktpolicy(krb5_context kcontext,
|
|
||||||
char *policy_dn = NULL;
|
|
||||||
LDAPMessage *res = NULL;
|
|
||||||
LDAPMessage *first;
|
|
||||||
+ bool final_tktflags, has_local_tktpolicy = true;
|
|
||||||
int result;
|
|
||||||
int ret;
|
|
||||||
|
|
||||||
@@ -1288,12 +1424,18 @@ static krb5_error_code ipadb_fetch_tktpolicy(krb5_context kcontext,
|
|
||||||
return KRB5_KDB_DBNOTINITED;
|
|
||||||
}
|
|
||||||
|
|
||||||
+ kerr = are_final_tktflags(ipactx, entry, &final_tktflags);
|
|
||||||
+ if (kerr)
|
|
||||||
+ goto done;
|
|
||||||
+
|
|
||||||
ret = ipadb_ldap_attr_to_str(ipactx->lcontext, lentry,
|
|
||||||
"krbticketpolicyreference", &policy_dn);
|
|
||||||
switch (ret) {
|
|
||||||
case 0:
|
|
||||||
break;
|
|
||||||
case ENOENT:
|
|
||||||
+ /* If no principal ticket policy, fallback to the global one. */
|
|
||||||
+ has_local_tktpolicy = false;
|
|
||||||
ret = asprintf(&policy_dn, "cn=%s,cn=kerberos,%s",
|
|
||||||
ipactx->realm, ipactx->base);
|
|
||||||
if (ret == -1) {
|
|
||||||
@@ -1337,12 +1479,13 @@ static krb5_error_code ipadb_fetch_tktpolicy(krb5_context kcontext,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (polmask & TKTFLAGS_BIT) {
|
|
||||||
- ret = ipadb_ldap_attr_to_int(ipactx->lcontext, first,
|
|
||||||
- "krbticketflags", &result);
|
|
||||||
- if (ret == 0) {
|
|
||||||
- entry->attributes |= result;
|
|
||||||
- } else {
|
|
||||||
- entry->attributes |= maybe_require_preauth(ipactx, entry);
|
|
||||||
+ /* If global ticket policy is being applied, set flags only if
|
|
||||||
+ * user principal ticket flags are not final. */
|
|
||||||
+ if (has_local_tktpolicy || !final_tktflags) {
|
|
||||||
+ ret = ipadb_ldap_attr_to_int(ipactx->lcontext, first,
|
|
||||||
+ "krbticketflags", &result);
|
|
||||||
+ if (ret == 0)
|
|
||||||
+ entry->attributes |= result;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@@ -1366,13 +1509,27 @@ static krb5_error_code ipadb_fetch_tktpolicy(krb5_context kcontext,
|
|
||||||
if (polmask & MAXRENEWABLEAGE_BIT) {
|
|
||||||
entry->max_renewable_life = 604800;
|
|
||||||
}
|
|
||||||
- if (polmask & TKTFLAGS_BIT) {
|
|
||||||
- entry->attributes |= maybe_require_preauth(ipactx, entry);
|
|
||||||
- }
|
|
||||||
|
|
||||||
kerr = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
+ if (polmask & TKTFLAGS_BIT) {
|
|
||||||
+ /* If the principal ticket flags were applied, then flags from the
|
|
||||||
+ * global ticket policy has to be applied atop of them if user principal
|
|
||||||
+ * ticket flags are not final. */
|
|
||||||
+ if (has_local_tktpolicy && !final_tktflags) {
|
|
||||||
+ kerr = add_global_ticket_policy_flags(ipactx, NULL,
|
|
||||||
+ &entry->attributes);
|
|
||||||
+ if (kerr)
|
|
||||||
+ goto done;
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ /* Virtual static ticket flags are set regardless of database content */
|
|
||||||
+ kerr = add_virtual_static_tktflags(ipactx, entry, &entry->attributes);
|
|
||||||
+ if (kerr)
|
|
||||||
+ goto done;
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
done:
|
|
||||||
ldap_msgfree(res);
|
|
||||||
free(policy_dn);
|
|
||||||
@@ -1864,6 +2021,36 @@ static void ipadb_mods_free_tip(struct ipadb_mods *imods)
|
|
||||||
imods->tip--;
|
|
||||||
}
|
|
||||||
|
|
||||||
+/* Use LDAP REPLACE operation to remove an attribute.
|
|
||||||
+ * Contrary to the DELETE operation, it will not fail if the attribute does not
|
|
||||||
+ * exist. */
|
|
||||||
+static krb5_error_code
|
|
||||||
+ipadb_ldap_replace_remove(struct ipadb_mods *imods, char *attribute)
|
|
||||||
+{
|
|
||||||
+ krb5_error_code kerr;
|
|
||||||
+ LDAPMod *m = NULL;
|
|
||||||
+
|
|
||||||
+ kerr = ipadb_mods_new(imods, &m);
|
|
||||||
+ if (kerr)
|
|
||||||
+ return kerr;
|
|
||||||
+
|
|
||||||
+ m->mod_op = LDAP_MOD_REPLACE;
|
|
||||||
+ m->mod_type = strdup(attribute);
|
|
||||||
+ if (!m->mod_type) {
|
|
||||||
+ kerr = ENOMEM;
|
|
||||||
+ goto end;
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ m->mod_values = NULL;
|
|
||||||
+
|
|
||||||
+ kerr = 0;
|
|
||||||
+
|
|
||||||
+end:
|
|
||||||
+ if (kerr)
|
|
||||||
+ ipadb_mods_free_tip(imods);
|
|
||||||
+ return kerr;
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
static krb5_error_code ipadb_get_ldap_mod_str(struct ipadb_mods *imods,
|
|
||||||
char *attribute, char *value,
|
|
||||||
int mod_op)
|
|
||||||
@@ -2275,6 +2462,93 @@ static krb5_error_code ipadb_get_ldap_mod_auth_ind(krb5_context kcontext,
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
|
|
||||||
+static krb5_error_code
|
|
||||||
+update_tktflags(krb5_context kcontext, struct ipadb_mods *imods,
|
|
||||||
+ krb5_db_entry *entry, int mod_op)
|
|
||||||
+{
|
|
||||||
+ krb5_error_code kerr;
|
|
||||||
+ struct ipadb_context *ipactx;
|
|
||||||
+ struct ipadb_e_data *ied;
|
|
||||||
+ bool final_tktflags;
|
|
||||||
+ krb5_flags tktflags_mask;
|
|
||||||
+ int tktflags;
|
|
||||||
+
|
|
||||||
+ ipactx = ipadb_get_context(kcontext);
|
|
||||||
+ if (!ipactx) {
|
|
||||||
+ kerr = KRB5_KDB_DBNOTINITED;
|
|
||||||
+ goto end;
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ if (ipactx->override_restrictions) {
|
|
||||||
+ /* In IPA setup mode, IPA edata might not be available. In this mode,
|
|
||||||
+ * ticket flags are written as they are provided. */
|
|
||||||
+ tktflags = (int)entry->attributes;
|
|
||||||
+ } else {
|
|
||||||
+ kerr = ipadb_get_edata(entry, &ied);
|
|
||||||
+ if (kerr)
|
|
||||||
+ goto end;
|
|
||||||
+
|
|
||||||
+ kerr = get_virtual_static_tktflags_mask(ipactx, entry, &tktflags_mask);
|
|
||||||
+ if (kerr)
|
|
||||||
+ goto end;
|
|
||||||
+
|
|
||||||
+ kerr = are_final_tktflags(ipactx, entry, &final_tktflags);
|
|
||||||
+ if (kerr)
|
|
||||||
+ goto end;
|
|
||||||
+
|
|
||||||
+ /* Flags from the global ticket policy are filtered out only if the user
|
|
||||||
+ * principal flags are not final. */
|
|
||||||
+ if (!final_tktflags) {
|
|
||||||
+ krb5_flags gbl_tktflags = 0;
|
|
||||||
+
|
|
||||||
+ kerr = add_global_ticket_policy_flags(ipactx, NULL, &gbl_tktflags);
|
|
||||||
+ if (kerr)
|
|
||||||
+ goto end;
|
|
||||||
+
|
|
||||||
+ tktflags_mask &= ~gbl_tktflags;
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ tktflags = (int)(entry->attributes & tktflags_mask);
|
|
||||||
+
|
|
||||||
+ if (LDAP_MOD_REPLACE == mod_op && ied && !ied->has_tktpolaux) {
|
|
||||||
+ if (0 == tktflags) {
|
|
||||||
+ /* No point initializing principal ticket policy if there are no
|
|
||||||
+ * flags left after filtering out virtual and global ticket
|
|
||||||
+ * policy ones. */
|
|
||||||
+ kerr = 0;
|
|
||||||
+ goto end;
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ /* if the object does not have the krbTicketPolicyAux class
|
|
||||||
+ * we need to add it or this will fail, only for modifications.
|
|
||||||
+ * We always add this objectclass by default when doing an add
|
|
||||||
+ * from scratch. */
|
|
||||||
+ kerr = ipadb_get_ldap_mod_str(imods, "objectclass",
|
|
||||||
+ "krbTicketPolicyAux", LDAP_MOD_ADD);
|
|
||||||
+ if (kerr)
|
|
||||||
+ goto end;
|
|
||||||
+ }
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ if (tktflags != 0) {
|
|
||||||
+ kerr = ipadb_get_ldap_mod_int(imods, "krbTicketFlags", tktflags,
|
|
||||||
+ mod_op);
|
|
||||||
+ if (kerr)
|
|
||||||
+ goto end;
|
|
||||||
+ } else if (LDAP_MOD_REPLACE == mod_op) {
|
|
||||||
+ /* If the principal is not being created, and there are no custom ticket
|
|
||||||
+ * flags to be set, remove the "krbTicketFlags" attribute. */
|
|
||||||
+ kerr = ipadb_ldap_replace_remove(imods, "krbTicketFlags");
|
|
||||||
+ if (kerr)
|
|
||||||
+ goto end;
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ kerr = 0;
|
|
||||||
+
|
|
||||||
+end:
|
|
||||||
+ return kerr;
|
|
||||||
+}
|
|
||||||
+
|
|
||||||
static krb5_error_code ipadb_entry_to_mods(krb5_context kcontext,
|
|
||||||
struct ipadb_mods *imods,
|
|
||||||
krb5_db_entry *entry,
|
|
||||||
@@ -2350,36 +2624,9 @@ static krb5_error_code ipadb_entry_to_mods(krb5_context kcontext,
|
|
||||||
|
|
||||||
/* KADM5_ATTRIBUTES */
|
|
||||||
if (entry->mask & KMASK_ATTRIBUTES) {
|
|
||||||
- /* if the object does not have the krbTicketPolicyAux class
|
|
||||||
- * we need to add it or this will fail, only for modifications.
|
|
||||||
- * We always add this objectclass by default when doing an add
|
|
||||||
- * from scratch. */
|
|
||||||
- if ((mod_op == LDAP_MOD_REPLACE) && entry->e_data) {
|
|
||||||
- struct ipadb_e_data *ied;
|
|
||||||
-
|
|
||||||
- ied = (struct ipadb_e_data *)entry->e_data;
|
|
||||||
- if (ied->magic != IPA_E_DATA_MAGIC) {
|
|
||||||
- kerr = EINVAL;
|
|
||||||
- goto done;
|
|
||||||
- }
|
|
||||||
-
|
|
||||||
- if (!ied->has_tktpolaux) {
|
|
||||||
- kerr = ipadb_get_ldap_mod_str(imods, "objectclass",
|
|
||||||
- "krbTicketPolicyAux",
|
|
||||||
- LDAP_MOD_ADD);
|
|
||||||
- if (kerr) {
|
|
||||||
- goto done;
|
|
||||||
- }
|
|
||||||
- }
|
|
||||||
- }
|
|
||||||
-
|
|
||||||
- kerr = ipadb_get_ldap_mod_int(imods,
|
|
||||||
- "krbTicketFlags",
|
|
||||||
- (int)entry->attributes,
|
|
||||||
- mod_op);
|
|
||||||
- if (kerr) {
|
|
||||||
+ kerr = update_tktflags(kcontext, imods, entry, mod_op);
|
|
||||||
+ if (kerr)
|
|
||||||
goto done;
|
|
||||||
- }
|
|
||||||
}
|
|
||||||
|
|
||||||
/* KADM5_MAX_LIFE */
|
|
||||||
diff --git a/util/ipa_krb5.c b/util/ipa_krb5.c
|
|
||||||
index 1ba6d25ee..2e663c506 100644
|
|
||||||
--- a/util/ipa_krb5.c
|
|
||||||
+++ b/util/ipa_krb5.c
|
|
||||||
@@ -38,6 +38,12 @@ const char *ipapwd_password_max_len_errmsg = \
|
|
||||||
TOSTR(IPAPWD_PASSWORD_MAX_LEN) \
|
|
||||||
" chars)!";
|
|
||||||
|
|
||||||
+/* Case-insensitive string values to by parsed as boolean true */
|
|
||||||
+static const char *const conf_yes[] = {
|
|
||||||
+ "y", "yes", "true", "t", "1", "on",
|
|
||||||
+ NULL,
|
|
||||||
+};
|
|
||||||
+
|
|
||||||
/* Salt types */
|
|
||||||
#define KRB5P_SALT_SIZE 16
|
|
||||||
|
|
||||||
@@ -1237,3 +1243,15 @@ done:
|
|
||||||
}
|
|
||||||
return ret;
|
|
||||||
}
|
|
||||||
+
|
|
||||||
+bool ipa_krb5_parse_bool(const char *str)
|
|
||||||
+{
|
|
||||||
+ const char *const *p;
|
|
||||||
+
|
|
||||||
+ for (p = conf_yes; *p; p++) {
|
|
||||||
+ if (!strcasecmp(*p, str))
|
|
||||||
+ return true;
|
|
||||||
+ }
|
|
||||||
+
|
|
||||||
+ return false;
|
|
||||||
+}
|
|
||||||
diff --git a/util/ipa_krb5.h b/util/ipa_krb5.h
|
|
||||||
index 7d2ebae98..d0280940a 100644
|
|
||||||
--- a/util/ipa_krb5.h
|
|
||||||
+++ b/util/ipa_krb5.h
|
|
||||||
@@ -174,3 +174,7 @@ static inline bool
|
|
||||||
krb5_ts_after(krb5_timestamp a, krb5_timestamp b) {
|
|
||||||
return (uint32_t)a > (uint32_t)b;
|
|
||||||
}
|
|
||||||
+
|
|
||||||
+/* Implement boolean string parsing function from MIT krb5:
|
|
||||||
+ * src/lib/krb5/krb/libdef_parse.c:_krb5_conf_boolean() */
|
|
||||||
+bool ipa_krb5_parse_bool(const char *str);
|
|
||||||
--
|
|
||||||
2.45.1
|
|
||||||
|
|
@ -1,127 +0,0 @@
|
|||||||
diff --git a/ipaserver/plugins/user.py b/ipaserver/plugins/user.py
|
|
||||||
index 6f5e349..febc22f 100644
|
|
||||||
--- a/ipaserver/plugins/user.py
|
|
||||||
+++ b/ipaserver/plugins/user.py
|
|
||||||
@@ -144,8 +144,7 @@ PROTECTED_USERS = ('admin',)
|
|
||||||
def check_protected_member(user, protected_group_name=u'admins'):
|
|
||||||
'''
|
|
||||||
Ensure admin and the last enabled member of a protected group cannot
|
|
||||||
- be deleted or disabled by raising ProtectedEntryError or
|
|
||||||
- LastMemberError as appropriate.
|
|
||||||
+ be deleted.
|
|
||||||
'''
|
|
||||||
|
|
||||||
if user in PROTECTED_USERS:
|
|
||||||
@@ -155,6 +154,12 @@ def check_protected_member(user, protected_group_name=u'admins'):
|
|
||||||
reason=_("privileged user"),
|
|
||||||
)
|
|
||||||
|
|
||||||
+
|
|
||||||
+def check_last_member(user, protected_group_name=u'admins'):
|
|
||||||
+ '''
|
|
||||||
+ Ensure the last enabled member of a protected group cannot
|
|
||||||
+ be disabled.
|
|
||||||
+ '''
|
|
||||||
# Get all users in the protected group
|
|
||||||
result = api.Command.user_find(in_group=protected_group_name)
|
|
||||||
|
|
||||||
@@ -796,6 +801,7 @@ class user_del(baseuser_del):
|
|
||||||
# If the target entry is a Delete entry, skip the orphaning/removal
|
|
||||||
# of OTP tokens.
|
|
||||||
check_protected_member(keys[-1])
|
|
||||||
+ check_last_member(keys[-1])
|
|
||||||
|
|
||||||
preserve = options.get('preserve', False)
|
|
||||||
|
|
||||||
@@ -1128,7 +1134,7 @@ class user_disable(LDAPQuery):
|
|
||||||
def execute(self, *keys, **options):
|
|
||||||
ldap = self.obj.backend
|
|
||||||
|
|
||||||
- check_protected_member(keys[-1])
|
|
||||||
+ check_last_member(keys[-1])
|
|
||||||
|
|
||||||
dn, _oc = self.obj.get_either_dn(*keys, **options)
|
|
||||||
ldap.deactivate_entry(dn)
|
|
||||||
diff --git a/ipatests/test_integration/test_commands.py b/ipatests/test_integration/test_commands.py
|
|
||||||
index c0cb4d0..c2a55b8 100644
|
|
||||||
--- a/ipatests/test_integration/test_commands.py
|
|
||||||
+++ b/ipatests/test_integration/test_commands.py
|
|
||||||
@@ -1530,6 +1530,30 @@ class TestIPACommand(IntegrationTest):
|
|
||||||
|
|
||||||
assert 'Discovered server %s' % self.master.hostname in result
|
|
||||||
|
|
||||||
+ def test_delete_last_enabled_admin(self):
|
|
||||||
+ """
|
|
||||||
+ The admin user may be disabled. Don't allow all other
|
|
||||||
+ members of admins to be removed if the admin user is
|
|
||||||
+ disabled which would leave the install with no
|
|
||||||
+ usable admins users
|
|
||||||
+ """
|
|
||||||
+ user = 'adminuser2'
|
|
||||||
+ passwd = 'Secret123'
|
|
||||||
+ tasks.create_active_user(self.master, user, passwd)
|
|
||||||
+ tasks.kinit_admin(self.master)
|
|
||||||
+ self.master.run_command(['ipa', 'group-add-member', 'admins',
|
|
||||||
+ '--users', user])
|
|
||||||
+ tasks.kinit_user(self.master, user, passwd)
|
|
||||||
+ self.master.run_command(['ipa', 'user-disable', 'admin'])
|
|
||||||
+ result = self.master.run_command(
|
|
||||||
+ ['ipa', 'user-del', user],
|
|
||||||
+ raiseonerr=False
|
|
||||||
+ )
|
|
||||||
+ self.master.run_command(['ipa', 'user-enable', 'admin'])
|
|
||||||
+ tasks.kdestroy_all(self.master)
|
|
||||||
+ assert result.returncode == 1
|
|
||||||
+ assert 'cannot be deleted or disabled' in result.stderr_text
|
|
||||||
+
|
|
||||||
|
|
||||||
class TestIPACommandWithoutReplica(IntegrationTest):
|
|
||||||
"""
|
|
||||||
diff --git a/ipatests/test_xmlrpc/test_user_plugin.py b/ipatests/test_xmlrpc/test_user_plugin.py
|
|
||||||
index 3c58845..68c6c48 100644
|
|
||||||
--- a/ipatests/test_xmlrpc/test_user_plugin.py
|
|
||||||
+++ b/ipatests/test_xmlrpc/test_user_plugin.py
|
|
||||||
@@ -1045,8 +1045,8 @@ class TestAdmins(XMLRPC_test):
|
|
||||||
tracker = Tracker()
|
|
||||||
command = tracker.make_command('user_disable', admin1)
|
|
||||||
|
|
||||||
- with raises_exact(errors.ProtectedEntryError(label=u'user',
|
|
||||||
- key=admin1, reason='privileged user')):
|
|
||||||
+ with raises_exact(errors.LastMemberError(label=u'group',
|
|
||||||
+ key=admin1, container=admin_group)):
|
|
||||||
command()
|
|
||||||
|
|
||||||
def test_create_admin2(self, admin2):
|
|
||||||
@@ -1064,8 +1064,8 @@ class TestAdmins(XMLRPC_test):
|
|
||||||
admin2.disable()
|
|
||||||
tracker = Tracker()
|
|
||||||
|
|
||||||
- with raises_exact(errors.ProtectedEntryError(label=u'user',
|
|
||||||
- key=admin1, reason='privileged user')):
|
|
||||||
+ with raises_exact(errors.LastMemberError(label=u'group',
|
|
||||||
+ key=admin1, container=admin_group)):
|
|
||||||
tracker.run_command('user_disable', admin1)
|
|
||||||
admin2.delete()
|
|
||||||
|
|
||||||
diff --git a/ipatests/test_webui/test_user.py b/ipatests/test_webui/test_user.py
|
|
||||||
index a8a92d0..9083e50 100644
|
|
||||||
--- a/ipatests/test_webui/test_user.py
|
|
||||||
+++ b/ipatests/test_webui/test_user.py
|
|
||||||
@@ -50,6 +50,8 @@ INV_FIRSTNAME = ("invalid 'first': Leading and trailing spaces are "
|
|
||||||
FIELD_REQ = 'Required field'
|
|
||||||
ERR_INCLUDE = 'may only include letters, numbers, _, -, . and $'
|
|
||||||
ERR_MISMATCH = 'Passwords must match'
|
|
||||||
+ERR_ADMIN_DISABLE = ('admin cannot be deleted or disabled because '
|
|
||||||
+ 'it is the last member of group admins')
|
|
||||||
ERR_ADMIN_DEL = ('user admin cannot be deleted/modified: privileged user')
|
|
||||||
USR_EXIST = 'user with name "{}" already exists'
|
|
||||||
ENTRY_EXIST = 'This entry already exists'
|
|
||||||
@@ -546,7 +548,7 @@ class test_user(user_tasks):
|
|
||||||
self.select_record('admin')
|
|
||||||
self.facet_button_click('disable')
|
|
||||||
self.dialog_button_click('ok')
|
|
||||||
- self.assert_last_error_dialog(ERR_ADMIN_DEL, details=True)
|
|
||||||
+ self.assert_last_error_dialog(ERR_ADMIN_DISABLE, details=True)
|
|
||||||
self.dialog_button_click('ok')
|
|
||||||
self.assert_record('admin')
|
|
||||||
|
|
@ -1,13 +0,0 @@
|
|||||||
diff --git a/ipaserver/install/ipa_otptoken_import.py b/ipaserver/install/ipa_otptoken_import.py
|
|
||||||
index b3f9347..75e8680 100644
|
|
||||||
--- a/ipaserver/install/ipa_otptoken_import.py
|
|
||||||
+++ b/ipaserver/install/ipa_otptoken_import.py
|
|
||||||
@@ -539,7 +539,7 @@ class OTPTokenImport(admintool.AdminTool):
|
|
||||||
|
|
||||||
# Load the keyfile.
|
|
||||||
keyfile = self.safe_options.keyfile
|
|
||||||
- with open(keyfile) as f:
|
|
||||||
+ with open(keyfile, "rb") as f:
|
|
||||||
self.doc.setKey(f.read())
|
|
||||||
|
|
||||||
def run(self):
|
|
@ -1,114 +0,0 @@
|
|||||||
diff --git a/ipaserver/install/cainstance.py b/ipaserver/install/cainstance.py
|
|
||||||
index 38693c9..35cec89 100644
|
|
||||||
--- a/ipaserver/install/cainstance.py
|
|
||||||
+++ b/ipaserver/install/cainstance.py
|
|
||||||
@@ -1327,6 +1327,8 @@ class CAInstance(DogtagInstance):
|
|
||||||
generation master:
|
|
||||||
- in CS.cfg ca.crl.MasterCRL.enableCRLCache=true
|
|
||||||
- in CS.cfg ca.crl.MasterCRL.enableCRLUpdates=true
|
|
||||||
+ - in CS.cfg ca.listenToCloneModifications=true
|
|
||||||
+ - in CS.cfg ca.certStatusUpdateInterval != 0
|
|
||||||
- in /etc/httpd/conf.d/ipa-pki-proxy.conf the RewriteRule
|
|
||||||
^/ipa/crl/MasterCRL.bin is disabled (commented or removed)
|
|
||||||
|
|
||||||
@@ -1342,15 +1344,30 @@ class CAInstance(DogtagInstance):
|
|
||||||
updates = directivesetter.get_directive(
|
|
||||||
self.config, 'ca.crl.MasterCRL.enableCRLUpdates', '=')
|
|
||||||
enableCRLUpdates = updates.lower() == 'true'
|
|
||||||
+ listen = directivesetter.get_directive(
|
|
||||||
+ self.config, 'ca.listenToCloneModifications', '=')
|
|
||||||
+ enableToClone = listen.lower() == 'true'
|
|
||||||
+ updateinterval = directivesetter.get_directive(
|
|
||||||
+ self.config, 'ca.certStatusUpdateInterval', '=')
|
|
||||||
|
|
||||||
# If the values are different, the config is inconsistent
|
|
||||||
- if enableCRLCache != enableCRLUpdates:
|
|
||||||
+ if not (enableCRLCache == enableCRLUpdates == enableToClone):
|
|
||||||
raise InconsistentCRLGenConfigException(
|
|
||||||
"Configuration is inconsistent, please check "
|
|
||||||
- "ca.crl.MasterCRL.enableCRLCache and "
|
|
||||||
- "ca.crl.MasterCRL.enableCRLUpdates in {} and "
|
|
||||||
+ "ca.crl.MasterCRL.enableCRLCache, "
|
|
||||||
+ "ca.crl.MasterCRL.enableCRLUpdates and "
|
|
||||||
+ "ca.listenToCloneModifications in {} and "
|
|
||||||
"run ipa-crlgen-manage [enable|disable] to repair".format(
|
|
||||||
self.config))
|
|
||||||
+ # If they are the same then we are the CRL renewal master. Ensure
|
|
||||||
+ # the update task is configured.
|
|
||||||
+ if enableCRLCache and updateinterval == '0':
|
|
||||||
+ raise InconsistentCRLGenConfigException(
|
|
||||||
+ "Configuration is inconsistent, please check "
|
|
||||||
+ "ca.certStatusUpdateInterval in {}. It should "
|
|
||||||
+ "be either not present or not zero. Run "
|
|
||||||
+ "ipa-crlgen-manage [enable|disable] to repair".format(
|
|
||||||
+ self.config))
|
|
||||||
except IOError:
|
|
||||||
raise RuntimeError(
|
|
||||||
"Unable to read {}".format(self.config))
|
|
||||||
@@ -1407,6 +1424,11 @@ class CAInstance(DogtagInstance):
|
|
||||||
str_value = str(setup_crlgen).lower()
|
|
||||||
ds.set('ca.crl.MasterCRL.enableCRLCache', str_value)
|
|
||||||
ds.set('ca.crl.MasterCRL.enableCRLUpdates', str_value)
|
|
||||||
+ ds.set('ca.listenToCloneModifications', str_value)
|
|
||||||
+ if setup_crlgen:
|
|
||||||
+ ds.set('ca.certStatusUpdateInterval', None)
|
|
||||||
+ else:
|
|
||||||
+ ds.set('ca.certStatusUpdateInterval', '0')
|
|
||||||
|
|
||||||
# Start pki-tomcat
|
|
||||||
logger.info("Starting %s", self.service_name)
|
|
||||||
diff --git a/ipatests/test_integration/test_crlgen_manage.py b/ipatests/test_integration/test_crlgen_manage.py
|
|
||||||
index 2a733bd..c6f41eb 100644
|
|
||||||
--- a/ipatests/test_integration/test_crlgen_manage.py
|
|
||||||
+++ b/ipatests/test_integration/test_crlgen_manage.py
|
|
||||||
@@ -61,6 +61,16 @@ def check_crlgen_status(host, rc=0, msg=None, enabled=True, check_crl=False):
|
|
||||||
ext.value.crl_number)
|
|
||||||
assert number_msg in result.stdout_text
|
|
||||||
|
|
||||||
+ try:
|
|
||||||
+ value = get_CS_cfg_value(host, 'ca.certStatusUpdateInterval')
|
|
||||||
+ except IOError:
|
|
||||||
+ return
|
|
||||||
+
|
|
||||||
+ if enabled:
|
|
||||||
+ assert value is None
|
|
||||||
+ else:
|
|
||||||
+ assert value == '0'
|
|
||||||
+
|
|
||||||
|
|
||||||
def check_crlgen_enable(host, rc=0, msg=None, check_crl=False):
|
|
||||||
"""Check ipa-crlgen-manage enable command
|
|
||||||
@@ -125,6 +135,23 @@ def break_crlgen_with_CS_cfg(host):
|
|
||||||
check_crlgen_status(host, rc=1, msg="Configuration is inconsistent")
|
|
||||||
|
|
||||||
|
|
||||||
+def get_CS_cfg_value(host, directive):
|
|
||||||
+ """Retrieve and return the a directive from the CA CS.cfg
|
|
||||||
+
|
|
||||||
+ This returns None if the directives is not found.
|
|
||||||
+ """
|
|
||||||
+ content = host.get_file_contents(paths.CA_CS_CFG_PATH,
|
|
||||||
+ encoding='utf-8')
|
|
||||||
+ value = None
|
|
||||||
+ for line in content.split('\n'):
|
|
||||||
+ l = line.lower()
|
|
||||||
+
|
|
||||||
+ if l.startswith(directive.lower()):
|
|
||||||
+ value = line.split('=', 1)[1]
|
|
||||||
+
|
|
||||||
+ return value
|
|
||||||
+
|
|
||||||
+
|
|
||||||
class TestCRLGenManage(IntegrationTest):
|
|
||||||
"""Tests the ipa-crlgen-manage command.
|
|
||||||
|
|
||||||
@@ -196,6 +223,9 @@ class TestCRLGenManage(IntegrationTest):
|
|
||||||
|
|
||||||
Install a CA clone and enable CRLgen"""
|
|
||||||
tasks.install_ca(self.replicas[0])
|
|
||||||
+ value = get_CS_cfg_value(self.replicas[0],
|
|
||||||
+ 'ca.certStatusUpdateInterval')
|
|
||||||
+ assert value == '0'
|
|
||||||
check_crlgen_enable(
|
|
||||||
self.replicas[0], rc=0,
|
|
||||||
msg="make sure to have only a single CRL generation master",
|
|
@ -1,337 +0,0 @@
|
|||||||
diff --git a/ipaserver/plugins/idrange.py b/ipaserver/plugins/idrange.py
|
|
||||||
index d5b184f..b38ea73 100644
|
|
||||||
--- a/ipaserver/plugins/idrange.py
|
|
||||||
+++ b/ipaserver/plugins/idrange.py
|
|
||||||
@@ -549,6 +549,12 @@ class idrange_add(LDAPCreate):
|
|
||||||
self.obj.handle_ipabaserid(entry_attrs, options)
|
|
||||||
self.obj.handle_iparangetype(entry_attrs, options,
|
|
||||||
keep_objectclass=True)
|
|
||||||
+ self.add_message(
|
|
||||||
+ messages.ServiceRestartRequired(
|
|
||||||
+ service=services.knownservices.dirsrv.service_instance(""),
|
|
||||||
+ server=_('<all IPA servers>')
|
|
||||||
+ )
|
|
||||||
+ )
|
|
||||||
return dn
|
|
||||||
|
|
||||||
|
|
||||||
diff --git a/ipatests/test_xmlrpc/test_range_plugin.py b/ipatests/test_xmlrpc/test_range_plugin.py
|
|
||||||
index f912e04..e3f4c23 100644
|
|
||||||
--- a/ipatests/test_xmlrpc/test_range_plugin.py
|
|
||||||
+++ b/ipatests/test_xmlrpc/test_range_plugin.py
|
|
||||||
@@ -372,6 +372,8 @@ IPA_LOCAL_RANGE_MOD_ERR = (
|
|
||||||
"domain. Run `ipa help idrange` for more information"
|
|
||||||
)
|
|
||||||
|
|
||||||
+dirsrv_instance = services.knownservices.dirsrv.service_instance("")
|
|
||||||
+
|
|
||||||
|
|
||||||
@pytest.mark.tier1
|
|
||||||
class test_range(Declarative):
|
|
||||||
@@ -464,6 +466,11 @@ class test_range(Declarative):
|
|
||||||
),
|
|
||||||
value=testrange1,
|
|
||||||
summary=u'Added ID range "%s"' % (testrange1),
|
|
||||||
+ messages=(
|
|
||||||
+ messages.ServiceRestartRequired(
|
|
||||||
+ service=dirsrv_instance,
|
|
||||||
+ server='<all IPA servers>').to_dict(),
|
|
||||||
+ ),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
|
|
||||||
@@ -633,6 +640,11 @@ class test_range(Declarative):
|
|
||||||
),
|
|
||||||
value=testrange2,
|
|
||||||
summary=u'Added ID range "%s"' % (testrange2),
|
|
||||||
+ messages=(
|
|
||||||
+ messages.ServiceRestartRequired(
|
|
||||||
+ service=dirsrv_instance,
|
|
||||||
+ server='<all IPA servers>').to_dict(),
|
|
||||||
+ ),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
|
|
||||||
@@ -792,6 +804,11 @@ class test_range(Declarative):
|
|
||||||
),
|
|
||||||
value=unicode(domain7range1),
|
|
||||||
summary=u'Added ID range "%s"' % (domain7range1),
|
|
||||||
+ messages=(
|
|
||||||
+ messages.ServiceRestartRequired(
|
|
||||||
+ service=dirsrv_instance,
|
|
||||||
+ server='<all IPA servers>').to_dict(),
|
|
||||||
+ ),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
|
|
||||||
@@ -1079,6 +1096,11 @@ class test_range(Declarative):
|
|
||||||
),
|
|
||||||
value=testrange9,
|
|
||||||
summary=u'Added ID range "%s"' % (testrange9),
|
|
||||||
+ messages=(
|
|
||||||
+ messages.ServiceRestartRequired(
|
|
||||||
+ service=dirsrv_instance,
|
|
||||||
+ server='<all IPA servers>').to_dict(),
|
|
||||||
+ ),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
|
|
||||||
diff --git a/ipaserver/plugins/idrange.py b/ipaserver/plugins/idrange.py
|
|
||||||
index b38ea73..b12e1b8 100644
|
|
||||||
--- a/ipaserver/plugins/idrange.py
|
|
||||||
+++ b/ipaserver/plugins/idrange.py
|
|
||||||
@@ -549,12 +549,15 @@ class idrange_add(LDAPCreate):
|
|
||||||
self.obj.handle_ipabaserid(entry_attrs, options)
|
|
||||||
self.obj.handle_iparangetype(entry_attrs, options,
|
|
||||||
keep_objectclass=True)
|
|
||||||
- self.add_message(
|
|
||||||
- messages.ServiceRestartRequired(
|
|
||||||
- service=services.knownservices.dirsrv.service_instance(""),
|
|
||||||
- server=_('<all IPA servers>')
|
|
||||||
+
|
|
||||||
+ if entry_attrs.single_value.get('iparangetype') in (
|
|
||||||
+ 'ipa-local', self.obj.range_types.get('ipa-local', None)):
|
|
||||||
+ self.add_message(
|
|
||||||
+ messages.ServiceRestartRequired(
|
|
||||||
+ service=services.knownservices.dirsrv.service_instance(""),
|
|
||||||
+ server=_('<all IPA servers>')
|
|
||||||
+ )
|
|
||||||
)
|
|
||||||
- )
|
|
||||||
return dn
|
|
||||||
|
|
||||||
|
|
||||||
@@ -568,7 +571,8 @@ class idrange_del(LDAPDelete):
|
|
||||||
try:
|
|
||||||
old_attrs = ldap.get_entry(dn, ['ipabaseid',
|
|
||||||
'ipaidrangesize',
|
|
||||||
- 'ipanttrusteddomainsid'])
|
|
||||||
+ 'ipanttrusteddomainsid',
|
|
||||||
+ 'iparangetype'])
|
|
||||||
except errors.NotFound:
|
|
||||||
raise self.obj.handle_not_found(*keys)
|
|
||||||
|
|
||||||
@@ -602,6 +606,20 @@ class idrange_del(LDAPDelete):
|
|
||||||
key=keys[0],
|
|
||||||
dependent=trust_domains[0].dn[0].value)
|
|
||||||
|
|
||||||
+ self.add_message(
|
|
||||||
+ messages.ServiceRestartRequired(
|
|
||||||
+ service=services.knownservices['sssd'].systemd_name,
|
|
||||||
+ server=_('<all IPA servers>')
|
|
||||||
+ )
|
|
||||||
+ )
|
|
||||||
+
|
|
||||||
+ if old_attrs.single_value.get('iparangetype') == 'ipa-local':
|
|
||||||
+ self.add_message(
|
|
||||||
+ messages.ServiceRestartRequired(
|
|
||||||
+ service=services.knownservices.dirsrv.service_instance(""),
|
|
||||||
+ server=_('<all IPA servers>')
|
|
||||||
+ )
|
|
||||||
+ )
|
|
||||||
|
|
||||||
return dn
|
|
||||||
|
|
||||||
@@ -804,10 +822,20 @@ class idrange_mod(LDAPUpdate):
|
|
||||||
assert isinstance(dn, DN)
|
|
||||||
self.obj.handle_ipabaserid(entry_attrs, options)
|
|
||||||
self.obj.handle_iparangetype(entry_attrs, options)
|
|
||||||
+
|
|
||||||
+ if entry_attrs.single_value.get('iparangetype') in (
|
|
||||||
+ 'ipa-local', self.obj.range_types.get('ipa-local', None)):
|
|
||||||
+ self.add_message(
|
|
||||||
+ messages.ServiceRestartRequired(
|
|
||||||
+ service=services.knownservices.dirsrv.service_instance(""),
|
|
||||||
+ server=_('<all IPA servers>')
|
|
||||||
+ )
|
|
||||||
+ )
|
|
||||||
+
|
|
||||||
self.add_message(
|
|
||||||
messages.ServiceRestartRequired(
|
|
||||||
service=services.knownservices['sssd'].systemd_name,
|
|
||||||
- server=keys[0]
|
|
||||||
+ server=_('<all IPA servers>')
|
|
||||||
)
|
|
||||||
)
|
|
||||||
return dn
|
|
||||||
diff --git a/ipatests/test_xmlrpc/test_range_plugin.py b/ipatests/test_xmlrpc/test_range_plugin.py
|
|
||||||
index e3f4c23..531fe4a 100644
|
|
||||||
--- a/ipatests/test_xmlrpc/test_range_plugin.py
|
|
||||||
+++ b/ipatests/test_xmlrpc/test_range_plugin.py
|
|
||||||
@@ -26,7 +26,8 @@ import six
|
|
||||||
from ipalib import api, errors, messages
|
|
||||||
from ipalib import constants
|
|
||||||
from ipaplatform import services
|
|
||||||
-from ipatests.test_xmlrpc.xmlrpc_test import Declarative, fuzzy_uuid
|
|
||||||
+from ipatests.test_xmlrpc.xmlrpc_test import (
|
|
||||||
+ Declarative, fuzzy_uuid, Fuzzy, fuzzy_sequence_of)
|
|
||||||
from ipatests.test_xmlrpc import objectclasses
|
|
||||||
from ipatests.util import MockLDAP
|
|
||||||
from ipapython.dn import DN
|
|
||||||
@@ -374,6 +375,8 @@ IPA_LOCAL_RANGE_MOD_ERR = (
|
|
||||||
|
|
||||||
dirsrv_instance = services.knownservices.dirsrv.service_instance("")
|
|
||||||
|
|
||||||
+fuzzy_restart_messages = fuzzy_sequence_of(Fuzzy(type=dict))
|
|
||||||
+
|
|
||||||
|
|
||||||
@pytest.mark.tier1
|
|
||||||
class test_range(Declarative):
|
|
||||||
@@ -610,7 +613,8 @@ class test_range(Declarative):
|
|
||||||
desc='Delete ID range %r' % testrange1,
|
|
||||||
command=('idrange_del', [testrange1], {}),
|
|
||||||
expected=dict(
|
|
||||||
- result=dict(failed=[]),
|
|
||||||
+ result=dict(failed=[],
|
|
||||||
+ messages=fuzzy_restart_messages),
|
|
||||||
value=[testrange1],
|
|
||||||
summary=u'Deleted ID range "%s"' % testrange1,
|
|
||||||
),
|
|
||||||
@@ -714,7 +718,8 @@ class test_range(Declarative):
|
|
||||||
desc='Delete ID range %r' % testrange2,
|
|
||||||
command=('idrange_del', [testrange2], {}),
|
|
||||||
expected=dict(
|
|
||||||
- result=dict(failed=[]),
|
|
||||||
+ result=dict(failed=[],
|
|
||||||
+ messages=fuzzy_restart_messages),
|
|
||||||
value=[testrange2],
|
|
||||||
summary=u'Deleted ID range "%s"' % testrange2,
|
|
||||||
),
|
|
||||||
diff --git a/ipatests/test_xmlrpc/test_range_plugin.py b/ipatests/test_xmlrpc/test_range_plugin.py
|
|
||||||
index 531fe4a..3646952 100644
|
|
||||||
--- a/ipatests/test_xmlrpc/test_range_plugin.py
|
|
||||||
+++ b/ipatests/test_xmlrpc/test_range_plugin.py
|
|
||||||
@@ -613,8 +613,8 @@ class test_range(Declarative):
|
|
||||||
desc='Delete ID range %r' % testrange1,
|
|
||||||
command=('idrange_del', [testrange1], {}),
|
|
||||||
expected=dict(
|
|
||||||
- result=dict(failed=[],
|
|
||||||
- messages=fuzzy_restart_messages),
|
|
||||||
+ result=dict(failed=[]),
|
|
||||||
+ messages=fuzzy_restart_messages,
|
|
||||||
value=[testrange1],
|
|
||||||
summary=u'Deleted ID range "%s"' % testrange1,
|
|
||||||
),
|
|
||||||
@@ -718,8 +718,8 @@ class test_range(Declarative):
|
|
||||||
desc='Delete ID range %r' % testrange2,
|
|
||||||
command=('idrange_del', [testrange2], {}),
|
|
||||||
expected=dict(
|
|
||||||
- result=dict(failed=[],
|
|
||||||
- messages=fuzzy_restart_messages),
|
|
||||||
+ result=dict(failed=[]),
|
|
||||||
+ messages=fuzzy_restart_messages,
|
|
||||||
value=[testrange2],
|
|
||||||
summary=u'Deleted ID range "%s"' % testrange2,
|
|
||||||
),
|
|
||||||
@@ -809,11 +809,6 @@ class test_range(Declarative):
|
|
||||||
),
|
|
||||||
value=unicode(domain7range1),
|
|
||||||
summary=u'Added ID range "%s"' % (domain7range1),
|
|
||||||
- messages=(
|
|
||||||
- messages.ServiceRestartRequired(
|
|
||||||
- service=dirsrv_instance,
|
|
||||||
- server='<all IPA servers>').to_dict(),
|
|
||||||
- ),
|
|
||||||
),
|
|
||||||
),
|
|
||||||
|
|
||||||
@@ -836,6 +831,7 @@ class test_range(Declarative):
|
|
||||||
result=dict(failed=[]),
|
|
||||||
value=[domain1range1],
|
|
||||||
summary=u'Deleted ID range "%s"' % domain1range1,
|
|
||||||
+ messages=fuzzy_restart_messages,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
|
|
||||||
@@ -862,12 +858,7 @@ class test_range(Declarative):
|
|
||||||
command=('idrange_mod', [domain3range2],
|
|
||||||
dict(ipabaseid=domain3range1_base_id)),
|
|
||||||
expected=dict(
|
|
||||||
- messages=(
|
|
||||||
- messages.ServiceRestartRequired(
|
|
||||||
- service=services.knownservices['sssd'].systemd_name,
|
|
||||||
- server=domain3range2
|
|
||||||
- ).to_dict(),
|
|
||||||
- ),
|
|
||||||
+ messages=fuzzy_restart_messages,
|
|
||||||
result=dict(
|
|
||||||
cn=[domain3range2],
|
|
||||||
ipabaseid=[unicode(domain3range1_base_id)],
|
|
||||||
@@ -933,12 +924,7 @@ class test_range(Declarative):
|
|
||||||
command=('idrange_mod', [domain2range1],
|
|
||||||
dict(ipabaserid=domain5range1_base_rid)),
|
|
||||||
expected=dict(
|
|
||||||
- messages=(
|
|
||||||
- messages.ServiceRestartRequired(
|
|
||||||
- service=services.knownservices['sssd'].systemd_name,
|
|
||||||
- server=domain2range1
|
|
||||||
- ).to_dict(),
|
|
||||||
- ),
|
|
||||||
+ messages=fuzzy_restart_messages,
|
|
||||||
result=dict(
|
|
||||||
cn=[domain2range1],
|
|
||||||
ipabaseid=[unicode(domain2range1_base_id)],
|
|
||||||
@@ -973,12 +959,7 @@ class test_range(Declarative):
|
|
||||||
command=('idrange_mod', [domain2range1],
|
|
||||||
dict(ipaautoprivategroups='true')),
|
|
||||||
expected=dict(
|
|
||||||
- messages=(
|
|
||||||
- messages.ServiceRestartRequired(
|
|
||||||
- service=services.knownservices['sssd'].systemd_name,
|
|
||||||
- server=domain2range1
|
|
||||||
- ).to_dict(),
|
|
||||||
- ),
|
|
||||||
+ messages=fuzzy_restart_messages,
|
|
||||||
result=dict(
|
|
||||||
cn=[domain2range1],
|
|
||||||
ipabaseid=[unicode(domain2range1_base_id)],
|
|
||||||
@@ -1000,12 +981,7 @@ class test_range(Declarative):
|
|
||||||
command=('idrange_mod', [domain2range1],
|
|
||||||
dict(ipaautoprivategroups='false')),
|
|
||||||
expected=dict(
|
|
||||||
- messages=(
|
|
||||||
- messages.ServiceRestartRequired(
|
|
||||||
- service=services.knownservices['sssd'].systemd_name,
|
|
||||||
- server=domain2range1
|
|
||||||
- ).to_dict(),
|
|
||||||
- ),
|
|
||||||
+ messages=fuzzy_restart_messages,
|
|
||||||
result=dict(
|
|
||||||
cn=[domain2range1],
|
|
||||||
ipabaseid=[unicode(domain2range1_base_id)],
|
|
||||||
@@ -1027,12 +1003,7 @@ class test_range(Declarative):
|
|
||||||
command=('idrange_mod', [domain2range1],
|
|
||||||
dict(ipaautoprivategroups='hybrid')),
|
|
||||||
expected=dict(
|
|
||||||
- messages=(
|
|
||||||
- messages.ServiceRestartRequired(
|
|
||||||
- service=services.knownservices['sssd'].systemd_name,
|
|
||||||
- server=domain2range1
|
|
||||||
- ).to_dict(),
|
|
||||||
- ),
|
|
||||||
+ messages=fuzzy_restart_messages,
|
|
||||||
result=dict(
|
|
||||||
cn=[domain2range1],
|
|
||||||
ipabaseid=[unicode(domain2range1_base_id)],
|
|
||||||
@@ -1054,12 +1025,7 @@ class test_range(Declarative):
|
|
||||||
command=('idrange_mod', [domain2range1],
|
|
||||||
dict(ipaautoprivategroups='')),
|
|
||||||
expected=dict(
|
|
||||||
- messages=(
|
|
||||||
- messages.ServiceRestartRequired(
|
|
||||||
- service=services.knownservices['sssd'].systemd_name,
|
|
||||||
- server=domain2range1
|
|
||||||
- ).to_dict(),
|
|
||||||
- ),
|
|
||||||
+ messages=fuzzy_restart_messages,
|
|
||||||
result=dict(
|
|
||||||
cn=[domain2range1],
|
|
||||||
ipabaseid=[unicode(domain2range1_base_id)],
|
|
||||||
@@ -1116,6 +1082,7 @@ class test_range(Declarative):
|
|
||||||
result=dict(failed=[]),
|
|
||||||
value=[testrange9],
|
|
||||||
summary=u'Deleted ID range "%s"' % testrange9,
|
|
||||||
+ messages=fuzzy_restart_messages,
|
|
||||||
),
|
|
||||||
),
|
|
||||||
|
|
@ -1,58 +0,0 @@
|
|||||||
diff --git a/ipaserver/plugins/cert.py b/ipaserver/plugins/cert.py
|
|
||||||
index 619be83..9be1b67 100644
|
|
||||||
--- a/ipaserver/plugins/cert.py
|
|
||||||
+++ b/ipaserver/plugins/cert.py
|
|
||||||
@@ -55,7 +55,7 @@ from ipapython.dn import DN
|
|
||||||
from ipapython.ipautil import datetime_from_utctimestamp
|
|
||||||
from ipaserver.plugins.service import normalize_principal, validate_realm
|
|
||||||
from ipaserver.masters import (
|
|
||||||
- ENABLED_SERVICE, CONFIGURED_SERVICE, is_service_enabled
|
|
||||||
+ ENABLED_SERVICE, CONFIGURED_SERVICE, HIDDEN_SERVICE, is_service_enabled
|
|
||||||
)
|
|
||||||
|
|
||||||
try:
|
|
||||||
@@ -300,7 +300,7 @@ def caacl_check(principal, ca, profile_id):
|
|
||||||
def ca_kdc_check(api_instance, hostname):
|
|
||||||
master_dn = api_instance.Object.server.get_dn(unicode(hostname))
|
|
||||||
kdc_dn = DN(('cn', 'KDC'), master_dn)
|
|
||||||
- wanted = {ENABLED_SERVICE, CONFIGURED_SERVICE}
|
|
||||||
+ wanted = {ENABLED_SERVICE, CONFIGURED_SERVICE, HIDDEN_SERVICE}
|
|
||||||
try:
|
|
||||||
kdc_entry = api_instance.Backend.ldap2.get_entry(
|
|
||||||
kdc_dn, ['ipaConfigString'])
|
|
||||||
diff --git a/ipatests/test_integration/test_replica_promotion.py b/ipatests/test_integration/test_replica_promotion.py
|
|
||||||
index b71f2d5..7ef44c5 100644
|
|
||||||
--- a/ipatests/test_integration/test_replica_promotion.py
|
|
||||||
+++ b/ipatests/test_integration/test_replica_promotion.py
|
|
||||||
@@ -26,6 +26,7 @@ from ipalib.constants import (
|
|
||||||
)
|
|
||||||
from ipaplatform.paths import paths
|
|
||||||
from ipapython import certdb
|
|
||||||
+from ipatests.test_integration.test_cert import get_certmonger_fs_id
|
|
||||||
from ipatests.test_integration.test_dns_locations import (
|
|
||||||
resolve_records_from_server, IPA_DEFAULT_MASTER_SRV_REC
|
|
||||||
)
|
|
||||||
@@ -1241,6 +1242,23 @@ class TestHiddenReplicaPromotion(IntegrationTest):
|
|
||||||
'ipa-crlgen-manage', 'status'])
|
|
||||||
assert "CRL generation: enabled" in result.stdout_text
|
|
||||||
|
|
||||||
+ def test_hidden_replica_renew_pkinit_cert(self):
|
|
||||||
+ """Renew the PKINIT cert on a hidden replica.
|
|
||||||
+
|
|
||||||
+ Test for https://pagure.io/freeipa/issue/9611
|
|
||||||
+ """
|
|
||||||
+ # Get Request ID
|
|
||||||
+ cmd = ['getcert', 'list', '-f', paths.KDC_CERT]
|
|
||||||
+ result = self.replicas[0].run_command(cmd)
|
|
||||||
+ req_id = get_certmonger_fs_id(result.stdout_text)
|
|
||||||
+
|
|
||||||
+ self.replicas[0].run_command([
|
|
||||||
+ 'getcert', 'resubmit', '-f', paths.KDC_CERT
|
|
||||||
+ ])
|
|
||||||
+ tasks.wait_for_certmonger_status(
|
|
||||||
+ self.replicas[0], ('MONITORING'), req_id, timeout=600
|
|
||||||
+ )
|
|
||||||
+
|
|
||||||
|
|
||||||
class TestHiddenReplicaKRA(IntegrationTest):
|
|
||||||
"""Test KRA & hidden replica features.
|
|
@ -1,69 +0,0 @@
|
|||||||
From 0d44e959e5bbe822b51137a8e7cf48fa25533805 Mon Sep 17 00:00:00 2001
|
|
||||||
From: Rafael Guterres Jeffman <rjeffman@redhat.com>
|
|
||||||
Date: Fri, 10 Dec 2021 12:15:36 -0300
|
|
||||||
Subject: [PATCH] Revert "freeipa.spec: depend on bind-dnssec-utils"
|
|
||||||
|
|
||||||
This reverts commit f89d59b6e18b54967682f6a37ce92ae67ab3fcda.
|
|
||||||
---
|
|
||||||
freeipa.spec.in | 4 +---
|
|
||||||
ipaplatform/base/paths.py | 2 +-
|
|
||||||
ipaplatform/fedora/paths.py | 1 +
|
|
||||||
ipaserver/dnssec/bindmgr.py | 1 -
|
|
||||||
4 files changed, 3 insertions(+), 5 deletions(-)
|
|
||||||
|
|
||||||
diff --git a/freeipa.spec.in b/freeipa.spec.in
|
|
||||||
index 8f5c370e5..e20edb7bc 100755
|
|
||||||
--- a/freeipa.spec.in
|
|
||||||
+++ b/freeipa.spec.in
|
|
||||||
@@ -576,11 +576,9 @@ Requires: %{name}-server = %{version}-%{release}
|
|
||||||
Requires: bind-dyndb-ldap >= 11.2-2
|
|
||||||
Requires: bind >= %{bind_version}
|
|
||||||
Requires: bind-utils >= %{bind_version}
|
|
||||||
-# bind-dnssec-utils is required by the OpenDNSSec integration
|
|
||||||
-# https://pagure.io/freeipa/issue/9026
|
|
||||||
-Requires: bind-dnssec-utils >= %{bind_version}
|
|
||||||
%if %{with bind_pkcs11}
|
|
||||||
Requires: bind-pkcs11 >= %{bind_version}
|
|
||||||
+Requires: bind-pkcs11-utils >= %{bind_version}
|
|
||||||
%else
|
|
||||||
Requires: softhsm >= %{softhsm_version}
|
|
||||||
Requires: openssl-pkcs11 >= %{openssl_pkcs11_version}
|
|
||||||
diff --git a/ipaplatform/base/paths.py b/ipaplatform/base/paths.py
|
|
||||||
index 7d21367ec..42a47f1df 100644
|
|
||||||
--- a/ipaplatform/base/paths.py
|
|
||||||
+++ b/ipaplatform/base/paths.py
|
|
||||||
@@ -259,7 +259,6 @@ class BasePathNamespace:
|
|
||||||
IPA_PKI_RETRIEVE_KEY = "/usr/libexec/ipa/ipa-pki-retrieve-key"
|
|
||||||
IPA_HTTPD_PASSWD_READER = "/usr/libexec/ipa/ipa-httpd-pwdreader"
|
|
||||||
IPA_PKI_WAIT_RUNNING = "/usr/libexec/ipa/ipa-pki-wait-running"
|
|
||||||
- DNSSEC_KEYFROMLABEL = "/usr/sbin/dnssec-keyfromlabel"
|
|
||||||
+ DNSSEC_KEYFROMLABEL = "/usr/sbin/dnssec-keyfromlabel-pkcs11"
|
|
||||||
- DNSSEC_KEYFROMLABEL_9_17 = "/usr/bin/dnssec-keyfromlabel"
|
|
||||||
GETSEBOOL = "/usr/sbin/getsebool"
|
|
||||||
GROUPADD = "/usr/sbin/groupadd"
|
|
||||||
diff --git a/ipaplatform/fedora/paths.py b/ipaplatform/fedora/paths.py
|
|
||||||
index 4e993c063..92a948966 100644
|
|
||||||
--- a/ipaplatform/fedora/paths.py
|
|
||||||
+++ b/ipaplatform/fedora/paths.py
|
|
||||||
@@ -36,6 +36,7 @@ class FedoraPathNamespace(RedHatPathNamespace):
|
|
||||||
NAMED_CRYPTO_POLICY_FILE = "/etc/crypto-policies/back-ends/bind.config"
|
|
||||||
if HAS_NFS_CONF:
|
|
||||||
SYSCONFIG_NFS = '/etc/nfs.conf'
|
|
||||||
+ DNSSEC_KEYFROMLABEL = "/usr/sbin/dnssec-keyfromlabel"
|
|
||||||
|
|
||||||
|
|
||||||
paths = FedoraPathNamespace()
|
|
||||||
diff --git a/ipaserver/dnssec/bindmgr.py b/ipaserver/dnssec/bindmgr.py
|
|
||||||
index 0c79cc03d..a15c0e601 100644
|
|
||||||
--- a/ipaserver/dnssec/bindmgr.py
|
|
||||||
+++ b/ipaserver/dnssec/bindmgr.py
|
|
||||||
@@ -127,7 +127,6 @@ class BINDMgr:
|
|
||||||
)
|
|
||||||
cmd = [
|
|
||||||
paths.DNSSEC_KEYFROMLABEL,
|
|
||||||
- '-E', 'pkcs11',
|
|
||||||
'-K', workdir,
|
|
||||||
'-a', attrs['idnsSecAlgorithm'][0],
|
|
||||||
'-l', uri
|
|
||||||
--
|
|
||||||
2.31.1
|
|
@ -1,60 +0,0 @@
|
|||||||
From 7807bcc55b4927fc327830d2237200772d2e1106 Mon Sep 17 00:00:00 2001
|
|
||||||
From: Rafael Guterres Jeffman <rjeffman@redhat.com>
|
|
||||||
Date: Fri, 17 Jun 2022 15:40:04 -0300
|
|
||||||
Subject: [PATCH] webui IdP: Remove arrow notation due to uglify-js limitation.
|
|
||||||
|
|
||||||
uglify-js 2.x series do not support ECMAScript 6 arrow notation ('=>')
|
|
||||||
for callback definition.
|
|
||||||
|
|
||||||
This patch changes the arrow definition callbacks for regular anonymous
|
|
||||||
function definitions.
|
|
||||||
---
|
|
||||||
install/ui/src/freeipa/idp.js | 10 +++++-----
|
|
||||||
1 file changed, 5 insertions(+), 5 deletions(-)
|
|
||||||
|
|
||||||
diff --git a/install/ui/src/freeipa/idp.js b/install/ui/src/freeipa/idp.js
|
|
||||||
index ada09c075..be3c4f0e6 100644
|
|
||||||
--- a/install/ui/src/freeipa/idp.js
|
|
||||||
+++ b/install/ui/src/freeipa/idp.js
|
|
||||||
@@ -227,7 +227,7 @@ IPA.add_idp_policy = function() {
|
|
||||||
// For custom template we show custom fields
|
|
||||||
// and mark all of them required and passed to the RPC
|
|
||||||
// If show_custom is false, the opposite happens
|
|
||||||
- custom_fields.forEach(fname => {
|
|
||||||
+ custom_fields.forEach(function(fname) {
|
|
||||||
widget_f = that.container.fields.get_field(fname);
|
|
||||||
widget_f.set_required(show_custom);
|
|
||||||
widget_f.set_enabled(show_custom);
|
|
||||||
@@ -235,7 +235,7 @@ IPA.add_idp_policy = function() {
|
|
||||||
});
|
|
||||||
|
|
||||||
// For template fields we show them if custom aren't shown
|
|
||||||
- template_fields.forEach(fname => {
|
|
||||||
+ template_fields.forEach(function(fname) {
|
|
||||||
widget_f = that.container.fields.get_field(fname);
|
|
||||||
widget_f.set_enabled(!show_custom);
|
|
||||||
widget_f.widget.set_visible(!show_custom);
|
|
||||||
@@ -252,7 +252,7 @@ IPA.add_idp_policy = function() {
|
|
||||||
var value = prov_f.get_value()[0];
|
|
||||||
|
|
||||||
// First, clear template fields from the previous provider choice
|
|
||||||
- template_fields.forEach(fname => {
|
|
||||||
+ template_fields.forEach(function(fname) {
|
|
||||||
widget_f = that.container.fields.get_field(fname);
|
|
||||||
widget_f.widget.set_visible(false);
|
|
||||||
widget_f.set_required(false);
|
|
||||||
@@ -260,9 +260,9 @@ IPA.add_idp_policy = function() {
|
|
||||||
});
|
|
||||||
|
|
||||||
// Second, enable and get required template-specific fields
|
|
||||||
- idp.templates.forEach(idp_v => {
|
|
||||||
+ idp.templates.forEach(function(idp_v) {
|
|
||||||
if (idp_v['value'] == value) {
|
|
||||||
- idp_v['fields'].forEach(fname => {
|
|
||||||
+ idp_v['fields'].forEach(function(fname) {
|
|
||||||
widget_f = that.container.fields.get_field(fname);
|
|
||||||
widget_f.set_required(true);
|
|
||||||
widget_f.set_enabled(true);
|
|
||||||
--
|
|
||||||
2.36.1
|
|
||||||
|
|
@ -1,120 +0,0 @@
|
|||||||
From 9a33838407f244e481523fe643bc0626874e8b1a Mon Sep 17 00:00:00 2001
|
|
||||||
From: Rafael Guterres Jeffman <rjeffman@redhat.com>
|
|
||||||
Date: Mon, 19 Dec 2022 14:57:03 -0300
|
|
||||||
Subject: [PATCH] Revert "DNSResolver: Fix use of nameservers with ports"
|
|
||||||
|
|
||||||
This reverts commit 5e2e4664aec641886923c2bec61ce25b96edb62a.
|
|
||||||
|
|
||||||
diff --git a/ipapython/dnsutil.py b/ipapython/dnsutil.py
|
|
||||||
index 58de365ab..4baeaf8cc 100644
|
|
||||||
--- a/ipapython/dnsutil.py 2023-05-19 05:12:52.471239297 -0300
|
|
||||||
+++ b/ipapython/dnsutil.py 2023-05-24 12:20:13.588867053 -0300
|
|
||||||
@@ -145,55 +145,6 @@
|
|
||||||
nameservers.remove(ipv4_loopback)
|
|
||||||
self.nameservers = nameservers
|
|
||||||
|
|
||||||
- @property
|
|
||||||
- def nameservers(self):
|
|
||||||
- return self._nameservers
|
|
||||||
-
|
|
||||||
- @nameservers.setter
|
|
||||||
- def nameservers(self, nameservers):
|
|
||||||
- """
|
|
||||||
- *nameservers*, a ``list`` of nameservers with optional ports:
|
|
||||||
- "SERVER_IP port PORT_NUMBER".
|
|
||||||
-
|
|
||||||
- Overloads dns.resolver.Resolver.nameservers setter to split off ports
|
|
||||||
- into nameserver_ports after setting nameservers successfully with the
|
|
||||||
- setter in dns.resolver.Resolver.
|
|
||||||
- """
|
|
||||||
- # Get nameserver_ports if it is already set
|
|
||||||
- if hasattr(self, "nameserver_ports"):
|
|
||||||
- nameserver_ports = self.nameserver_ports
|
|
||||||
- else:
|
|
||||||
- nameserver_ports = {}
|
|
||||||
-
|
|
||||||
- # Check nameserver items in list and split out converted port number
|
|
||||||
- # into nameserver_ports: { nameserver: port }
|
|
||||||
- if isinstance(nameservers, list):
|
|
||||||
- _nameservers = []
|
|
||||||
- for nameserver in nameservers:
|
|
||||||
- splits = nameserver.split()
|
|
||||||
- if len(splits) == 3 and splits[1] == "port":
|
|
||||||
- nameserver = splits[0]
|
|
||||||
- try:
|
|
||||||
- port = int(splits[2])
|
|
||||||
- if port < 0 or port > 65535:
|
|
||||||
- raise ValueError()
|
|
||||||
- except ValueError:
|
|
||||||
- raise ValueError(
|
|
||||||
- "invalid nameserver: %s is not a valid port" %
|
|
||||||
- splits[2])
|
|
||||||
- nameserver_ports[nameserver] = port
|
|
||||||
- _nameservers.append(nameserver)
|
|
||||||
- nameservers = _nameservers
|
|
||||||
-
|
|
||||||
- # Call dns.resolver.Resolver.nameservers setter
|
|
||||||
- if hasattr(dns.resolver.Resolver, "nameservers"):
|
|
||||||
- dns.resolver.Resolver.nameservers.__set__(self, nameservers)
|
|
||||||
- else:
|
|
||||||
- # old dnspython (<2) doesn't have 'nameservers' property
|
|
||||||
- self._nameservers = nameservers
|
|
||||||
- # Set nameserver_ports after successfull call to setter
|
|
||||||
- self.nameserver_ports = nameserver_ports
|
|
||||||
-
|
|
||||||
|
|
||||||
class DNSZoneAlreadyExists(dns.exception.DNSException):
|
|
||||||
supp_kwargs = {'zone', 'ns'}
|
|
||||||
diff --git a/ipatests/test_ipapython/test_dnsutil.py b/ipatests/test_ipapython/test_dnsutil.py
|
|
||||||
index 9070d89ad..5e7a46197 100644
|
|
||||||
--- a/ipatests/test_ipapython/test_dnsutil.py
|
|
||||||
+++ b/ipatests/test_ipapython/test_dnsutil.py
|
|
||||||
@@ -101,48 +101,3 @@ class TestSortURI:
|
|
||||||
assert dnsutil.sort_prio_weight([h3, h2, h1]) == [h1, h2, h3]
|
|
||||||
assert dnsutil.sort_prio_weight([h3, h3, h3]) == [h3]
|
|
||||||
assert dnsutil.sort_prio_weight([h2, h2, h1, h1]) == [h1, h2]
|
|
||||||
-
|
|
||||||
-
|
|
||||||
-class TestDNSResolver:
|
|
||||||
- @pytest.fixture(name="res")
|
|
||||||
- def resolver(self):
|
|
||||||
- """Resolver that doesn't read /etc/resolv.conf
|
|
||||||
-
|
|
||||||
- /etc/resolv.conf is not mandatory on systems
|
|
||||||
- """
|
|
||||||
- return dnsutil.DNSResolver(configure=False)
|
|
||||||
-
|
|
||||||
- def test_nameservers(self, res):
|
|
||||||
- res.nameservers = ["4.4.4.4", "8.8.8.8"]
|
|
||||||
- assert res.nameservers == ["4.4.4.4", "8.8.8.8"]
|
|
||||||
-
|
|
||||||
- def test_nameservers_with_ports(self, res):
|
|
||||||
- res.nameservers = ["4.4.4.4 port 53", "8.8.8.8 port 8053"]
|
|
||||||
- assert res.nameservers == ["4.4.4.4", "8.8.8.8"]
|
|
||||||
- assert res.nameserver_ports == {"4.4.4.4": 53, "8.8.8.8": 8053}
|
|
||||||
-
|
|
||||||
- res.nameservers = ["4.4.4.4 port 53", "8.8.8.8 port 8053"]
|
|
||||||
- assert res.nameservers == ["4.4.4.4", "8.8.8.8"]
|
|
||||||
- assert res.nameserver_ports == {"4.4.4.4": 53, "8.8.8.8": 8053}
|
|
||||||
-
|
|
||||||
- def test_nameservers_with_bad_ports(self, res):
|
|
||||||
- try:
|
|
||||||
- res.nameservers = ["4.4.4.4 port a"]
|
|
||||||
- except ValueError:
|
|
||||||
- pass
|
|
||||||
- else:
|
|
||||||
- pytest.fail("No fail on bad port a")
|
|
||||||
-
|
|
||||||
- try:
|
|
||||||
- res.nameservers = ["4.4.4.4 port -1"]
|
|
||||||
- except ValueError:
|
|
||||||
- pass
|
|
||||||
- else:
|
|
||||||
- pytest.fail("No fail on bad port -1")
|
|
||||||
-
|
|
||||||
- try:
|
|
||||||
- res.nameservers = ["4.4.4.4 port 65536"]
|
|
||||||
- except ValueError:
|
|
||||||
- pass
|
|
||||||
- else:
|
|
||||||
- pytest.fail("No fail on bad port 65536")
|
|
16
SOURCES/freeipa-4.10.1.tar.gz.asc
Normal file
16
SOURCES/freeipa-4.10.1.tar.gz.asc
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
-----BEGIN PGP SIGNATURE-----
|
||||||
|
|
||||||
|
iQIzBAABCAAdFiEE11Z2TU1+KXxtrRFyaYdvcqbi008FAmN/lzsACgkQaYdvcqbi
|
||||||
|
00+f7w//a5Y4tgTD2A52fQdihv/Q2ZZqhPlSt8rP7bTLtGSjvANHpuF2u0FcjmtB
|
||||||
|
tADL4zljBGGMGONGt1qMF3d0BkAXhyVp8YNIBcL+abEhgeKL++3egOrI/8iyl4FB
|
||||||
|
Sr8Gvr1T4AJXjz+yZ/SsPNKerqFrJwi3flavZ7Bk1c2W2V3rJubSAvK9XwO7Cyxs
|
||||||
|
4afYpLAiknPh999q3zt7NyviivBTYXirif2T45Lelln9sQaB1ksqDA7oYYAfhV0B
|
||||||
|
wmUJMTtDoaMAcsLT29aC8wGSB5dttI5GmrF60gnbq/t5Tr+7x9ch8VYE60qkynU9
|
||||||
|
n1WcouuIwvcwfPpYSONak0vFrnMV2USNQj5OnzlKXl1HnHsSqoaQWRUA6bbBPjdb
|
||||||
|
xQLdQRrZiYtjG5YcwuZyg8TRP2ph1aY3yL2gAR1ZI13yJeNV0pwU+IwouJDiVPvS
|
||||||
|
dwiG8en1jUxYCeXFKjPX3A8/2ei+LvLTVY65qZKT+BsXyE2CymmUx7P20lFN5Ksb
|
||||||
|
RN+g37mJI8CwqmlemdTZiF+wJnlswS9SOS5zxhpVRiJHAV1sfxu66515c9+n890A
|
||||||
|
Hlm9sZn9UBQN5nLwuak8jaOr73E+d8uieXsSCA4wTtXKQSJs67fNY6ldeUmNximc
|
||||||
|
fq6Yh+lDau14rDlPZUkXx0Jd/oPEWDrtnJzfHECeoRn3KoNzezU=
|
||||||
|
=pxPY
|
||||||
|
-----END PGP SIGNATURE-----
|
@ -1,16 +0,0 @@
|
|||||||
-----BEGIN PGP SIGNATURE-----
|
|
||||||
|
|
||||||
iQIzBAABCAAdFiEE11Z2TU1+KXxtrRFyaYdvcqbi008FAmVbZU0ACgkQaYdvcqbi
|
|
||||||
009Fgw/+PzHGNOJPs67TtoYITV/3ZCzMyrYTcazVACjD61Zw7JBgbZzZpQXxBSbj
|
|
||||||
7QWpNJa3P2JFtv2qOUXJto40mOGpMynyYpuYs4CtyJ86eHTUJyYTFppBmCzzozhT
|
|
||||||
2C2BeKKjzV8OOWQ7yO/2BTEZ7KtOcIr4ZI7iZCnLJF9Yt8x7TURjGRqxsHwT62Ip
|
|
||||||
vcrtm0LkkYv/fQ6pFZZfinKU1OBrZphwHMCU4Mlv411iQg4+NOxLSsVU/kegeKIO
|
|
||||||
adp4Y9g5dfAfdXEXb2Zt7gkmLaWMgf+XNSFDL/wkzRYt74HKwvbIPJQlTZ6pqLxQ
|
|
||||||
yTtiHGuMb7xNDWolpoueo1/lbxaHRRGJaSPs7zUht3IBxb7hiF65Gm3UaJhoeAXc
|
|
||||||
gVleZf/+0titOdkRfTD2N0P0hli7gaiRrbpw8K4joxMFpYrQGUxD8SI376gkOj6o
|
|
||||||
5RWSioPoG9txNM7Co+lVpci7WHhL+Tmhf1SlHyVJGKoNe/z4VHnjHeYlFWRVdDEI
|
|
||||||
OOupZzJQoLnso3lTwR5VEN8xGURnhbGV4MdUfD/6FhwmyHiPlYkytdZIsGsNDOab
|
|
||||||
978PPaKcIpbsZ4gUhshcbn7qaY809lNSpMtg8saYOP4J/5Nu+i9X5bJqOmoX0rKa
|
|
||||||
gAJDY5har+lExRnTEdYEGVB8qen5lqi8r1oYjnDpkSpq6BRoAHA=
|
|
||||||
=uQom
|
|
||||||
-----END PGP SIGNATURE-----
|
|
3276
SPECS/freeipa.spec
Normal file
3276
SPECS/freeipa.spec
Normal file
File diff suppressed because it is too large
Load Diff
5401
SPECS/ipa.spec
5401
SPECS/ipa.spec
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue
Block a user