Compare commits
No commits in common. "c8-stream-DL1" and "c9-beta" have entirely different histories.
c8-stream-
...
c9-beta
2
.gitignore
vendored
2
.gitignore
vendored
@ -1 +1 @@
|
||||
SOURCES/freeipa-4.9.13.tar.gz
|
||||
SOURCES/freeipa-4.13.1.tar.gz
|
||||
|
||||
@ -1 +1 @@
|
||||
da1bb0220894d8dc06afb98dcf087fea38076a79 SOURCES/freeipa-4.9.13.tar.gz
|
||||
bc82618e66b45376464cd2206bf6e7a02f766a11 SOURCES/freeipa-4.13.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 '
|
||||
238
SOURCES/0001-Revert-Replace-netifaces-with-ifaddr.patch
Normal file
238
SOURCES/0001-Revert-Replace-netifaces-with-ifaddr.patch
Normal file
@ -0,0 +1,238 @@
|
||||
From dfeb01d5e4e8934bae8f495bbbd5b6570f3dc862 Mon Sep 17 00:00:00 2001
|
||||
From: Florence Blanc-Renaud <flo@redhat.com>
|
||||
Date: Tue, 4 Jun 2024 13:56:56 +0200
|
||||
Subject: [PATCH] Revert "Replace netifaces with ifaddr"
|
||||
|
||||
This reverts commit 6c6b9354b5f970983655ca5423c726763d9015fa.
|
||||
---
|
||||
doc/requirements.txt | 1 -
|
||||
freeipa.spec.in | 4 +--
|
||||
ipaclient/install/client.py | 43 ++++++++++++++-----------------
|
||||
ipapython/ipautil.py | 51 ++++++++++++++++++++-----------------
|
||||
ipapython/setup.py | 2 +-
|
||||
ipasetup.py.in | 2 +-
|
||||
pylintrc | 1 +
|
||||
7 files changed, 52 insertions(+), 52 deletions(-)
|
||||
|
||||
diff --git a/doc/requirements.txt b/doc/requirements.txt
|
||||
index 8d91b893fbcb5de784f5abdf802b976c6b2ae277..cdaa42658c73e495379d87b7c50195fbd79533ee 100644
|
||||
--- a/doc/requirements.txt
|
||||
+++ b/doc/requirements.txt
|
||||
@@ -11,7 +11,6 @@ m2r2
|
||||
## ipa dependencies
|
||||
dnspython
|
||||
jwcrypto
|
||||
-ifaddr
|
||||
netaddr
|
||||
qrcode
|
||||
six
|
||||
diff --git a/freeipa.spec.in b/freeipa.spec.in
|
||||
index 6803de752bc122bf6e1eafd610d399cde994cad5..a532fe85c34a523519187b6fd1297c198d9f4a4e 100755
|
||||
--- a/freeipa.spec.in
|
||||
+++ b/freeipa.spec.in
|
||||
@@ -403,7 +403,7 @@ BuildRequires: python3-libipa_hbac
|
||||
BuildRequires: python3-libsss_nss_idmap
|
||||
BuildRequires: python3-lxml
|
||||
BuildRequires: python3-netaddr >= %{python_netaddr_version}
|
||||
-BuildRequires: python3-ifaddr
|
||||
+BuildRequires: python3-netifaces
|
||||
BuildRequires: python3-pki >= %{pki_version}
|
||||
BuildRequires: python3-polib
|
||||
BuildRequires: python3-pyasn1
|
||||
@@ -885,7 +885,7 @@ Requires: python3-gssapi >= 1.2.0
|
||||
Requires: python3-jwcrypto >= 0.4.2
|
||||
Requires: python3-libipa_hbac
|
||||
Requires: python3-netaddr >= %{python_netaddr_version}
|
||||
-Requires: python3-ifaddr
|
||||
+Requires: python3-netifaces >= 0.10.4
|
||||
Requires: python3-packaging
|
||||
Requires: python3-pyasn1 >= 0.3.2-2
|
||||
Requires: python3-pyasn1-modules >= 0.3.2-2
|
||||
diff --git a/ipaclient/install/client.py b/ipaclient/install/client.py
|
||||
index 29aff5f413e7f27136e236382031171f068284c5..263dc8e302cccd76412c294e0740af9744a2c62d 100644
|
||||
--- a/ipaclient/install/client.py
|
||||
+++ b/ipaclient/install/client.py
|
||||
@@ -18,7 +18,7 @@ import logging
|
||||
import dns
|
||||
import getpass
|
||||
import gssapi
|
||||
-import ifaddr
|
||||
+import netifaces
|
||||
import os
|
||||
import re
|
||||
import SSSDConfig
|
||||
@@ -1374,36 +1374,31 @@ def unconfigure_nisdomain(statestore):
|
||||
|
||||
|
||||
def get_iface_from_ip(ip_addr):
|
||||
- for adapter in ifaddr.get_adapters():
|
||||
- for ips in adapter.ips:
|
||||
- # IPv6 is reported as a tuple, IPv4 is reported as str
|
||||
- if ip_addr in (ips.ip[0], ips.ip):
|
||||
- return adapter.name
|
||||
+ for interface in netifaces.interfaces():
|
||||
+ if_addrs = netifaces.ifaddresses(interface)
|
||||
+ for family in [netifaces.AF_INET, netifaces.AF_INET6]:
|
||||
+ for ip in if_addrs.get(family, []):
|
||||
+ if ip['addr'] == ip_addr:
|
||||
+ return interface
|
||||
raise RuntimeError("IP %s not assigned to any interface." % ip_addr)
|
||||
|
||||
|
||||
-def __get_ifaddr_adapters(iface=None):
|
||||
+def get_local_ipaddresses(iface=None):
|
||||
if iface:
|
||||
- interfaces = set(iface if isinstance(iface, (list, tuple)) else [iface])
|
||||
+ interfaces = [iface]
|
||||
else:
|
||||
- interfaces = set(adapter.name for adapter in ifaddr.get_adapters())
|
||||
- return [
|
||||
- adapter
|
||||
- for adapter in ifaddr.get_adapters()
|
||||
- if adapter.name in interfaces or adapter.nice_name in interfaces
|
||||
- ]
|
||||
+ interfaces = netifaces.interfaces()
|
||||
|
||||
-
|
||||
-def get_local_ipaddresses(iface=None):
|
||||
ips = []
|
||||
- for adapter in __get_ifaddr_adapters(iface):
|
||||
- for ifip in adapter.ips:
|
||||
- try:
|
||||
- ip_addr = ifip.ip[0] if isinstance(ifip.ip, tuple) else ifip.ip
|
||||
- ips.append(ipautil.CheckedIPAddress(ip_addr))
|
||||
- logger.debug('IP check successful: %s', ip_addr)
|
||||
- except ValueError as e:
|
||||
- logger.debug('IP check failed: %s', e)
|
||||
+ for interface in interfaces:
|
||||
+ if_addrs = netifaces.ifaddresses(interface)
|
||||
+ for family in [netifaces.AF_INET, netifaces.AF_INET6]:
|
||||
+ for ip in if_addrs.get(family, []):
|
||||
+ try:
|
||||
+ ips.append(ipautil.CheckedIPAddress(ip['addr']))
|
||||
+ logger.debug('IP check successful: %s', ip['addr'])
|
||||
+ except ValueError as e:
|
||||
+ logger.debug('IP check failed: %s', e)
|
||||
return ips
|
||||
|
||||
|
||||
diff --git a/ipapython/ipautil.py b/ipapython/ipautil.py
|
||||
index b02d58839ed74215d7253fc23be94846d689f91e..6802dde574d179b2d4bf868a2c38546cc01fc46b 100644
|
||||
--- a/ipapython/ipautil.py
|
||||
+++ b/ipapython/ipautil.py
|
||||
@@ -48,9 +48,9 @@ import six
|
||||
from six.moves import input
|
||||
|
||||
try:
|
||||
- import ifaddr
|
||||
+ import netifaces
|
||||
except ImportError:
|
||||
- ifaddr = None
|
||||
+ netifaces = None
|
||||
|
||||
from ipapython.dn import DN
|
||||
from ipaplatform.paths import paths
|
||||
@@ -203,37 +203,42 @@ class CheckedIPAddress(UnsafeIPAddress):
|
||||
:return: InterfaceDetails named tuple or None if no interface has
|
||||
this address
|
||||
"""
|
||||
- if ifaddr is None:
|
||||
- raise ImportError("ifaddr")
|
||||
+ if netifaces is None:
|
||||
+ raise ImportError("netifaces")
|
||||
logger.debug("Searching for an interface of IP address: %s", self)
|
||||
-
|
||||
if self.version == 4:
|
||||
- family_ips = (
|
||||
- (ip.ip, ip.network_prefix, ip.nice_name)
|
||||
- for ips in [a.ips for a in ifaddr.get_adapters()]
|
||||
- for ip in ips if not isinstance(ip.ip, tuple)
|
||||
- )
|
||||
+ family = netifaces.AF_INET
|
||||
elif self.version == 6:
|
||||
- family_ips = (
|
||||
- (ip.ip[0], ip.network_prefix, ip.nice_name)
|
||||
- for ips in [a.ips for a in ifaddr.get_adapters()]
|
||||
- for ip in ips if isinstance(ip.ip, tuple)
|
||||
- )
|
||||
+ family = netifaces.AF_INET6
|
||||
else:
|
||||
raise ValueError(
|
||||
"Unsupported address family ({})".format(self.version)
|
||||
)
|
||||
|
||||
- for ip, prefix, ifname in family_ips:
|
||||
- ifaddrmask = "{ip}/{prefix}".format(ip=ip, prefix=prefix)
|
||||
- logger.debug(
|
||||
- "Testing local IP address: %s (interface: %s)",
|
||||
- ifaddrmask, ifname)
|
||||
- ifnet = netaddr.IPNetwork(ifaddrmask)
|
||||
+ for interface in netifaces.interfaces():
|
||||
+ for ifdata in netifaces.ifaddresses(interface).get(family, []):
|
||||
+
|
||||
+ # link-local addresses contain '%suffix' that causes parse
|
||||
+ # errors in IPNetwork
|
||||
+ ifaddr = ifdata['addr'].split(u'%', 1)[0]
|
||||
+
|
||||
+ # newer versions of netifaces provide IPv6 netmask in format
|
||||
+ # 'ffff:ffff:ffff:ffff::/64'. We have to split and use prefix
|
||||
+ # or the netmask with older versions
|
||||
+ ifmask = ifdata['netmask'].split(u'/')[-1]
|
||||
+
|
||||
+ ifaddrmask = '{addr}/{netmask}'.format(
|
||||
+ addr=ifaddr,
|
||||
+ netmask=ifmask
|
||||
+ )
|
||||
+ logger.debug(
|
||||
+ "Testing local IP address: %s (interface: %s)",
|
||||
+ ifaddrmask, interface)
|
||||
|
||||
- if ifnet.ip == self:
|
||||
- return InterfaceDetails(ifname, ifnet)
|
||||
+ ifnet = netaddr.IPNetwork(ifaddrmask)
|
||||
|
||||
+ if ifnet.ip == self:
|
||||
+ return InterfaceDetails(interface, ifnet)
|
||||
return None
|
||||
|
||||
def set_ip_net(self, ifnet):
|
||||
diff --git a/ipapython/setup.py b/ipapython/setup.py
|
||||
index b7b25c8b480f0dc80ae20c4027554b27fe6d87b3..ea55f5c729e8ba65a4436ebf3eb4898870558648 100644
|
||||
--- a/ipapython/setup.py
|
||||
+++ b/ipapython/setup.py
|
||||
@@ -48,6 +48,6 @@ if __name__ == '__main__':
|
||||
extras_require={
|
||||
"ldap": ["python-ldap"], # ipapython.ipaldap
|
||||
# CheckedIPAddress.get_matching_interface
|
||||
- "ifaddr": ["ifaddr"],
|
||||
+ "netifaces": ["netifaces"],
|
||||
},
|
||||
)
|
||||
diff --git a/ipasetup.py.in b/ipasetup.py.in
|
||||
index 56a1f3b066c70385949c86d0d35ce393331ad1c5..25eac3b214b4e6443fe29dd3d656c2fdbe84343a 100644
|
||||
--- a/ipasetup.py.in
|
||||
+++ b/ipasetup.py.in
|
||||
@@ -75,7 +75,7 @@ PACKAGE_VERSION = {
|
||||
'ipaserver': 'ipaserver == {}'.format(VERSION),
|
||||
'jwcrypto': 'jwcrypto >= 0.4.2',
|
||||
'kdcproxy': 'kdcproxy >= 0.3',
|
||||
- 'ifaddr': 'ifaddr >= 0.1.7',
|
||||
+ 'netifaces': 'netifaces >= 0.10.4',
|
||||
'python-ldap': 'python-ldap >= 3.0.0',
|
||||
'python-yubico': 'python-yubico >= 1.2.3',
|
||||
'qrcode': 'qrcode >= 5.0',
|
||||
diff --git a/pylintrc b/pylintrc
|
||||
index 50278cc76031af68959a138f6ea185c4288c7155..22053a9b55e9dc8e71c1835a63c9393e8ad6803d 100644
|
||||
--- a/pylintrc
|
||||
+++ b/pylintrc
|
||||
@@ -13,6 +13,7 @@ extension-pkg-allow-list=
|
||||
_ldap,
|
||||
cryptography,
|
||||
gssapi,
|
||||
+ netifaces,
|
||||
lxml.etree,
|
||||
pysss_murmur,
|
||||
|
||||
--
|
||||
2.45.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,91 @@
|
||||
From bf6653418aa772b47e53f1af092382df5810661c Mon Sep 17 00:00:00 2001
|
||||
From: Florence Blanc-Renaud <flo@redhat.com>
|
||||
Date: Wed, 5 Jun 2024 15:03:54 +0200
|
||||
Subject: [PATCH] Revert "custodia: do not use deprecated jwcrypto wrappers"
|
||||
|
||||
This reverts commit 536812080502baa51818d9a33ea6533675800b30.
|
||||
---
|
||||
install/tools/ipa-custodia-check.in | 4 ++--
|
||||
ipaserver/custodia/message/kem.py | 14 +++++++-------
|
||||
2 files changed, 9 insertions(+), 9 deletions(-)
|
||||
|
||||
diff --git a/install/tools/ipa-custodia-check.in b/install/tools/ipa-custodia-check.in
|
||||
index f3bbf8e7f0eca6e35080fb6770c9d4b1887384ea..4f526b433f872fa7d94e827df0bb206b78a9b58d 100644
|
||||
--- a/install/tools/ipa-custodia-check.in
|
||||
+++ b/install/tools/ipa-custodia-check.in
|
||||
@@ -192,10 +192,10 @@ class IPACustodiaTester:
|
||||
usage, IPA_CUSTODIA_KEYFILE
|
||||
))
|
||||
|
||||
- if pkey.get('kid') != self.host_spn:
|
||||
+ if pkey.key_id != self.host_spn:
|
||||
raise self.error( # pylint: disable=raising-bad-type, #4772
|
||||
"KID '{}' != host service principal name '{}' "
|
||||
- "(usage: {})".format(pkey.get('kid'), self.host_spn, usage),
|
||||
+ "(usage: {})".format(pkey.key_id, self.host_spn, usage),
|
||||
fatal=True
|
||||
)
|
||||
else:
|
||||
diff --git a/ipaserver/custodia/message/kem.py b/ipaserver/custodia/message/kem.py
|
||||
index c2996bc921aeac0241111d95194977f9aa630cae..fbbc3fe46f60d25fe1754af70b18bb769c127fa2 100644
|
||||
--- a/ipaserver/custodia/message/kem.py
|
||||
+++ b/ipaserver/custodia/message/kem.py
|
||||
@@ -85,7 +85,7 @@ class KEMKeysStore(SimplePathAuthz):
|
||||
if self._alg is None:
|
||||
alg = self.config.get('signing_algorithm', None)
|
||||
if alg is None:
|
||||
- ktype = self.server_keys[KEY_USAGE_SIG]['kty']
|
||||
+ ktype = self.server_keys[KEY_USAGE_SIG].key_type
|
||||
if ktype == 'RSA':
|
||||
alg = 'RS256'
|
||||
elif ktype == 'EC':
|
||||
@@ -125,9 +125,9 @@ class KEMHandler(MessageHandler):
|
||||
if 'kid' not in header:
|
||||
raise InvalidMessage("Missing key identifier")
|
||||
|
||||
- key = self.kkstore.find_key(header.get('kid'), usage)
|
||||
+ key = self.kkstore.find_key(header['kid'], usage)
|
||||
if key is None:
|
||||
- raise UnknownPublicKey('Key found [kid:%s]' % header.get('kid'))
|
||||
+ raise UnknownPublicKey('Key found [kid:%s]' % header['kid'])
|
||||
return json_decode(key)
|
||||
|
||||
def parse(self, msg, name):
|
||||
@@ -179,14 +179,14 @@ class KEMHandler(MessageHandler):
|
||||
self.msg_type = 'kem'
|
||||
|
||||
return {'type': self.msg_type,
|
||||
- 'value': {'kid': self.client_keys[KEY_USAGE_ENC].get('kid'),
|
||||
+ 'value': {'kid': self.client_keys[KEY_USAGE_ENC].key_id,
|
||||
'claims': claims}}
|
||||
|
||||
def reply(self, output):
|
||||
if self.client_keys is None:
|
||||
raise UnknownPublicKey("Peer key not defined")
|
||||
|
||||
- ktype = self.client_keys[KEY_USAGE_ENC]['kty']
|
||||
+ ktype = self.client_keys[KEY_USAGE_ENC].key_type
|
||||
if ktype == 'RSA':
|
||||
enc = ('RSA-OAEP', 'A256CBC-HS512')
|
||||
else:
|
||||
@@ -224,7 +224,7 @@ class KEMClient:
|
||||
|
||||
|
||||
def make_sig_kem(name, value, key, alg):
|
||||
- header = {'kid': key.get('kid'), 'alg': alg}
|
||||
+ header = {'kid': key.key_id, 'alg': alg}
|
||||
claims = {'sub': name, 'exp': int(time.time() + (5 * 60))}
|
||||
if value is not None:
|
||||
claims['value'] = value
|
||||
@@ -235,7 +235,7 @@ def make_sig_kem(name, value, key, alg):
|
||||
|
||||
def make_enc_kem(name, value, sig_key, alg, enc_key, enc):
|
||||
plaintext = make_sig_kem(name, value, sig_key, alg)
|
||||
- eprot = {'kid': enc_key.get('kid'), 'alg': enc[0], 'enc': enc[1]}
|
||||
+ eprot = {'kid': enc_key.key_id, 'alg': enc[0], 'enc': enc[1]}
|
||||
jwe = JWE(plaintext, json_encode(eprot))
|
||||
jwe.add_recipient(enc_key)
|
||||
return jwe.serialize(compact=True)
|
||||
--
|
||||
2.45.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
|
||||
|
||||
890
SOURCES/0003-Revert-Remove-NIS-server-support.patch
Normal file
890
SOURCES/0003-Revert-Remove-NIS-server-support.patch
Normal file
@ -0,0 +1,890 @@
|
||||
From 941d6a54e163ca0d5eee6ac391c0d2f5afb6cf55 Mon Sep 17 00:00:00 2001
|
||||
From: Florence Blanc-Renaud <flo@redhat.com>
|
||||
Date: Tue, 9 Dec 2025 10:27:12 +0100
|
||||
Subject: [PATCH] Revert "Remove NIS server support"
|
||||
|
||||
This reverts commit e98a80827bcc42dc16b516355077fed844220107.
|
||||
---
|
||||
freeipa.spec.in | 2 +
|
||||
install/share/Makefile.am | 2 +
|
||||
install/share/nis-update.uldif | 38 ++++
|
||||
install/share/nis.uldif | 96 ++++++++++
|
||||
install/tools/Makefile.am | 2 +
|
||||
install/tools/ipa-compat-manage.in | 17 +-
|
||||
install/tools/ipa-nis-manage.in | 205 +++++++++++++++++++++
|
||||
install/tools/man/Makefile.am | 1 +
|
||||
install/tools/man/ipa-nis-manage.1 | 51 +++++
|
||||
install/updates/10-enable-betxn.update | 3 +
|
||||
install/updates/50-nis.update | 3 +
|
||||
install/updates/Makefile.am | 1 +
|
||||
ipaplatform/base/paths.py | 2 +
|
||||
ipaserver/install/ipa_migrate.py | 27 ++-
|
||||
ipaserver/install/ipa_migrate_constants.py | 24 +++
|
||||
ipaserver/install/plugins/update_nis.py | 92 +++++++++
|
||||
ipatests/test_cmdline/test_cli.py | 1 +
|
||||
ipatests/test_integration/test_commands.py | 87 +++++++++
|
||||
18 files changed, 638 insertions(+), 16 deletions(-)
|
||||
create mode 100644 install/share/nis-update.uldif
|
||||
create mode 100644 install/share/nis.uldif
|
||||
create mode 100644 install/tools/ipa-nis-manage.in
|
||||
create mode 100644 install/tools/man/ipa-nis-manage.1
|
||||
create mode 100644 install/updates/50-nis.update
|
||||
create mode 100644 ipaserver/install/plugins/update_nis.py
|
||||
|
||||
diff --git a/freeipa.spec.in b/freeipa.spec.in
|
||||
index b93199a5783a89e0ff58affd3d416556f72dd00c..6ee0f43f059dad4bba2288b1b0d3a63437371861 100644
|
||||
--- a/freeipa.spec.in
|
||||
+++ b/freeipa.spec.in
|
||||
@@ -1619,6 +1619,7 @@ fi
|
||||
%{_sbindir}/ipa-ldap-updater
|
||||
%{_sbindir}/ipa-otptoken-import
|
||||
%{_sbindir}/ipa-compat-manage
|
||||
+%{_sbindir}/ipa-nis-manage
|
||||
%{_sbindir}/ipa-managed-entries
|
||||
%{_sbindir}/ipactl
|
||||
%{_sbindir}/ipa-advise
|
||||
@@ -1694,6 +1695,7 @@ fi
|
||||
%{_mandir}/man1/ipa-ca-install.1*
|
||||
%{_mandir}/man1/ipa-kra-install.1*
|
||||
%{_mandir}/man1/ipa-compat-manage.1*
|
||||
+%{_mandir}/man1/ipa-nis-manage.1*
|
||||
%{_mandir}/man1/ipa-managed-entries.1*
|
||||
%{_mandir}/man1/ipa-ldap-updater.1*
|
||||
%{_mandir}/man8/ipactl.8*
|
||||
diff --git a/install/share/Makefile.am b/install/share/Makefile.am
|
||||
index bb2eb7531b083e36827ba1ee111e39c29589acaa..87578d29644f897622f46a56c06cb24c7df3efe6 100644
|
||||
--- a/install/share/Makefile.am
|
||||
+++ b/install/share/Makefile.am
|
||||
@@ -66,6 +66,8 @@ dist_app_DATA = \
|
||||
master-entry.ldif \
|
||||
memberof-task.ldif \
|
||||
memberof-conf.ldif \
|
||||
+ nis.uldif \
|
||||
+ nis-update.uldif \
|
||||
opendnssec_conf.template \
|
||||
opendnssec_kasp.template \
|
||||
unique-attributes.ldif \
|
||||
diff --git a/install/share/nis-update.uldif b/install/share/nis-update.uldif
|
||||
new file mode 100644
|
||||
index 0000000000000000000000000000000000000000..e602c1de061fbcece349b2d86970c4db5051473b
|
||||
--- /dev/null
|
||||
+++ b/install/share/nis-update.uldif
|
||||
@@ -0,0 +1,38 @@
|
||||
+# Updates for NIS
|
||||
+
|
||||
+# Correct syntax error that caused users to not appear
|
||||
+dn: nis-domain=$DOMAIN+nis-map=netgroup, cn=NIS Server, cn=plugins, cn=config
|
||||
+replace:nis-value-format: %merge(" ","%{memberNisNetgroup}","(%link(\"%ifeq(\\\"hostCategory\\\",\\\"all\\\",\\\"\\\",\\\"%collect(\\\\\\\"%{externalHost}\\\\\\\",\\\\\\\"%deref(\\\\\\\\\\\\\\\"memberHost\\\\\\\\\\\\\\\",\\\\\\\\\\\\\\\"fqdn\\\\\\\\\\\\\\\")\\\\\\\",\\\\\\\"%deref_r(\\\\\\\\\\\\\\\"member\\\\\\\\\\\\\\\",\\\\\\\\\\\\\\\"fqdn\\\\\\\\\\\\\\\")\\\\\\\",\\\\\\\"%deref_r(\\\\\\\\\\\\\\\"memberHost\\\\\\\\\\\\\\\",\\\\\\\\\\\\\\\"member\\\\\\\\\\\\\\\",\\\\\\\\\\\\\\\"fqdn\\\\\\\\\\\\\\\")\\\\\\\")\\\")\",\"-\",\",\",\"%ifeq(\\\"userCategory\\\",\\\"all\\\",\\\"\\\",\\\"%collect(\\\"%deref(\\\\\\\"memberUser\\\\\\\",\\\\\\\"uid\\\\\\\")\\\",\\\"%deref_r(\\\\\\\"member\\\\\\\",\\\\\\\"uid\\\\\\\")\\\",\\\"%deref_r(\\\\\\\"memberUser\\\\\\\",\\\\\\\"member\\\\\\\",\\\\\\\"uid\\\\\\\")\\\")\\\")\",\"-\"),%{nisDomainName:-})")::%merge(" ","%{memberNisNetgroup}","(%link(\"%ifeq(\\\"hostCategory\\\",\\\"all\\\",\\\"\\\",\\\"%collect(\\\\\\\"%{externalHost}\\\\\\\",\\\\\\\"%deref(\\\\\\\\\\\\\\\"memberHost\\\\\\\\\\\\\\\",\\\\\\\\\\\\\\\"fqdn\\\\\\\\\\\\\\\")\\\\\\\",\\\\\\\"%deref_r(\\\\\\\\\\\\\\\"member\\\\\\\\\\\\\\\",\\\\\\\\\\\\\\\"fqdn\\\\\\\\\\\\\\\")\\\\\\\",\\\\\\\"%deref_r(\\\\\\\\\\\\\\\"memberHost\\\\\\\\\\\\\\\",\\\\\\\\\\\\\\\"member\\\\\\\\\\\\\\\",\\\\\\\\\\\\\\\"fqdn\\\\\\\\\\\\\\\")\\\\\\\")\\\")\",\"-\",\",\",\"%ifeq(\\\"userCategory\\\",\\\"all\\\",\\\"\\\",\\\"%collect(\\\\\\\"%deref(\\\\\\\\\\\\\\\"memberUser\\\\\\\\\\\\\\\",\\\\\\\\\\\\\\\"uid\\\\\\\\\\\\\\\")\\\\\\\",\\\\\\\"%deref_r(\\\\\\\\\\\\\\\"member\\\\\\\\\\\\\\\",\\\\\\\\\\\\\\\"uid\\\\\\\\\\\\\\\")\\\\\\\",\\\\\\\"%deref_r(\\\\\\\\\\\\\\\"memberUser\\\\\\\\\\\\\\\",\\\\\\\\\\\\\\\"member\\\\\\\\\\\\\\\",\\\\\\\\\\\\\\\"uid\\\\\\\\\\\\\\\")\\\\\\\")\\\")\",\"-\"),%{nisDomainName:-})")
|
||||
+
|
||||
+# Correct syntax error that caused nested netgroups to not work
|
||||
+# https://bugzilla.redhat.com/show_bug.cgi?id=788625
|
||||
+dn: nis-domain=$DOMAIN+nis-map=netgroup, cn=NIS Server, cn=plugins, cn=config
|
||||
+replace:nis-value-format: %merge(" ","%{memberNisNetgroup}","(%link(\"%ifeq(\\\"hostCategory\\\",\\\"all\\\",\\\"\\\",\\\"%collect(\\\\\\\"%{externalHost}\\\\\\\",\\\\\\\"%deref(\\\\\\\\\\\\\\\"memberHost\\\\\\\\\\\\\\\",\\\\\\\\\\\\\\\"fqdn\\\\\\\\\\\\\\\")\\\\\\\",\\\\\\\"%deref_r(\\\\\\\\\\\\\\\"member\\\\\\\\\\\\\\\",\\\\\\\\\\\\\\\"fqdn\\\\\\\\\\\\\\\")\\\\\\\",\\\\\\\"%deref_r(\\\\\\\\\\\\\\\"memberHost\\\\\\\\\\\\\\\",\\\\\\\\\\\\\\\"member\\\\\\\\\\\\\\\",\\\\\\\\\\\\\\\"fqdn\\\\\\\\\\\\\\\")\\\\\\\")\\\")\",\"-\",\",\",\"%ifeq(\\\"userCategory\\\",\\\"all\\\",\\\"\\\",\\\"%collect(\\\\\\\"%deref(\\\\\\\\\\\\\\\"memberUser\\\\\\\\\\\\\\\",\\\\\\\\\\\\\\\"uid\\\\\\\\\\\\\\\")\\\\\\\",\\\\\\\"%deref_r(\\\\\\\\\\\\\\\"member\\\\\\\\\\\\\\\",\\\\\\\\\\\\\\\"uid\\\\\\\\\\\\\\\")\\\\\\\",\\\\\\\"%deref_r(\\\\\\\\\\\\\\\"memberUser\\\\\\\\\\\\\\\",\\\\\\\\\\\\\\\"member\\\\\\\\\\\\\\\",\\\\\\\\\\\\\\\"uid\\\\\\\\\\\\\\\")\\\\\\\")\\\")\",\"-\"),%{nisDomainName:-})")::%merge(" ","%deref_f(\"member\",\"(objectclass=ipanisNetgroup)\",\"cn\")","(%link(\"%ifeq(\\\"hostCategory\\\",\\\"all\\\",\\\"\\\",\\\"%collect(\\\\\\\"%{externalHost}\\\\\\\",\\\\\\\"%deref(\\\\\\\\\\\\\\\"memberHost\\\\\\\\\\\\\\\",\\\\\\\\\\\\\\\"fqdn\\\\\\\\\\\\\\\")\\\\\\\",\\\\\\\"%deref_r(\\\\\\\\\\\\\\\"member\\\\\\\\\\\\\\\",\\\\\\\\\\\\\\\"fqdn\\\\\\\\\\\\\\\")\\\\\\\",\\\\\\\"%deref_r(\\\\\\\\\\\\\\\"memberHost\\\\\\\\\\\\\\\",\\\\\\\\\\\\\\\"member\\\\\\\\\\\\\\\",\\\\\\\\\\\\\\\"fqdn\\\\\\\\\\\\\\\")\\\\\\\")\\\")\",\"-\",\",\",\"%ifeq(\\\"userCategory\\\",\\\"all\\\",\\\"\\\",\\\"%collect(\\\\\\\"%deref(\\\\\\\\\\\\\\\"memberUser\\\\\\\\\\\\\\\",\\\\\\\\\\\\\\\"uid\\\\\\\\\\\\\\\")\\\\\\\",\\\\\\\"%deref_r(\\\\\\\\\\\\\\\"member\\\\\\\\\\\\\\\",\\\\\\\\\\\\\\\"uid\\\\\\\\\\\\\\\")\\\\\\\",\\\\\\\"%deref_r(\\\\\\\\\\\\\\\"memberUser\\\\\\\\\\\\\\\",\\\\\\\\\\\\\\\"member\\\\\\\\\\\\\\\",\\\\\\\\\\\\\\\"uid\\\\\\\\\\\\\\\")\\\\\\\")\\\")\",\"-\"),%{nisDomainName:-})")
|
||||
+
|
||||
+# Make the padding an expression so usercat and hostcat always gets
|
||||
+# evaluated when displaying entries.
|
||||
+# https://bugzilla.redhat.com/show_bug.cgi?id=767372
|
||||
+dn: nis-domain=$DOMAIN+nis-map=netgroup, cn=NIS Server, cn=plugins, cn=config
|
||||
+replace:nis-value-format: %merge(" ","%deref_f(\"member\",\"(objectclass=ipanisNetgroup)\",\"cn\")","(%link(\"%ifeq(\\\"hostCategory\\\",\\\"all\\\",\\\"\\\",\\\"%collect(\\\\\\\"%{externalHost}\\\\\\\",\\\\\\\"%deref(\\\\\\\\\\\\\\\"memberHost\\\\\\\\\\\\\\\",\\\\\\\\\\\\\\\"fqdn\\\\\\\\\\\\\\\")\\\\\\\",\\\\\\\"%deref_r(\\\\\\\\\\\\\\\"member\\\\\\\\\\\\\\\",\\\\\\\\\\\\\\\"fqdn\\\\\\\\\\\\\\\")\\\\\\\",\\\\\\\"%deref_r(\\\\\\\\\\\\\\\"memberHost\\\\\\\\\\\\\\\",\\\\\\\\\\\\\\\"member\\\\\\\\\\\\\\\",\\\\\\\\\\\\\\\"fqdn\\\\\\\\\\\\\\\")\\\\\\\")\\\")\",\"-\",\",\",\"%ifeq(\\\"userCategory\\\",\\\"all\\\",\\\"\\\",\\\"%collect(\\\\\\\"%deref(\\\\\\\\\\\\\\\"memberUser\\\\\\\\\\\\\\\",\\\\\\\\\\\\\\\"uid\\\\\\\\\\\\\\\")\\\\\\\",\\\\\\\"%deref_r(\\\\\\\\\\\\\\\"member\\\\\\\\\\\\\\\",\\\\\\\\\\\\\\\"uid\\\\\\\\\\\\\\\")\\\\\\\",\\\\\\\"%deref_r(\\\\\\\\\\\\\\\"memberUser\\\\\\\\\\\\\\\",\\\\\\\\\\\\\\\"member\\\\\\\\\\\\\\\",\\\\\\\\\\\\\\\"uid\\\\\\\\\\\\\\\")\\\\\\\")\\\")\",\"-\"),%{nisDomainName:-})")::%merge(" ","%deref_f(\"member\",\"(objectclass=ipanisNetgroup)\",\"cn\")","(%link(\"%ifeq(\\\"hostCategory\\\",\\\"all\\\",\\\"\\\",\\\"%collect(\\\\\\\"%{externalHost}\\\\\\\",\\\\\\\"%deref(\\\\\\\\\\\\\\\"memberHost\\\\\\\\\\\\\\\",\\\\\\\\\\\\\\\"fqdn\\\\\\\\\\\\\\\")\\\\\\\",\\\\\\\"%deref_r(\\\\\\\\\\\\\\\"member\\\\\\\\\\\\\\\",\\\\\\\\\\\\\\\"fqdn\\\\\\\\\\\\\\\")\\\\\\\",\\\\\\\"%deref_r(\\\\\\\\\\\\\\\"memberHost\\\\\\\\\\\\\\\",\\\\\\\\\\\\\\\"member\\\\\\\\\\\\\\\",\\\\\\\\\\\\\\\"fqdn\\\\\\\\\\\\\\\")\\\\\\\")\\\")\",\"%ifeq(\\\"hostCategory\\\",\\\"all\\\",\\\"\\\",\\\"-\\\")\",\",\",\"%ifeq(\\\"userCategory\\\",\\\"all\\\",\\\"\\\",\\\"%collect(\\\\\\\"%deref(\\\\\\\\\\\\\\\"memberUser\\\\\\\\\\\\\\\",\\\\\\\\\\\\\\\"uid\\\\\\\\\\\\\\\")\\\\\\\",\\\\\\\"%deref_r(\\\\\\\\\\\\\\\"member\\\\\\\\\\\\\\\",\\\\\\\\\\\\\\\"uid\\\\\\\\\\\\\\\")\\\\\\\",\\\\\\\"%deref_r(\\\\\\\\\\\\\\\"memberUser\\\\\\\\\\\\\\\",\\\\\\\\\\\\\\\"member\\\\\\\\\\\\\\\",\\\\\\\\\\\\\\\"uid\\\\\\\\\\\\\\\")\\\\\\\")\\\")\",\"%ifeq(\\\"userCategory\\\",\\\"all\\\",\\\"\\\",\\\"-\\\")\"),%{nisDomainName:-})")
|
||||
+
|
||||
+dn: nis-domain=$DOMAIN+nis-map=ethers.byaddr, cn=NIS Server, cn=plugins, cn=config
|
||||
+default:objectclass: top
|
||||
+default:objectclass: extensibleObject
|
||||
+default:nis-domain: $DOMAIN
|
||||
+default:nis-map: ethers.byaddr
|
||||
+default:nis-base: cn=computers, cn=accounts, $SUFFIX
|
||||
+default:nis-filter: (&(macAddress=*)(fqdn=*)(objectClass=ipaHost))
|
||||
+default:nis-keys-format: %mregsub("%{macAddress} %{fqdn}","(..)[:\\\|-](..)[:\\\|-](..)[:\\\|-](..)[:\\\|-](..)[:\\\|-](..) (.*)","%1:%2:%3:%4:%5:%6")
|
||||
+default:nis-values-format: %mregsub("%{macAddress} %{fqdn}","(..)[:\\\|-](..)[:\\\|-](..)[:\\\|-](..)[:\\\|-](..)[:\\\|-](..) (.*)","%1:%2:%3:%4:%5:%6 %7")
|
||||
+default:nis-secure: no
|
||||
+
|
||||
+dn: nis-domain=$DOMAIN+nis-map=ethers.byname, cn=NIS Server, cn=plugins, cn=config
|
||||
+default:objectclass: top
|
||||
+default:objectclass: extensibleObject
|
||||
+default:nis-domain: $DOMAIN
|
||||
+default:nis-map: ethers.byname
|
||||
+default:nis-base: cn=computers, cn=accounts, $SUFFIX
|
||||
+default:nis-filter: (&(macAddress=*)(fqdn=*)(objectClass=ipaHost))
|
||||
+default:nis-keys-format: %mregsub("%{macAddress} %{fqdn}","(..)[:\\\|-](..)[:\\\|-](..)[:\\\|-](..)[:\\\|-](..)[:\\\|-](..) (.*)","%7")
|
||||
+default:nis-values-format: %mregsub("%{macAddress} %{fqdn}","(..)[:\\\|-](..)[:\\\|-](..)[:\\\|-](..)[:\\\|-](..)[:\\\|-](..) (.*)","%1:%2:%3:%4:%5:%6 %7")
|
||||
+default:nis-secure: no
|
||||
diff --git a/install/share/nis.uldif b/install/share/nis.uldif
|
||||
new file mode 100644
|
||||
index 0000000000000000000000000000000000000000..1735fb55299d28ab40d864b24062309fca43241f
|
||||
--- /dev/null
|
||||
+++ b/install/share/nis.uldif
|
||||
@@ -0,0 +1,96 @@
|
||||
+dn: cn=NIS Server, cn=plugins, cn=config
|
||||
+default:objectclass: top
|
||||
+default:objectclass: nsSlapdPlugin
|
||||
+default:objectclass: extensibleObject
|
||||
+default:cn: NIS Server
|
||||
+default:nsslapd-pluginpath: /usr/lib$LIBARCH/dirsrv/plugins/nisserver-plugin.so
|
||||
+default:nsslapd-plugininitfunc: nis_plugin_init
|
||||
+default:nsslapd-plugintype: object
|
||||
+default:nsslapd-pluginbetxn: on
|
||||
+default:nsslapd-pluginenabled: on
|
||||
+default:nsslapd-pluginid: nis-server
|
||||
+default:nsslapd-pluginversion: 0.10
|
||||
+default:nsslapd-pluginvendor: redhat.com
|
||||
+default:nsslapd-plugindescription: NIS Server Plugin
|
||||
+default:nis-tcp-wrappers-name: nis-server
|
||||
+
|
||||
+dn: nis-domain=$DOMAIN+nis-map=passwd.byname, cn=NIS Server, cn=plugins, cn=config
|
||||
+default:objectclass: top
|
||||
+default:objectclass: extensibleObject
|
||||
+default:nis-domain: $DOMAIN
|
||||
+default:nis-map: passwd.byname
|
||||
+default:nis-base: cn=users, cn=accounts, $SUFFIX
|
||||
+default:nis-secure: no
|
||||
+
|
||||
+dn: nis-domain=$DOMAIN+nis-map=passwd.byuid, cn=NIS Server, cn=plugins, cn=config
|
||||
+default:objectclass: top
|
||||
+default:objectclass: extensibleObject
|
||||
+default:nis-domain: $DOMAIN
|
||||
+default:nis-map: passwd.byuid
|
||||
+default:nis-base: cn=users, cn=accounts, $SUFFIX
|
||||
+default:nis-secure: no
|
||||
+
|
||||
+dn: nis-domain=$DOMAIN+nis-map=group.byname, cn=NIS Server, cn=plugins, cn=config
|
||||
+default:objectclass: top
|
||||
+default:objectclass: extensibleObject
|
||||
+default:nis-domain: $DOMAIN
|
||||
+default:nis-map: group.byname
|
||||
+default:nis-base: cn=groups, cn=accounts, $SUFFIX
|
||||
+default:nis-secure: no
|
||||
+
|
||||
+dn: nis-domain=$DOMAIN+nis-map=group.bygid, cn=NIS Server, cn=plugins, cn=config
|
||||
+default:objectclass: top
|
||||
+default:objectclass: extensibleObject
|
||||
+default:nis-domain: $DOMAIN
|
||||
+default:nis-map: group.bygid
|
||||
+default:nis-base: cn=groups, cn=accounts, $SUFFIX
|
||||
+default:nis-secure: no
|
||||
+
|
||||
+dn: nis-domain=$DOMAIN+nis-map=netid.byname, cn=NIS Server, cn=plugins, cn=config
|
||||
+default:objectclass: top
|
||||
+default:objectclass: extensibleObject
|
||||
+default:nis-domain: $DOMAIN
|
||||
+default:nis-map: netid.byname
|
||||
+default:nis-base: cn=users, cn=accounts, $SUFFIX
|
||||
+default:nis-secure: no
|
||||
+
|
||||
+# Note that the escapes in this entry can be quite confusing. The trick
|
||||
+# is that each level of nesting requires (2^n) - 1 escapes. So the
|
||||
+# first level is \", the second is \\\", the third is \\\\\\\", etc.
|
||||
+# (1, 3, 7, 15, more than that and you'll go insane)
|
||||
+
|
||||
+# Note that this configuration mirrors the Schema Compat configuration for
|
||||
+# triples.
|
||||
+dn: nis-domain=$DOMAIN+nis-map=netgroup, cn=NIS Server, cn=plugins, cn=config
|
||||
+default:objectclass: top
|
||||
+default:objectclass: extensibleObject
|
||||
+default:nis-domain: $DOMAIN
|
||||
+default:nis-map: netgroup
|
||||
+default:nis-base: cn=ng, cn=alt, $SUFFIX
|
||||
+default:nis-filter: (objectClass=ipanisNetgroup)
|
||||
+default:nis-key-format: %{cn}
|
||||
+default:nis-value-format:%merge(" ","%deref_f(\"member\",\"(objectclass=ipanisNetgroup)\",\"cn\")","(%link(\"%ifeq(\\\"hostCategory\\\",\\\"all\\\",\\\"\\\",\\\"%collect(\\\\\\\"%{externalHost}\\\\\\\",\\\\\\\"%deref(\\\\\\\\\\\\\\\"memberHost\\\\\\\\\\\\\\\",\\\\\\\\\\\\\\\"fqdn\\\\\\\\\\\\\\\")\\\\\\\",\\\\\\\"%deref_r(\\\\\\\\\\\\\\\"member\\\\\\\\\\\\\\\",\\\\\\\\\\\\\\\"fqdn\\\\\\\\\\\\\\\")\\\\\\\",\\\\\\\"%deref_r(\\\\\\\\\\\\\\\"memberHost\\\\\\\\\\\\\\\",\\\\\\\\\\\\\\\"member\\\\\\\\\\\\\\\",\\\\\\\\\\\\\\\"fqdn\\\\\\\\\\\\\\\")\\\\\\\")\\\")\",\"%ifeq(\\\"hostCategory\\\",\\\"all\\\",\\\"\\\",\\\"-\\\")\",\",\",\"%ifeq(\\\"userCategory\\\",\\\"all\\\",\\\"\\\",\\\"%collect(\\\\\\\"%deref(\\\\\\\\\\\\\\\"memberUser\\\\\\\\\\\\\\\",\\\\\\\\\\\\\\\"uid\\\\\\\\\\\\\\\")\\\\\\\",\\\\\\\"%deref_r(\\\\\\\\\\\\\\\"member\\\\\\\\\\\\\\\",\\\\\\\\\\\\\\\"uid\\\\\\\\\\\\\\\")\\\\\\\",\\\\\\\"%deref_r(\\\\\\\\\\\\\\\"memberUser\\\\\\\\\\\\\\\",\\\\\\\\\\\\\\\"member\\\\\\\\\\\\\\\",\\\\\\\\\\\\\\\"uid\\\\\\\\\\\\\\\")\\\\\\\")\\\")\",\"%ifeq(\\\"userCategory\\\",\\\"all\\\",\\\"\\\",\\\"-\\\")\"),%{nisDomainName:-})")
|
||||
+default:nis-secure: no
|
||||
+
|
||||
+dn: nis-domain=$DOMAIN+nis-map=ethers.byaddr, cn=NIS Server, cn=plugins, cn=config
|
||||
+default:objectclass: top
|
||||
+default:objectclass: extensibleObject
|
||||
+default:nis-domain: $DOMAIN
|
||||
+default:nis-map: ethers.byaddr
|
||||
+default:nis-base: cn=computers, cn=accounts, $SUFFIX
|
||||
+default:nis-filter: (&(macAddress=*)(fqdn=*)(objectClass=ipaHost))
|
||||
+default:nis-keys-format: %mregsub("%{macAddress} %{fqdn}","(..)[:\\\|-](..)[:\\\|-](..)[:\\\|-](..)[:\\\|-](..)[:\\\|-](..) (.*)","%1:%2:%3:%4:%5:%6")
|
||||
+default:nis-values-format: %mregsub("%{macAddress} %{fqdn}","(..)[:\\\|-](..)[:\\\|-](..)[:\\\|-](..)[:\\\|-](..)[:\\\|-](..) (.*)","%1:%2:%3:%4:%5:%6 %7")
|
||||
+default:nis-secure: no
|
||||
+
|
||||
+dn: nis-domain=$DOMAIN+nis-map=ethers.byname, cn=NIS Server, cn=plugins, cn=config
|
||||
+default:objectclass: top
|
||||
+default:objectclass: extensibleObject
|
||||
+default:nis-domain: $DOMAIN
|
||||
+default:nis-map: ethers.byname
|
||||
+default:nis-base: cn=computers, cn=accounts, $SUFFIX
|
||||
+default:nis-filter: (&(macAddress=*)(fqdn=*)(objectClass=ipaHost))
|
||||
+default:nis-keys-format: %mregsub("%{macAddress} %{fqdn}","(..)[:\\\|-](..)[:\\\|-](..)[:\\\|-](..)[:\\\|-](..)[:\\\|-](..) (.*)","%7")
|
||||
+default:nis-values-format: %mregsub("%{macAddress} %{fqdn}","(..)[:\\\|-](..)[:\\\|-](..)[:\\\|-](..)[:\\\|-](..)[:\\\|-](..) (.*)","%1:%2:%3:%4:%5:%6 %7")
|
||||
+default:nis-secure: no
|
||||
+
|
||||
diff --git a/install/tools/Makefile.am b/install/tools/Makefile.am
|
||||
index 74cd11c67dd3af623dd29802935ae63fe82fcecb..ca484ec37969c9c06ae7b408b55fa30cd4e8e4fe 100644
|
||||
--- a/install/tools/Makefile.am
|
||||
+++ b/install/tools/Makefile.am
|
||||
@@ -19,6 +19,7 @@ dist_noinst_DATA = \
|
||||
ipa-server-upgrade.in \
|
||||
ipactl.in \
|
||||
ipa-compat-manage.in \
|
||||
+ ipa-nis-manage.in \
|
||||
ipa-managed-entries.in \
|
||||
ipa-ldap-updater.in \
|
||||
ipa-otptoken-import.in \
|
||||
@@ -56,6 +57,7 @@ nodist_sbin_SCRIPTS = \
|
||||
ipa-server-upgrade \
|
||||
ipactl \
|
||||
ipa-compat-manage \
|
||||
+ ipa-nis-manage \
|
||||
ipa-managed-entries \
|
||||
ipa-ldap-updater \
|
||||
ipa-otptoken-import \
|
||||
diff --git a/install/tools/ipa-compat-manage.in b/install/tools/ipa-compat-manage.in
|
||||
index fb25c22edd5b2fac123144846ffc232cb5bbecc7..9650abd6f83ebc0a8ef347fee83989d4e9f13f09 100644
|
||||
--- a/install/tools/ipa-compat-manage.in
|
||||
+++ b/install/tools/ipa-compat-manage.in
|
||||
@@ -25,7 +25,6 @@ import sys
|
||||
from ipaplatform.paths import paths
|
||||
try:
|
||||
from ipapython import ipautil, config
|
||||
- from ipapython.ipaldap import realm_to_serverid
|
||||
from ipaserver.install import installutils
|
||||
from ipaserver.install.ldapupdate import LDAPUpdate
|
||||
from ipalib import api, errors
|
||||
@@ -153,19 +152,9 @@ def main():
|
||||
try:
|
||||
entry = get_entry(nis_config_dn)
|
||||
# We can't disable schema compat if the NIS plugin is enabled
|
||||
- if (
|
||||
- entry is not None
|
||||
- and entry.get("nsslapd-pluginenabled", [""])[0].lower() == "on"
|
||||
- ):
|
||||
- instance = realm_to_serverid(api.env.realm)
|
||||
- print(
|
||||
- "The NIS plugin is configured, cannot "
|
||||
- "disable compatibility.", file=sys.stderr,
|
||||
- )
|
||||
- print(
|
||||
- f"Run \"dsconf {instance} plugin set --enabled off "
|
||||
- "'NIS Server'\" first.", file=sys.stderr,
|
||||
- )
|
||||
+ if entry is not None and entry.get('nsslapd-pluginenabled', [''])[0].lower() == 'on':
|
||||
+ print("The NIS plugin is configured, cannot disable compatibility.", file=sys.stderr)
|
||||
+ print("Run 'ipa-nis-manage disable' first.", file=sys.stderr)
|
||||
retval = 2
|
||||
except errors.ExecutionError as lde:
|
||||
print("An error occurred while talking to the server.")
|
||||
diff --git a/install/tools/ipa-nis-manage.in b/install/tools/ipa-nis-manage.in
|
||||
new file mode 100644
|
||||
index 0000000000000000000000000000000000000000..6b156ce6a80d05d9cd2d7cab48e6dba08b0954f5
|
||||
--- /dev/null
|
||||
+++ b/install/tools/ipa-nis-manage.in
|
||||
@@ -0,0 +1,205 @@
|
||||
+#!/usr/bin/python3
|
||||
+# Authors: Rob Crittenden <rcritten@redhat.com>
|
||||
+# Authors: Simo Sorce <ssorce@redhat.com>
|
||||
+#
|
||||
+# Copyright (C) 2009 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/>.
|
||||
+#
|
||||
+
|
||||
+from __future__ import print_function
|
||||
+
|
||||
+import sys
|
||||
+import os
|
||||
+from ipaplatform.paths import paths
|
||||
+try:
|
||||
+ from optparse import OptionParser # pylint: disable=deprecated-module
|
||||
+ from ipapython import ipautil, config
|
||||
+ from ipaserver.install import installutils
|
||||
+ from ipaserver.install.ldapupdate import LDAPUpdate
|
||||
+ from ipalib import api, errors
|
||||
+ from ipapython.ipa_log_manager import standard_logging_setup
|
||||
+ from ipapython.dn import DN
|
||||
+ from ipaplatform import services
|
||||
+except ImportError as e:
|
||||
+ print("""\
|
||||
+There was a problem importing one of the required Python modules. The
|
||||
+error was:
|
||||
+
|
||||
+ %s
|
||||
+""" % e, file=sys.stderr)
|
||||
+ sys.exit(1)
|
||||
+
|
||||
+nis_config_dn = DN(('cn', 'NIS Server'), ('cn', 'plugins'), ('cn', 'config'))
|
||||
+compat_dn = DN(('cn', 'Schema Compatibility'), ('cn', 'plugins'), ('cn', 'config'))
|
||||
+
|
||||
+def parse_options():
|
||||
+ usage = "%prog [options] <enable|disable|status>\n"
|
||||
+ usage += "%prog [options]\n"
|
||||
+ parser = OptionParser(usage=usage, formatter=config.IPAFormatter())
|
||||
+
|
||||
+ parser.add_option("-d", "--debug", action="store_true", dest="debug",
|
||||
+ help="Display debugging information about the update(s)")
|
||||
+ parser.add_option("-y", dest="password",
|
||||
+ help="File containing the Directory Manager password")
|
||||
+
|
||||
+ config.add_standard_options(parser)
|
||||
+ options, args = parser.parse_args()
|
||||
+
|
||||
+ return options, args
|
||||
+
|
||||
+def get_dirman_password():
|
||||
+ """Prompt the user for the Directory Manager password and verify its
|
||||
+ correctness.
|
||||
+ """
|
||||
+ password = installutils.read_password("Directory Manager", confirm=False, validate=False, retry=False)
|
||||
+
|
||||
+ return password
|
||||
+
|
||||
+def get_entry(dn):
|
||||
+ """
|
||||
+ Return the entry for the given DN. If the entry is not found return
|
||||
+ None.
|
||||
+ """
|
||||
+ entry = None
|
||||
+ try:
|
||||
+ entry = api.Backend.ldap2.get_entry(dn)
|
||||
+ except errors.NotFound:
|
||||
+ pass
|
||||
+ return entry
|
||||
+
|
||||
+def main():
|
||||
+ retval = 0
|
||||
+ files = [paths.NIS_ULDIF]
|
||||
+ servicemsg = ""
|
||||
+
|
||||
+ if os.getegid() != 0:
|
||||
+ sys.exit('Must be root to use this tool.')
|
||||
+
|
||||
+ installutils.check_server_configuration()
|
||||
+
|
||||
+ options, args = parse_options()
|
||||
+
|
||||
+ if len(args) != 1:
|
||||
+ sys.exit("You must specify one action: enable | disable | status")
|
||||
+ elif args[0] not in {"enable", "disable", "status"}:
|
||||
+ sys.exit("Unrecognized action [" + args[0] + "]")
|
||||
+
|
||||
+ standard_logging_setup(None, debug=options.debug)
|
||||
+ dirman_password = ""
|
||||
+ if options.password:
|
||||
+ try:
|
||||
+ pw = ipautil.template_file(options.password, [])
|
||||
+ except IOError:
|
||||
+ sys.exit("File \"%s\" not found or not readable" % options.password)
|
||||
+ dirman_password = pw.strip()
|
||||
+ else:
|
||||
+ dirman_password = get_dirman_password()
|
||||
+ if dirman_password is None:
|
||||
+ sys.exit("Directory Manager password required")
|
||||
+
|
||||
+ if not dirman_password:
|
||||
+ sys.exit("No password supplied")
|
||||
+
|
||||
+ api.bootstrap(
|
||||
+ context='cli', confdir=paths.ETC_IPA,
|
||||
+ debug=options.debug, in_server=True)
|
||||
+ api.finalize()
|
||||
+ api.Backend.ldap2.connect(bind_pw=dirman_password)
|
||||
+
|
||||
+ if args[0] == "enable":
|
||||
+ compat = get_entry(compat_dn)
|
||||
+ if compat is None or compat.get('nsslapd-pluginenabled', [''])[0].lower() == 'off':
|
||||
+ sys.exit("The compat plugin needs to be enabled: ipa-compat-manage enable")
|
||||
+ entry = None
|
||||
+ try:
|
||||
+ entry = get_entry(nis_config_dn)
|
||||
+ except errors.ExecutionError as lde:
|
||||
+ print("An error occurred while talking to the server.")
|
||||
+ print(lde)
|
||||
+ retval = 1
|
||||
+
|
||||
+ # Enable either the portmap or rpcbind service
|
||||
+ portmap = services.knownservices.portmap
|
||||
+ rpcbind = services.knownservices.rpcbind
|
||||
+
|
||||
+ if portmap.is_installed():
|
||||
+ portmap.enable()
|
||||
+ servicemsg = portmap.service_name
|
||||
+ elif rpcbind.is_installed():
|
||||
+ rpcbind.enable()
|
||||
+ servicemsg = rpcbind.service_name
|
||||
+ else:
|
||||
+ print("Unable to enable either %s or %s" % (portmap.service_name, rpcbind.service_name))
|
||||
+ retval = 3
|
||||
+
|
||||
+ # The cn=config entry for the plugin may already exist but it
|
||||
+ # could be turned off, handle both cases.
|
||||
+ if entry is None:
|
||||
+ print("Enabling plugin")
|
||||
+ ld = LDAPUpdate()
|
||||
+ if ld.update(files) != True:
|
||||
+ retval = 1
|
||||
+ elif entry.get('nsslapd-pluginenabled', [''])[0].lower() == 'off':
|
||||
+ print("Enabling plugin")
|
||||
+ # Already configured, just enable the plugin
|
||||
+ entry['nsslapd-pluginenabled'] = ['on']
|
||||
+ api.Backend.ldap2.update_entry(entry)
|
||||
+ else:
|
||||
+ print("Plugin already Enabled")
|
||||
+ retval = 2
|
||||
+
|
||||
+ elif args[0] == "disable":
|
||||
+ try:
|
||||
+ entry = api.Backend.ldap2.get_entry(nis_config_dn, ['nsslapd-pluginenabled'])
|
||||
+ entry['nsslapd-pluginenabled'] = ['off']
|
||||
+ api.Backend.ldap2.update_entry(entry)
|
||||
+ except (errors.NotFound, errors.EmptyModlist):
|
||||
+ print("Plugin is already disabled")
|
||||
+ retval = 2
|
||||
+ except errors.LDAPError as lde:
|
||||
+ print("An error occurred while talking to the server.")
|
||||
+ print(lde)
|
||||
+ retval = 1
|
||||
+
|
||||
+ elif args[0] == "status":
|
||||
+ nis_entry = get_entry(nis_config_dn)
|
||||
+ enabled = (nis_entry and
|
||||
+ nis_entry.get(
|
||||
+ 'nsslapd-pluginenabled', '')[0].lower() == "on")
|
||||
+ if enabled:
|
||||
+ print("Plugin is enabled")
|
||||
+ retval = 0
|
||||
+ else:
|
||||
+ print("Plugin is not enabled")
|
||||
+ retval = 4
|
||||
+
|
||||
+ else:
|
||||
+ retval = 1
|
||||
+
|
||||
+ if retval == 0:
|
||||
+ if args[0] in {"enable", "disable"}:
|
||||
+ print("This setting will not take effect until you restart "
|
||||
+ "Directory Server.")
|
||||
+
|
||||
+ if args[0] == "enable":
|
||||
+ print("The %s service may need to be started." % servicemsg)
|
||||
+
|
||||
+ api.Backend.ldap2.disconnect()
|
||||
+
|
||||
+ return retval
|
||||
+
|
||||
+if __name__ == '__main__':
|
||||
+ installutils.run_script(main, operation_name='ipa-nis-manage')
|
||||
diff --git a/install/tools/man/Makefile.am b/install/tools/man/Makefile.am
|
||||
index bffce402c7df428a47c1710bdc47d59a95993a0d..e9542a77bbbb88054eae1e64311d6e9ec5bee499 100644
|
||||
--- a/install/tools/man/Makefile.am
|
||||
+++ b/install/tools/man/Makefile.am
|
||||
@@ -18,6 +18,7 @@ dist_man1_MANS = \
|
||||
ipa-kra-install.1 \
|
||||
ipa-ldap-updater.1 \
|
||||
ipa-compat-manage.1 \
|
||||
+ ipa-nis-manage.1 \
|
||||
ipa-managed-entries.1 \
|
||||
ipa-backup.1 \
|
||||
ipa-restore.1 \
|
||||
diff --git a/install/tools/man/ipa-nis-manage.1 b/install/tools/man/ipa-nis-manage.1
|
||||
new file mode 100644
|
||||
index 0000000000000000000000000000000000000000..1107b7790531aa695defd660040734143c02474d
|
||||
--- /dev/null
|
||||
+++ b/install/tools/man/ipa-nis-manage.1
|
||||
@@ -0,0 +1,51 @@
|
||||
+.\" A man page for ipa-nis-manage
|
||||
+.\" Copyright (C) 2009 Red Hat, Inc.
|
||||
+.\"
|
||||
+.\" 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/>.
|
||||
+.\"
|
||||
+.\" Author: Rob Crittenden <rcritten@redhat.com>
|
||||
+.\"
|
||||
+.TH "ipa-nis-manage" "1" "April 25 2016" "IPA" "IPA Manual Pages"
|
||||
+.SH "NAME"
|
||||
+ipa\-nis\-manage \- Enables or disables the NIS listener plugin
|
||||
+.SH "SYNOPSIS"
|
||||
+ipa\-nis\-manage [options] <enable|disable|status>
|
||||
+.SH "DESCRIPTION"
|
||||
+Run the command with the \fBenable\fR option to enable the NIS plugin.
|
||||
+
|
||||
+Run the command with the \fBdisable\fR option to disable the NIS plugin.
|
||||
+
|
||||
+Run the command with the \fBstatus\fR option to read status of the NIS plugin. Return code 0 indicates enabled plugin, return code 4 indicates disabled plugin.
|
||||
+
|
||||
+In all cases the user will be prompted to provide the Directory Manager's password unless option \fB\-y\fR is used.
|
||||
+
|
||||
+Directory Server will need to be restarted after the NIS listener plugin has been enabled.
|
||||
+
|
||||
+.SH "OPTIONS"
|
||||
+.TP
|
||||
+\fB\-d\fR, \fB\-\-debug\fR
|
||||
+Enable debug logging when more verbose output is needed
|
||||
+.TP
|
||||
+\fB\-y\fR \fIfile\fR
|
||||
+File containing the Directory Manager password
|
||||
+.SH "EXIT STATUS"
|
||||
+0 if the command was successful
|
||||
+
|
||||
+1 if an error occurred
|
||||
+
|
||||
+2 if the plugin is already in the required status (enabled or disabled)
|
||||
+
|
||||
+3 if RPC services cannot be enabled.
|
||||
+
|
||||
+4 if status command detected plugin in disabled state.
|
||||
diff --git a/install/updates/10-enable-betxn.update b/install/updates/10-enable-betxn.update
|
||||
index 9525292cb2d66a738a2dec9cd881dbf960d2d944..1f89341c7f68616161d03b8a2e0c34f499bc3317 100644
|
||||
--- a/install/updates/10-enable-betxn.update
|
||||
+++ b/install/updates/10-enable-betxn.update
|
||||
@@ -44,3 +44,6 @@ only: nsslapd-pluginbetxn: on
|
||||
|
||||
dn: cn=Schema Compatibility, cn=plugins, cn=config
|
||||
onlyifexist: nsslapd-pluginbetxn: on
|
||||
+
|
||||
+dn: cn=NIS Server, cn=plugins, cn=config
|
||||
+onlyifexist: nsslapd-pluginbetxn: on
|
||||
diff --git a/install/updates/50-nis.update b/install/updates/50-nis.update
|
||||
new file mode 100644
|
||||
index 0000000000000000000000000000000000000000..05a166f003aefc50fc25f10f01f7364d752425bc
|
||||
--- /dev/null
|
||||
+++ b/install/updates/50-nis.update
|
||||
@@ -0,0 +1,3 @@
|
||||
+# Updates are applied only if NIS plugin has been configured
|
||||
+# update definitions are located in install/share/nis-update.uldif
|
||||
+plugin: update_nis_configuration
|
||||
diff --git a/install/updates/Makefile.am b/install/updates/Makefile.am
|
||||
index cce2670a6c31803dc534ac5b0093ec0aa317233e..fd96831d8fd4904b51d933847865d37c58a3508c 100644
|
||||
--- a/install/updates/Makefile.am
|
||||
+++ b/install/updates/Makefile.am
|
||||
@@ -52,6 +52,7 @@ app_DATA = \
|
||||
50-groupuuid.update \
|
||||
50-hbacservice.update \
|
||||
50-krbenctypes.update \
|
||||
+ 50-nis.update \
|
||||
50-ipaconfig.update \
|
||||
55-pbacmemberof.update \
|
||||
59-trusts-sysacount.update \
|
||||
diff --git a/ipaplatform/base/paths.py b/ipaplatform/base/paths.py
|
||||
index 5bc0e3ac3eed257fd7fa6a0830e3e2dbeb3d01d2..b84925bfc7d9af8584d9d4b185aa04b635c1e325 100644
|
||||
--- a/ipaplatform/base/paths.py
|
||||
+++ b/ipaplatform/base/paths.py
|
||||
@@ -297,6 +297,8 @@ class BasePathNamespace:
|
||||
CA_TOPOLOGY_ULDIF = "/usr/share/ipa/ca-topology.uldif"
|
||||
IPA_HTML_DIR = "/usr/share/ipa/html"
|
||||
CA_CRT = "/usr/share/ipa/html/ca.crt"
|
||||
+ NIS_ULDIF = "/usr/share/ipa/nis.uldif"
|
||||
+ NIS_UPDATE_ULDIF = "/usr/share/ipa/nis-update.uldif"
|
||||
SCHEMA_COMPAT_ULDIF = "/usr/share/ipa/updates/91-schema_compat.update"
|
||||
SCHEMA_COMPAT_POST_ULDIF = "/usr/share/ipa/schema_compat_post.uldif"
|
||||
SUBID_GENERATORS_ULDIF = "/usr/share/ipa/subid-generators.uldif"
|
||||
diff --git a/ipaserver/install/ipa_migrate.py b/ipaserver/install/ipa_migrate.py
|
||||
index f07a878e739002346df894f93745ea60345e0557..b0a27de5dd40aa661271faa3a8a0855226b5892f 100644
|
||||
--- a/ipaserver/install/ipa_migrate.py
|
||||
+++ b/ipaserver/install/ipa_migrate.py
|
||||
@@ -32,7 +32,7 @@ from ipapython.admintool import admin_cleanup_global_argv
|
||||
from ipaserver.install.ipa_migrate_constants import (
|
||||
DS_CONFIG, DB_OBJECTS, DS_INDEXES, BIND_DN, LOG_FILE_NAME,
|
||||
STRIP_OP_ATTRS, STRIP_ATTRS, STRIP_OC, PROD_ATTRS,
|
||||
- DNA_REGEN_VAL, DNA_REGEN_ATTRS, IGNORE_ATTRS,
|
||||
+ DNA_REGEN_VAL, DNA_REGEN_ATTRS, NIS_PLUGIN, IGNORE_ATTRS,
|
||||
DB_EXCLUDE_TREES, POLICY_OP_ATTRS, STATE_OPTIONS
|
||||
)
|
||||
|
||||
@@ -749,7 +749,8 @@ class IPAMigrate():
|
||||
self.log_info(title)
|
||||
self.log_info('-' * (len(title) - 1))
|
||||
logged_something = self.log_stats(DS_CONFIG)
|
||||
- if self.args.verbose:
|
||||
+ if self.args.verbose or NIS_PLUGIN['count'] > 0:
|
||||
+ self.log_info(f" - NIS Server Plugin: {NIS_PLUGIN['count']}")
|
||||
logged_something = True
|
||||
if not self.log_stats(DS_INDEXES) and not logged_something:
|
||||
self.log_info(" - No updates")
|
||||
@@ -1983,6 +1984,28 @@ class IPAMigrate():
|
||||
add_missing=True)
|
||||
stats['config_processed'] += 1
|
||||
|
||||
+ # Slapi NIS Plugin
|
||||
+ if DN(NIS_PLUGIN['dn']) == DN(entry['dn']):
|
||||
+ # Parent plugin entry
|
||||
+ self.process_config_entry(
|
||||
+ entry['dn'], entry['attrs'], NIS_PLUGIN,
|
||||
+ add_missing=True)
|
||||
+ stats['config_processed'] += 1
|
||||
+ elif DN(NIS_PLUGIN['dn']) in DN(entry['dn']):
|
||||
+ # Child NIS plugin entry
|
||||
+ nis_dn = entry['dn']
|
||||
+ lc_remote_realm = self.remote_realm.lower()
|
||||
+ lc_realm = self.realm.lower()
|
||||
+ nis_dn = nis_dn.replace(lc_remote_realm, lc_realm)
|
||||
+ if 'nis-domain' in entry['attrs']:
|
||||
+ value = entry['attrs']['nis-domain'][0]
|
||||
+ value = value.replace(lc_remote_realm, lc_realm)
|
||||
+ entry['attrs']['nis-domain'][0] = value
|
||||
+ # Process the entry
|
||||
+ self.process_config_entry(nis_dn, entry['attrs'], NIS_PLUGIN,
|
||||
+ add_missing=True)
|
||||
+ stats['config_processed'] += 1
|
||||
+
|
||||
#
|
||||
# Migration
|
||||
#
|
||||
diff --git a/ipaserver/install/ipa_migrate_constants.py b/ipaserver/install/ipa_migrate_constants.py
|
||||
index 65e920d4bdc7511adb60bbdf87db88beb2630645..4fe8b467978936e4f8d621b7297ff577127c7298 100644
|
||||
--- a/ipaserver/install/ipa_migrate_constants.py
|
||||
+++ b/ipaserver/install/ipa_migrate_constants.py
|
||||
@@ -527,6 +527,30 @@ DS_CONFIG = {
|
||||
},
|
||||
}
|
||||
|
||||
+#
|
||||
+# Slpai NIS is an optional plugin. It requires special handling
|
||||
+#
|
||||
+NIS_PLUGIN = {
|
||||
+ 'dn': 'cn=NIS Server,cn=plugins,cn=config',
|
||||
+ 'attrs': [
|
||||
+ 'nis-domain',
|
||||
+ 'nis-base',
|
||||
+ 'nis-map',
|
||||
+ 'nis-filter',
|
||||
+ 'nis-key-format:',
|
||||
+ 'nis-values-format:',
|
||||
+ 'nis-secure',
|
||||
+ 'nis-disallowed-chars',
|
||||
+ # Parent plugin entry
|
||||
+ 'nsslapd-pluginarg0',
|
||||
+ 'nsslapd-pluginenabled'
|
||||
+ ],
|
||||
+ 'multivalued': [],
|
||||
+ 'label': 'NIS Server Plugin',
|
||||
+ 'mode': 'all',
|
||||
+ 'count': 0,
|
||||
+}
|
||||
+
|
||||
#
|
||||
# This mapping is simliar to above but it handles container entries
|
||||
# This could be built into the above mapping using the "comma" approach
|
||||
diff --git a/ipaserver/install/plugins/update_nis.py b/ipaserver/install/plugins/update_nis.py
|
||||
new file mode 100644
|
||||
index 0000000000000000000000000000000000000000..c02eb5f838a99b27cdd21e4aee17e5104f0e22e2
|
||||
--- /dev/null
|
||||
+++ b/ipaserver/install/plugins/update_nis.py
|
||||
@@ -0,0 +1,92 @@
|
||||
+#
|
||||
+# Copyright (C) 2015 FreeIPA Contributors see COPYING for license
|
||||
+#
|
||||
+
|
||||
+from __future__ import absolute_import
|
||||
+
|
||||
+import logging
|
||||
+
|
||||
+from ipalib.plugable import Registry
|
||||
+from ipalib import errors
|
||||
+from ipalib import Updater
|
||||
+from ipaplatform.paths import paths
|
||||
+from ipapython.dn import DN
|
||||
+from ipaserver.install import sysupgrade
|
||||
+from ipaserver.install.ldapupdate import LDAPUpdate
|
||||
+
|
||||
+logger = logging.getLogger(__name__)
|
||||
+
|
||||
+register = Registry()
|
||||
+
|
||||
+
|
||||
+@register()
|
||||
+class update_nis_configuration(Updater):
|
||||
+ """Update NIS configuration
|
||||
+
|
||||
+ NIS configuration can be updated only if NIS Server was configured via
|
||||
+ ipa-nis-manage command.
|
||||
+ """
|
||||
+
|
||||
+ def __recover_from_missing_maps(self, ldap):
|
||||
+ # https://fedorahosted.org/freeipa/ticket/5507
|
||||
+ # if all following DNs are missing, but 'NIS Server' container exists
|
||||
+ # we are experiencig bug and maps should be fixed
|
||||
+
|
||||
+ if sysupgrade.get_upgrade_state('nis',
|
||||
+ 'done_recover_from_missing_maps'):
|
||||
+ # this recover must be done only once, a user may deleted some
|
||||
+ # maps, we do not want to restore them again
|
||||
+ return
|
||||
+
|
||||
+ logger.debug("Recovering from missing NIS maps bug")
|
||||
+
|
||||
+ suffix = "cn=NIS Server,cn=plugins,cn=config"
|
||||
+ domain = self.api.env.domain
|
||||
+ missing_dn_list = [
|
||||
+ DN(nis_map.format(domain=domain, suffix=suffix)) for nis_map in [
|
||||
+ "nis-domain={domain}+nis-map=passwd.byname,{suffix}",
|
||||
+ "nis-domain={domain}+nis-map=passwd.byuid,{suffix}",
|
||||
+ "nis-domain={domain}+nis-map=group.byname,{suffix}",
|
||||
+ "nis-domain={domain}+nis-map=group.bygid,{suffix}",
|
||||
+ "nis-domain={domain}+nis-map=netid.byname,{suffix}",
|
||||
+ "nis-domain={domain}+nis-map=netgroup,{suffix}",
|
||||
+ ]
|
||||
+ ]
|
||||
+
|
||||
+ for dn in missing_dn_list:
|
||||
+ try:
|
||||
+ ldap.get_entry(dn, attrs_list=['cn'])
|
||||
+ except errors.NotFound:
|
||||
+ pass
|
||||
+ else:
|
||||
+ # bug is not effective, at least one of 'possible missing'
|
||||
+ # maps was detected
|
||||
+ return
|
||||
+
|
||||
+ sysupgrade.set_upgrade_state('nis', 'done_recover_from_missing_maps',
|
||||
+ True)
|
||||
+
|
||||
+ # bug is effective run update to recreate missing maps
|
||||
+ ld = LDAPUpdate(api=self.api)
|
||||
+ ld.update([paths.NIS_ULDIF])
|
||||
+
|
||||
+ def execute(self, **options):
|
||||
+ ldap = self.api.Backend.ldap2
|
||||
+ dn = DN(('cn', 'NIS Server'), ('cn', 'plugins'), ('cn', 'config'))
|
||||
+ try:
|
||||
+ ldap.get_entry(dn, attrs_list=['cn'])
|
||||
+ except errors.NotFound:
|
||||
+ # NIS is not configured on system, do not execute update
|
||||
+ logger.debug("Skipping NIS update, NIS Server is not configured")
|
||||
+
|
||||
+ # container does not exist, bug #5507 is not effective
|
||||
+ sysupgrade.set_upgrade_state(
|
||||
+ 'nis', 'done_recover_from_missing_maps', True)
|
||||
+ else:
|
||||
+ self.__recover_from_missing_maps(ldap)
|
||||
+
|
||||
+ logger.debug("Executing NIS Server update")
|
||||
+ ld = LDAPUpdate(api=self.api)
|
||||
+ ld.update([paths.NIS_UPDATE_ULDIF])
|
||||
+
|
||||
+ return False, ()
|
||||
diff --git a/ipatests/test_cmdline/test_cli.py b/ipatests/test_cmdline/test_cli.py
|
||||
index 6c86bbb657a0d9a7b74ef34ad20a796a10073315..4acf8dd5290c687723a9c43a21e194bf663b6a3b 100644
|
||||
--- a/ipatests/test_cmdline/test_cli.py
|
||||
+++ b/ipatests/test_cmdline/test_cli.py
|
||||
@@ -372,6 +372,7 @@ IPA_CLIENT_NOT_CONFIGURED = b'IPA client is not configured on this system'
|
||||
'/usr/share/ipa/updates/05-pre_upgrade_plugins.update'],
|
||||
2, None, IPA_NOT_CONFIGURED),
|
||||
(['ipa-managed-entries'], 2, None, IPA_NOT_CONFIGURED),
|
||||
+ (['ipa-nis-manage'], 2, None, IPA_NOT_CONFIGURED),
|
||||
(['ipa-pkinit-manage'], 2, None, IPA_NOT_CONFIGURED),
|
||||
(['ipa-replica-manage', 'list'], 1, IPA_NOT_CONFIGURED, None),
|
||||
(['ipa-server-certinstall'], 2, None, IPA_NOT_CONFIGURED),
|
||||
diff --git a/ipatests/test_integration/test_commands.py b/ipatests/test_integration/test_commands.py
|
||||
index 5bc871ab71bc05ec1c26df8352e996a9e627b466..8f8548494eaec3afd00961c096772b9d0b1ab3a4 100644
|
||||
--- a/ipatests/test_integration/test_commands.py
|
||||
+++ b/ipatests/test_integration/test_commands.py
|
||||
@@ -1382,6 +1382,93 @@ class TestIPACommand(IntegrationTest):
|
||||
serverid = realm_to_serverid(self.master.domain.realm)
|
||||
return ("dirsrv@%s.service" % serverid)
|
||||
|
||||
+ def test_ipa_nis_manage_enable(self):
|
||||
+ """
|
||||
+ This testcase checks if ipa-nis-manage enable
|
||||
+ command enables plugin on an IPA master
|
||||
+ """
|
||||
+ dirsrv_service = self.get_dirsrv_id()
|
||||
+ console_msg = (
|
||||
+ "Enabling plugin\n"
|
||||
+ "This setting will not take effect until "
|
||||
+ "you restart Directory Server.\n"
|
||||
+ "The rpcbind service may need to be started"
|
||||
+ )
|
||||
+ status_msg = "Plugin is enabled"
|
||||
+ tasks.kinit_admin(self.master)
|
||||
+ result = self.master.run_command(
|
||||
+ ["ipa-nis-manage", "enable"],
|
||||
+ stdin_text=self.master.config.admin_password,
|
||||
+ )
|
||||
+ assert console_msg in result.stdout_text
|
||||
+ # verify using backend
|
||||
+ conn = self.master.ldap_connect()
|
||||
+ dn = DN(('cn', 'NIS Server'), ('cn', 'plugins'), ('cn', 'config'))
|
||||
+ entry = conn.get_entry(dn)
|
||||
+ nispluginstring = entry.get('nsslapd-pluginEnabled')
|
||||
+ assert 'on' in nispluginstring
|
||||
+ # restart for changes to take effect
|
||||
+ self.master.run_command(["systemctl", "restart", dirsrv_service])
|
||||
+ self.master.run_command(["systemctl", "restart", "rpcbind"])
|
||||
+ time.sleep(DIRSRV_SLEEP)
|
||||
+ # check status msg on the console
|
||||
+ result = self.master.run_command(
|
||||
+ ["ipa-nis-manage", "status"],
|
||||
+ stdin_text=self.master.config.admin_password,
|
||||
+ )
|
||||
+ assert status_msg in result.stdout_text
|
||||
+
|
||||
+ def test_ipa_nis_manage_disable(self):
|
||||
+ """
|
||||
+ This testcase checks if ipa-nis-manage disable
|
||||
+ command disable plugin on an IPA Master
|
||||
+ """
|
||||
+ dirsrv_service = self.get_dirsrv_id()
|
||||
+ msg = (
|
||||
+ "This setting will not take effect "
|
||||
+ "until you restart Directory Server."
|
||||
+ )
|
||||
+ status_msg = "Plugin is not enabled"
|
||||
+ tasks.kinit_admin(self.master)
|
||||
+ result = self.master.run_command(
|
||||
+ ["ipa-nis-manage", "disable"],
|
||||
+ stdin_text=self.master.config.admin_password,
|
||||
+ )
|
||||
+ assert msg in result.stdout_text
|
||||
+ # verify using backend
|
||||
+ conn = self.master.ldap_connect()
|
||||
+ dn = DN(('cn', 'NIS Server'), ('cn', 'plugins'), ('cn', 'config'))
|
||||
+ entry = conn.get_entry(dn)
|
||||
+ nispluginstring = entry.get('nsslapd-pluginEnabled')
|
||||
+ assert 'off' in nispluginstring
|
||||
+ # restart dirsrv for changes to take effect
|
||||
+ self.master.run_command(["systemctl", "restart", dirsrv_service])
|
||||
+ time.sleep(DIRSRV_SLEEP)
|
||||
+ # check status msg on the console
|
||||
+ result = self.master.run_command(
|
||||
+ ["ipa-nis-manage", "status"],
|
||||
+ stdin_text=self.master.config.admin_password,
|
||||
+ raiseonerr=False,
|
||||
+ )
|
||||
+ assert result.returncode == 4
|
||||
+ assert status_msg in result.stdout_text
|
||||
+
|
||||
+ def test_ipa_nis_manage_enable_incorrect_password(self):
|
||||
+ """
|
||||
+ This testcase checks if ipa-nis-manage enable
|
||||
+ command throws error on console for invalid DS admin password
|
||||
+ """
|
||||
+ msg1 = "Insufficient access: "
|
||||
+ msg2 = "Invalid credentials"
|
||||
+ result = self.master.run_command(
|
||||
+ ["ipa-nis-manage", "enable"],
|
||||
+ stdin_text='Invalid_pwd',
|
||||
+ raiseonerr=False,
|
||||
+ )
|
||||
+ assert result.returncode == 1
|
||||
+ assert msg1 in result.stderr_text
|
||||
+ assert msg2 in result.stderr_text
|
||||
+
|
||||
def test_pkispawn_log_is_present(self):
|
||||
"""
|
||||
This testcase checks if pkispawn logged properly.
|
||||
--
|
||||
2.52.0
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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")
|
||||
|
||||
321
SOURCES/0006-Revert-Stop-using-deprecated-pkg_resources.patch
Normal file
321
SOURCES/0006-Revert-Stop-using-deprecated-pkg_resources.patch
Normal file
@ -0,0 +1,321 @@
|
||||
From 257740b66daf004a7333bfaeddece4732be5a48c Mon Sep 17 00:00:00 2001
|
||||
From: Florence Blanc-Renaud <flo@redhat.com>
|
||||
Date: Tue, 9 Dec 2025 17:52:36 +0100
|
||||
Subject: [PATCH] Revert "Stop using deprecated pkg_resources"
|
||||
|
||||
This reverts commit ac791f7372d32d25c75eb61f949f1db38fe2f0d6.
|
||||
---
|
||||
freeipa.spec.in | 6 +++++-
|
||||
ipaplatform/base/tasks.py | 2 +-
|
||||
ipapython/version.py.in | 2 +-
|
||||
ipaserver/custodia/server/__init__.py | 13 +++++++++----
|
||||
ipaserver/install/cainstance.py | 2 +-
|
||||
ipaserver/install/krbinstance.py | 2 +-
|
||||
ipaserver/install/server/replicainstall.py | 2 +-
|
||||
ipaserver/install/server/upgrade.py | 2 +-
|
||||
.../pytest_ipa/integration/create_caless_pki.py | 2 +-
|
||||
ipatests/pytest_ipa/integration/tasks.py | 2 +-
|
||||
ipatests/test_custodia/test_plugins.py | 13 ++++++++-----
|
||||
ipatests/test_integration/test_adtrust_install.py | 2 +-
|
||||
ipatests/test_integration/test_cert.py | 2 +-
|
||||
ipatests/test_integration/test_commands.py | 2 +-
|
||||
ipatests/test_integration/test_idp.py | 2 +-
|
||||
ipatests/test_integration/test_ipahealthcheck.py | 2 +-
|
||||
ipatests/test_webui/ui_driver.py | 2 +-
|
||||
ipatests/test_xmlrpc/test_automember_plugin.py | 1 +
|
||||
18 files changed, 37 insertions(+), 24 deletions(-)
|
||||
|
||||
diff --git a/freeipa.spec.in b/freeipa.spec.in
|
||||
index b93199a5783a89e0ff58affd3d416556f72dd00c..bf6fa216dcc4cfbac903aad43c6219f1e57db4dd 100644
|
||||
--- a/freeipa.spec.in
|
||||
+++ b/freeipa.spec.in
|
||||
@@ -983,7 +983,6 @@ Requires: python3-jwcrypto >= 0.4.2
|
||||
Requires: python3-libipa_hbac
|
||||
Requires: python3-netaddr >= %{python_netaddr_version}
|
||||
Requires: python3-netifaces >= 0.10.4
|
||||
-Requires: python3-packaging
|
||||
Requires: python3-pyasn1 >= 0.3.2-2
|
||||
Requires: python3-pyasn1-modules >= 0.3.2-2
|
||||
Requires: python3-pyusb
|
||||
@@ -992,6 +991,11 @@ Requires: python3-requests
|
||||
Requires: python3-six
|
||||
Requires: python3-sss-murmur
|
||||
Requires: python3-yubico >= 1.3.2-7
|
||||
+%if 0%{?rhel} && 0%{?rhel} == 8
|
||||
+Requires: platform-python-setuptools
|
||||
+%else
|
||||
+Requires: python3-setuptools
|
||||
+%endif
|
||||
%if 0%{?rhel}
|
||||
Requires: python3-urllib3 >= 1.24.2-3
|
||||
%else
|
||||
diff --git a/ipaplatform/base/tasks.py b/ipaplatform/base/tasks.py
|
||||
index 9e221d872e7ca9ac0607ff29e1b51dedcf688d75..ab3563aba3decf370a72c9ec273c8bccc2d85ca2 100644
|
||||
--- a/ipaplatform/base/tasks.py
|
||||
+++ b/ipaplatform/base/tasks.py
|
||||
@@ -29,7 +29,7 @@ import os
|
||||
import logging
|
||||
import textwrap
|
||||
|
||||
-from packaging.version import parse as parse_version
|
||||
+from pkg_resources import parse_version
|
||||
|
||||
from ipaplatform.paths import paths
|
||||
from ipapython import ipautil
|
||||
diff --git a/ipapython/version.py.in b/ipapython/version.py.in
|
||||
index eee8900be5fa72759fd7ee87f72952599231e138..a8f4218a7bac2a2f52389ad3dc5f31a98a822e82 100644
|
||||
--- a/ipapython/version.py.in
|
||||
+++ b/ipapython/version.py.in
|
||||
@@ -17,7 +17,7 @@
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
#
|
||||
|
||||
-from packaging.version import parse as parse_version
|
||||
+from pkg_resources import parse_version
|
||||
|
||||
# The full version including strings
|
||||
VERSION = "@VERSION@"
|
||||
diff --git a/ipaserver/custodia/server/__init__.py b/ipaserver/custodia/server/__init__.py
|
||||
index 26ca0a481559da101891034798e0434f063367b8..e713de20d0e9ff42e65ed3d2170d6446baa8d4e8 100644
|
||||
--- a/ipaserver/custodia/server/__init__.py
|
||||
+++ b/ipaserver/custodia/server/__init__.py
|
||||
@@ -2,9 +2,10 @@
|
||||
from __future__ import absolute_import
|
||||
|
||||
import importlib
|
||||
-import importlib.metadata
|
||||
import os
|
||||
|
||||
+import pkg_resources
|
||||
+
|
||||
import six
|
||||
|
||||
from ipaserver.custodia import log
|
||||
@@ -36,13 +37,17 @@ def _load_plugin_class(menu, name):
|
||||
Entry points are preferred over dotted import path.
|
||||
"""
|
||||
group = 'custodia.{}'.format(menu)
|
||||
- eps = importlib.metadata.entry_points(group=group, name=name)
|
||||
+ eps = list(pkg_resources.iter_entry_points(group, name))
|
||||
if len(eps) > 1:
|
||||
raise ValueError(
|
||||
"Multiple entry points for {} {}: {}".format(menu, name, eps))
|
||||
elif len(eps) == 1:
|
||||
- ep, *_ = eps
|
||||
- return ep.load(require=False)
|
||||
+ # backwards compatibility with old setuptools
|
||||
+ ep = eps[0]
|
||||
+ if hasattr(ep, 'resolve'):
|
||||
+ return ep.resolve()
|
||||
+ else:
|
||||
+ return ep.load(require=False)
|
||||
elif '.' in name:
|
||||
# fall back to old style dotted name
|
||||
module, classname = name.rsplit('.', 1)
|
||||
diff --git a/ipaserver/install/cainstance.py b/ipaserver/install/cainstance.py
|
||||
index b8267a625554f9375d27160f39b67ee2e64a2dbb..a92b3ed6f01db97a86464e5cb77a084f8a1de90f 100644
|
||||
--- a/ipaserver/install/cainstance.py
|
||||
+++ b/ipaserver/install/cainstance.py
|
||||
@@ -35,7 +35,7 @@ import syslog
|
||||
import time
|
||||
import tempfile
|
||||
from configparser import RawConfigParser
|
||||
-from packaging.version import parse as parse_version
|
||||
+from pkg_resources import parse_version
|
||||
|
||||
from ipalib import api
|
||||
from ipalib import x509
|
||||
diff --git a/ipaserver/install/krbinstance.py b/ipaserver/install/krbinstance.py
|
||||
index 4a521a611bd1bebf925061ea88a60876dff48467..0f568122c94aab8e55604179003276d29580de76 100644
|
||||
--- a/ipaserver/install/krbinstance.py
|
||||
+++ b/ipaserver/install/krbinstance.py
|
||||
@@ -27,7 +27,7 @@ import tempfile
|
||||
import dbus
|
||||
|
||||
import dns.name
|
||||
-from packaging.version import parse as parse_version
|
||||
+from pkg_resources import parse_version
|
||||
|
||||
from ipalib import x509
|
||||
from ipalib.install import certstore
|
||||
diff --git a/ipaserver/install/server/replicainstall.py b/ipaserver/install/server/replicainstall.py
|
||||
index b4d06d8e24097e12c97a8b881dfcc9b028467272..5b4f0161083bbf65ac423ebacad30c5743ffab8e 100644
|
||||
--- a/ipaserver/install/server/replicainstall.py
|
||||
+++ b/ipaserver/install/server/replicainstall.py
|
||||
@@ -18,7 +18,7 @@ import tempfile
|
||||
import textwrap
|
||||
import traceback
|
||||
|
||||
-from packaging.version import parse as parse_version
|
||||
+from pkg_resources import parse_version
|
||||
import six
|
||||
|
||||
from ipaclient.install.client import check_ldap_conf, sssd_enable_ifp
|
||||
diff --git a/ipaserver/install/server/upgrade.py b/ipaserver/install/server/upgrade.py
|
||||
index 548ee02e1e8524ce0002dca1764d48728eb0509a..d9cb6656b4731b5cb803b870469635862f4269e5 100644
|
||||
--- a/ipaserver/install/server/upgrade.py
|
||||
+++ b/ipaserver/install/server/upgrade.py
|
||||
@@ -17,7 +17,7 @@ import sys
|
||||
import tempfile
|
||||
from contextlib import contextmanager
|
||||
from augeas import Augeas
|
||||
-from packaging.version import parse as parse_version
|
||||
+from pkg_resources import parse_version
|
||||
|
||||
from ipalib import api, x509
|
||||
from ipalib.constants import RENEWAL_CA_NAME, RA_AGENT_PROFILE, IPA_CA_RECORD
|
||||
diff --git a/ipatests/pytest_ipa/integration/create_caless_pki.py b/ipatests/pytest_ipa/integration/create_caless_pki.py
|
||||
index d06f1dd8c328628bd692c2abf3acfc88ba6a7408..01d64462ce76eb76cf7efdbeb850955ca4990535 100644
|
||||
--- a/ipatests/pytest_ipa/integration/create_caless_pki.py
|
||||
+++ b/ipatests/pytest_ipa/integration/create_caless_pki.py
|
||||
@@ -26,7 +26,7 @@ from cryptography.hazmat.backends import default_backend
|
||||
from cryptography.hazmat.primitives import hashes, serialization
|
||||
from cryptography.hazmat.primitives.asymmetric import rsa
|
||||
from cryptography.x509.oid import NameOID
|
||||
-from packaging.version import parse as parse_version
|
||||
+from pkg_resources import parse_version
|
||||
from pyasn1.type import univ, char, namedtype, tag
|
||||
from pyasn1.codec.der import encoder as der_encoder
|
||||
from pyasn1.codec.native import decoder as native_decoder
|
||||
diff --git a/ipatests/pytest_ipa/integration/tasks.py b/ipatests/pytest_ipa/integration/tasks.py
|
||||
index 3ef361807ee0e1c858f4bef8b7d9e26c5f0fa20a..c6e0cf3937a12c7bb13da4b83250ecced470b543 100755
|
||||
--- a/ipatests/pytest_ipa/integration/tasks.py
|
||||
+++ b/ipatests/pytest_ipa/integration/tasks.py
|
||||
@@ -36,7 +36,7 @@ import time
|
||||
from shlex import quote
|
||||
import configparser
|
||||
from contextlib import contextmanager
|
||||
-from packaging.version import parse as parse_version
|
||||
+from pkg_resources import parse_version
|
||||
import uuid
|
||||
|
||||
import dns
|
||||
diff --git a/ipatests/test_custodia/test_plugins.py b/ipatests/test_custodia/test_plugins.py
|
||||
index 9d3f6c24ff2eecad9e9a1c574aaae2edd30e4921..be8aa936db2bc553e0a13bb0975e88d186e3da1c 100644
|
||||
--- a/ipatests/test_custodia/test_plugins.py
|
||||
+++ b/ipatests/test_custodia/test_plugins.py
|
||||
@@ -1,6 +1,5 @@
|
||||
# Copyright (C) 2016 Custodia Project Contributors - see LICENSE file
|
||||
-import importlib.metadata
|
||||
-
|
||||
+import pkg_resources
|
||||
import pytest
|
||||
|
||||
from ipaserver.custodia.plugin import (
|
||||
@@ -13,8 +12,8 @@ class TestCustodiaPlugins:
|
||||
|
||||
def get_entry_points(self, group):
|
||||
eps = []
|
||||
- for e in importlib.metadata.entry_points(group=group):
|
||||
- if e.dist.name != self.project_name:
|
||||
+ for e in pkg_resources.iter_entry_points(group):
|
||||
+ if e.dist.project_name != self.project_name:
|
||||
# only interested in our own entry points
|
||||
continue
|
||||
eps.append(e)
|
||||
@@ -22,7 +21,11 @@ class TestCustodiaPlugins:
|
||||
|
||||
def assert_ep(self, ep, basecls):
|
||||
try:
|
||||
- cls = ep.load(require=False)
|
||||
+ # backwards compatibility with old setuptools
|
||||
+ if hasattr(ep, "resolve"):
|
||||
+ cls = ep.resolve()
|
||||
+ else:
|
||||
+ cls = ep.load(require=False)
|
||||
except Exception as e: # pylint: disable=broad-except
|
||||
pytest.fail("Failed to load %r: %r" % (ep, e))
|
||||
if not issubclass(cls, basecls):
|
||||
diff --git a/ipatests/test_integration/test_adtrust_install.py b/ipatests/test_integration/test_adtrust_install.py
|
||||
index 09e227ec8125e90b37d1d92f0512f9819f5b48c3..ac7e5663aa1c70184f5a09ea5e14cca8c671d78e 100644
|
||||
--- a/ipatests/test_integration/test_adtrust_install.py
|
||||
+++ b/ipatests/test_integration/test_adtrust_install.py
|
||||
@@ -13,7 +13,7 @@ from ipaplatform.paths import paths
|
||||
from ipapython.dn import DN
|
||||
from ipatests.pytest_ipa.integration import tasks
|
||||
from ipatests.test_integration.base import IntegrationTest
|
||||
-from packaging.version import parse as parse_version
|
||||
+from pkg_resources import parse_version
|
||||
|
||||
import pytest
|
||||
|
||||
diff --git a/ipatests/test_integration/test_cert.py b/ipatests/test_integration/test_cert.py
|
||||
index 21568c2421c21855df06bcf5fbb4d52b3651a523..03ea5cfb000200d4d77b97d8808e30dbd5392871 100644
|
||||
--- a/ipatests/test_integration/test_cert.py
|
||||
+++ b/ipatests/test_integration/test_cert.py
|
||||
@@ -20,7 +20,7 @@ from ipapython.dn import DN
|
||||
from cryptography import x509
|
||||
from cryptography.x509.oid import ExtensionOID
|
||||
from cryptography.hazmat.backends import default_backend
|
||||
-from packaging.version import parse as parse_version
|
||||
+from pkg_resources import parse_version
|
||||
|
||||
from ipatests.pytest_ipa.integration import tasks
|
||||
from ipatests.test_integration.base import IntegrationTest
|
||||
diff --git a/ipatests/test_integration/test_commands.py b/ipatests/test_integration/test_commands.py
|
||||
index 5bc871ab71bc05ec1c26df8352e996a9e627b466..04d8f71ff922f98c71331fa9a5f0f6508dd3fc12 100644
|
||||
--- a/ipatests/test_integration/test_commands.py
|
||||
+++ b/ipatests/test_integration/test_commands.py
|
||||
@@ -41,7 +41,7 @@ from ipatests.test_ipalib.test_x509 import good_pkcs7, badcert
|
||||
from ipapython.ipautil import realm_to_suffix, ipa_generate_password
|
||||
from ipatests.test_integration.test_topology import find_segment
|
||||
from ipaserver.install.installutils import realm_to_serverid
|
||||
-from packaging.version import parse as parse_version
|
||||
+from pkg_resources import parse_version
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
diff --git a/ipatests/test_integration/test_idp.py b/ipatests/test_integration/test_idp.py
|
||||
index 9983b3c9d62826afa395c2739b4fb9945afed80d..a29333ef232b0ae8229484e404866d8bb720554a 100644
|
||||
--- a/ipatests/test_integration/test_idp.py
|
||||
+++ b/ipatests/test_integration/test_idp.py
|
||||
@@ -12,7 +12,7 @@ from ipatests.pytest_ipa.integration import tasks, create_keycloak
|
||||
user_code_script = textwrap.dedent("""
|
||||
from selenium import webdriver
|
||||
from datetime import datetime
|
||||
-from packaging.version import parse as parse_version
|
||||
+from pkg_resources import parse_version
|
||||
from selenium.webdriver.firefox.options import Options
|
||||
from selenium.webdriver.common.by import By
|
||||
from selenium.webdriver.support.ui import WebDriverWait
|
||||
diff --git a/ipatests/test_integration/test_ipahealthcheck.py b/ipatests/test_integration/test_ipahealthcheck.py
|
||||
index 47392cec9345b6d2447b728fc21050edabe8f0cd..15fc38a6e155b343259ee61c6c7c5c561c7f6410 100644
|
||||
--- a/ipatests/test_integration/test_ipahealthcheck.py
|
||||
+++ b/ipatests/test_integration/test_ipahealthcheck.py
|
||||
@@ -28,7 +28,7 @@ from ipaplatform.osinfo import osinfo
|
||||
from ipaserver.install.installutils import resolve_ip_addresses_nss
|
||||
from ipatests.test_integration.test_caless import CALessBase
|
||||
from ipatests.test_integration.base import IntegrationTest
|
||||
-from packaging.version import parse as parse_version
|
||||
+from pkg_resources import parse_version
|
||||
from ipatests.test_integration.test_cert import get_certmonger_fs_id
|
||||
from ipatests.test_integration.test_external_ca import (
|
||||
install_server_external_ca_step1,
|
||||
diff --git a/ipatests/test_webui/ui_driver.py b/ipatests/test_webui/ui_driver.py
|
||||
index 356c4a0998cf01a6dbf1f207f38db87504aa5522..5dcea8979346119b2c11af7cf3f0b8c9b438cb40 100644
|
||||
--- a/ipatests/test_webui/ui_driver.py
|
||||
+++ b/ipatests/test_webui/ui_driver.py
|
||||
@@ -29,7 +29,7 @@ import re
|
||||
import time
|
||||
from datetime import datetime
|
||||
from functools import wraps
|
||||
-from packaging.version import parse as parse_version
|
||||
+from pkg_resources import parse_version
|
||||
from urllib.error import URLError
|
||||
|
||||
import pytest
|
||||
diff --git a/ipatests/test_xmlrpc/test_automember_plugin.py b/ipatests/test_xmlrpc/test_automember_plugin.py
|
||||
index aa7c1d65a7059a6a1e911f31204c05a42b6d9c3f..94f1b10985de61046bb65c092ab3d31c6f99e17c 100644
|
||||
--- a/ipatests/test_xmlrpc/test_automember_plugin.py
|
||||
+++ b/ipatests/test_xmlrpc/test_automember_plugin.py
|
||||
@@ -35,6 +35,7 @@ from ipaserver.plugins.automember import REBUILD_TASK_CONTAINER
|
||||
|
||||
import time
|
||||
import pytest
|
||||
+from pkg_resources import parse_version
|
||||
|
||||
try:
|
||||
from ipaserver.plugins.ldap2 import ldap2
|
||||
--
|
||||
2.52.0
|
||||
|
||||
@ -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,120 @@
|
||||
From 1c86c973c8dc778a71c8e2abf54ff37ececdd696 Mon Sep 17 00:00:00 2001
|
||||
From: PRANAV THUBE <pthube@redhat.com>
|
||||
Date: Tue, 13 Jan 2026 19:30:09 +0530
|
||||
Subject: [PATCH] ipatests: Move expire_password fixture into TestIPACommand
|
||||
class
|
||||
|
||||
Reviewed-By: Florence Blanc-Renaud <frenaud@redhat.com>
|
||||
Reviewed-By: Rob Crittenden <rcritten@redhat.com>
|
||||
---
|
||||
.../nightly_ipa-4-13_latest.yaml | 2 +-
|
||||
.../nightly_ipa-4-13_latest_selinux.yaml | 2 +-
|
||||
ipatests/test_integration/test_commands.py | 59 +++++++++----------
|
||||
3 files changed, 31 insertions(+), 32 deletions(-)
|
||||
|
||||
diff --git a/ipatests/prci_definitions/nightly_ipa-4-13_latest.yaml b/ipatests/prci_definitions/nightly_ipa-4-13_latest.yaml
|
||||
index 210739d4b576882ee43e06d85bce819ff30d2357..aff55727e463207fb235ff340989491e62162149 100644
|
||||
--- a/ipatests/prci_definitions/nightly_ipa-4-13_latest.yaml
|
||||
+++ b/ipatests/prci_definitions/nightly_ipa-4-13_latest.yaml
|
||||
@@ -1914,7 +1914,7 @@ jobs:
|
||||
class: RunPytest
|
||||
args:
|
||||
build_url: '{fedora-latest-ipa-4-13/build_url}'
|
||||
- test_suite: test_integration/test_random_serial_numbers.py::TestIPACommand_RSN::test_certificate_out_write_to_file
|
||||
+ test_suite: test_integration/test_random_serial_numbers.py::TestIPACommand_RSN
|
||||
template: *ci-ipa-4-13-latest
|
||||
timeout: 5400
|
||||
topology: *master_1repl_1client
|
||||
diff --git a/ipatests/prci_definitions/nightly_ipa-4-13_latest_selinux.yaml b/ipatests/prci_definitions/nightly_ipa-4-13_latest_selinux.yaml
|
||||
index 0fb7c050b97bf66645599fbff46b53c048211f96..e6c57ea060b3bb8bfdf8b6f981f8fd28e4a7d320 100644
|
||||
--- a/ipatests/prci_definitions/nightly_ipa-4-13_latest_selinux.yaml
|
||||
+++ b/ipatests/prci_definitions/nightly_ipa-4-13_latest_selinux.yaml
|
||||
@@ -2066,7 +2066,7 @@ jobs:
|
||||
args:
|
||||
build_url: '{fedora-latest-ipa-4-13/build_url}'
|
||||
selinux_enforcing: True
|
||||
- test_suite: test_integration/test_random_serial_numbers.py::TestIPACommand_RSN::test_certificate_out_write_to_file
|
||||
+ test_suite: test_integration/test_random_serial_numbers.py::TestIPACommand_RSN
|
||||
template: *ci-ipa-4-13-latest
|
||||
timeout: 5400
|
||||
topology: *master_1repl_1client
|
||||
diff --git a/ipatests/test_integration/test_commands.py b/ipatests/test_integration/test_commands.py
|
||||
index 01001e4c92037599b075e9bca3ddda7d0c8e8ffa..eda98e92b6b494752d74c6381bdcb6d5c47a26d1 100644
|
||||
--- a/ipatests/test_integration/test_commands.py
|
||||
+++ b/ipatests/test_integration/test_commands.py
|
||||
@@ -199,36 +199,6 @@ duplicatesubject = (
|
||||
duplicate_serial = "4097"
|
||||
|
||||
|
||||
-@pytest.fixture()
|
||||
-def expire_password():
|
||||
- """
|
||||
- Fixture to expire a user's password far into the future past
|
||||
- 2038, then revert time back.
|
||||
- """
|
||||
- hosts = dict()
|
||||
-
|
||||
- def _expire_password(host):
|
||||
- hosts['host'] = host
|
||||
- tasks.move_date(host, 'stop', '+20Years')
|
||||
- host.run_command(
|
||||
- ['ipactl', 'restart', '--ignore-service-failures']
|
||||
- )
|
||||
-
|
||||
- yield _expire_password
|
||||
-
|
||||
- host = hosts.pop('host')
|
||||
- # Prior to uninstall remove all the cert tracking to prevent
|
||||
- # errors from certmonger trying to check the status of certs
|
||||
- # that don't matter because we are uninstalling.
|
||||
- host.run_command(['systemctl', 'stop', 'certmonger'])
|
||||
- # Important: run_command with a str argument is able to
|
||||
- # perform shell expansion but run_command with a list of
|
||||
- # arguments is not
|
||||
- host.run_command('rm -fv ' + paths.CERTMONGER_REQUESTS_DIR + '*')
|
||||
- tasks.uninstall_master(host)
|
||||
- tasks.move_date(host, 'start', '-20Years')
|
||||
-
|
||||
-
|
||||
class TestIPACommand(IntegrationTest):
|
||||
"""
|
||||
A lot of commands can be executed against a single IPA installation
|
||||
@@ -239,6 +209,35 @@ class TestIPACommand(IntegrationTest):
|
||||
num_replicas = 1
|
||||
num_clients = 1
|
||||
|
||||
+ @pytest.fixture
|
||||
+ def expire_password(self):
|
||||
+ """
|
||||
+ Fixture to expire a user's password far into the future past
|
||||
+ 2038, then revert time back.
|
||||
+ """
|
||||
+ hosts = dict()
|
||||
+
|
||||
+ def _expire_password(host):
|
||||
+ hosts['host'] = host
|
||||
+ tasks.move_date(host, 'stop', '+20Years')
|
||||
+ host.run_command(
|
||||
+ ['ipactl', 'restart', '--ignore-service-failures']
|
||||
+ )
|
||||
+
|
||||
+ yield _expire_password
|
||||
+
|
||||
+ host = hosts.pop('host')
|
||||
+ # Prior to uninstall remove all the cert tracking to prevent
|
||||
+ # errors from certmonger trying to check the status of certs
|
||||
+ # that don't matter because we are uninstalling.
|
||||
+ host.run_command(['systemctl', 'stop', 'certmonger'])
|
||||
+ # Important: run_command with a str argument is able to
|
||||
+ # perform shell expansion but run_command with a list of
|
||||
+ # arguments is not
|
||||
+ host.run_command('rm -fv ' + paths.CERTMONGER_REQUESTS_DIR + '*')
|
||||
+ tasks.uninstall_master(host)
|
||||
+ tasks.move_date(host, 'start', '-20Years')
|
||||
+
|
||||
@pytest.fixture
|
||||
def pwpolicy_global(self):
|
||||
"""Fixture to change global password history policy and reset it"""
|
||||
--
|
||||
2.52.0
|
||||
|
||||
@ -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])
|
||||
|
||||
@ -0,0 +1,48 @@
|
||||
From ecc0efa96e3c446586425d470657de7f2d5376bf Mon Sep 17 00:00:00 2001
|
||||
From: Florence Blanc-Renaud <flo@redhat.com>
|
||||
Date: Mon, 19 Jan 2026 17:00:41 +0100
|
||||
Subject: [PATCH] ipatests: Fix xfail assertion for sssd 2.12.0
|
||||
|
||||
SSSD 2.12.0 provides fixes for
|
||||
https://github.com/SSSD/sssd/issues/5989
|
||||
https://github.com/SSSD/sssd/issues/7169
|
||||
|
||||
Update the condition expecting test failures in test_trust.py
|
||||
|
||||
Signed-off-by: Florence Blanc-Renaud <flo@redhat.com>
|
||||
Reviewed-By: David Hanina <dhanina@redhat.com>
|
||||
---
|
||||
ipatests/test_integration/test_trust.py | 10 ++++++----
|
||||
1 file changed, 6 insertions(+), 4 deletions(-)
|
||||
|
||||
diff --git a/ipatests/test_integration/test_trust.py b/ipatests/test_integration/test_trust.py
|
||||
index 13ad0afa4c1fb032d50f40cf7cb9b79283203225..0cab277c910a6d35f35b57e3068ee6f38706af59 100644
|
||||
--- a/ipatests/test_integration/test_trust.py
|
||||
+++ b/ipatests/test_integration/test_trust.py
|
||||
@@ -1219,9 +1219,10 @@ class TestNonPosixAutoPrivateGroup(BaseTestTrust):
|
||||
assert (uid == self.uid_override and gid == self.gid_override)
|
||||
test_group = self.clients[0].run_command(
|
||||
["id", nonposixuser]).stdout_text
|
||||
- cond2 = ((type == 'false'
|
||||
- and sssd_version >= tasks.parse_version("2.9.4"))
|
||||
- or type == 'hybrid')
|
||||
+ cond2 = (((type == 'false'
|
||||
+ and sssd_version >= tasks.parse_version("2.9.4"))
|
||||
+ or type == 'hybrid')
|
||||
+ and sssd_version < tasks.parse_version("2.12.0"))
|
||||
with xfail_context(cond2,
|
||||
'https://github.com/SSSD/sssd/issues/5989 '
|
||||
'and 7169'):
|
||||
@@ -1347,7 +1348,8 @@ class TestPosixAutoPrivateGroup(BaseTestTrust):
|
||||
and gid == self.gid_override)
|
||||
result = self.clients[0].run_command(['id', posixuser])
|
||||
sssd_version = tasks.get_sssd_version(self.clients[0])
|
||||
- bad_version = sssd_version >= tasks.parse_version("2.9.4")
|
||||
+ bad_version = (tasks.parse_version("2.9.4") <= sssd_version
|
||||
+ < tasks.parse_version("2.12.0"))
|
||||
with xfail_context(bad_version and type in ('false', 'hybrid'),
|
||||
"https://github.com/SSSD/sssd/issues/7169"):
|
||||
assert "10047(testgroup@{0})".format(
|
||||
--
|
||||
2.52.0
|
||||
|
||||
1279
SOURCES/0009-ipatests-Add-DNS-functional-integration-tests.patch
Normal file
1279
SOURCES/0009-ipatests-Add-DNS-functional-integration-tests.patch
Normal file
File diff suppressed because it is too large
Load Diff
@ -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);
|
||||
|
||||
@ -0,0 +1,99 @@
|
||||
From 5756ed2af940378c16d9d52e083b8c4005d41a13 Mon Sep 17 00:00:00 2001
|
||||
From: Florence Blanc-Renaud <flo@redhat.com>
|
||||
Date: Wed, 21 Jan 2026 17:19:18 +0100
|
||||
Subject: [PATCH] ipa-advise: smart card client script does not need krb ticket
|
||||
|
||||
The script generated by ipa-advise config-client-for-smart-card-auth
|
||||
currently requires a kerberos ticket because it calls ipa-certupdate.
|
||||
|
||||
Since IPA 4.9.0 and commit 1a09ce9, ipa-certupdate can be called
|
||||
without a ticket. Update the script so that it detects if it gets
|
||||
executed on a client recent enough to skip that requirement.
|
||||
|
||||
Update the test for config-client-for-smart-card-auth, do not
|
||||
call kinit admin on the client.
|
||||
|
||||
Fixes: https://pagure.io/freeipa/issue/9923
|
||||
Signed-off-by: Florence Blanc-Renaud <flo@redhat.com>
|
||||
Reviewed-By: Rob Crittenden <rcritten@redhat.com>
|
||||
---
|
||||
ipaserver/advise/plugins/smart_card_auth.py | 22 ++++++++++++++++++++-
|
||||
ipatests/test_integration/test_advise.py | 10 +++++++---
|
||||
2 files changed, 28 insertions(+), 4 deletions(-)
|
||||
|
||||
diff --git a/ipaserver/advise/plugins/smart_card_auth.py b/ipaserver/advise/plugins/smart_card_auth.py
|
||||
index b79797dcaee0c881d3ef752a268ed520d96b433b..a0e50e9806f7843d2981141d8941d5e37f53c0cd 100644
|
||||
--- a/ipaserver/advise/plugins/smart_card_auth.py
|
||||
+++ b/ipaserver/advise/plugins/smart_card_auth.py
|
||||
@@ -34,6 +34,26 @@ class common_smart_card_auth_config(Advice):
|
||||
'Use kinit as privileged user to obtain Kerberos credentials'
|
||||
])
|
||||
|
||||
+ def check_ccache_not_empty_if_old_version(self):
|
||||
+ self.log.comment("On version before IPA 4.9, "
|
||||
+ "check that the credential cache is not empty")
|
||||
+ self.log.command(
|
||||
+ "python3 -c \"from ipapython.version import VERSION;"
|
||||
+ "from ipaplatform.tasks import tasks;"
|
||||
+ "exit(tasks.parse_ipa_version(VERSION) >= "
|
||||
+ "tasks.parse_ipa_version('4.9.0'))\"")
|
||||
+ with self.log.if_branch('[ "$?" -eq "0" ]'):
|
||||
+ self.log.exit_on_failed_command(
|
||||
+ 'klist',
|
||||
+ [
|
||||
+ "Credential cache is empty",
|
||||
+ 'Use kinit as privileged user to obtain Kerberos '
|
||||
+ 'credentials'
|
||||
+ ])
|
||||
+ with self.log.else_branch():
|
||||
+ self.log.command(
|
||||
+ "echo 'Version 4.9.0+ does not require Kerberos credentials'")
|
||||
+
|
||||
def check_and_set_ca_cert_paths(self):
|
||||
ca_paths_variable = self.smart_card_ca_certs_variable_name
|
||||
single_ca_path_variable = self.single_ca_cert_variable_name
|
||||
@@ -260,7 +280,7 @@ class config_client_for_smart_card_auth(common_smart_card_auth_config):
|
||||
def get_info(self):
|
||||
self.log.exit_on_nonroot_euid()
|
||||
self.check_and_set_ca_cert_paths()
|
||||
- self.check_ccache_not_empty()
|
||||
+ self.check_ccache_not_empty_if_old_version()
|
||||
self.check_and_remove_pam_pkcs11()
|
||||
self.install_opensc_and_dconf_packages()
|
||||
self.install_krb5_client_dependencies()
|
||||
diff --git a/ipatests/test_integration/test_advise.py b/ipatests/test_integration/test_advise.py
|
||||
index 3d5cadee319ebba14ebc43ebb1dc90a502e5d3b8..a336634ae9627133c5ad4dea4b1c43ffd726df10 100644
|
||||
--- a/ipatests/test_integration/test_advise.py
|
||||
+++ b/ipatests/test_integration/test_advise.py
|
||||
@@ -60,13 +60,17 @@ class TestAdvice(IntegrationTest):
|
||||
)
|
||||
tasks.install_client(cls.master, cls.clients[0])
|
||||
|
||||
- def execute_advise(self, host, advice_id, *args):
|
||||
+ def execute_advise(self, host, advice_id, *args, kinit=True):
|
||||
# ipa-advise script is only available on a server
|
||||
tasks.kinit_admin(self.master)
|
||||
advice = self.master.run_command(['ipa-advise', advice_id])
|
||||
# execute script on host (client or master)
|
||||
if host is not self.master:
|
||||
- tasks.kinit_admin(host)
|
||||
+ if kinit:
|
||||
+ tasks.kinit_admin(host)
|
||||
+ else:
|
||||
+ # Make sure we don't have any ticket
|
||||
+ tasks.kdestroy_all(host)
|
||||
filename = tasks.upload_temp_contents(host, advice.stdout_text)
|
||||
cmd = ['sh', filename]
|
||||
cmd.extend(args)
|
||||
@@ -181,7 +185,7 @@ class TestAdvice(IntegrationTest):
|
||||
ca_pem = ExternalCA().create_ca()
|
||||
ca_file = tasks.upload_temp_contents(client, ca_pem)
|
||||
try:
|
||||
- self.execute_advise(client, advice_id, ca_file)
|
||||
+ self.execute_advise(client, advice_id, ca_file, kinit=False)
|
||||
finally:
|
||||
client.run_command(['rm', '-f', ca_file])
|
||||
|
||||
--
|
||||
2.52.0
|
||||
|
||||
@ -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))
|
||||
|
||||
|
||||
@ -0,0 +1,177 @@
|
||||
From fd84cec77d18e0e608285ab712f8211b0b49a7fe Mon Sep 17 00:00:00 2001
|
||||
From: PRANAV THUBE <pthube@redhat.com>
|
||||
Date: Thu, 18 Dec 2025 17:13:29 +0530
|
||||
Subject: [PATCH] ipatests: Fix resolver state tracking in enforced DNS policy
|
||||
tests.
|
||||
|
||||
Use resolver.setup_resolver() instead of direct file manipulation to
|
||||
prevent state tracking failures in IDM-CI. Remove redundant manual
|
||||
resolv.conf changes and fix operation order.
|
||||
|
||||
Related: https://pagure.io/freeipa/issue/9904
|
||||
Reviewed-By: Antonio Torres <antorres@redhat.com>
|
||||
---
|
||||
ipatests/test_integration/test_edns.py | 70 ++++++++++++++++++--------
|
||||
1 file changed, 50 insertions(+), 20 deletions(-)
|
||||
|
||||
diff --git a/ipatests/test_integration/test_edns.py b/ipatests/test_integration/test_edns.py
|
||||
index d02ce45a1f9c0b04332e4ff4a055ab4c61b35eb4..54b7859da1b9fa320c07bdf29f79d2fdb292c337 100644
|
||||
--- a/ipatests/test_integration/test_edns.py
|
||||
+++ b/ipatests/test_integration/test_edns.py
|
||||
@@ -15,6 +15,9 @@ from ipatests.test_integration.test_dns import TestDNS
|
||||
from ipatests.pytest_ipa.integration.firewall import Firewall
|
||||
from ipaplatform.osinfo import osinfo
|
||||
from ipaplatform.paths import paths
|
||||
+from ipatests.pytest_ipa.integration.resolver import (
|
||||
+ resolver as detect_resolver
|
||||
+)
|
||||
|
||||
|
||||
def apply_enforced_dns_preconfig(host, master_ip, master_host,
|
||||
@@ -30,6 +33,9 @@ def apply_enforced_dns_preconfig(host, master_ip, master_host,
|
||||
ca_cert_path: Path to the CA certificate on the master
|
||||
dest_cert_name: Destination certificate filename in trust anchors
|
||||
"""
|
||||
+ # Backup resolver before making any changes (with original resolver type)
|
||||
+ host.resolver.backup()
|
||||
+
|
||||
# Get the network interface for routing
|
||||
iface_cmd = host.run_command([
|
||||
"bash", "-c",
|
||||
@@ -43,7 +49,8 @@ def apply_enforced_dns_preconfig(host, master_ip, master_host,
|
||||
host.put_file_contents(dest_ca_path, ca_cert_data)
|
||||
host.run_command(["update-ca-trust", "extract"])
|
||||
|
||||
- if osinfo.id in ['rhel', 'centos']:
|
||||
+ platform = tasks.get_platform(host)
|
||||
+ if platform in ['rhel', 'centos']:
|
||||
# RHEL/CentOS configuration
|
||||
# Install and configure dnsconfd
|
||||
tasks.install_packages(host, ['dnsconfd'])
|
||||
@@ -51,31 +58,60 @@ def apply_enforced_dns_preconfig(host, master_ip, master_host,
|
||||
host.run_command(["systemctl", "enable", "--now", "dnsconfd"])
|
||||
host.run_command(["nmcli", "g", "reload"])
|
||||
|
||||
- # Configure DNS over TLS via NetworkManager
|
||||
+ # Configure DNS over TLS via NetworkManager device-specific settings.
|
||||
+ # Device-specific DNS settings work alongside global resolver config.
|
||||
host.run_command([
|
||||
"nmcli", "device", "modify", iface,
|
||||
"ipv4.dns", f"dns+tls://{master_ip}"
|
||||
])
|
||||
|
||||
- elif osinfo.id == 'fedora':
|
||||
- # Fedora configuration
|
||||
+ elif platform == 'fedora':
|
||||
# Configure systemd-resolved for DNS over TLS
|
||||
host.run_command([
|
||||
- "ln", "-sf", "../run/systemd/resolve/stub-resolv.conf",
|
||||
+ "ln", "-sf", "/run/systemd/resolve/stub-resolv.conf",
|
||||
"/etc/resolv.conf"
|
||||
])
|
||||
+ # Restart systemd-resolved to ensure DoT settings are fully applied
|
||||
+ # and resolv.conf is updated
|
||||
host.run_command(["systemctl", "restart", "systemd-resolved"])
|
||||
-
|
||||
- # Configure DNS over TLS via systemd-resolve
|
||||
+ # Configure DNS over TLS via systemd-resolve per-interface settings
|
||||
+ # Per-interface DNS settings work alongside the global resolver config
|
||||
host.run_command([
|
||||
"systemd-resolve", "--set-dns", master_ip,
|
||||
"--set-dnsovertls=yes", f"--interface={iface}"
|
||||
])
|
||||
else:
|
||||
raise ValueError(
|
||||
- f"Unsupported OS for enforced DNS policy: {osinfo.id}"
|
||||
+ f"Unsupported OS for enforced DNS policy: {platform}"
|
||||
)
|
||||
|
||||
+ # Re-detect resolver since we may have changed the resolver type
|
||||
+ # (e.g., from PlainFileResolver to ResolvedResolver on Fedora)
|
||||
+ # This ensures state tracking works correctly with the new resolver type
|
||||
+ # Store the original resolver so we can restore from it during uninstall
|
||||
+ host._enforced_dns_original_resolver = host.resolver
|
||||
+ host.resolver = detect_resolver(host)
|
||||
+ host.resolver.current_state = host.resolver._get_state()
|
||||
+
|
||||
+
|
||||
+def restore_enforced_dns_resolver(host):
|
||||
+ """
|
||||
+ Restore resolver state after enforced DNS preconfig.
|
||||
+
|
||||
+ This restores the original resolver backup created by
|
||||
+ apply_enforced_dns_preconfig() and syncs host.resolver state.
|
||||
+ """
|
||||
+ if not hasattr(host, '_enforced_dns_original_resolver'):
|
||||
+ return
|
||||
+ original_resolver = host._enforced_dns_original_resolver
|
||||
+ if original_resolver.has_backups():
|
||||
+ original_resolver.current_state = original_resolver._get_state()
|
||||
+ original_resolver.restore()
|
||||
+ # Sync host.resolver state since original_resolver changed the config
|
||||
+ host.resolver.backups.clear()
|
||||
+ host.resolver.current_state = host.resolver._get_state()
|
||||
+ delattr(host, '_enforced_dns_original_resolver')
|
||||
+
|
||||
|
||||
def setup_dns_over_tls_environment(cls):
|
||||
"""
|
||||
@@ -451,17 +487,12 @@ class TestDNSOverTLS_EnforcedPolicy_IPA_CA(IntegrationTest):
|
||||
tasks.install_master(self.master, extra_args=args)
|
||||
|
||||
# Apply pre-configuration on client before installation
|
||||
+ # This configures the resolver with master IP and domain
|
||||
apply_enforced_dns_preconfig(
|
||||
self.clients[0], self.master.ip, self.master,
|
||||
paths.IPA_CA_CRT, "ca.crt"
|
||||
)
|
||||
|
||||
- # Configure client nameserver
|
||||
- self.clients[0].put_file_contents(
|
||||
- paths.RESOLV_CONF,
|
||||
- "nameserver %s" % self.master.ip
|
||||
- )
|
||||
-
|
||||
# Install client with enforced policy
|
||||
args = [
|
||||
"--dns-over-tls",
|
||||
@@ -506,6 +537,8 @@ class TestDNSOverTLS_EnforcedPolicy_IPA_CA(IntegrationTest):
|
||||
"""
|
||||
This test ensures that all hosts can be uninstalled correctly.
|
||||
"""
|
||||
+ for host in [self.clients[0], self.replicas[0]]:
|
||||
+ restore_enforced_dns_resolver(host)
|
||||
tasks.uninstall_client(self.clients[0])
|
||||
tasks.uninstall_replica(self.master, self.replicas[0])
|
||||
tasks.uninstall_master(self.master)
|
||||
@@ -559,17 +592,12 @@ class TestDNSOverTLS_EnforcedPolicy_External_CA(IntegrationTest):
|
||||
|
||||
# Apply pre-configuration on client before installation
|
||||
# (includes CA cert setup)
|
||||
+ # This configures the resolver with master IP and domain
|
||||
apply_enforced_dns_preconfig(
|
||||
self.clients[0], self.master.ip, self.master,
|
||||
cert_dest, "certificate.pem"
|
||||
)
|
||||
|
||||
- # Configure client nameserver
|
||||
- self.clients[0].put_file_contents(
|
||||
- paths.RESOLV_CONF,
|
||||
- "nameserver %s" % self.master.ip
|
||||
- )
|
||||
-
|
||||
# Install client with enforced policy
|
||||
args = [
|
||||
"--dns-over-tls",
|
||||
@@ -615,6 +643,8 @@ class TestDNSOverTLS_EnforcedPolicy_External_CA(IntegrationTest):
|
||||
"""
|
||||
This test ensures that all hosts can be uninstalled correctly.
|
||||
"""
|
||||
+ for host in [self.clients[0], self.replicas[0]]:
|
||||
+ restore_enforced_dns_resolver(host)
|
||||
tasks.uninstall_client(self.clients[0])
|
||||
tasks.uninstall_replica(self.master, self.replicas[0])
|
||||
tasks.uninstall_master(self.master)
|
||||
--
|
||||
2.52.0
|
||||
|
||||
@ -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,224 @@
|
||||
From 0800065ac5555dba102f05c947ca47b5dc9a81af Mon Sep 17 00:00:00 2001
|
||||
From: Rafael Guterres Jeffman <rjeffman@redhat.com>
|
||||
Date: Fri, 23 Jan 2026 16:49:31 -0300
|
||||
Subject: [PATCH] freeipa.spec.in: Use systemd-sysusers to setup users and
|
||||
groups
|
||||
|
||||
System accounts for `kdcproxy` and `ipaapi` are now created with
|
||||
sysusers configuration and macros. User `apache` is updated, by
|
||||
adding it to group `ipaapi` using sysusers configuration.
|
||||
|
||||
Fixes: https://pagure.io/freeipa/issue/9572
|
||||
|
||||
AI agent usage info:
|
||||
|
||||
The initial changes were created by Claude by providing the following
|
||||
context:
|
||||
|
||||
>> Add support for creating users through systemd-sysusers by creating
|
||||
>> a folder init/sysusersd, similar to init/tmpfilesd, changing install
|
||||
>> paths in init/sysusersd/Makefile.am, adding configure option
|
||||
>> --with-systemdsysusersdir similar to --with-systemdtmpfilesdir, and
|
||||
>> adding a new file init/sysusersd/freeeipo.sysusers.in with the
|
||||
>> contents:
|
||||
>> ```
|
||||
>> # system accounts for IPA
|
||||
>> u! kdcproxy - "IPA KDC Proxy Uer"
|
||||
>> u! ipaapi - "IPA Framework User"
|
||||
>> # - add Apache HTTPd user to ipaapi group
|
||||
>> m apache ipaapi
|
||||
>> ```
|
||||
>> and updating de spec file freeipa.spec.in
|
||||
|
||||
LLM model used was Claude Sonnet 4.5, and a CLAUDE.md file was
|
||||
automatically created by claude based on the freeipa repository.
|
||||
No custom context was available for the agent.
|
||||
|
||||
Assisted-by: Claude <noreply@anthropic.com>
|
||||
Signed-off-by: Rafael Guterres Jeffman <rjeffman@redhat.com>
|
||||
Reviewed-By: David Hanina <dhanina@redhat.com>
|
||||
Reviewed-By: Florence Blanc-Renaud <flo@redhat.com>
|
||||
Reviewed-By: Alexander Bokovoy <abokovoy@redhat.com>
|
||||
---
|
||||
configure.ac | 42 ++++++++++++++++++++++++++------------
|
||||
freeipa.spec.in | 16 +++------------
|
||||
init/Makefile.am | 2 +-
|
||||
init/sysusersd/Makefile.am | 12 +++++++++++
|
||||
init/sysusersd/ipa.conf.in | 8 ++++++++
|
||||
5 files changed, 53 insertions(+), 27 deletions(-)
|
||||
create mode 100644 init/sysusersd/Makefile.am
|
||||
create mode 100644 init/sysusersd/ipa.conf.in
|
||||
|
||||
diff --git a/configure.ac b/configure.ac
|
||||
index 8b9adec1559c8831ef39c27860c1d31496ec5474..b0462bf779dedb7c2fe59494d4eb64a6dd121b1a 100644
|
||||
--- a/configure.ac
|
||||
+++ b/configure.ac
|
||||
@@ -267,6 +267,13 @@ AC_ARG_WITH([systemdtmpfilesdir],
|
||||
[systemdtmpfilesdir=$($PKG_CONFIG --define-variable=prefix='${prefix}' --variable=tmpfilesdir systemd)])
|
||||
AC_SUBST([systemdtmpfilesdir])
|
||||
|
||||
+AC_ARG_WITH([systemdsysusersdir],
|
||||
+ AS_HELP_STRING([--with-systemdsysusersdir=DIR],
|
||||
+ [Directory for systemd-sysusers configuration files]),
|
||||
+ [systemdsysusersdir=$with_systemdsysusersdir],
|
||||
+ [systemdsysusersdir=$($PKG_CONFIG --define-variable=prefix='${prefix}' --variable=sysusersdir systemd)])
|
||||
+AC_SUBST([systemdsysusersdir])
|
||||
+
|
||||
AC_ARG_WITH([systemdcatalogdir],
|
||||
AS_HELP_STRING([--with-systemdcatalogdir=DIR],
|
||||
[Directory for systemd journal catalog files]),
|
||||
@@ -398,22 +405,29 @@ AC_SUBST([IPAPLATFORM])
|
||||
AC_MSG_RESULT([${IPAPLATFORM}])
|
||||
|
||||
if test "x${IPAPLATFORM}" == "xdebian"; then
|
||||
- HTTPD_GROUP="www-data"
|
||||
- KRB5KDC_SERVICE="krb5-kdc.service"
|
||||
- NAMED_GROUP="bind"
|
||||
- ODS_USER="opendnssec"
|
||||
- ODS_GROUP="opendnssec"
|
||||
- # see https://www.debian.org/doc/packaging-manuals/python-policy/ap-packaging_tools.html
|
||||
- PYTHON_INSTALL_EXTRA_OPTIONS="--install-layout=deb"
|
||||
+ dnl Ubuntu http user is www-data
|
||||
+ HTTPD_USER="www-data"
|
||||
+ HTTPD_GROUP="www-data"
|
||||
+ KRB5KDC_SERVICE="krb5-kdc.service"
|
||||
+ NAMED_GROUP="bind"
|
||||
+ ODS_USER="opendnssec"
|
||||
+ ODS_GROUP="opendnssec"
|
||||
+ # see https://www.debian.org/doc/packaging-manuals/python-policy/ap-packaging_tools.html
|
||||
+ PYTHON_INSTALL_EXTRA_OPTIONS="--install-layout=deb"
|
||||
else
|
||||
- HTTPD_GROUP="apache"
|
||||
- KRB5KDC_SERVICE="krb5kdc.service"
|
||||
- NAMED_GROUP="named"
|
||||
- ODS_USER="ods"
|
||||
- ODS_GROUP="ods"
|
||||
- PYTHON_INSTALL_EXTRA_OPTIONS=""
|
||||
+ HTTPD_USER="apache"
|
||||
+ HTTPD_GROUP="apache"
|
||||
+ KRB5KDC_SERVICE="krb5kdc.service"
|
||||
+ NAMED_GROUP="named"
|
||||
+ ODS_USER="ods"
|
||||
+ ODS_GROUP="ods"
|
||||
+ PYTHON_INSTALL_EXTRA_OPTIONS=""
|
||||
fi
|
||||
|
||||
+AC_MSG_CHECKING([HTTPD_USER])
|
||||
+AC_SUBST([HTTPD_USER])
|
||||
+AC_MSG_RESULT([${HTTPD_USER}])
|
||||
+
|
||||
AC_MSG_CHECKING([HTTPD_GROUP])
|
||||
AC_SUBST([HTTPD_GROUP])
|
||||
AC_MSG_RESULT([${HTTPD_GROUP}])
|
||||
@@ -654,6 +668,7 @@ AC_CONFIG_FILES([
|
||||
daemons/ipa-slapi-plugins/topology/Makefile
|
||||
init/systemd/Makefile
|
||||
init/tmpfilesd/Makefile
|
||||
+ init/sysusersd/Makefile
|
||||
init/Makefile
|
||||
install/Makefile
|
||||
install/certmonger/Makefile
|
||||
@@ -736,6 +751,7 @@ AM_COND_IF([ENABLE_SERVER], [
|
||||
KRAD libs: ${KRAD_LIBS}
|
||||
krb5rundir: ${krb5rundir}
|
||||
systemdtmpfilesdir: ${systemdtmpfilesdir}
|
||||
+ systemdsysusersdir: ${systemdsysusersdir}
|
||||
build mode: server & client"
|
||||
], [
|
||||
echo "\
|
||||
diff --git a/freeipa.spec.in b/freeipa.spec.in
|
||||
index f3b45a5308f93928a4d4bb4cbb2ae96c487cf88a..48912185073472c11f08d000dacf3a0b7f2ec668 100644
|
||||
--- a/freeipa.spec.in
|
||||
+++ b/freeipa.spec.in
|
||||
@@ -620,7 +620,7 @@ Requires: systemd-units >= %{systemd_version}
|
||||
Requires: system-logos-ipa >= 80.4
|
||||
%endif
|
||||
|
||||
-# The list below is automatically generated by `fix-spec.sh -i`
|
||||
+# The list below is automatically generated by `fix-spec.sh -i`
|
||||
# from the install/freeipa-webui
|
||||
Provides: bundled(npm(attr-accept)) = 2.2.5
|
||||
Provides: bundled(npm(cookie)) = 1.0.2
|
||||
@@ -1274,6 +1274,7 @@ fi
|
||||
/bin/systemctl reload-or-try-restart dbus
|
||||
/bin/systemctl reload-or-try-restart oddjobd
|
||||
|
||||
+%sysusers_create %{_sysusersdir}/ipa.conf
|
||||
%tmpfiles_create ipa.conf
|
||||
%journal_catalog_update
|
||||
|
||||
@@ -1331,18 +1332,6 @@ if [ -e /usr/sbin/ipa_kpasswd ]; then
|
||||
fi
|
||||
|
||||
|
||||
-%pre server-common
|
||||
-# create users and groups
|
||||
-# create kdcproxy group and user
|
||||
-getent group kdcproxy >/dev/null || groupadd -f -r kdcproxy
|
||||
-getent passwd kdcproxy >/dev/null || useradd -r -g kdcproxy -s /sbin/nologin -d / -c "IPA KDC Proxy User" kdcproxy
|
||||
-# create ipaapi group and user
|
||||
-getent group ipaapi >/dev/null || groupadd -f -r ipaapi
|
||||
-getent passwd ipaapi >/dev/null || useradd -r -g ipaapi -s /sbin/nologin -d / -c "IPA Framework User" ipaapi
|
||||
-# add apache to ipaaapi group
|
||||
-id -Gn apache | grep '\bipaapi\b' >/dev/null || usermod apache -a -G ipaapi
|
||||
-
|
||||
-
|
||||
%post server-dns
|
||||
%systemd_post ipa-dnskeysyncd.service ipa-ods-exporter.socket ipa-ods-exporter.service
|
||||
|
||||
@@ -1729,6 +1718,7 @@ fi
|
||||
%dir %attr(0755,root,root) %{_sysconfdir}/ipa/kdcproxy
|
||||
%config(noreplace) %{_sysconfdir}/ipa/kdcproxy/kdcproxy.conf
|
||||
# NOTE: systemd specific section
|
||||
+%{_sysusersdir}/ipa.conf
|
||||
%{_tmpfilesdir}/ipa.conf
|
||||
%attr(644,root,root) %{_unitdir}/ipa-custodia.service
|
||||
%ghost %attr(644,root,root) %{etc_systemd_dir}/httpd.d/ipa.conf
|
||||
diff --git a/init/Makefile.am b/init/Makefile.am
|
||||
index 8f4d1d0a8f7e9739cf7587de6e000dd027a85146..1d4a85ab20e892c8a7c428b84a6393d29e9616e5 100644
|
||||
--- a/init/Makefile.am
|
||||
+++ b/init/Makefile.am
|
||||
@@ -2,7 +2,7 @@
|
||||
#
|
||||
AUTOMAKE_OPTIONS = 1.7
|
||||
|
||||
-SUBDIRS = systemd tmpfilesd
|
||||
+SUBDIRS = systemd tmpfilesd sysusersd
|
||||
|
||||
dist_sysconfenv_DATA = \
|
||||
ipa-dnskeysyncd \
|
||||
diff --git a/init/sysusersd/Makefile.am b/init/sysusersd/Makefile.am
|
||||
new file mode 100644
|
||||
index 0000000000000000000000000000000000000000..8577255a61ac796353995d3d1f99de195f9bd7c0
|
||||
--- /dev/null
|
||||
+++ b/init/sysusersd/Makefile.am
|
||||
@@ -0,0 +1,12 @@
|
||||
+dist_noinst_DATA = \
|
||||
+ ipa.conf.in
|
||||
+
|
||||
+systemdsysusers_DATA = \
|
||||
+ ipa.conf
|
||||
+
|
||||
+CLEANFILES = $(systemdsysusers_DATA)
|
||||
+
|
||||
+%: %.in Makefile
|
||||
+ sed \
|
||||
+ -e 's|@HTTPD_USER[@]|$(HTTPD_USER)|g' \
|
||||
+ '$(srcdir)/$@.in' >$@
|
||||
diff --git a/init/sysusersd/ipa.conf.in b/init/sysusersd/ipa.conf.in
|
||||
new file mode 100644
|
||||
index 0000000000000000000000000000000000000000..dcddfc2fc7969b86913ffcd8c397152e4f800fda
|
||||
--- /dev/null
|
||||
+++ b/init/sysusersd/ipa.conf.in
|
||||
@@ -0,0 +1,8 @@
|
||||
+# IPA KDC Proxy user and group
|
||||
+u kdcproxy - "IPA KDC Proxy User"
|
||||
+
|
||||
+# IPA API user and group
|
||||
+u ipaapi - "IPA API User"
|
||||
+
|
||||
+# - add Apache system account to ipaapi group (platform-specific)
|
||||
+m @HTTPD_USER@ ipaapi
|
||||
--
|
||||
2.52.0
|
||||
|
||||
@ -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,196 @@
|
||||
From a55f9185c96457bdffe9099ddde39ec696f1f998 Mon Sep 17 00:00:00 2001
|
||||
From: Anuja More <amore@redhat.com>
|
||||
Date: Tue, 6 Jan 2026 18:30:06 +0530
|
||||
Subject: [PATCH] ipatests: add Random Password based replica promotion
|
||||
coverage
|
||||
|
||||
Added missing test coverage for :
|
||||
- Installing IPA replica server using random password.
|
||||
- Installing IPA replica server using random password installed client
|
||||
|
||||
- Automated with Cursor+Claude
|
||||
|
||||
Fixes: https://pagure.io/freeipa/issue/9922
|
||||
|
||||
Signed-off-by: Anuja More <amore@redhat.com>
|
||||
Reviewed-By: David Hanina <dhanina@redhat.com>
|
||||
Reviewed-By: Florence Blanc-Renaud <flo@redhat.com>
|
||||
Reviewed-By: Rob Crittenden <rcritten@redhat.com>
|
||||
Reviewed-By: Rob Crittenden <rcritten@redhat.com>
|
||||
Reviewed-By: Florence Blanc-Renaud <frenaud@redhat.com>
|
||||
Reviewed-By: David Hanina <dhanina@redhat.com>
|
||||
---
|
||||
.../nightly_ipa-4-13_latest.yaml | 12 +++
|
||||
.../nightly_ipa-4-13_latest_selinux.yaml | 13 +++
|
||||
ipatests/pytest_ipa/integration/tasks.py | 15 ++++
|
||||
.../test_replica_promotion.py | 87 +++++++++++++++++++
|
||||
4 files changed, 127 insertions(+)
|
||||
|
||||
diff --git a/ipatests/prci_definitions/nightly_ipa-4-13_latest.yaml b/ipatests/prci_definitions/nightly_ipa-4-13_latest.yaml
|
||||
index aff55727e463207fb235ff340989491e62162149..c61701ef5f88760f1d6fc36d4acce453a22b6f8f 100644
|
||||
--- a/ipatests/prci_definitions/nightly_ipa-4-13_latest.yaml
|
||||
+++ b/ipatests/prci_definitions/nightly_ipa-4-13_latest.yaml
|
||||
@@ -1000,6 +1000,18 @@ jobs:
|
||||
timeout: 7200
|
||||
topology: *ad_master_1repl_1client
|
||||
|
||||
+ fedora-latest-ipa-4-13/test_replica_promotion_TestReplicaPromotionRandomPassword:
|
||||
+ requires: [fedora-latest-ipa-4-13/build]
|
||||
+ priority: 50
|
||||
+ job:
|
||||
+ class: RunPytest
|
||||
+ args:
|
||||
+ build_url: '{fedora-latest-ipa-4-13/build_url}'
|
||||
+ test_suite: test_integration/test_replica_promotion.py::TestReplicaPromotionRandomPassword
|
||||
+ template: *ci-ipa-4-13-latest
|
||||
+ timeout: 7200
|
||||
+ topology: *master_1repl
|
||||
+
|
||||
fedora-latest-ipa-4-13/test_upgrade:
|
||||
requires: [fedora-latest-ipa-4-13/build]
|
||||
priority: 50
|
||||
diff --git a/ipatests/prci_definitions/nightly_ipa-4-13_latest_selinux.yaml b/ipatests/prci_definitions/nightly_ipa-4-13_latest_selinux.yaml
|
||||
index e6c57ea060b3bb8bfdf8b6f981f8fd28e4a7d320..9b96f3e857e2125478b45632d8d58e42b6e92668 100644
|
||||
--- a/ipatests/prci_definitions/nightly_ipa-4-13_latest_selinux.yaml
|
||||
+++ b/ipatests/prci_definitions/nightly_ipa-4-13_latest_selinux.yaml
|
||||
@@ -1078,6 +1078,19 @@ jobs:
|
||||
timeout: 7200
|
||||
topology: *ad_master_1repl_1client
|
||||
|
||||
+ fedora-latest-ipa-4-13/test_replica_promotion_TestReplicaPromotionRandomPassword:
|
||||
+ requires: [fedora-latest-ipa-4-13/build]
|
||||
+ priority: 50
|
||||
+ job:
|
||||
+ class: RunPytest
|
||||
+ args:
|
||||
+ build_url: '{fedora-latest-ipa-4-13/build_url}'
|
||||
+ selinux_enforcing: True
|
||||
+ test_suite: test_integration/test_replica_promotion.py::TestReplicaPromotionRandomPassword
|
||||
+ template: *ci-ipa-4-13-latest
|
||||
+ timeout: 7200
|
||||
+ topology: *master_1repl
|
||||
+
|
||||
fedora-latest-ipa-4-13/test_upgrade:
|
||||
requires: [fedora-latest-ipa-4-13/build]
|
||||
priority: 50
|
||||
diff --git a/ipatests/pytest_ipa/integration/tasks.py b/ipatests/pytest_ipa/integration/tasks.py
|
||||
index 32ac5cbc2c6fe87850dfb15c1d5beae6fa648dfb..ff2ea9792d04ebd2e6bd7bb3b51d97f35cb3fbfb 100755
|
||||
--- a/ipatests/pytest_ipa/integration/tasks.py
|
||||
+++ b/ipatests/pytest_ipa/integration/tasks.py
|
||||
@@ -3340,3 +3340,18 @@ def service_control_dirsrv(host, function='restart'):
|
||||
instance = realm_to_serverid(host.domain.realm)
|
||||
cmd = host.run_command(['systemctl', function, f"dirsrv@{instance}"])
|
||||
assert cmd.returncode == 0
|
||||
+
|
||||
+
|
||||
+def host_add_with_random_password(host, new_host):
|
||||
+ """
|
||||
+ Add a new host with a random password and return the generated password.
|
||||
+ """
|
||||
+ kinit_admin(host)
|
||||
+ cmd = host.run_command(
|
||||
+ ['ipa', 'host-add', new_host.hostname, '--random']
|
||||
+ )
|
||||
+ result = re.search("Random password: (?P<password>.*$)",
|
||||
+ cmd.stdout_text,
|
||||
+ re.MULTILINE)
|
||||
+ randpasswd1 = result.group('password')
|
||||
+ return randpasswd1
|
||||
diff --git a/ipatests/test_integration/test_replica_promotion.py b/ipatests/test_integration/test_replica_promotion.py
|
||||
index 76d6aa24e2ab3d88b7013e0d107d0e27ae7f3426..f8c8414eefbc015cfc0947de575ea349a65a5e73 100644
|
||||
--- a/ipatests/test_integration/test_replica_promotion.py
|
||||
+++ b/ipatests/test_integration/test_replica_promotion.py
|
||||
@@ -1368,3 +1368,90 @@ class TestReplicaConn(IntegrationTest):
|
||||
logs = self.replica.get_file_contents(paths.IPAREPLICA_CONNCHECK_LOG)
|
||||
error = "not allowed to perform server connection check"
|
||||
assert error.encode() not in logs
|
||||
+
|
||||
+
|
||||
+class TestReplicaPromotionRandomPassword(IntegrationTest):
|
||||
+ """
|
||||
+ Test installation of a replica using Random Password
|
||||
+ (one step install and two-steps installation
|
||||
+ with client and promotion).
|
||||
+ """
|
||||
+ num_replicas = 1
|
||||
+
|
||||
+ @classmethod
|
||||
+ def install(cls, mh):
|
||||
+ tasks.install_master(cls.master, setup_dns=True)
|
||||
+ cls.replicas[0].resolver.backup()
|
||||
+ nameservers = cls.master.ip
|
||||
+ cls.replicas[0].resolver.setup_resolver(
|
||||
+ nameservers, cls.master.domain.name
|
||||
+ )
|
||||
+
|
||||
+ @replicas_cleanup
|
||||
+ def test_replica_random_password_install(self):
|
||||
+ """
|
||||
+ Installing IPA replica server using Random Password.
|
||||
+
|
||||
+ Steps:
|
||||
+ 1. Ensure replica host/server entries are clean and add DNS A record.
|
||||
+ 2. Add the replica host with a random password and add it to
|
||||
+ the ipaservers hostgroup.
|
||||
+ 3. Install the replica using random password.
|
||||
+ """
|
||||
+ replica = self.replicas[0]
|
||||
+ tasks.kinit_admin(self.master)
|
||||
+ tasks.add_a_record(self.master, replica)
|
||||
+ randpasswd = tasks.host_add_with_random_password(self.master,
|
||||
+ replica)
|
||||
+ self.master.run_command([
|
||||
+ 'ipa', 'hostgroup-add-member', '--hosts',
|
||||
+ replica.hostname, 'ipaservers'
|
||||
+ ])
|
||||
+ replica.run_command(
|
||||
+ ['ipa-replica-install', '-p', randpasswd, '-U']
|
||||
+ )
|
||||
+
|
||||
+ @replicas_cleanup
|
||||
+ def test_replica_two_step_install(self):
|
||||
+ """
|
||||
+ Installing IPA replica server using Random Password installed client
|
||||
+
|
||||
+ Steps:
|
||||
+ 1. Ensure replica host/server entries are clean and add DNS A record.
|
||||
+ 2. Add the replica host with a random password and add it to
|
||||
+ the ipaservers hostgroup.
|
||||
+ 3. Install the IPA client using the Random Password.
|
||||
+ 4. Promote the client to a replica.
|
||||
+ 5. Install CA on the replica and verify the server role.
|
||||
+ """
|
||||
+ replica = self.replicas[0]
|
||||
+ replica.resolver.backup()
|
||||
+ tasks.kinit_admin(self.master)
|
||||
+ tasks.add_a_record(self.master, replica)
|
||||
+ randpasswd = tasks.host_add_with_random_password(self.master,
|
||||
+ replica)
|
||||
+ self.master.run_command([
|
||||
+ 'ipa', 'hostgroup-add-member', '--hosts',
|
||||
+ replica.hostname, 'ipaservers'
|
||||
+ ])
|
||||
+ replica.resolver.setup_resolver(
|
||||
+ self.master.ip, self.master.domain.name
|
||||
+ )
|
||||
+ replica.run_command(
|
||||
+ ['ipa-client-install', '-w', randpasswd, '-U']
|
||||
+ )
|
||||
+ Firewall(replica).enable_services(["freeipa-ldap",
|
||||
+ "freeipa-ldaps"])
|
||||
+ replica.run_command(['ipa-replica-install', '-U'])
|
||||
+ tasks.kinit_admin(replica)
|
||||
+ replica.run_command([
|
||||
+ 'ipa-ca-install', '-p',
|
||||
+ self.master.config.admin_password,
|
||||
+ '-w', self.master.config.admin_password
|
||||
+ ])
|
||||
+ result = self.replicas[0].run_command([
|
||||
+ 'ipa', 'server-role-find',
|
||||
+ '--server', self.replicas[0].hostname,
|
||||
+ '--role', 'CA server'
|
||||
+ ])
|
||||
+ assert 'Role status: enabled' in result.stdout_text
|
||||
--
|
||||
2.52.0
|
||||
|
||||
@ -0,0 +1,760 @@
|
||||
From 7cb2c5d38a84eb31aa9ddd20cf9dc0b6d90fa242 Mon Sep 17 00:00:00 2001
|
||||
From: PRANAV THUBE <pthube@redhat.com>
|
||||
Date: Tue, 27 Jan 2026 18:45:53 +0530
|
||||
Subject: [PATCH] ipatests: Add integration tests for ipa-join command
|
||||
|
||||
Add tests for ipa-join command covering hostname, server, keytab,
|
||||
and bindpw options with positive and negative scenarios.
|
||||
|
||||
Related: https://pagure.io/freeipa/issue/9930
|
||||
Reviewed-By: Rob Crittenden <rcritten@redhat.com>
|
||||
---
|
||||
ipatests/prci_definitions/gating.yaml | 12 +
|
||||
.../nightly_ipa-4-13_latest.yaml | 12 +
|
||||
.../nightly_ipa-4-13_latest_selinux.yaml | 13 +
|
||||
ipatests/pytest_ipa/integration/tasks.py | 49 ++
|
||||
ipatests/test_integration/test_ipa_join.py | 614 ++++++++++++++++++
|
||||
5 files changed, 700 insertions(+)
|
||||
create mode 100644 ipatests/test_integration/test_ipa_join.py
|
||||
|
||||
diff --git a/ipatests/prci_definitions/gating.yaml b/ipatests/prci_definitions/gating.yaml
|
||||
index 0c2a0dafa9de12add8959d9974f080ba8bec0706..182f2c5a097856d22336fa66a0876bdfcf3f3f8d 100644
|
||||
--- a/ipatests/prci_definitions/gating.yaml
|
||||
+++ b/ipatests/prci_definitions/gating.yaml
|
||||
@@ -394,3 +394,15 @@ jobs:
|
||||
template: *ci-ipa-4-13-latest
|
||||
timeout: 7200
|
||||
topology: *master_3client
|
||||
+
|
||||
+ fedora-latest-ipa-4-13/test_ipa_join:
|
||||
+ requires: [fedora-latest-ipa-4-13/build]
|
||||
+ priority: 100
|
||||
+ job:
|
||||
+ class: RunPytest
|
||||
+ args:
|
||||
+ build_url: '{fedora-latest-ipa-4-13/build_url}'
|
||||
+ test_suite: test_integration/test_ipa_join.py
|
||||
+ template: *ci-ipa-4-13-latest
|
||||
+ timeout: 3600
|
||||
+ topology: *master_1repl_1client
|
||||
diff --git a/ipatests/prci_definitions/nightly_ipa-4-13_latest.yaml b/ipatests/prci_definitions/nightly_ipa-4-13_latest.yaml
|
||||
index c61701ef5f88760f1d6fc36d4acce453a22b6f8f..aba33a5a05185460305c7516c93ae25c60f9dda7 100644
|
||||
--- a/ipatests/prci_definitions/nightly_ipa-4-13_latest.yaml
|
||||
+++ b/ipatests/prci_definitions/nightly_ipa-4-13_latest.yaml
|
||||
@@ -2292,3 +2292,15 @@ jobs:
|
||||
template: *ci-ipa-4-13-latest
|
||||
timeout: 2400
|
||||
topology: *ipaserver
|
||||
+
|
||||
+ fedora-latest-ipa-4-13/test_ipa_join:
|
||||
+ requires: [fedora-latest-ipa-4-13/build]
|
||||
+ priority: 50
|
||||
+ job:
|
||||
+ class: RunPytest
|
||||
+ args:
|
||||
+ build_url: '{fedora-latest-ipa-4-13/build_url}'
|
||||
+ test_suite: test_integration/test_ipa_join.py
|
||||
+ template: *ci-ipa-4-13-latest
|
||||
+ timeout: 3600
|
||||
+ topology: *master_1repl_1client
|
||||
diff --git a/ipatests/prci_definitions/nightly_ipa-4-13_latest_selinux.yaml b/ipatests/prci_definitions/nightly_ipa-4-13_latest_selinux.yaml
|
||||
index 9b96f3e857e2125478b45632d8d58e42b6e92668..7184b722076ba2cab7d782d990a9bc218158a09f 100644
|
||||
--- a/ipatests/prci_definitions/nightly_ipa-4-13_latest_selinux.yaml
|
||||
+++ b/ipatests/prci_definitions/nightly_ipa-4-13_latest_selinux.yaml
|
||||
@@ -2476,3 +2476,16 @@ jobs:
|
||||
template: *ci-ipa-4-13-latest
|
||||
timeout: 2400
|
||||
topology: *ipaserver
|
||||
+
|
||||
+ fedora-latest-ipa-4-13/test_ipa_join:
|
||||
+ requires: [fedora-latest-ipa-4-13/build]
|
||||
+ priority: 50
|
||||
+ job:
|
||||
+ class: RunPytest
|
||||
+ args:
|
||||
+ build_url: '{fedora-latest-ipa-4-13/build_url}'
|
||||
+ selinux_enforcing: True
|
||||
+ test_suite: test_integration/test_ipa_join.py
|
||||
+ template: *ci-ipa-4-13-latest
|
||||
+ timeout: 3600
|
||||
+ topology: *master_1repl_1client
|
||||
diff --git a/ipatests/pytest_ipa/integration/tasks.py b/ipatests/pytest_ipa/integration/tasks.py
|
||||
index ff2ea9792d04ebd2e6bd7bb3b51d97f35cb3fbfb..47330d6d93401485e4eb7b2501cf5ea37498d719 100755
|
||||
--- a/ipatests/pytest_ipa/integration/tasks.py
|
||||
+++ b/ipatests/pytest_ipa/integration/tasks.py
|
||||
@@ -3355,3 +3355,52 @@ def host_add_with_random_password(host, new_host):
|
||||
re.MULTILINE)
|
||||
randpasswd1 = result.group('password')
|
||||
return randpasswd1
|
||||
+
|
||||
+
|
||||
+def ipa_join(host, *extra_args, raiseonerr=True):
|
||||
+ """Run ipa-join command.
|
||||
+
|
||||
+ :param host: The host to run command on
|
||||
+ :param extra_args: Additional arguments (variable positional args)
|
||||
+ e.g., '--hostname=client.example.com',
|
||||
+ '--server=master.example.com',
|
||||
+ '--keytab=/tmp/test.keytab',
|
||||
+ '--bindpw=password',
|
||||
+ '-u' (for unenroll)
|
||||
+ :param raiseonerr: If True, raise exception on command failure
|
||||
+ :return: Command result object
|
||||
+ """
|
||||
+ command = ['ipa-join']
|
||||
+ command.extend(extra_args)
|
||||
+ return host.run_command(command, raiseonerr=raiseonerr)
|
||||
+
|
||||
+
|
||||
+def host_del(host, hostname, *extra_args, raiseonerr=True):
|
||||
+ """Delete a host from IPA.
|
||||
+
|
||||
+ :param host: The IPA host to run command on
|
||||
+ :param hostname: Hostname to delete
|
||||
+ :param extra_args: Additional arguments (variable positional args)
|
||||
+ :param raiseonerr: If True, raise exception on command failure
|
||||
+ :return: Command result object
|
||||
+ """
|
||||
+ command = ['ipa', 'host-del', hostname]
|
||||
+ command.extend(extra_args)
|
||||
+ return host.run_command(command, raiseonerr=raiseonerr)
|
||||
+
|
||||
+
|
||||
+def host_add(host, hostname, *extra_args, password=None, raiseonerr=True):
|
||||
+ """Add a host to IPA.
|
||||
+
|
||||
+ :param host: The IPA host to run command on
|
||||
+ :param hostname: Hostname to add
|
||||
+ :param extra_args: Additional arguments (variable positional args)
|
||||
+ :param password: OTP/enrollment password for the host (optional)
|
||||
+ :param raiseonerr: If True, raise exception on command failure
|
||||
+ :return: Command result object
|
||||
+ """
|
||||
+ command = ['ipa', 'host-add', hostname]
|
||||
+ if password:
|
||||
+ command.append(f'--password={password}')
|
||||
+ command.extend(extra_args)
|
||||
+ return host.run_command(command, raiseonerr=raiseonerr)
|
||||
diff --git a/ipatests/test_integration/test_ipa_join.py b/ipatests/test_integration/test_ipa_join.py
|
||||
new file mode 100644
|
||||
index 0000000000000000000000000000000000000000..1f7592aec8db1bfd048ec574d06d25bc24373499
|
||||
--- /dev/null
|
||||
+++ b/ipatests/test_integration/test_ipa_join.py
|
||||
@@ -0,0 +1,614 @@
|
||||
+#
|
||||
+# Copyright (C) 2026 FreeIPA Contributors see COPYING for license
|
||||
+#
|
||||
+
|
||||
+"""
|
||||
+Tests for ipa-join command functionality.
|
||||
+
|
||||
+This module tests various combinations of ipa-join options including:
|
||||
+- hostname
|
||||
+- server
|
||||
+- keytab
|
||||
+- bindpw (OTP/enrollment password)
|
||||
+- unenroll
|
||||
+
|
||||
+Ported from the shell-based test suite (t.ipajoin.sh and t.ipaotp.sh).
|
||||
+"""
|
||||
+
|
||||
+from __future__ import absolute_import
|
||||
+
|
||||
+from ipapython.ipautil import ipa_generate_password
|
||||
+from ipatests.pytest_ipa.integration import tasks
|
||||
+from ipatests.test_integration.base import IntegrationTest
|
||||
+
|
||||
+
|
||||
+# Constants
|
||||
+OTP = ipa_generate_password(special=None)
|
||||
+INVALID_PASSWORD = "WrongPassword"
|
||||
+INVALID_SERVER = "No.Such.IPA.Server.Domain.com"
|
||||
+TEST_KEYTAB = "/tmp/ipajoin.test.keytab"
|
||||
+
|
||||
+# Error messages
|
||||
+ERR_SASL_BIND_FAILED = "SASL Bind failed"
|
||||
+ERR_UNAUTHENTICATED_BIND = "Unauthenticated binds are not allowed"
|
||||
+ERR_COULD_NOT_RESOLVE = "JSON-RPC call failed: Could not resolve hostname"
|
||||
+ERR_UNABLE_ROOT_DN = "Unable to determine root DN"
|
||||
+ERR_NO_CONFIG = "Unable to determine IPA server from /etc/ipa/default.conf"
|
||||
+ERR_PREAUTH_FAILED = "Generic preauthentication failure"
|
||||
+
|
||||
+# Exit codes
|
||||
+EXIT_SUCCESS = 0
|
||||
+EXIT_GENERAL_ERROR = 1
|
||||
+EXIT_PREAUTH_ERROR = 19
|
||||
+EXIT_ROOT_DN_ERROR = 14
|
||||
+EXIT_SASL_BIND_FAILED = 15
|
||||
+EXIT_RESOLVE_ERROR = 17
|
||||
+
|
||||
+
|
||||
+class TestIPAJoin(IntegrationTest):
|
||||
+ """Tests for ipa-join command functionality.
|
||||
+
|
||||
+ This test class covers various ipa-join scenarios including:
|
||||
+ - Basic enrollment and unenrollment
|
||||
+ - Using hostname, server, keytab, and bindpw options
|
||||
+ - Positive and negative test cases
|
||||
+ - OTP (one-time password) enrollment tests
|
||||
+
|
||||
+ Tests require one master and one client.
|
||||
+ """
|
||||
+
|
||||
+ topology = 'line'
|
||||
+ num_clients = 1
|
||||
+
|
||||
+ @classmethod
|
||||
+ def install(cls, mh):
|
||||
+ tasks.install_master(cls.master, setup_dns=True)
|
||||
+ tasks.install_client(cls.master, cls.clients[0])
|
||||
+
|
||||
+ @classmethod
|
||||
+ def uninstall(cls, mh):
|
||||
+ # Cleanup test keytab if exists
|
||||
+ cls.clients[0].run_command(
|
||||
+ ['rm', '-f', TEST_KEYTAB],
|
||||
+ raiseonerr=False
|
||||
+ )
|
||||
+ tasks.uninstall_client(cls.clients[0])
|
||||
+ tasks.uninstall_master(cls.master)
|
||||
+
|
||||
+ # =========================================================================
|
||||
+ # ipa-join basic tests
|
||||
+ # =========================================================================
|
||||
+
|
||||
+ def test_unenroll(self):
|
||||
+ """Test ipa-join --unenroll option."""
|
||||
+ result = tasks.ipa_join(self.clients[0], '-u', raiseonerr=False)
|
||||
+ assert result.returncode == EXIT_SUCCESS
|
||||
+
|
||||
+ def test_unenroll_already_unenrolled(self):
|
||||
+ """Test ipa-join -u on an already unenrolled client.
|
||||
+
|
||||
+ When trying to unenroll a client that is not enrolled,
|
||||
+ ipa-join should fail with a preauthentication error.
|
||||
+ """
|
||||
+ # Client is already unenrolled from previous test
|
||||
+ result = tasks.ipa_join(self.clients[0], '-u', raiseonerr=False)
|
||||
+
|
||||
+ assert result.returncode == EXIT_PREAUTH_ERROR
|
||||
+ assert ERR_PREAUTH_FAILED in result.stderr_text
|
||||
+
|
||||
+ def test_hostname_with_kerberos(self):
|
||||
+ """Test ipa-join with --hostname using Kerberos auth."""
|
||||
+ tasks.kinit_admin(self.clients[0])
|
||||
+ try:
|
||||
+ result = tasks.ipa_join(
|
||||
+ self.clients[0],
|
||||
+ f'--hostname={self.clients[0].hostname}'
|
||||
+ )
|
||||
+ assert result.returncode == EXIT_SUCCESS
|
||||
+ finally:
|
||||
+ self.clients[0].run_command(['kdestroy', '-A'], raiseonerr=False)
|
||||
+ tasks.ipa_join(self.clients[0], '-u', raiseonerr=False)
|
||||
+
|
||||
+ def test_hostname_bindpw_invalid(self):
|
||||
+ """Test ipa-join with hostname and invalid bindpw."""
|
||||
+ tasks.kinit_admin(self.master)
|
||||
+ tasks.host_del(self.master, self.clients[0].hostname, raiseonerr=False)
|
||||
+ tasks.host_add(self.master, self.clients[0].hostname, password=OTP)
|
||||
+
|
||||
+ result = tasks.ipa_join(
|
||||
+ self.clients[0],
|
||||
+ f'--hostname={self.clients[0].hostname}',
|
||||
+ f'--bindpw={INVALID_PASSWORD}',
|
||||
+ raiseonerr=False
|
||||
+ )
|
||||
+
|
||||
+ assert result.returncode == EXIT_SASL_BIND_FAILED
|
||||
+ assert ERR_SASL_BIND_FAILED in result.stderr_text
|
||||
+
|
||||
+ def test_hostname_bindpw_valid(self):
|
||||
+ """Test ipa-join with hostname and valid OTP."""
|
||||
+ tasks.kinit_admin(self.master)
|
||||
+ tasks.host_del(self.master, self.clients[0].hostname, raiseonerr=False)
|
||||
+ tasks.host_add(self.master, self.clients[0].hostname, password=OTP)
|
||||
+
|
||||
+ result = tasks.ipa_join(
|
||||
+ self.clients[0],
|
||||
+ f'--hostname={self.clients[0].hostname}',
|
||||
+ f'--bindpw={OTP}'
|
||||
+ )
|
||||
+ assert result.returncode == EXIT_SUCCESS
|
||||
+ tasks.ipa_join(self.clients[0], '-u', raiseonerr=False)
|
||||
+
|
||||
+ def test_hostname_keytab_with_kerberos(self):
|
||||
+ """Test ipa-join with hostname and keytab using Kerberos."""
|
||||
+ tasks.kinit_admin(self.clients[0])
|
||||
+ tasks.kinit_admin(self.master)
|
||||
+ tasks.host_del(self.master, self.clients[0].hostname, raiseonerr=False)
|
||||
+
|
||||
+ try:
|
||||
+ result = tasks.ipa_join(
|
||||
+ self.clients[0],
|
||||
+ f'--hostname={self.clients[0].hostname}',
|
||||
+ f'--keytab={TEST_KEYTAB}'
|
||||
+ )
|
||||
+ assert result.returncode == EXIT_SUCCESS
|
||||
+ finally:
|
||||
+ self.clients[0].run_command(['kdestroy', '-A'], raiseonerr=False)
|
||||
+ tasks.ipa_join(self.clients[0], '-u', raiseonerr=False)
|
||||
+
|
||||
+ def test_hostname_keytab_bindpw_invalid(self):
|
||||
+ """Test ipa-join with hostname, keytab, and invalid bindpw."""
|
||||
+ tasks.kinit_admin(self.clients[0])
|
||||
+ tasks.kinit_admin(self.master)
|
||||
+ tasks.host_del(self.master, self.clients[0].hostname, raiseonerr=False)
|
||||
+ tasks.host_add(self.master, self.clients[0].hostname, password=OTP)
|
||||
+
|
||||
+ result = tasks.ipa_join(
|
||||
+ self.clients[0],
|
||||
+ f'--hostname={self.clients[0].hostname}',
|
||||
+ f'--keytab={TEST_KEYTAB}',
|
||||
+ f'--bindpw={INVALID_PASSWORD}',
|
||||
+ raiseonerr=False
|
||||
+ )
|
||||
+
|
||||
+ assert result.returncode == EXIT_SASL_BIND_FAILED
|
||||
+ assert ERR_SASL_BIND_FAILED in result.stderr_text
|
||||
+
|
||||
+ def test_hostname_keytab_bindpw_valid(self):
|
||||
+ """Test ipa-join with hostname, keytab, and valid OTP."""
|
||||
+ tasks.kinit_admin(self.master)
|
||||
+ tasks.host_del(self.master, self.clients[0].hostname, raiseonerr=False)
|
||||
+ tasks.host_add(self.master, self.clients[0].hostname, password=OTP)
|
||||
+
|
||||
+ result = tasks.ipa_join(
|
||||
+ self.clients[0],
|
||||
+ f'--hostname={self.clients[0].hostname}',
|
||||
+ f'--keytab={TEST_KEYTAB}',
|
||||
+ f'--bindpw={OTP}'
|
||||
+ )
|
||||
+ assert result.returncode == EXIT_SUCCESS
|
||||
+ tasks.ipa_join(self.clients[0], '-u', raiseonerr=False)
|
||||
+
|
||||
+ def test_hostname_server_invalid_with_kerberos(self):
|
||||
+ """Test ipa-join with hostname and invalid server."""
|
||||
+ tasks.kinit_admin(self.clients[0])
|
||||
+ try:
|
||||
+ result = tasks.ipa_join(
|
||||
+ self.clients[0],
|
||||
+ f'--hostname={self.clients[0].hostname}',
|
||||
+ f'--server={INVALID_SERVER}',
|
||||
+ raiseonerr=False
|
||||
+ )
|
||||
+
|
||||
+ assert result.returncode == EXIT_RESOLVE_ERROR
|
||||
+ assert ERR_COULD_NOT_RESOLVE in result.stderr_text
|
||||
+ finally:
|
||||
+ self.clients[0].run_command(['kdestroy', '-A'], raiseonerr=False)
|
||||
+
|
||||
+ def test_hostname_server_invalid_bindpw_valid(self):
|
||||
+ """Test ipa-join with hostname, invalid server, and valid OTP."""
|
||||
+ tasks.kinit_admin(self.clients[0])
|
||||
+ result = tasks.ipa_join(
|
||||
+ self.clients[0],
|
||||
+ f'--hostname={self.clients[0].hostname}',
|
||||
+ f'--server={INVALID_SERVER}',
|
||||
+ f'--bindpw={OTP}',
|
||||
+ raiseonerr=False
|
||||
+ )
|
||||
+
|
||||
+ assert result.returncode == EXIT_ROOT_DN_ERROR
|
||||
+ assert ERR_UNABLE_ROOT_DN in result.stderr_text
|
||||
+
|
||||
+ def test_hostname_server_invalid_keytab_with_kerberos(self):
|
||||
+ """Test ipa-join with hostname, invalid server, keytab."""
|
||||
+ tasks.kinit_admin(self.clients[0])
|
||||
+ try:
|
||||
+ result = tasks.ipa_join(
|
||||
+ self.clients[0],
|
||||
+ f'--hostname={self.clients[0].hostname}',
|
||||
+ f'--server={INVALID_SERVER}',
|
||||
+ f'--keytab={TEST_KEYTAB}',
|
||||
+ raiseonerr=False
|
||||
+ )
|
||||
+
|
||||
+ assert result.returncode == EXIT_RESOLVE_ERROR
|
||||
+ assert ERR_COULD_NOT_RESOLVE in result.stderr_text
|
||||
+ finally:
|
||||
+ self.clients[0].run_command(['kdestroy', '-A'], raiseonerr=False)
|
||||
+
|
||||
+ def test_hostname_server_invalid_keytab_bindpw_valid(self):
|
||||
+ """Test ipa-join with hostname, invalid server, keytab, valid OTP."""
|
||||
+ tasks.kinit_admin(self.clients[0])
|
||||
+ result = tasks.ipa_join(
|
||||
+ self.clients[0],
|
||||
+ f'--hostname={self.clients[0].hostname}',
|
||||
+ f'--server={INVALID_SERVER}',
|
||||
+ f'--keytab={TEST_KEYTAB}',
|
||||
+ f'--bindpw={OTP}',
|
||||
+ raiseonerr=False
|
||||
+ )
|
||||
+
|
||||
+ assert result.returncode == EXIT_ROOT_DN_ERROR
|
||||
+ assert ERR_UNABLE_ROOT_DN in result.stderr_text
|
||||
+
|
||||
+ def test_hostname_server_valid_with_kerberos(self):
|
||||
+ """Test ipa-join with hostname and valid server."""
|
||||
+ tasks.kinit_admin(self.master)
|
||||
+ tasks.host_del(self.master, self.clients[0].hostname, raiseonerr=False)
|
||||
+
|
||||
+ try:
|
||||
+ result = tasks.ipa_join(
|
||||
+ self.clients[0],
|
||||
+ f'--hostname={self.clients[0].hostname}',
|
||||
+ f'--server={self.master.hostname}'
|
||||
+ )
|
||||
+ assert result.returncode == EXIT_SUCCESS
|
||||
+ finally:
|
||||
+ self.clients[0].run_command(['kdestroy', '-A'], raiseonerr=False)
|
||||
+ tasks.ipa_join(self.clients[0], '-u', raiseonerr=False)
|
||||
+
|
||||
+ def test_hostname_server_valid_bindpw_invalid(self):
|
||||
+ """Test ipa-join with hostname, valid server, invalid bindpw."""
|
||||
+ tasks.kinit_admin(self.clients[0])
|
||||
+ tasks.kinit_admin(self.master)
|
||||
+ tasks.host_del(self.master, self.clients[0].hostname, raiseonerr=False)
|
||||
+ tasks.host_add(self.master, self.clients[0].hostname, password=OTP)
|
||||
+
|
||||
+ result = tasks.ipa_join(
|
||||
+ self.clients[0],
|
||||
+ f'--hostname={self.clients[0].hostname}',
|
||||
+ f'--server={self.master.hostname}',
|
||||
+ f'--bindpw={INVALID_PASSWORD}',
|
||||
+ raiseonerr=False
|
||||
+ )
|
||||
+
|
||||
+ assert result.returncode == EXIT_SASL_BIND_FAILED
|
||||
+ assert ERR_SASL_BIND_FAILED in result.stderr_text
|
||||
+
|
||||
+ def test_hostname_server_valid_bindpw_valid(self):
|
||||
+ """Test ipa-join with hostname, valid server, valid OTP."""
|
||||
+ tasks.kinit_admin(self.master)
|
||||
+ tasks.host_del(self.master, self.clients[0].hostname, raiseonerr=False)
|
||||
+ tasks.host_add(self.master, self.clients[0].hostname, password=OTP)
|
||||
+
|
||||
+ result = tasks.ipa_join(
|
||||
+ self.clients[0],
|
||||
+ f'--hostname={self.clients[0].hostname}',
|
||||
+ f'--server={self.master.hostname}',
|
||||
+ f'--bindpw={OTP}'
|
||||
+ )
|
||||
+ assert result.returncode == EXIT_SUCCESS
|
||||
+ tasks.ipa_join(self.clients[0], '-u', raiseonerr=False)
|
||||
+
|
||||
+ def test_hostname_server_valid_keytab_with_kerberos(self):
|
||||
+ """Test ipa-join with hostname, valid server, keytab."""
|
||||
+ tasks.kinit_admin(self.clients[0])
|
||||
+ tasks.kinit_admin(self.master)
|
||||
+ tasks.host_del(self.master, self.clients[0].hostname, raiseonerr=False)
|
||||
+ tasks.host_add(self.master, self.clients[0].hostname)
|
||||
+
|
||||
+ try:
|
||||
+ result = tasks.ipa_join(
|
||||
+ self.clients[0],
|
||||
+ f'--hostname={self.clients[0].hostname}',
|
||||
+ f'--server={self.master.hostname}',
|
||||
+ f'--keytab={TEST_KEYTAB}'
|
||||
+ )
|
||||
+ assert result.returncode == EXIT_SUCCESS
|
||||
+ finally:
|
||||
+ self.clients[0].run_command(['kdestroy', '-A'], raiseonerr=False)
|
||||
+ tasks.ipa_join(self.clients[0], '-u', raiseonerr=False)
|
||||
+
|
||||
+ def test_hostname_server_valid_keytab_bindpw_invalid(self):
|
||||
+ """Test ipa-join with hostname, valid server, keytab, bad bindpw."""
|
||||
+ tasks.kinit_admin(self.clients[0])
|
||||
+ tasks.kinit_admin(self.master)
|
||||
+ tasks.host_del(self.master, self.clients[0].hostname, raiseonerr=False)
|
||||
+ tasks.host_add(self.master, self.clients[0].hostname, password=OTP)
|
||||
+
|
||||
+ result = tasks.ipa_join(
|
||||
+ self.clients[0],
|
||||
+ f'--hostname={self.clients[0].hostname}',
|
||||
+ f'--server={self.master.hostname}',
|
||||
+ f'--keytab={TEST_KEYTAB}',
|
||||
+ f'--bindpw={INVALID_PASSWORD}',
|
||||
+ raiseonerr=False
|
||||
+ )
|
||||
+
|
||||
+ assert result.returncode == EXIT_SASL_BIND_FAILED
|
||||
+ # Note: Original test had "SASL Bind Failed" (capital F), checking both
|
||||
+ assert "SASL Bind" in result.stderr_text
|
||||
+ assert "ailed" in result.stderr_text
|
||||
+
|
||||
+ def test_hostname_server_valid_keytab_bindpw_valid(self):
|
||||
+ """Test ipa-join with hostname, valid server, keytab, valid OTP."""
|
||||
+ tasks.kinit_admin(self.master)
|
||||
+ tasks.host_del(self.master, self.clients[0].hostname, raiseonerr=False)
|
||||
+ tasks.host_add(self.master, self.clients[0].hostname, password=OTP)
|
||||
+
|
||||
+ result = tasks.ipa_join(
|
||||
+ self.clients[0],
|
||||
+ f'--hostname={self.clients[0].hostname}',
|
||||
+ f'--server={self.master.hostname}',
|
||||
+ f'--keytab={TEST_KEYTAB}',
|
||||
+ f'--bindpw={OTP}'
|
||||
+ )
|
||||
+ assert result.returncode == EXIT_SUCCESS
|
||||
+ tasks.ipa_join(self.clients[0], '-u', raiseonerr=False)
|
||||
+
|
||||
+ def test_keytab_only_with_kerberos(self):
|
||||
+ """Test ipa-join with keytab only using Kerberos."""
|
||||
+ tasks.kinit_admin(self.master)
|
||||
+ tasks.host_del(self.master, self.clients[0].hostname, raiseonerr=False)
|
||||
+ tasks.host_add(self.master, self.clients[0].hostname)
|
||||
+
|
||||
+ try:
|
||||
+ result = tasks.ipa_join(
|
||||
+ self.clients[0],
|
||||
+ f'--keytab={TEST_KEYTAB}'
|
||||
+ )
|
||||
+ assert result.returncode == EXIT_SUCCESS
|
||||
+ finally:
|
||||
+ self.clients[0].run_command(['kdestroy', '-A'], raiseonerr=False)
|
||||
+ tasks.ipa_join(self.clients[0], '-u', raiseonerr=False)
|
||||
+
|
||||
+ def test_keytab_bindpw_invalid(self):
|
||||
+ """Test ipa-join with keytab and invalid bindpw."""
|
||||
+ tasks.kinit_admin(self.clients[0])
|
||||
+ tasks.kinit_admin(self.master)
|
||||
+ tasks.host_del(self.master, self.clients[0].hostname, raiseonerr=False)
|
||||
+ tasks.host_add(self.master, self.clients[0].hostname, password=OTP)
|
||||
+
|
||||
+ result = tasks.ipa_join(
|
||||
+ self.clients[0],
|
||||
+ f'--keytab={TEST_KEYTAB}',
|
||||
+ f'--bindpw={INVALID_PASSWORD}',
|
||||
+ raiseonerr=False
|
||||
+ )
|
||||
+
|
||||
+ assert result.returncode == EXIT_SASL_BIND_FAILED
|
||||
+ assert ERR_SASL_BIND_FAILED in result.stderr_text
|
||||
+
|
||||
+ def test_keytab_bindpw_valid(self):
|
||||
+ """Test ipa-join with keytab and valid OTP."""
|
||||
+ tasks.kinit_admin(self.master)
|
||||
+ tasks.host_del(self.master, self.clients[0].hostname, raiseonerr=False)
|
||||
+ tasks.host_add(self.master, self.clients[0].hostname, password=OTP)
|
||||
+
|
||||
+ result = tasks.ipa_join(
|
||||
+ self.clients[0],
|
||||
+ f'--keytab={TEST_KEYTAB}',
|
||||
+ f'--bindpw={OTP}'
|
||||
+ )
|
||||
+ assert result.returncode == EXIT_SUCCESS
|
||||
+ tasks.ipa_join(self.clients[0], '-u', raiseonerr=False)
|
||||
+
|
||||
+ def test_server_invalid_only_with_kerberos(self):
|
||||
+ """Test ipa-join with invalid server only."""
|
||||
+ tasks.kinit_admin(self.clients[0])
|
||||
+ try:
|
||||
+ result = tasks.ipa_join(
|
||||
+ self.clients[0],
|
||||
+ f'--server={INVALID_SERVER}',
|
||||
+ raiseonerr=False
|
||||
+ )
|
||||
+
|
||||
+ assert result.returncode == EXIT_RESOLVE_ERROR
|
||||
+ assert ERR_COULD_NOT_RESOLVE in result.stderr_text
|
||||
+ finally:
|
||||
+ self.clients[0].run_command(['kdestroy', '-A'], raiseonerr=False)
|
||||
+
|
||||
+ def test_server_invalid_bindpw_valid(self):
|
||||
+ """Test ipa-join with invalid server and valid OTP."""
|
||||
+ tasks.kinit_admin(self.clients[0])
|
||||
+ result = tasks.ipa_join(
|
||||
+ self.clients[0],
|
||||
+ f'--server={INVALID_SERVER}',
|
||||
+ f'--bindpw={OTP}',
|
||||
+ raiseonerr=False
|
||||
+ )
|
||||
+
|
||||
+ assert result.returncode == EXIT_ROOT_DN_ERROR
|
||||
+ assert ERR_UNABLE_ROOT_DN in result.stderr_text
|
||||
+
|
||||
+ def test_server_invalid_keytab_with_kerberos(self):
|
||||
+ """Test ipa-join with invalid server and keytab."""
|
||||
+ tasks.kinit_admin(self.clients[0])
|
||||
+ try:
|
||||
+ result = tasks.ipa_join(
|
||||
+ self.clients[0],
|
||||
+ f'--server={INVALID_SERVER}',
|
||||
+ f'--keytab={TEST_KEYTAB}',
|
||||
+ raiseonerr=False
|
||||
+ )
|
||||
+
|
||||
+ assert result.returncode == EXIT_RESOLVE_ERROR
|
||||
+ assert ERR_COULD_NOT_RESOLVE in result.stderr_text
|
||||
+ finally:
|
||||
+ self.clients[0].run_command(['kdestroy', '-A'], raiseonerr=False)
|
||||
+
|
||||
+ def test_server_valid_only_with_kerberos(self):
|
||||
+ """Test ipa-join with valid server only."""
|
||||
+ tasks.kinit_admin(self.clients[0])
|
||||
+ tasks.kinit_admin(self.master)
|
||||
+ tasks.host_del(self.master, self.clients[0].hostname, raiseonerr=False)
|
||||
+ tasks.host_add(self.master, self.clients[0].hostname)
|
||||
+
|
||||
+ try:
|
||||
+ result = tasks.ipa_join(
|
||||
+ self.clients[0],
|
||||
+ f'--server={self.master.hostname}'
|
||||
+ )
|
||||
+ assert result.returncode == EXIT_SUCCESS
|
||||
+ finally:
|
||||
+ self.clients[0].run_command(['kdestroy', '-A'], raiseonerr=False)
|
||||
+ tasks.ipa_join(self.clients[0], '-u', raiseonerr=False)
|
||||
+
|
||||
+ def test_server_valid_bindpw_invalid(self):
|
||||
+ """Test ipa-join with valid server and invalid bindpw."""
|
||||
+ tasks.kinit_admin(self.clients[0])
|
||||
+ tasks.kinit_admin(self.master)
|
||||
+ tasks.host_del(self.master, self.clients[0].hostname, raiseonerr=False)
|
||||
+ tasks.host_add(self.master, self.clients[0].hostname, password=OTP)
|
||||
+
|
||||
+ result = tasks.ipa_join(
|
||||
+ self.clients[0],
|
||||
+ f'--server={self.master.hostname}',
|
||||
+ f'--bindpw={INVALID_PASSWORD}',
|
||||
+ raiseonerr=False
|
||||
+ )
|
||||
+
|
||||
+ assert result.returncode == EXIT_SASL_BIND_FAILED
|
||||
+ assert ERR_SASL_BIND_FAILED in result.stderr_text
|
||||
+
|
||||
+ def test_server_valid_bindpw_valid(self):
|
||||
+ """Test ipa-join with valid server and valid OTP."""
|
||||
+ tasks.kinit_admin(self.master)
|
||||
+ tasks.host_del(self.master, self.clients[0].hostname, raiseonerr=False)
|
||||
+ tasks.host_add(self.master, self.clients[0].hostname, password=OTP)
|
||||
+
|
||||
+ result = tasks.ipa_join(
|
||||
+ self.clients[0],
|
||||
+ f'--server={self.master.hostname}',
|
||||
+ f'--bindpw={OTP}'
|
||||
+ )
|
||||
+ assert result.returncode == EXIT_SUCCESS
|
||||
+ tasks.ipa_join(self.clients[0], '-u', raiseonerr=False)
|
||||
+
|
||||
+ def test_server_valid_keytab_with_kerberos(self):
|
||||
+ """Test ipa-join with valid server and keytab."""
|
||||
+ tasks.kinit_admin(self.clients[0])
|
||||
+ tasks.kinit_admin(self.master)
|
||||
+ tasks.host_del(self.master, self.clients[0].hostname, raiseonerr=False)
|
||||
+ tasks.host_add(self.master, self.clients[0].hostname)
|
||||
+
|
||||
+ try:
|
||||
+ result = tasks.ipa_join(
|
||||
+ self.clients[0],
|
||||
+ f'--server={self.master.hostname}',
|
||||
+ f'--keytab={TEST_KEYTAB}'
|
||||
+ )
|
||||
+ assert result.returncode == EXIT_SUCCESS
|
||||
+ finally:
|
||||
+ self.clients[0].run_command(['kdestroy', '-A'], raiseonerr=False)
|
||||
+ tasks.ipa_join(self.clients[0], '-u', raiseonerr=False)
|
||||
+
|
||||
+ def test_bindpw_invalid_only(self):
|
||||
+ """Test ipa-join with invalid bindpw only."""
|
||||
+ tasks.kinit_admin(self.clients[0])
|
||||
+ tasks.kinit_admin(self.master)
|
||||
+ tasks.host_del(self.master, self.clients[0].hostname, raiseonerr=False)
|
||||
+ tasks.host_add(self.master, self.clients[0].hostname, password=OTP)
|
||||
+
|
||||
+ result = tasks.ipa_join(
|
||||
+ self.clients[0],
|
||||
+ f'--bindpw={INVALID_PASSWORD}',
|
||||
+ raiseonerr=False
|
||||
+ )
|
||||
+
|
||||
+ assert result.returncode == EXIT_SASL_BIND_FAILED
|
||||
+ assert ERR_SASL_BIND_FAILED in result.stderr_text
|
||||
+
|
||||
+ # =========================================================================
|
||||
+ # OTP (One-Time Password) tests
|
||||
+ # =========================================================================
|
||||
+
|
||||
+ def test_otp_empty_password(self):
|
||||
+ """Test ipa-join with empty OTP password (ipa_otp_1001)."""
|
||||
+ tasks.kinit_admin(self.clients[0])
|
||||
+ tasks.kinit_admin(self.master)
|
||||
+ tasks.host_del(self.master, self.clients[0].hostname, raiseonerr=False)
|
||||
+ tasks.host_add(self.master, self.clients[0].hostname, password=OTP)
|
||||
+
|
||||
+ result = tasks.ipa_join(
|
||||
+ self.clients[0],
|
||||
+ f'--hostname={self.clients[0].hostname}',
|
||||
+ '--bindpw=',
|
||||
+ raiseonerr=False
|
||||
+ )
|
||||
+
|
||||
+ assert result.returncode == EXIT_ROOT_DN_ERROR
|
||||
+ assert ERR_UNAUTHENTICATED_BIND in result.stderr_text
|
||||
+
|
||||
+ def test_otp_wrong_password(self):
|
||||
+ """Test ipa-join with wrong OTP password (ipa_otp_1002)."""
|
||||
+ tasks.kinit_admin(self.clients[0])
|
||||
+ tasks.kinit_admin(self.master)
|
||||
+ tasks.host_del(self.master, self.clients[0].hostname, raiseonerr=False)
|
||||
+ tasks.host_add(self.master, self.clients[0].hostname, password=OTP)
|
||||
+
|
||||
+ result = tasks.ipa_join(
|
||||
+ self.clients[0],
|
||||
+ f'--hostname={self.clients[0].hostname}',
|
||||
+ f'--bindpw={INVALID_PASSWORD}',
|
||||
+ raiseonerr=False
|
||||
+ )
|
||||
+
|
||||
+ assert result.returncode == EXIT_SASL_BIND_FAILED
|
||||
+ assert ERR_SASL_BIND_FAILED in result.stderr_text
|
||||
+
|
||||
+ def test_otp_valid_password(self):
|
||||
+ """Test ipa-join with valid OTP password (ipa_otp_1003)."""
|
||||
+ tasks.kinit_admin(self.clients[0])
|
||||
+ tasks.kinit_admin(self.master)
|
||||
+ tasks.host_del(self.master, self.clients[0].hostname, raiseonerr=False)
|
||||
+ tasks.host_add(self.master, self.clients[0].hostname, password=OTP)
|
||||
+ try:
|
||||
+ result = tasks.ipa_join(
|
||||
+ self.clients[0],
|
||||
+ f'--hostname={self.clients[0].hostname}',
|
||||
+ f'--bindpw={OTP}'
|
||||
+ )
|
||||
+ assert result.returncode == EXIT_SUCCESS
|
||||
+ finally:
|
||||
+ self.clients[0].run_command(['kdestroy', '-A'], raiseonerr=False)
|
||||
+ tasks.ipa_join(self.clients[0], '-u', raiseonerr=False)
|
||||
+
|
||||
+ def test_otp_reuse_fails(self):
|
||||
+ """Test that reusing the same OTP fails (ipa_otp_1004)."""
|
||||
+ tasks.kinit_admin(self.clients[0])
|
||||
+ tasks.kinit_admin(self.master)
|
||||
+ tasks.host_del(self.master, self.clients[0].hostname, raiseonerr=False)
|
||||
+ tasks.host_add(self.master, self.clients[0].hostname, password=OTP)
|
||||
+ try:
|
||||
+ # First use should succeed
|
||||
+ result = tasks.ipa_join(
|
||||
+ self.clients[0],
|
||||
+ f'--hostname={self.clients[0].hostname}',
|
||||
+ f'--bindpw={OTP}'
|
||||
+ )
|
||||
+ assert result.returncode == EXIT_SUCCESS
|
||||
+ finally:
|
||||
+ self.clients[0].run_command(['kdestroy', '-A'], raiseonerr=False)
|
||||
+ tasks.ipa_join(self.clients[0], '-u', raiseonerr=False)
|
||||
+
|
||||
+ # Second use of same OTP should fail
|
||||
+ result = tasks.ipa_join(
|
||||
+ self.clients[0],
|
||||
+ f'--hostname={self.clients[0].hostname}',
|
||||
+ f'--bindpw={OTP}',
|
||||
+ raiseonerr=False
|
||||
+ )
|
||||
+
|
||||
+ assert result.returncode == EXIT_SASL_BIND_FAILED
|
||||
+ assert ERR_SASL_BIND_FAILED in result.stderr_text
|
||||
--
|
||||
2.52.0
|
||||
|
||||
@ -1,271 +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,5 +208,18 @@ static const struct {
|
||||
{ "idp", IPADB_USER_AUTH_IDP },
|
||||
{ }
|
||||
+},
|
||||
+ 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
|
||||
|
||||
@ -0,0 +1,48 @@
|
||||
From 7f6a2835f0972af5e94b58daf47fa60bfade4279 Mon Sep 17 00:00:00 2001
|
||||
From: Florence Blanc-Renaud <flo@redhat.com>
|
||||
Date: Thu, 22 Jan 2026 10:02:11 +0100
|
||||
Subject: [PATCH] fetch_domains: Use case-insensitive comparison for domains
|
||||
names
|
||||
|
||||
The fetch_domains method is using netr_DsRGetForestTrustInformation
|
||||
to retrieve the forest trust information. The returned data contains
|
||||
domain entries, with a DNS domain name that can contain mixed case
|
||||
(for instance adDomain.Test).
|
||||
The method compares the domain name with the provided parameter in a
|
||||
case sensitive comparison, while it should use a case-insensitive
|
||||
method (DNS names are case-insensitive).
|
||||
|
||||
Fix the method and compare the lowercase value instead.
|
||||
|
||||
Fixes: https://pagure.io/freeipa/issue/9924
|
||||
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 1182f128b4988bc699fe7a40d4834f1bead82cf5..5c05ffedb889e774e342cb6cb85ff954d06ac5e9 100644
|
||||
--- a/ipaserver/dcerpc.py
|
||||
+++ b/ipaserver/dcerpc.py
|
||||
@@ -1635,7 +1635,7 @@ def fetch_domains(api, mydomain, trustdomain, creds=None, server=None):
|
||||
t.forest_trust_data.netbios_domain_name.string
|
||||
|
||||
tname = unicode(t.forest_trust_data.dns_domain_name.string)
|
||||
- if tname != trustdomain:
|
||||
+ if tname.lower() != trustdomain.lower():
|
||||
result['domains'][tname] = {
|
||||
'cn': tname,
|
||||
'ipantflatname': unicode(
|
||||
@@ -1647,7 +1647,7 @@ def fetch_domains(api, mydomain, trustdomain, creds=None, server=None):
|
||||
record.data.string = t.forest_trust_data.string
|
||||
|
||||
tname = unicode(t.forest_trust_data.string)
|
||||
- if tname == trustdomain:
|
||||
+ if tname.lower() == trustdomain.lower():
|
||||
continue
|
||||
|
||||
result['suffixes'][tname] = {'cn': tname}
|
||||
--
|
||||
2.52.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
|
||||
|
||||
43
SOURCES/0016-Handle-IPACertificate-types-in-xmlrpc.patch
Normal file
43
SOURCES/0016-Handle-IPACertificate-types-in-xmlrpc.patch
Normal file
@ -0,0 +1,43 @@
|
||||
From 8deb4be0962b25dfd43e1245307a8bb9d58cfc48 Mon Sep 17 00:00:00 2001
|
||||
From: Rob Crittenden <rcritten@redhat.com>
|
||||
Date: Tue, 3 Feb 2026 09:46:25 -0500
|
||||
Subject: [PATCH] Handle IPACertificate types in xmlrpc
|
||||
|
||||
The wrapping code didn't understand the IPACertificate class
|
||||
so retrieving any entry that contained one would fail.
|
||||
|
||||
Treat it the same was as its parent class cryptography.Certificate.
|
||||
|
||||
Fixes: https://pagure.io/freeipa/issue/9935
|
||||
|
||||
Signed-off-by: Rob Crittenden <rcritten@redhat.com>
|
||||
Reviewed-By: David Hanina <dhanina@redhat.com>
|
||||
---
|
||||
ipalib/rpc.py | 4 ++--
|
||||
1 file changed, 2 insertions(+), 2 deletions(-)
|
||||
|
||||
diff --git a/ipalib/rpc.py b/ipalib/rpc.py
|
||||
index ed35afc965308e03269f05e01400660b207b548d..9773626eb054dd404256267c5fffbba1aa0579dd 100644
|
||||
--- a/ipalib/rpc.py
|
||||
+++ b/ipalib/rpc.py
|
||||
@@ -56,7 +56,7 @@ from ipalib.errors import (errors_by_code, UnknownError, NetworkError,
|
||||
XMLRPCMarshallError, JSONError)
|
||||
from ipalib import errors, capabilities
|
||||
from ipalib.request import context, Connection
|
||||
-from ipalib.x509 import Encoding as x509_Encoding
|
||||
+from ipalib.x509 import Encoding as x509_Encoding, IPACertificate
|
||||
from ipapython import ipautil
|
||||
from ipapython import session_storage
|
||||
from ipapython.cookie import Cookie
|
||||
@@ -220,7 +220,7 @@ def xml_wrap(value, version):
|
||||
if isinstance(value, Principal):
|
||||
return unicode(value)
|
||||
|
||||
- if isinstance(value, crypto_x509.Certificate):
|
||||
+ if isinstance(value, (crypto_x509.Certificate, IPACertificate)):
|
||||
return base64.b64encode(
|
||||
value.public_bytes(x509_Encoding.DER)).decode('ascii')
|
||||
|
||||
--
|
||||
2.52.0
|
||||
|
||||
@ -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
|
||||
|
||||
159
SOURCES/0017-Replace-None-with-when-uninstalling-CA.patch
Normal file
159
SOURCES/0017-Replace-None-with-when-uninstalling-CA.patch
Normal file
@ -0,0 +1,159 @@
|
||||
From a583b0dc08536a50e10b76e27861864b61906355 Mon Sep 17 00:00:00 2001
|
||||
From: David Hanina <dhanina@redhat.com>
|
||||
Date: Mon, 2 Feb 2026 11:14:48 +0100
|
||||
Subject: [PATCH] Replace None with '' when uninstalling CA
|
||||
|
||||
At many places we're obtaining records from a config file and expect the
|
||||
config to have those keys, but the user may delete the line instead of
|
||||
setting the value to false, this then leads to failed uninstall. This
|
||||
patch replaces None with '' at some places that do not check for None.
|
||||
|
||||
Fixes: https://pagure.io/freeipa/issue/9921
|
||||
Signed-off-by: David Hanina <dhanina@redhat.com>
|
||||
Reviewed-By: Florence Blanc-Renaud <frenaud@redhat.com>
|
||||
Reviewed-By: Rob Crittenden <rcritten@redhat.com>
|
||||
---
|
||||
ipaserver/install/ca.py | 12 ++++--
|
||||
ipaserver/install/cainstance.py | 14 ++++++-
|
||||
ipaserver/install/server/upgrade.py | 3 +-
|
||||
.../test_integration/test_crlgen_manage.py | 3 +-
|
||||
.../test_integration/test_uninstallation.py | 41 +++++++++++++++++++
|
||||
5 files changed, 66 insertions(+), 7 deletions(-)
|
||||
|
||||
diff --git a/ipaserver/install/ca.py b/ipaserver/install/ca.py
|
||||
index 5a026aa4c556f7012552052cd08223746f3c39ae..2e953a567a3a230cb2a5e35192af76c61f8c1047 100644
|
||||
--- a/ipaserver/install/ca.py
|
||||
+++ b/ipaserver/install/ca.py
|
||||
@@ -340,14 +340,20 @@ def uninstall_crl_check(options):
|
||||
|
||||
try:
|
||||
crlgen_enabled = ca.is_crlgen_enabled()
|
||||
- except cainstance.InconsistentCRLGenConfigException:
|
||||
+ except cainstance.InconsistentCRLGenConfigException as e:
|
||||
# If config is inconsistent, let's be safe and act as if
|
||||
# crl gen was enabled
|
||||
+ print(e)
|
||||
crlgen_enabled = True
|
||||
|
||||
if crlgen_enabled:
|
||||
- print("Deleting this server will leave your installation "
|
||||
- "without a CRL generation master.")
|
||||
+ if not options.ignore_last_of_role:
|
||||
+ print("Deleting this server will leave your installation "
|
||||
+ "without a CRL generation master. Use --ignore-last-of-role "
|
||||
+ "to bypass this check.")
|
||||
+ else:
|
||||
+ print("Deleting this server will leave your installation "
|
||||
+ "without a CRL generation master.")
|
||||
if (options.unattended and not options.ignore_last_of_role) or \
|
||||
not (options.unattended or ipautil.user_input(
|
||||
"Are you sure you want to continue with the uninstall "
|
||||
diff --git a/ipaserver/install/cainstance.py b/ipaserver/install/cainstance.py
|
||||
index b8267a625554f9375d27160f39b67ee2e64a2dbb..4933ad23d7323859af92bd02f6ae156803e29997 100644
|
||||
--- a/ipaserver/install/cainstance.py
|
||||
+++ b/ipaserver/install/cainstance.py
|
||||
@@ -1421,12 +1421,22 @@ class CAInstance(DogtagInstance):
|
||||
try:
|
||||
cache = directivesetter.get_directive(
|
||||
self.config, 'ca.crl.MasterCRL.enableCRLCache', '=')
|
||||
+
|
||||
+ if cache is None:
|
||||
+ raise InconsistentCRLGenConfigException(
|
||||
+ "Configuration is inconsistent, please check "
|
||||
+ "ca.crl.MasterCRL.enableCRLCache, "
|
||||
+ "ca.crl.MasterCRL.enableCRLUpdates and "
|
||||
+ "ca.listenToCloneModifications in {} and "
|
||||
+ "run ipa-crlgen-manage [enable|disable] to repair".format(
|
||||
+ self.config))
|
||||
+
|
||||
enableCRLCache = cache.lower() == 'true'
|
||||
updates = directivesetter.get_directive(
|
||||
- self.config, 'ca.crl.MasterCRL.enableCRLUpdates', '=')
|
||||
+ self.config, 'ca.crl.MasterCRL.enableCRLUpdates', '=') or ''
|
||||
enableCRLUpdates = updates.lower() == 'true'
|
||||
listen = directivesetter.get_directive(
|
||||
- self.config, 'ca.listenToCloneModifications', '=')
|
||||
+ self.config, 'ca.listenToCloneModifications', '=') or ''
|
||||
enableToClone = listen.lower() == 'true'
|
||||
updateinterval = directivesetter.get_directive(
|
||||
self.config, 'ca.certStatusUpdateInterval', '=')
|
||||
diff --git a/ipaserver/install/server/upgrade.py b/ipaserver/install/server/upgrade.py
|
||||
index 548ee02e1e8524ce0002dca1764d48728eb0509a..8692c983409426193e1746f07fa1a0514621cb4a 100644
|
||||
--- a/ipaserver/install/server/upgrade.py
|
||||
+++ b/ipaserver/install/server/upgrade.py
|
||||
@@ -1712,7 +1712,8 @@ def upgrade_configuration():
|
||||
if ca.is_configured():
|
||||
crl = directivesetter.get_directive(
|
||||
paths.CA_CS_CFG_PATH, 'ca.crl.MasterCRL.enableCRLUpdates', '=')
|
||||
- sub_dict['CLONE']='#' if crl.lower() == 'true' else ''
|
||||
+ sub_dict['CLONE'] = '#' if crl is not None and \
|
||||
+ crl.lower() == 'true' else ''
|
||||
|
||||
ds_dirname = dsinstance.config_dirname(ds.serverid)
|
||||
|
||||
diff --git a/ipatests/test_integration/test_crlgen_manage.py b/ipatests/test_integration/test_crlgen_manage.py
|
||||
index c6f41ebf8939bad8006b1e5eaf37bad30dbfd9d8..8a2a28a75b76158fcc61a8e7612f81343336b64f 100644
|
||||
--- a/ipatests/test_integration/test_crlgen_manage.py
|
||||
+++ b/ipatests/test_integration/test_crlgen_manage.py
|
||||
@@ -302,7 +302,8 @@ class TestCRLGenManage(IntegrationTest):
|
||||
['ipa-server-install', '--uninstall', '-U'], raiseonerr=False)
|
||||
assert result.returncode == 1
|
||||
expected_msg = "Deleting this server will leave your installation " \
|
||||
- "without a CRL generation master"
|
||||
+ "without a CRL generation master. Use " \
|
||||
+ "--ignore-last-of-role to bypass this check."
|
||||
assert expected_msg in result.stdout_text
|
||||
|
||||
def test_uninstall_with_ignore_last_of_role(self):
|
||||
diff --git a/ipatests/test_integration/test_uninstallation.py b/ipatests/test_integration/test_uninstallation.py
|
||||
index 8d83f72868f5c103b0c31d2aa96630c00b2dfbd8..12b10caa60745dcbc2d811bff65d57fb5d865f09 100644
|
||||
--- a/ipatests/test_integration/test_uninstallation.py
|
||||
+++ b/ipatests/test_integration/test_uninstallation.py
|
||||
@@ -237,3 +237,44 @@ class TestUninstallReinstall(IntegrationTest):
|
||||
|
||||
def test_reinstall_server(self):
|
||||
tasks.install_master(self.master, setup_dns=False)
|
||||
+
|
||||
+
|
||||
+class TestUninstallCRLGen(IntegrationTest):
|
||||
+ """Test uninstallation of a replica with broken CRL configuration.
|
||||
+
|
||||
+ Removing ca.crl.MasterCRL.enableCRLCache from CS.cfg crashed.
|
||||
+ https://pagure.io/freeipa/issue/9921
|
||||
+ """
|
||||
+
|
||||
+ num_replicas = 1
|
||||
+ topology = 'line'
|
||||
+
|
||||
+ @classmethod
|
||||
+ def install(cls, mh):
|
||||
+ tasks.install_master(cls.master, setup_dns=False)
|
||||
+ tasks.install_replica(cls.master, cls.replicas[0])
|
||||
+
|
||||
+ def test_uninstall_replica_with_broken_crlgen(self):
|
||||
+ self.replicas[0].run_command(['ipa-crlgen-manage', 'enable'])
|
||||
+
|
||||
+ self.replicas[0].run_command([
|
||||
+ '/bin/sed',
|
||||
+ '-i',
|
||||
+ '/ca.crl.MasterCRL.enableCRLCache=true/d',
|
||||
+ paths.CA_CS_CFG_PATH,
|
||||
+ ])
|
||||
+
|
||||
+ result = self.replicas[0].run_command([
|
||||
+ 'ipa-server-install',
|
||||
+ '--ignore-last-of-role',
|
||||
+ '--uninstall', '-U',
|
||||
+ ])
|
||||
+
|
||||
+ expected_msg = "Configuration is inconsistent, please check " \
|
||||
+ "ca.crl.MasterCRL.enableCRLCache, " \
|
||||
+ "ca.crl.MasterCRL.enableCRLUpdates and " \
|
||||
+ "ca.listenToCloneModifications in {} and run " \
|
||||
+ "ipa-crlgen-manage [enable|disable] to repair".format(
|
||||
+ paths.CA_CS_CFG_PATH)
|
||||
+
|
||||
+ assert expected_msg in result.stdout_text
|
||||
--
|
||||
2.52.0
|
||||
|
||||
@ -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
|
||||
|
||||
@ -0,0 +1,416 @@
|
||||
From 91a6618e51b0e767c5cc5e4b1719531dbbd7268d Mon Sep 17 00:00:00 2001
|
||||
From: Sudhir Menon <sumenon@redhat.com>
|
||||
Date: Thu, 22 Jan 2026 12:43:56 +0530
|
||||
Subject: [PATCH] ipatests: Add xmlrpc tests for ipa-delegation-cli
|
||||
|
||||
This patch adds below test cases to the the XML-RPC delegation plugin test suite
|
||||
coverage of delegation operations and important bug regressions.
|
||||
|
||||
Test cases added:
|
||||
|
||||
Test basic delegation creation with write permission
|
||||
Test delegation creation with --all flag
|
||||
Test delegation creation with --raw flag (ACI format)
|
||||
Test deletion of delegation with ipausers group
|
||||
Test finding delegation by name criteria
|
||||
Test finding delegation by membergroup filter
|
||||
Test showing delegation by name
|
||||
Test modifying delegation attrs
|
||||
Test modifying delegation permissions
|
||||
BZ 783548: Verify mod fails when membergroup doesn't exist
|
||||
BZ 783554: Verify mod with empty attrs fails properly
|
||||
BZ 888524: Verify find --group option works correctly
|
||||
|
||||
Fixes: https://pagure.io/freeipa/issue/9931
|
||||
Assisted-by: Claude <noreply@anthropic.com>
|
||||
Signed-off-by: Sudhir Menon <sumenon@redhat.com>
|
||||
Reviewed-By: Florence Blanc-Renaud <frenaud@redhat.com>
|
||||
Reviewed-By: Rob Crittenden <rcritten@redhat.com>
|
||||
Reviewed-By: Anuja More <amore@redhat.com>
|
||||
---
|
||||
.../test_xmlrpc/test_delegation_plugin.py | 371 ++++++++++++++++++
|
||||
1 file changed, 371 insertions(+)
|
||||
|
||||
diff --git a/ipatests/test_xmlrpc/test_delegation_plugin.py b/ipatests/test_xmlrpc/test_delegation_plugin.py
|
||||
index b3d2aadbddbaaff6f40e1046e4df32bcc9ee7e2d..9245f259e21cad166c3c5b0565da3bb56a341e6b 100644
|
||||
--- a/ipatests/test_xmlrpc/test_delegation_plugin.py
|
||||
+++ b/ipatests/test_xmlrpc/test_delegation_plugin.py
|
||||
@@ -333,4 +333,375 @@ class test_delegation(Declarative):
|
||||
summary=u'Deleted delegation "%s"' % delegation1,
|
||||
)
|
||||
),
|
||||
+
|
||||
+
|
||||
+ dict(
|
||||
+ desc='Create delegation with mobile attr and write permission',
|
||||
+ command=(
|
||||
+ 'delegation_add', [u'test_mobile_delegation'], dict(
|
||||
+ attrs=[u'mobile'],
|
||||
+ permissions=u'write',
|
||||
+ group=u'editors',
|
||||
+ memberof=u'admins',
|
||||
+ )
|
||||
+ ),
|
||||
+ expected=dict(
|
||||
+ value=u'test_mobile_delegation',
|
||||
+ summary=u'Added delegation "test_mobile_delegation"',
|
||||
+ result=dict(
|
||||
+ attrs=[u'mobile'],
|
||||
+ permissions=[u'write'],
|
||||
+ aciname=u'test_mobile_delegation',
|
||||
+ group=u'editors',
|
||||
+ memberof=member1,
|
||||
+ ),
|
||||
+ ),
|
||||
+ ),
|
||||
+
|
||||
+
|
||||
+ dict(
|
||||
+ desc='Create delegation with --all flag',
|
||||
+ command=(
|
||||
+ 'delegation_add', [u'test_all_flag'], dict(
|
||||
+ attrs=[u'mobile'],
|
||||
+ permissions=u'write',
|
||||
+ group=u'editors',
|
||||
+ memberof=u'admins',
|
||||
+ all=True,
|
||||
+ )
|
||||
+ ),
|
||||
+ expected=dict(
|
||||
+ value=u'test_all_flag',
|
||||
+ summary=u'Added delegation "test_all_flag"',
|
||||
+ result=dict(
|
||||
+ attrs=[u'mobile'],
|
||||
+ permissions=[u'write'],
|
||||
+ aciname=u'test_all_flag',
|
||||
+ group=u'editors',
|
||||
+ memberof=member1,
|
||||
+ ),
|
||||
+ ),
|
||||
+ ),
|
||||
+
|
||||
+
|
||||
+ dict(
|
||||
+ desc='Create delegation with --raw flag',
|
||||
+ command=(
|
||||
+ 'delegation_add', [u'test_raw_flag'], dict(
|
||||
+ attrs=[u'mobile'],
|
||||
+ permissions=u'write',
|
||||
+ group=u'editors',
|
||||
+ memberof=u'admins',
|
||||
+ raw=True,
|
||||
+ )
|
||||
+ ),
|
||||
+ expected=dict(
|
||||
+ value=u'test_raw_flag',
|
||||
+ summary=u'Added delegation "test_raw_flag"',
|
||||
+ result={
|
||||
+ 'aci': u'(targetattr = "mobile")(targetfilter = '
|
||||
+ u'"(memberOf=%s)")(version 3.0;acl '
|
||||
+ u'"delegation:test_raw_flag";allow (write) '
|
||||
+ u'groupdn = "ldap:///%s";)' % (
|
||||
+ DN(('cn', 'admins'), ('cn', 'groups'),
|
||||
+ ('cn', 'accounts'), api.env.basedn),
|
||||
+ DN(('cn', 'editors'), ('cn', 'groups'),
|
||||
+ ('cn', 'accounts'), api.env.basedn))
|
||||
+ },
|
||||
+ ),
|
||||
+ ),
|
||||
+
|
||||
+
|
||||
+ dict(
|
||||
+ desc='Delete test_mobile_delegation',
|
||||
+ command=('delegation_del', [u'test_mobile_delegation'], {}),
|
||||
+ expected=dict(
|
||||
+ result=True,
|
||||
+ value=u'test_mobile_delegation',
|
||||
+ summary=u'Deleted delegation "test_mobile_delegation"',
|
||||
+ )
|
||||
+ ),
|
||||
+
|
||||
+
|
||||
+ dict(
|
||||
+ desc='Delete test_all_flag',
|
||||
+ command=('delegation_del', [u'test_all_flag'], {}),
|
||||
+ expected=dict(
|
||||
+ result=True,
|
||||
+ value=u'test_all_flag',
|
||||
+ summary=u'Deleted delegation "test_all_flag"',
|
||||
+ )
|
||||
+ ),
|
||||
+
|
||||
+
|
||||
+ dict(
|
||||
+ desc='Delete test_raw_flag',
|
||||
+ command=('delegation_del', [u'test_raw_flag'], {}),
|
||||
+ expected=dict(
|
||||
+ result=True,
|
||||
+ value=u'test_raw_flag',
|
||||
+ summary=u'Deleted delegation "test_raw_flag"',
|
||||
+ )
|
||||
+ ),
|
||||
+
|
||||
+
|
||||
+ dict(
|
||||
+ desc='Create delegation for ipausers group',
|
||||
+ command=(
|
||||
+ 'delegation_add', [u'delegation_del_positive_1001'], dict(
|
||||
+ attrs=[u'mobile'],
|
||||
+ group=u'ipausers',
|
||||
+ memberof=u'admins',
|
||||
+ )
|
||||
+ ),
|
||||
+ expected=dict(
|
||||
+ value=u'delegation_del_positive_1001',
|
||||
+ summary=u'Added delegation "delegation_del_positive_1001"',
|
||||
+ result=dict(
|
||||
+ attrs=[u'mobile'],
|
||||
+ permissions=[u'write'],
|
||||
+ aciname=u'delegation_del_positive_1001',
|
||||
+ group=u'ipausers',
|
||||
+ memberof=member1,
|
||||
+ ),
|
||||
+ ),
|
||||
+ ),
|
||||
+
|
||||
+
|
||||
+ dict(
|
||||
+ desc='Delete delegation_del_positive_1001',
|
||||
+ command=('delegation_del', [u'delegation_del_positive_1001'], {}),
|
||||
+ expected=dict(
|
||||
+ result=True,
|
||||
+ value=u'delegation_del_positive_1001',
|
||||
+ summary=u'Deleted delegation "delegation_del_positive_1001"',
|
||||
+ )
|
||||
+ ),
|
||||
+
|
||||
+
|
||||
+ dict(
|
||||
+ desc='Create delegation for find, show, and mod tests',
|
||||
+ command=(
|
||||
+ 'delegation_add', [u'delegation_find_show_mod_test'], dict(
|
||||
+ attrs=[u'mobile'],
|
||||
+ permissions=u'write',
|
||||
+ group=u'editors',
|
||||
+ memberof=u'admins',
|
||||
+ )
|
||||
+ ),
|
||||
+ expected=dict(
|
||||
+ value=u'delegation_find_show_mod_test',
|
||||
+ summary=u'Added delegation "delegation_find_show_mod_test"',
|
||||
+ result=dict(
|
||||
+ attrs=[u'mobile'],
|
||||
+ permissions=[u'write'],
|
||||
+ aciname=u'delegation_find_show_mod_test',
|
||||
+ group=u'editors',
|
||||
+ memberof=member1,
|
||||
+ ),
|
||||
+ ),
|
||||
+ ),
|
||||
+
|
||||
+
|
||||
+ dict(
|
||||
+ desc='Find delegation by name',
|
||||
+ command=('delegation_find', [u'delegation_find_show_mod_test'], {}),
|
||||
+ expected=dict(
|
||||
+ count=1,
|
||||
+ truncated=False,
|
||||
+ summary=u'1 delegation matched',
|
||||
+ result=[
|
||||
+ {
|
||||
+ 'attrs': [u'mobile'],
|
||||
+ 'permissions': [u'write'],
|
||||
+ 'aciname': u'delegation_find_show_mod_test',
|
||||
+ 'group': u'editors',
|
||||
+ 'memberof': member1,
|
||||
+ },
|
||||
+ ],
|
||||
+ ),
|
||||
+ ),
|
||||
+
|
||||
+
|
||||
+ dict(
|
||||
+ desc='Find delegation by membergroup',
|
||||
+ command=('delegation_find', [], {'memberof': member1}),
|
||||
+ expected=dict(
|
||||
+ count=1,
|
||||
+ truncated=False,
|
||||
+ summary=u'1 delegation matched',
|
||||
+ result=[
|
||||
+ {
|
||||
+ 'attrs': [u'mobile'],
|
||||
+ 'permissions': [u'write'],
|
||||
+ 'aciname': u'delegation_find_show_mod_test',
|
||||
+ 'group': u'editors',
|
||||
+ 'memberof': member1,
|
||||
+ },
|
||||
+ ],
|
||||
+ ),
|
||||
+ ),
|
||||
+
|
||||
+
|
||||
+ dict(
|
||||
+ desc='Show delegation by name',
|
||||
+ command=('delegation_show', [u'delegation_find_show_mod_test'], {}),
|
||||
+ expected=dict(
|
||||
+ value=u'delegation_find_show_mod_test',
|
||||
+ summary=None,
|
||||
+ result={
|
||||
+ 'attrs': [u'mobile'],
|
||||
+ 'permissions': [u'write'],
|
||||
+ 'aciname': u'delegation_find_show_mod_test',
|
||||
+ 'group': u'editors',
|
||||
+ 'memberof': member1,
|
||||
+ },
|
||||
+ ),
|
||||
+ ),
|
||||
+
|
||||
+
|
||||
+ dict(
|
||||
+ desc='Modify delegation attrs',
|
||||
+ command=(
|
||||
+ 'delegation_mod', [u'delegation_find_show_mod_test'],
|
||||
+ dict(attrs=[u'l'])
|
||||
+ ),
|
||||
+ expected=dict(
|
||||
+ value=u'delegation_find_show_mod_test',
|
||||
+ summary=u'Modified delegation "delegation_find_show_mod_test"',
|
||||
+ result=dict(
|
||||
+ attrs=[u'l'],
|
||||
+ permissions=[u'write'],
|
||||
+ aciname=u'delegation_find_show_mod_test',
|
||||
+ group=u'editors',
|
||||
+ memberof=member1,
|
||||
+ ),
|
||||
+ ),
|
||||
+ ),
|
||||
+
|
||||
+
|
||||
+ dict(
|
||||
+ desc='Modify delegation permissions',
|
||||
+ command=(
|
||||
+ 'delegation_mod', [u'delegation_find_show_mod_test'],
|
||||
+ dict(permissions=u'read')
|
||||
+ ),
|
||||
+ expected=dict(
|
||||
+ value=u'delegation_find_show_mod_test',
|
||||
+ summary=u'Modified delegation "delegation_find_show_mod_test"',
|
||||
+ result=dict(
|
||||
+ attrs=[u'l'],
|
||||
+ permissions=[u'read'],
|
||||
+ aciname=u'delegation_find_show_mod_test',
|
||||
+ group=u'editors',
|
||||
+ memberof=member1,
|
||||
+ ),
|
||||
+ ),
|
||||
+ ),
|
||||
+
|
||||
+
|
||||
+ dict(
|
||||
+ desc='Delete delegation_find_show_mod_test',
|
||||
+ command=('delegation_del', [u'delegation_find_show_mod_test'], {}),
|
||||
+ expected=dict(
|
||||
+ result=True,
|
||||
+ value=u'delegation_find_show_mod_test',
|
||||
+ summary=u'Deleted delegation "delegation_find_show_mod_test"',
|
||||
+ )
|
||||
+ ),
|
||||
+
|
||||
+
|
||||
+ dict(
|
||||
+ desc='Create delegation for BZ tests',
|
||||
+ command=(
|
||||
+ 'delegation_add', [u'delegation_bz_test'], dict(
|
||||
+ attrs=[u'mobile'],
|
||||
+ permissions=u'write',
|
||||
+ group=u'ipausers',
|
||||
+ memberof=u'admins',
|
||||
+ )
|
||||
+ ),
|
||||
+ expected=dict(
|
||||
+ value=u'delegation_bz_test',
|
||||
+ summary=u'Added delegation "delegation_bz_test"',
|
||||
+ result=dict(
|
||||
+ attrs=[u'mobile'],
|
||||
+ permissions=[u'write'],
|
||||
+ aciname=u'delegation_bz_test',
|
||||
+ group=u'ipausers',
|
||||
+ memberof=member1,
|
||||
+ ),
|
||||
+ ),
|
||||
+ ),
|
||||
+
|
||||
+
|
||||
+ dict(
|
||||
+ desc='Try to modify with non-existent membergroup (BZ 783548)',
|
||||
+ command=(
|
||||
+ 'delegation_mod', [u'delegation_bz_test'],
|
||||
+ dict(memberof=u'badmembergroup')
|
||||
+ ),
|
||||
+ expected=errors.NotFound(
|
||||
+ reason=u'badmembergroup: group not found'),
|
||||
+ ),
|
||||
+
|
||||
+
|
||||
+ dict(
|
||||
+ desc='Try to modify attrs with empty value (BZ 783554)',
|
||||
+ command=(
|
||||
+ 'delegation_mod', [u'delegation_bz_test'], dict(attrs=u'')
|
||||
+ ),
|
||||
+ expected=errors.RequirementError(name='attrs'),
|
||||
+ ),
|
||||
+
|
||||
+
|
||||
+ dict(
|
||||
+ desc='Modify attrs to prepare for next BZ test',
|
||||
+ command=(
|
||||
+ 'delegation_mod', [u'delegation_bz_test'], dict(attrs=[u'l'])
|
||||
+ ),
|
||||
+ expected=dict(
|
||||
+ value=u'delegation_bz_test',
|
||||
+ summary=u'Modified delegation "delegation_bz_test"',
|
||||
+ result=dict(
|
||||
+ attrs=[u'l'],
|
||||
+ permissions=[u'write'],
|
||||
+ aciname=u'delegation_bz_test',
|
||||
+ group=u'ipausers',
|
||||
+ memberof=member1,
|
||||
+ ),
|
||||
+ ),
|
||||
+ ),
|
||||
+
|
||||
+
|
||||
+ dict(
|
||||
+ desc='Find delegation by group filter (BZ 888524)',
|
||||
+ command=('delegation_find', [], {'group': u'ipausers'}),
|
||||
+ expected=dict(
|
||||
+ count=1,
|
||||
+ truncated=False,
|
||||
+ summary=u'1 delegation matched',
|
||||
+ result=[
|
||||
+ {
|
||||
+ 'attrs': [u'l'],
|
||||
+ 'permissions': [u'write'],
|
||||
+ 'aciname': u'delegation_bz_test',
|
||||
+ 'group': u'ipausers',
|
||||
+ 'memberof': member1,
|
||||
+ },
|
||||
+ ],
|
||||
+ ),
|
||||
+ ),
|
||||
+
|
||||
+
|
||||
+ dict(
|
||||
+ desc='Delete delegation_bz_test',
|
||||
+ command=('delegation_del', [u'delegation_bz_test'], {}),
|
||||
+ expected=dict(
|
||||
+ result=True,
|
||||
+ value=u'delegation_bz_test',
|
||||
+ summary=u'Deleted delegation "delegation_bz_test"',
|
||||
+ )
|
||||
+ ),
|
||||
+
|
||||
]
|
||||
--
|
||||
2.52.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:
|
||||
|
||||
@ -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
|
||||
|
||||
49
SOURCES/0019-ipa-join-initialize-pointer.patch
Normal file
49
SOURCES/0019-ipa-join-initialize-pointer.patch
Normal file
@ -0,0 +1,49 @@
|
||||
From 7cc96e42683a6d3ec9f2dc2a19e99330b6f3ce58 Mon Sep 17 00:00:00 2001
|
||||
From: Florence Blanc-Renaud <flo@redhat.com>
|
||||
Date: Wed, 4 Feb 2026 09:21:14 +0100
|
||||
Subject: [PATCH] ipa-join: initialize pointer
|
||||
|
||||
OpenScanHub detected an uninitialized pointer in ipa_join:
|
||||
Slapi_DN *sdn;
|
||||
...
|
||||
if (sdn) slapi_sdn_free(&sdn);
|
||||
|
||||
Initialize to NULL
|
||||
Also initialize Slapi_Backend *be=NULL and char * filter=NULL
|
||||
to avoid potential issues.
|
||||
|
||||
Fixes: https://pagure.io/freeipa/issue/9936
|
||||
Signed-off-by: Florence Blanc-Renaud <flo@redhat.com>
|
||||
Reviewed-By: Rob Crittenden <rcritten@redhat.com>
|
||||
Reviewed-By: Rafael Guterres Jeffman <rjeffman@redhat.com>
|
||||
---
|
||||
daemons/ipa-slapi-plugins/ipa-enrollment/ipa_enrollment.c | 6 +++---
|
||||
1 file changed, 3 insertions(+), 3 deletions(-)
|
||||
|
||||
diff --git a/daemons/ipa-slapi-plugins/ipa-enrollment/ipa_enrollment.c b/daemons/ipa-slapi-plugins/ipa-enrollment/ipa_enrollment.c
|
||||
index 3a70dd0a5594fc623e7e808ab8a734349a748a49..2f8923e10310a8a6e19ac701070d6451915c3be3 100644
|
||||
--- a/daemons/ipa-slapi-plugins/ipa-enrollment/ipa_enrollment.c
|
||||
+++ b/daemons/ipa-slapi-plugins/ipa-enrollment/ipa_enrollment.c
|
||||
@@ -129,8 +129,8 @@ ipa_join(Slapi_PBlock *pb)
|
||||
Slapi_PBlock *pbte = NULL;
|
||||
Slapi_PBlock *pbtm = NULL;
|
||||
Slapi_Entry *targetEntry=NULL;
|
||||
- Slapi_DN *sdn;
|
||||
- Slapi_Backend *be;
|
||||
+ Slapi_DN *sdn=NULL;
|
||||
+ Slapi_Backend *be=NULL;
|
||||
Slapi_Entry **es = NULL;
|
||||
int rc=0, ret=0, res;
|
||||
size_t i;
|
||||
@@ -139,7 +139,7 @@ ipa_join(Slapi_PBlock *pb)
|
||||
char *fqdn = NULL;
|
||||
Slapi_Mods *smods = NULL;
|
||||
char *attrlist[] = {"fqdn", "krbPrincipalKey", "krbLastPwdChange", "krbPrincipalName", NULL };
|
||||
- char * filter;
|
||||
+ char * filter=NULL;
|
||||
|
||||
int scope = LDAP_SCOPE_SUBTREE;
|
||||
char *principal = NULL;
|
||||
--
|
||||
2.52.0
|
||||
|
||||
@ -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
|
||||
|
||||
@ -0,0 +1,39 @@
|
||||
From d5efb4decb74b50c05b1d252add1c075e660d154 Mon Sep 17 00:00:00 2001
|
||||
From: Florence Blanc-Renaud <flo@redhat.com>
|
||||
Date: Wed, 11 Feb 2026 10:23:36 +0100
|
||||
Subject: [PATCH] ipatests: pruning is enabled when RSN is enabled
|
||||
|
||||
The test TestACMEPrune installs the server with --random-serial-numbers
|
||||
but expects pruning to be disabled if the 389ds backend is BDB.
|
||||
|
||||
This is a wrong expectation as pruning is enabled as soon as RSN
|
||||
are enabled (since commit 3777d2b).
|
||||
Fix the test expectation.
|
||||
|
||||
Signed-off-by: Florence Blanc-Renaud <flo@redhat.com>
|
||||
---
|
||||
ipatests/test_integration/test_acme.py | 8 ++------
|
||||
1 file changed, 2 insertions(+), 6 deletions(-)
|
||||
|
||||
diff --git a/ipatests/test_integration/test_acme.py b/ipatests/test_integration/test_acme.py
|
||||
index 4c66e4348beeaca95577a786a46e53fdc1532ef7..bedec9d1f9f0c168c11aceb155978b6a0dae8dd7 100644
|
||||
--- a/ipatests/test_integration/test_acme.py
|
||||
+++ b/ipatests/test_integration/test_acme.py
|
||||
@@ -718,12 +718,8 @@ class TestACMEPrune(IntegrationTest):
|
||||
< tasks.parse_version('11.3.0')):
|
||||
raise pytest.skip("Certificate pruning is not available")
|
||||
|
||||
- # Pruning is enabled by default when the host supports lmdb
|
||||
- if get_389ds_backend(self.master) == 'bdb':
|
||||
- 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'])
|
||||
-
|
||||
+ # Pruning is enabled by default when server is installed
|
||||
+ # with --random-serial-numbers
|
||||
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
|
||||
--
|
||||
2.52.0
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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
|
||||
|
||||
|
||||
@ -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",
|
||||
]
|
||||
@ -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.
|
||||
File diff suppressed because it is too large
Load Diff
@ -1,87 +0,0 @@
|
||||
diff --git a/install/updates/50-krbenctypes.update b/install/updates/50-krbenctypes.update
|
||||
index 1058a92..1bf2bf3 100644
|
||||
--- a/install/updates/50-krbenctypes.update
|
||||
+++ b/install/updates/50-krbenctypes.update
|
||||
@@ -7,3 +7,5 @@ add: krbSupportedEncSaltTypes: aes128-sha2:normal
|
||||
add: krbSupportedEncSaltTypes: aes128-sha2:special
|
||||
add: krbSupportedEncSaltTypes: aes256-sha2:normal
|
||||
add: krbSupportedEncSaltTypes: aes256-sha2:special
|
||||
+remove: krbDefaultEncSaltTypes: des3-hmac-sha1:special
|
||||
+remove: krbDefaultEncSaltTypes: arcfour-hmac:special
|
||||
diff --git a/install/updates/60-trusts.update b/install/updates/60-trusts.update
|
||||
index 56e3920..b2fdcca 100644
|
||||
--- a/install/updates/60-trusts.update
|
||||
+++ b/install/updates/60-trusts.update
|
||||
@@ -54,4 +54,4 @@ add:aci: (target="ldap:///krbprincipalname=cifs/($$dn),cn=services,cn=accounts,$
|
||||
|
||||
# Add the default PAC type to configuration
|
||||
dn: cn=ipaConfig,cn=etc,$SUFFIX
|
||||
-addifnew: ipaKrbAuthzData: MS-PAC
|
||||
+add: ipaKrbAuthzData: MS-PAC
|
||||
diff --git a/ipatests/test_integration/test_installation.py b/ipatests/test_integration/test_installation.py
|
||||
index d41c1ee..ef0727e 100644
|
||||
--- a/ipatests/test_integration/test_installation.py
|
||||
+++ b/ipatests/test_integration/test_installation.py
|
||||
@@ -1188,6 +1188,21 @@ class TestInstallMaster(IntegrationTest):
|
||||
expected_stdout=f'href="https://{self.master.hostname}/'
|
||||
)
|
||||
|
||||
+ def test_pac_configuration_enabled(self):
|
||||
+ """
|
||||
+ This testcase checks that the default PAC type
|
||||
+ is added to configuration.
|
||||
+ """
|
||||
+ base_dn = str(self.master.domain.basedn)
|
||||
+ dn = DN(
|
||||
+ ("cn", "ipaConfig"),
|
||||
+ ("cn", "etc"),
|
||||
+ base_dn
|
||||
+ )
|
||||
+ result = tasks.ldapsearch_dm(self.master, str(dn),
|
||||
+ ["ipaKrbAuthzData"])
|
||||
+ assert 'ipaKrbAuthzData: MS-PAC' in result.stdout_text
|
||||
+
|
||||
def test_hostname_parameter(self, server_cleanup):
|
||||
"""
|
||||
Test that --hostname parameter is respected in interactive mode.
|
||||
diff --git a/ipatests/test_integration/test_upgrade.py b/ipatests/test_integration/test_upgrade.py
|
||||
index 182e3b5..8465cf9 100644
|
||||
--- a/ipatests/test_integration/test_upgrade.py
|
||||
+++ b/ipatests/test_integration/test_upgrade.py
|
||||
@@ -165,7 +165,6 @@ class TestUpgrade(IntegrationTest):
|
||||
ldap.update_entry(location_krb_rec)
|
||||
|
||||
yield _setup_locations
|
||||
-
|
||||
ldap = self.master.ldap_connect()
|
||||
|
||||
modified = False
|
||||
@@ -477,3 +476,28 @@ class TestUpgrade(IntegrationTest):
|
||||
self.master.run_command(['ipa-server-upgrade'])
|
||||
assert self.master.transport.file_exists(
|
||||
paths.SYSTEMD_PKI_TOMCAT_IPA_CONF)
|
||||
+
|
||||
+ def test_mspac_attribute_set(self):
|
||||
+ """
|
||||
+ This testcase deletes the already existing attribute
|
||||
+ 'ipaKrbAuthzData: MS-PAC'.
|
||||
+ The test then runs ipa-server-upgrade and checks that
|
||||
+ the attribute 'ipaKrbAuthzData: MS-PAC' is added again.
|
||||
+ """
|
||||
+ base_dn = str(self.master.domain.basedn)
|
||||
+ dn = DN(
|
||||
+ ("cn", "ipaConfig"),
|
||||
+ ("cn", "etc"),
|
||||
+ base_dn
|
||||
+ )
|
||||
+ ldif = textwrap.dedent("""
|
||||
+ dn: cn=ipaConfig,cn=etc,{}
|
||||
+ changetype: modify
|
||||
+ delete: ipaKrbAuthzData
|
||||
+ """).format(base_dn)
|
||||
+ tasks.ldapmodify_dm(self.master, ldif)
|
||||
+ tasks.kinit_admin(self.master)
|
||||
+ self.master.run_command(['ipa-server-upgrade'])
|
||||
+ result = tasks.ldapsearch_dm(self.master, str(dn),
|
||||
+ ["ipaKrbAuthzData"])
|
||||
+ assert 'ipaKrbAuthzData: MS-PAC' in result.stdout_text
|
||||
@ -1,72 +0,0 @@
|
||||
From f6645ebe5c0c0c030ec2e62e007d8dacd1b4e4cf Mon Sep 17 00:00:00 2001
|
||||
From: Erik Belko <ebelko@redhat.com>
|
||||
Date: Sep 03 2024 12:54:30 +0000
|
||||
Subject: ipatests: Update ipa-adtrust-install test
|
||||
|
||||
|
||||
update test_user_connects_smb_share_if_locked_specific_group with wait
|
||||
for SSSD to be online after ipa-adtrust-install command
|
||||
|
||||
Related: https://pagure.io/freeipa/issue/9655
|
||||
|
||||
Signed-off-by: Erik Belko <ebelko@redhat.com>
|
||||
Reviewed-By: Alexander Bokovoy <abokovoy@redhat.com>
|
||||
|
||||
---
|
||||
|
||||
diff --git a/ipatests/test_integration/test_adtrust_install.py b/ipatests/test_integration/test_adtrust_install.py
|
||||
index 72e8d87..de252db 100644
|
||||
--- a/ipatests/test_integration/test_adtrust_install.py
|
||||
+++ b/ipatests/test_integration/test_adtrust_install.py
|
||||
@@ -853,6 +853,8 @@ class TestIpaAdTrustInstall(IntegrationTest):
|
||||
self.master.config.admin_password,
|
||||
"-U"]
|
||||
)
|
||||
+ # Wait for SSSD to become online before doing any other check
|
||||
+ tasks.wait_for_sssd_domain_status_online(self.master)
|
||||
self.master.run_command(["mkdir", "/freeipa4234"])
|
||||
self.master.run_command(
|
||||
["chcon", "-t", "samba_share_t",
|
||||
|
||||
From 47920e78c81380c0a40986e55f05246aac132fbb Mon Sep 17 00:00:00 2001
|
||||
From: Erik Belko <ebelko@redhat.com>
|
||||
Date: May 21 2024 12:50:46 +0000
|
||||
Subject: ipatests: Update ipa-adtrust-install test
|
||||
|
||||
|
||||
update after change in implementation of `krb_utils.get_principal()` now using GSSAPI
|
||||
|
||||
Related: https://pagure.io/freeipa/issue/9575
|
||||
|
||||
Signed-off-by: Erik Belko <ebelko@redhat.com>
|
||||
Reviewed-By: Michal Polovka <mpolovka@redhat.com>
|
||||
|
||||
---
|
||||
|
||||
diff --git a/ipatests/test_integration/test_adtrust_install.py b/ipatests/test_integration/test_adtrust_install.py
|
||||
index 86d8d20..72e8d87 100644
|
||||
--- a/ipatests/test_integration/test_adtrust_install.py
|
||||
+++ b/ipatests/test_integration/test_adtrust_install.py
|
||||
@@ -464,18 +464,15 @@ class TestIpaAdTrustInstall(IntegrationTest):
|
||||
password
|
||||
"""
|
||||
password = "wrong_pwd"
|
||||
- msg = (
|
||||
- "Must have Kerberos credentials to setup AD trusts on server: "
|
||||
- "Major (458752): No credentials were supplied, or the credentials "
|
||||
- "were unavailable or inaccessible, Minor (2529639053): "
|
||||
- "No Kerberos credentials available (default cache: KCM:)\n"
|
||||
+ expected_substring = (
|
||||
+ "Must have Kerberos credentials to setup AD trusts on server:"
|
||||
)
|
||||
self.master.run_command(["kdestroy", "-A"])
|
||||
result = self.master.run_command(
|
||||
["ipa-adtrust-install", "-A", "admin", "-a",
|
||||
password, "-U"], raiseonerr=False
|
||||
)
|
||||
- assert msg in result.stderr_text
|
||||
+ assert expected_substring in result.stderr_text
|
||||
assert result.returncode != 0
|
||||
|
||||
def test_adtrust_install_with_invalid_rid_base_value(self):
|
||||
|
||||
@ -1,245 +0,0 @@
|
||||
From 19f22cf75ae768dd2b6c0d674cf55f8d6ffafb31 Mon Sep 17 00:00:00 2001
|
||||
From: Florence Blanc-Renaud <flo@redhat.com>
|
||||
Date: Mar 07 2025 06:48:02 +0000
|
||||
Subject: Replica CA installation: ignore time skew during initial replication
|
||||
|
||||
|
||||
During a replica CA installation, the initial replication step may fail
|
||||
if there is too much time skew between the server and replica.
|
||||
|
||||
The replica installer already takes care of this for the replication of
|
||||
the domain suffix but the replica CA installer does not set
|
||||
nssldapd-ignore-time-skew to on for o=ipaca suffix.
|
||||
|
||||
During a replica CA installation, read the initial value of
|
||||
nssldapd-ignore-time-skew, force it to on, start replication and
|
||||
revert to the initial value.
|
||||
|
||||
Apply the same logic to dsinstance and ipa-replica-manage force-sync.
|
||||
|
||||
Fixes: https://pagure.io/freeipa/issue/9635
|
||||
Signed-off-by: Florence Blanc-Renaud <flo@redhat.com>
|
||||
Reviewed-By: Rob Crittenden <rcritten@redhat.com>
|
||||
Reviewed-By: Rob Crittenden <rcritten@redhat.com>
|
||||
|
||||
---
|
||||
|
||||
diff --git a/install/share/Makefile.am b/install/share/Makefile.am
|
||||
index e0fe4b7..4029297 100644
|
||||
--- a/install/share/Makefile.am
|
||||
+++ b/install/share/Makefile.am
|
||||
@@ -38,7 +38,6 @@ dist_app_DATA = \
|
||||
default-trust-view.ldif \
|
||||
delegation.ldif \
|
||||
replica-acis.ldif \
|
||||
- replica-prevent-time-skew.ldif \
|
||||
ds-nfiles.ldif \
|
||||
ds-ipa-env.conf.template \
|
||||
dns.ldif \
|
||||
diff --git a/install/share/replica-prevent-time-skew.ldif b/install/share/replica-prevent-time-skew.ldif
|
||||
deleted file mode 100644
|
||||
index 5d301fe..0000000
|
||||
--- a/install/share/replica-prevent-time-skew.ldif
|
||||
+++ /dev/null
|
||||
@@ -1,4 +0,0 @@
|
||||
-dn: cn=config
|
||||
-changetype: modify
|
||||
-replace: nsslapd-ignore-time-skew
|
||||
-nsslapd-ignore-time-skew: $SKEWVALUE
|
||||
diff --git a/install/tools/ipa-replica-manage.in b/install/tools/ipa-replica-manage.in
|
||||
index cebf73a..71851be 100644
|
||||
--- a/install/tools/ipa-replica-manage.in
|
||||
+++ b/install/tools/ipa-replica-manage.in
|
||||
@@ -1237,12 +1237,13 @@ def force_sync(realm, thishost, fromhost, dirman_passwd, nolookup=False):
|
||||
repl.force_sync(repl.conn, fromhost)
|
||||
else:
|
||||
ds = dsinstance.DsInstance(realm_name=realm)
|
||||
- ds.replica_manage_time_skew(prevent=False)
|
||||
+ ds.replica_ignore_initial_time_skew()
|
||||
repl = replication.ReplicationManager(realm, fromhost, dirman_passwd)
|
||||
repl.force_sync(repl.conn, thishost)
|
||||
agreement = repl.get_replication_agreement(thishost)
|
||||
repl.wait_for_repl_update(repl.conn, agreement.dn)
|
||||
- ds.replica_manage_time_skew(prevent=True)
|
||||
+ ds.replica_revert_time_skew()
|
||||
+
|
||||
|
||||
def show_DNA_ranges(hostname, master, realm, dirman_passwd, nextrange=False,
|
||||
nolookup=False):
|
||||
diff --git a/ipaserver/install/cainstance.py b/ipaserver/install/cainstance.py
|
||||
index 35cec89..e15e629 100644
|
||||
--- a/ipaserver/install/cainstance.py
|
||||
+++ b/ipaserver/install/cainstance.py
|
||||
@@ -409,7 +409,11 @@ class CAInstance(DogtagInstance):
|
||||
if promote:
|
||||
# Setup Database
|
||||
self.step("creating certificate server db", self.__create_ds_db)
|
||||
+ self.step("ignore time skew for initial replication",
|
||||
+ self.replica_ignore_initial_time_skew)
|
||||
self.step("setting up initial replication", self.__setup_replication)
|
||||
+ self.step("revert time skew after initial replication",
|
||||
+ self.replica_revert_time_skew)
|
||||
self.step("creating ACIs for admin", self.add_ipaca_aci)
|
||||
self.step("creating installation admin user", self.setup_admin)
|
||||
self.step("configuring certificate server instance",
|
||||
diff --git a/ipaserver/install/dsinstance.py b/ipaserver/install/dsinstance.py
|
||||
index cbacfae..ba4bf8a 100644
|
||||
--- a/ipaserver/install/dsinstance.py
|
||||
+++ b/ipaserver/install/dsinstance.py
|
||||
@@ -385,11 +385,11 @@ class DsInstance(service.Service):
|
||||
# This helps with initial replication or force-sync because
|
||||
# the receiving side has no valuable changes itself yet.
|
||||
self.step("ignore time skew for initial replication",
|
||||
- self.__replica_ignore_initial_time_skew)
|
||||
+ self.replica_ignore_initial_time_skew)
|
||||
|
||||
self.step("setting up initial replication", self.__setup_replica)
|
||||
self.step("prevent time skew after initial replication",
|
||||
- self.replica_manage_time_skew)
|
||||
+ self.replica_revert_time_skew)
|
||||
self.step("adding sasl mappings to the directory", self.__configure_sasl_mappings)
|
||||
self.step("updating schema", self.__update_schema)
|
||||
# See LDIFs for automember configuration during replica install
|
||||
@@ -995,16 +995,6 @@ class DsInstance(service.Service):
|
||||
def __add_replication_acis(self):
|
||||
self._ldap_mod("replica-acis.ldif", self.sub_dict)
|
||||
|
||||
- def __replica_ignore_initial_time_skew(self):
|
||||
- self.replica_manage_time_skew(prevent=False)
|
||||
-
|
||||
- def replica_manage_time_skew(self, prevent=True):
|
||||
- if prevent:
|
||||
- self.sub_dict['SKEWVALUE'] = 'off'
|
||||
- else:
|
||||
- self.sub_dict['SKEWVALUE'] = 'on'
|
||||
- self._ldap_mod("replica-prevent-time-skew.ldif", self.sub_dict)
|
||||
-
|
||||
def __setup_s4u2proxy(self):
|
||||
|
||||
def __add_principal(last_cn, principal, self):
|
||||
diff --git a/ipaserver/install/service.py b/ipaserver/install/service.py
|
||||
index 13ae346..15ca70b 100644
|
||||
--- a/ipaserver/install/service.py
|
||||
+++ b/ipaserver/install/service.py
|
||||
@@ -811,6 +811,31 @@ class Service:
|
||||
self.run_getkeytab(self.api.env.ldap_uri, self.keytab, self.principal)
|
||||
self.set_keytab_owner()
|
||||
|
||||
+ def replica_ignore_initial_time_skew(self):
|
||||
+ """
|
||||
+ Set nsslapd-ignore-time-skew = on if not already set
|
||||
+ and store the initial value in order to restore it later.
|
||||
+
|
||||
+ The on value allows replica initialization even if there
|
||||
+ are excessive time skews.
|
||||
+ """
|
||||
+ dn = DN(('cn', 'config'))
|
||||
+ entry_attrs = api.Backend.ldap2.get_entry(dn)
|
||||
+ self.original_time_skew = entry_attrs['nsslapd-ignore-time-skew'][0]
|
||||
+ if self.original_time_skew != 'on':
|
||||
+ entry_attrs['nsslapd-ignore-time-skew'] = 'on'
|
||||
+ api.Backend.ldap2.update_entry(entry_attrs)
|
||||
+
|
||||
+ def replica_revert_time_skew(self):
|
||||
+ """
|
||||
+ Revert nsslapd-ignore-time-skew to its previous value.
|
||||
+ """
|
||||
+ dn = DN(('cn', 'config'))
|
||||
+ entry_attrs = api.Backend.ldap2.get_entry(dn)
|
||||
+ if self.original_time_skew != 'on':
|
||||
+ entry_attrs['nsslapd-ignore-time-skew'] = self.original_time_skew
|
||||
+ api.Backend.ldap2.update_entry(entry_attrs)
|
||||
+
|
||||
|
||||
class SimpleServiceInstance(Service):
|
||||
def create_instance(self, gensvc_name=None, fqdn=None, ldap_suffix=None,
|
||||
|
||||
From a6bb2fa4997dd7894dbf75d1c3fd1deaebd3e05c Mon Sep 17 00:00:00 2001
|
||||
From: Sudhir Menon <sumenon@redhat.com>
|
||||
Date: Mar 07 2025 06:48:02 +0000
|
||||
Subject: ipatests: Test to check that the configured value for "nsslapd-ignore-time-skew" remains on even after a "force-sync" is done
|
||||
|
||||
|
||||
Related: https://pagure.io/freeipa/issue/9635
|
||||
|
||||
Signed-off-by: Sudhir Menon <sumenon@redhat.com>
|
||||
Reviewed-By: Florence Blanc-Renaud <flo@redhat.com>
|
||||
Reviewed-By: Rob Crittenden <rcritten@redhat.com>
|
||||
|
||||
---
|
||||
|
||||
diff --git a/ipatests/test_integration/test_installation.py b/ipatests/test_integration/test_installation.py
|
||||
index ef0727e..3673f7f 100644
|
||||
--- a/ipatests/test_integration/test_installation.py
|
||||
+++ b/ipatests/test_integration/test_installation.py
|
||||
@@ -2132,3 +2132,69 @@ class TestHostnameValidator(IntegrationTest):
|
||||
assert result.returncode == 1
|
||||
assert 'hostname cannot be the same as the domain name' \
|
||||
in result.stderr_text
|
||||
+
|
||||
+
|
||||
+class TestNsslapdIgnoreTimeSkew(IntegrationTest):
|
||||
+ """
|
||||
+ Test to check nsslapd-ignore-time-skew is not disabled.
|
||||
+ """
|
||||
+ num_replicas = 1
|
||||
+ topology = 'line'
|
||||
+
|
||||
+ @pytest.fixture
|
||||
+ def update_time_skew(self):
|
||||
+ """
|
||||
+ Fixture enables nsslapd-ignore-time-skew
|
||||
+ parameter and reverts it back
|
||||
+ """
|
||||
+ ldap = self.replicas[0].ldap_connect()
|
||||
+ dn = DN(
|
||||
+ ("cn", "config"),
|
||||
+ )
|
||||
+ entry = ldap.get_entry(dn)
|
||||
+ entry.single_value["nsslapd-ignore-time-skew"] = 'on'
|
||||
+ ldap.update_entry(entry)
|
||||
+
|
||||
+ yield
|
||||
+
|
||||
+ entry = ldap.get_entry(dn)
|
||||
+ entry.single_value["nsslapd-ignore-time-skew"] = 'off'
|
||||
+ ldap.update_entry(entry)
|
||||
+
|
||||
+ def test_check_nsslapd_ignore_time_skew(self):
|
||||
+ """
|
||||
+ This testcase checks that the ignore time skew parameter
|
||||
+ is set to on during the directory server replica
|
||||
+ installation (replication of the suffix) and during
|
||||
+ the CA replica (replication of o=ipaca).
|
||||
+ It also checks that the time skew is reverted during
|
||||
+ pki_tomcat setup stage.
|
||||
+ """
|
||||
+ DIRSRV_LOG = (
|
||||
+ "ignore time skew for initial replication"
|
||||
+ )
|
||||
+ PKI_TOMCAT_LOG = (
|
||||
+ "revert time skew after initial replication"
|
||||
+ )
|
||||
+ install_msg = self.replicas[0].get_file_contents(
|
||||
+ paths.IPAREPLICA_INSTALL_LOG, encoding="utf-8"
|
||||
+ )
|
||||
+ dirsrv_msg = re.findall(DIRSRV_LOG, install_msg)
|
||||
+ assert len(dirsrv_msg) == 2
|
||||
+ assert PKI_TOMCAT_LOG in install_msg
|
||||
+
|
||||
+ def test_forcesync_does_not_overwrite_ignore_time_skew(
|
||||
+ self, update_time_skew):
|
||||
+ """
|
||||
+ This testcase checks that calling ipa-replica-manage
|
||||
+ force-sync does not overwrite the value of ignore
|
||||
+ time skew
|
||||
+ """
|
||||
+ result = self.replicas[0].run_command(
|
||||
+ ["ipa-replica-manage", "force-sync",
|
||||
+ "--from", self.master.hostname,
|
||||
+ "--no-lookup", "-v"])
|
||||
+ assert result.returncode == 0
|
||||
+ conn = self.replicas[0].ldap_connect()
|
||||
+ ldap_entry = conn.get_entry(DN("cn=config"))
|
||||
+ assert ldap_entry.single_value['nsslapd-ignore-time-skew'] == "on"
|
||||
|
||||
@ -1,82 +0,0 @@
|
||||
From ac6eee670d8a753e66ba69a65eff55447fff2822 Mon Sep 17 00:00:00 2001
|
||||
From: Aleksandr Sharov <asharov@redhat.com>
|
||||
Date: Mar 25 2025 09:33:06 +0000
|
||||
Subject: Add a check into ipa-cert-fix tool to avoid updating certs if CA is close to being expired.
|
||||
|
||||
|
||||
Fixes: https://pagure.io/freeipa/issue/9760
|
||||
Signed-off-by: Aleksandr Sharov <asharov@redhat.com>
|
||||
Reviewed-By: Rob Crittenden <rcritten@redhat.com>
|
||||
Reviewed-By: Florence Blanc-Renaud <flo@redhat.com>
|
||||
|
||||
---
|
||||
|
||||
diff --git a/ipaserver/install/ipa_cert_fix.py b/ipaserver/install/ipa_cert_fix.py
|
||||
index 8e02d1e..960d7b9 100644
|
||||
--- a/ipaserver/install/ipa_cert_fix.py
|
||||
+++ b/ipaserver/install/ipa_cert_fix.py
|
||||
@@ -69,6 +69,7 @@ logger = logging.getLogger(__name__)
|
||||
|
||||
|
||||
cert_nicknames = {
|
||||
+ 'ca_issuing': 'caSigningCert cert-pki-ca',
|
||||
'sslserver': 'Server-Cert cert-pki-ca',
|
||||
'subsystem': 'subsystemCert cert-pki-ca',
|
||||
'ca_ocsp_signing': 'ocspSigningCert cert-pki-ca',
|
||||
@@ -137,6 +138,16 @@ class IPACertFix(AdminTool):
|
||||
print("Nothing to do.")
|
||||
return 0
|
||||
|
||||
+ if any(key == 'ca_issuing' for key, _ in certs):
|
||||
+ logger.debug("CA signing cert is expired, exiting!")
|
||||
+ print(
|
||||
+ "The CA signing certificate is expired or will expire within "
|
||||
+ "the next two weeks.\n\nipa-cert-fix cannot proceed, please "
|
||||
+ "refer to the ipa-cacert-manage tool to renew the CA "
|
||||
+ "certificate before proceeding."
|
||||
+ )
|
||||
+ return 1
|
||||
+
|
||||
print(msg)
|
||||
|
||||
print_intentions(certs, extra_certs, non_renewed)
|
||||
|
||||
From cdc03d7b6233f736c51c10aa07225aac9715e4c0 Mon Sep 17 00:00:00 2001
|
||||
From: Aleksandr Sharov <asharov@redhat.com>
|
||||
Date: Mar 25 2025 18:03:54 +0000
|
||||
Subject: Test fix for the update
|
||||
|
||||
|
||||
Fixes: https://pagure.io/freeipa/issue/9760
|
||||
Signed-off-by: Aleksandr Sharov <asharov@redhat.com>
|
||||
Reviewed-By: Rob Crittenden <rcritten@redhat.com>
|
||||
Reviewed-By: Florence Blanc-Renaud <flo@redhat.com>
|
||||
|
||||
---
|
||||
|
||||
diff --git a/ipatests/test_integration/test_ipa_cert_fix.py b/ipatests/test_integration/test_ipa_cert_fix.py
|
||||
index 15d8a81..d11fd3d 100644
|
||||
--- a/ipatests/test_integration/test_ipa_cert_fix.py
|
||||
+++ b/ipatests/test_integration/test_ipa_cert_fix.py
|
||||
@@ -301,13 +301,18 @@ class TestIpaCertFix(IntegrationTest):
|
||||
valid. If CA cert expired, ipa-cert-fix won't work.
|
||||
|
||||
related: https://pagure.io/freeipa/issue/8721
|
||||
+
|
||||
+ If CA cert is close to expiry, there's no reason to issue new certs
|
||||
+ with short validity period. So, ipa-cert-fix should fail in this case.
|
||||
+
|
||||
+ related: https://pagure.io/freeipa/issue/9760
|
||||
"""
|
||||
result = self.master.run_command(['ipa-cert-fix', '-v'],
|
||||
stdin_text='yes\n',
|
||||
raiseonerr=False)
|
||||
# check that pki-server cert-fix command fails
|
||||
- err_msg = ("ERROR: CalledProcessError(Command "
|
||||
- "['pki-server', 'cert-fix'")
|
||||
+ err_msg = ("CA signing cert is expired, exiting!")
|
||||
+ assert result.returncode == 1
|
||||
assert err_msg in result.stderr_text
|
||||
|
||||
|
||||
|
||||
@ -1,84 +0,0 @@
|
||||
From ae37b3e6ed12bddb650bdce8e9729e81fef40840 Mon Sep 17 00:00:00 2001
|
||||
From: Julien Rische <jrische@redhat.com>
|
||||
Date: May 08 2025 06:21:00 +0000
|
||||
Subject: kdb: keep ipadb_get_connection() from succeeding with null LDAP context
|
||||
|
||||
|
||||
The final call to ipadb_reinit_mspac() in ipadb_get_connection() is not
|
||||
considered essential for the function to succeed, as there might be
|
||||
cases where the required pieces of information to generate PACs are not
|
||||
yet configured in the database. However, in environments where 389ds is
|
||||
overwhelmed, the LDAP connection established at the beginning of
|
||||
ipadb_get_connection() might already be lost while executing
|
||||
ipadb_reinit_mspac().
|
||||
|
||||
Connection errors were not distinguished from configuration errors,
|
||||
which could result in ipadb_get_connection() succeeding while the LDAP
|
||||
context is set to null, leading to a KDC crash on the next LDAP request.
|
||||
|
||||
ipadb_get_connection() now explicitly checks the value of the LDAP
|
||||
context before returning.
|
||||
|
||||
Fixes: https://pagure.io/freeipa/issue/9777
|
||||
Reviewed-By: Rob Crittenden <rcritten@redhat.com>
|
||||
Reviewed-By: Rob Crittenden <rcritten@redhat.com>
|
||||
Reviewed-By: Rafael Guterres Jeffman <rjeffman@redhat.com>
|
||||
|
||||
---
|
||||
|
||||
diff --git a/daemons/ipa-kdb/ipa_kdb.c b/daemons/ipa-kdb/ipa_kdb.c
|
||||
index fcadb8e..98315a0 100644
|
||||
--- a/daemons/ipa-kdb/ipa_kdb.c
|
||||
+++ b/daemons/ipa-kdb/ipa_kdb.c
|
||||
@@ -524,26 +524,43 @@ int ipadb_get_connection(struct ipadb_context *ipactx)
|
||||
|
||||
/* get adtrust options using default refresh interval */
|
||||
ret = ipadb_reinit_mspac(ipactx, false, &stmsg);
|
||||
- if (ret && stmsg)
|
||||
- krb5_klog_syslog(LOG_WARNING, "MS-PAC generator: %s", stmsg);
|
||||
+ if (ret) {
|
||||
+ if (stmsg) {
|
||||
+ krb5_klog_syslog(LOG_WARNING, "MS-PAC generator: %s", stmsg);
|
||||
+ }
|
||||
+ /* Initialization of the MS-PAC generator is an optional dependency.
|
||||
+ * Fail only if the connection was lost. */
|
||||
+ if (!ipactx->lcontext) {
|
||||
+ goto done;
|
||||
+ }
|
||||
+ }
|
||||
|
||||
ret = 0;
|
||||
|
||||
done:
|
||||
ldap_msgfree(res);
|
||||
|
||||
+ /* LDAP context should never be null on success, but keep this test out of
|
||||
+ * security to make sure we do not return an invalid context. */
|
||||
+ if (ret == 0 && !ipactx->lcontext) {
|
||||
+ krb5_klog_syslog(LOG_WARNING, "Internal malfunction: LDAP connection "
|
||||
+ "process resulted in an invalid context "
|
||||
+ "(please report this incident)");
|
||||
+ ret = LDAP_SERVER_DOWN;
|
||||
+ }
|
||||
+
|
||||
if (ret) {
|
||||
+ /* Cleanup LDAP context if connection failed. */
|
||||
if (ipactx->lcontext) {
|
||||
ldap_unbind_ext_s(ipactx->lcontext, NULL, NULL);
|
||||
ipactx->lcontext = NULL;
|
||||
}
|
||||
- if (ret == LDAP_SERVER_DOWN) {
|
||||
- return ETIMEDOUT;
|
||||
- }
|
||||
- return EIO;
|
||||
+
|
||||
+ /* Replace LDAP error code by POSIX error code. */
|
||||
+ ret = ret == LDAP_SERVER_DOWN ? ETIMEDOUT : EIO;
|
||||
}
|
||||
|
||||
- return 0;
|
||||
+ return ret;
|
||||
}
|
||||
|
||||
static krb5_principal ipadb_create_local_tgs(krb5_context kcontext,
|
||||
|
||||
@ -1,189 +0,0 @@
|
||||
From d6d2282f9f1b93ae7fb6e074920e41e64f35ab12 Mon Sep 17 00:00:00 2001
|
||||
From: Rob Crittenden <rcritten@redhat.com>
|
||||
Date: Mon, 28 Apr 2025 13:43:40 -0400
|
||||
Subject: [PATCH] Set krbCanonicalName=admin@REALM on the admin user
|
||||
|
||||
The admin must always own this name. If another entry has this
|
||||
value set then remove it.
|
||||
|
||||
There is a uniqueness plugin for this attribute so the only two
|
||||
possibilities are:
|
||||
|
||||
- no entry has this value set
|
||||
- the admin user has this value set
|
||||
- a different entry has the value set
|
||||
|
||||
Still, for robustness purposes, the upgrade plugin will handle
|
||||
more entries.
|
||||
|
||||
Signed-off-by: Rob Crittenden <rcritten@redhat.com>
|
||||
---
|
||||
install/share/bootstrap-template.ldif | 1 +
|
||||
.../updates/90-post_upgrade_plugins.update | 1 +
|
||||
.../plugins/add_admin_krbcanonicalname.py | 79 +++++++++++++++++++
|
||||
ipatests/test_integration/test_commands.py | 38 +++++++++
|
||||
4 files changed, 119 insertions(+)
|
||||
create mode 100644 ipaserver/install/plugins/add_admin_krbcanonicalname.py
|
||||
|
||||
diff --git a/install/share/bootstrap-template.ldif b/install/share/bootstrap-template.ldif
|
||||
index 325eb8450..94972eb72 100644
|
||||
--- a/install/share/bootstrap-template.ldif
|
||||
+++ b/install/share/bootstrap-template.ldif
|
||||
@@ -239,6 +239,7 @@ objectClass: ipasshuser
|
||||
uid: admin
|
||||
krbPrincipalName: admin@$REALM
|
||||
krbPrincipalName: root@$REALM
|
||||
+krbCanonicalName: admin@$REALM
|
||||
cn: Administrator
|
||||
sn: Administrator
|
||||
uidNumber: $IDSTART
|
||||
diff --git a/install/updates/90-post_upgrade_plugins.update b/install/updates/90-post_upgrade_plugins.update
|
||||
index 7c3bba3e0..3d78c7b5a 100644
|
||||
--- a/install/updates/90-post_upgrade_plugins.update
|
||||
+++ b/install/updates/90-post_upgrade_plugins.update
|
||||
@@ -25,6 +25,7 @@ plugin: update_mapping_Guests_to_nobody
|
||||
plugin: fix_kra_people_entry
|
||||
plugin: update_pwpolicy
|
||||
plugin: update_pwpolicy_grace
|
||||
+plugin: add_admin_krbcanonicalname
|
||||
|
||||
# last
|
||||
# DNS version 1
|
||||
diff --git a/ipaserver/install/plugins/add_admin_krbcanonicalname.py b/ipaserver/install/plugins/add_admin_krbcanonicalname.py
|
||||
new file mode 100644
|
||||
index 000000000..e9ffdf55a
|
||||
--- /dev/null
|
||||
+++ b/ipaserver/install/plugins/add_admin_krbcanonicalname.py
|
||||
@@ -0,0 +1,79 @@
|
||||
+#
|
||||
+# Copyright (C) 2025 FreeIPA Contributors see COPYING for license
|
||||
+#
|
||||
+
|
||||
+from __future__ import absolute_import
|
||||
+
|
||||
+import logging
|
||||
+
|
||||
+from ipalib import errors
|
||||
+from ipalib import Registry
|
||||
+from ipalib import Updater
|
||||
+from ipapython.dn import DN
|
||||
+
|
||||
+logger = logging.getLogger(__name__)
|
||||
+
|
||||
+register = Registry()
|
||||
+
|
||||
+
|
||||
+@register()
|
||||
+class add_admin_krbcanonicalname(Updater):
|
||||
+ """
|
||||
+ Ensures that only the admin user has the krbCanonicalName of
|
||||
+ admin@$REALM.
|
||||
+ """
|
||||
+
|
||||
+ def execute(self, **options):
|
||||
+ ldap = self.api.Backend.ldap2
|
||||
+
|
||||
+ search_filter = (
|
||||
+ "(krbcanonicalname=admin@{})".format(self.api.env.realm))
|
||||
+ try:
|
||||
+ (entries, _truncated) = ldap.find_entries(
|
||||
+ filter=search_filter, base_dn=self.api.env.basedn,
|
||||
+ time_limit=0, size_limit=0)
|
||||
+ except errors.EmptyResult:
|
||||
+ logger.debug("add_admin_krbcanonicalname: No user set with "
|
||||
+ "admin krbcanonicalname")
|
||||
+ entries = []
|
||||
+ # fall through
|
||||
+ except errors.ExecutionError as e:
|
||||
+ logger.error("add_admin_krbcanonicalname: Can not get list "
|
||||
+ "of krbcanonicalname: %s", e)
|
||||
+ return False, []
|
||||
+
|
||||
+ admin_set = False
|
||||
+ # admin should be only user with admin@ as krbcanonicalname
|
||||
+ # It has a uniquness setting so there can be only one, we
|
||||
+ # just didn't automatically set it for admin.
|
||||
+ for entry in entries:
|
||||
+ if entry.single_value.get('uid') != 'admin':
|
||||
+ logger.critical(
|
||||
+ "add_admin_krbcanonicalname: "
|
||||
+ "entry %s has a krbcanonicalname of admin. Removing.",
|
||||
+ entry.dn)
|
||||
+ del entry['krbcanonicalname']
|
||||
+ ldap.update_entry(entry)
|
||||
+ else:
|
||||
+ admin_set = True
|
||||
+
|
||||
+ if not admin_set:
|
||||
+ dn = DN(
|
||||
+ ('uid', 'admin'),
|
||||
+ self.api.env.container_user,
|
||||
+ self.api.env.basedn)
|
||||
+ entry = ldap.get_entry(dn)
|
||||
+ entry['krbcanonicalname'] = 'admin@%s' % self.api.env.realm
|
||||
+ try:
|
||||
+ ldap.update_entry(entry)
|
||||
+ except errors.DuplicateEntry:
|
||||
+ logger.critical(
|
||||
+ "add_admin_krbcanonicalname: "
|
||||
+ "Failed to set krbcanonicalname on admin. It is set "
|
||||
+ "on another entry.")
|
||||
+ except errors.ExecutionError as e:
|
||||
+ logger.critical(
|
||||
+ "add_admin_krbcanonicalname: "
|
||||
+ "Failed to set krbcanonicalname on admin: %s", e)
|
||||
+
|
||||
+ return False, []
|
||||
diff --git a/ipatests/test_integration/test_commands.py b/ipatests/test_integration/test_commands.py
|
||||
index 621982c4f..1526a6e0d 100644
|
||||
--- a/ipatests/test_integration/test_commands.py
|
||||
+++ b/ipatests/test_integration/test_commands.py
|
||||
@@ -1796,6 +1796,44 @@ class TestIPACommandWithoutReplica(IntegrationTest):
|
||||
assert result.returncode == 1
|
||||
assert 'cannot be deleted or disabled' in result.stderr_text
|
||||
|
||||
+ def test_unique_krbcanonicalname(self):
|
||||
+ """Verify that the uniqueness for krbcanonicalname is working"""
|
||||
+ master = self.master
|
||||
+
|
||||
+ base_dn = str(master.domain.basedn)
|
||||
+ hostname = master.hostname
|
||||
+ realm = master.domain.realm
|
||||
+ principal = f'test/{hostname}@{realm}'
|
||||
+ entry_ldif = textwrap.dedent("""
|
||||
+ dn: krbprincipalname={principal},cn=services,cn=accounts,{base_dn}
|
||||
+ changetype: add
|
||||
+ ipakrbprincipalalias: test/{hostname}@{realm}
|
||||
+ krbprincipalname: {principal}
|
||||
+ objectclass: ipakrbprincipal
|
||||
+ objectclass: ipaobject
|
||||
+ objectclass: ipaservice
|
||||
+ objectclass: krbprincipal
|
||||
+ objectclass: krbprincipalaux
|
||||
+ objectclass: top
|
||||
+ krbcanonicalname: admin@{realm}
|
||||
+ managedby: fqdn={hostname},cn=computers,cn=accounts,{base_dn}
|
||||
+ """).format(
|
||||
+ base_dn=base_dn,
|
||||
+ hostname=hostname,
|
||||
+ principal=principal,
|
||||
+ realm=realm)
|
||||
+ tasks.kdestroy_all(master)
|
||||
+ master.run_command(
|
||||
+ ['kinit', '-kt', '/etc/krb5.keytab', f'host/{hostname}@{realm}'])
|
||||
+ args = [
|
||||
+ 'ldapmodify',
|
||||
+ '-Y',
|
||||
+ 'GSSAPI'
|
||||
+ ]
|
||||
+ result = master.run_command(args, stdin_text=entry_ldif,
|
||||
+ raiseonerr=False)
|
||||
+ assert "entry with the same attribute value" in result.stderr_text
|
||||
+
|
||||
|
||||
class TestIPACommandWithoutReplica(IntegrationTest):
|
||||
"""
|
||||
--
|
||||
2.48.1
|
||||
|
||||
|
||||
@ -1,106 +0,0 @@
|
||||
From a37de8c22976a75caf969e232229ff6521ff4936 Mon Sep 17 00:00:00 2001
|
||||
From: Rob Crittenden <rcritten@redhat.com>
|
||||
Date: Thu, 10 Jul 2025 11:44:36 -0400
|
||||
Subject: [PATCH] Enforce uniqueness across krbprincipalname and
|
||||
krbcanonicalname
|
||||
|
||||
This relies on a fix in 389-ds that extends the uniqueness plugin
|
||||
to be able to compare attributes with different matching syntax.
|
||||
|
||||
This will prevent privilege escalation attacks if one of the
|
||||
attributes is not set on an entry if it is set elsewhere.
|
||||
|
||||
Signed-off-by: Rob Crittenden <rcritten@redhat.com>
|
||||
---
|
||||
install/share/unique-attributes.ldif | 28 +++++-----------------------
|
||||
install/updates/10-uniqueness.update | 27 +++++++++++++++++++++++----
|
||||
2 files changed, 28 insertions(+), 27 deletions(-)
|
||||
|
||||
diff --git a/install/share/unique-attributes.ldif b/install/share/unique-attributes.ldif
|
||||
index 60f2c3470..b28d981b5 100644
|
||||
--- a/install/share/unique-attributes.ldif
|
||||
+++ b/install/share/unique-attributes.ldif
|
||||
@@ -1,34 +1,16 @@
|
||||
-dn: cn=krbPrincipalName uniqueness,cn=plugins,cn=config
|
||||
+dn: cn=kerberos name uniqueness,cn=plugins,cn=config
|
||||
changetype: add
|
||||
objectClass: top
|
||||
objectClass: nsSlapdPlugin
|
||||
objectClass: extensibleObject
|
||||
-cn: krbPrincipalName uniqueness
|
||||
+cn: kerberos name uniqueness
|
||||
nsslapd-pluginPath: libattr-unique-plugin
|
||||
nsslapd-pluginInitfunc: NSUniqueAttr_Init
|
||||
nsslapd-pluginType: preoperation
|
||||
nsslapd-pluginEnabled: on
|
||||
-uniqueness-attribute-name: krbPrincipalName
|
||||
-nsslapd-plugin-depends-on-type: database
|
||||
-nsslapd-pluginId: NSUniqueAttr
|
||||
-nsslapd-pluginVersion: 1.1.0
|
||||
-nsslapd-pluginVendor: Fedora Project
|
||||
-nsslapd-pluginDescription: Enforce unique attribute values
|
||||
-uniqueness-subtrees: $SUFFIX
|
||||
-uniqueness-exclude-subtrees: cn=staged users,cn=accounts,cn=provisioning,$SUFFIX
|
||||
-uniqueness-across-all-subtrees: on
|
||||
-
|
||||
-dn: cn=krbCanonicalName uniqueness,cn=plugins,cn=config
|
||||
-changetype: add
|
||||
-objectClass: top
|
||||
-objectClass: nsSlapdPlugin
|
||||
-objectClass: extensibleObject
|
||||
-cn: krbCanonicalName uniqueness
|
||||
-nsslapd-pluginPath: libattr-unique-plugin
|
||||
-nsslapd-pluginInitfunc: NSUniqueAttr_Init
|
||||
-nsslapd-pluginType: preoperation
|
||||
-nsslapd-pluginEnabled: on
|
||||
-uniqueness-attribute-name: krbCanonicalName
|
||||
+uniqueness-attribute-name: krbPrincipalName:CaseIgnoreMatch:
|
||||
+uniqueness-attribute-name: krbPrincipalAlias:CaseIgnoreMatch:
|
||||
+uniqueness-attribute-name: krbCanonicalName:CaseIgnoreMatch:
|
||||
nsslapd-plugin-depends-on-type: database
|
||||
nsslapd-pluginId: NSUniqueAttr
|
||||
nsslapd-pluginVersion: 1.1.0
|
||||
diff --git a/install/updates/10-uniqueness.update b/install/updates/10-uniqueness.update
|
||||
index fa17911f2..5c5bfd3e0 100644
|
||||
--- a/install/updates/10-uniqueness.update
|
||||
+++ b/install/updates/10-uniqueness.update
|
||||
@@ -63,13 +63,32 @@ add:uniqueness-subtree-entries-oc: posixAccount
|
||||
|
||||
# krbPrincipalName uniqueness scopes Active/Delete containers
|
||||
dn: cn=krbPrincipalName uniqueness,cn=plugins,cn=config
|
||||
-add:uniqueness-exclude-subtrees: cn=staged users,cn=accounts,cn=provisioning,$SUFFIX
|
||||
-add:uniqueness-across-all-subtrees: on
|
||||
+deleteentry: cn=krbPrincipalName uniqueness,cn=plugins,cn=config
|
||||
|
||||
# krbCanonicalName uniqueness scopes Active/Delete containers
|
||||
dn: cn=krbCanonicalName uniqueness,cn=plugins,cn=config
|
||||
-add:uniqueness-exclude-subtrees: cn=staged users,cn=accounts,cn=provisioning,$SUFFIX
|
||||
-add:uniqueness-across-all-subtrees: on
|
||||
+deleteentry: dn: cn=krbCanonicalName uniqueness,cn=plugins,cn=config
|
||||
+
|
||||
+dn: cn=kerberos name uniqueness,cn=plugins,cn=config
|
||||
+default:objectClass: top
|
||||
+default:objectClass: nsSlapdPlugin
|
||||
+default:objectClass: extensibleObject
|
||||
+default:cn: kerberos name uniqueness
|
||||
+default:nsslapd-pluginPath: libattr-unique-plugin
|
||||
+default:nsslapd-pluginInitfunc: NSUniqueAttr_Init
|
||||
+default:nsslapd-pluginType: preoperation
|
||||
+default:nsslapd-pluginEnabled: on
|
||||
+default:uniqueness-attribute-name: krbPrincipalName:CaseIgnoreMatch:
|
||||
+default:uniqueness-attribute-name: krbPrincipalAlias:CaseIgnoreMatch:
|
||||
+default:uniqueness-attribute-name: krbCanonicalName:CaseIgnoreMatch:
|
||||
+default:nsslapd-plugin-depends-on-type: database
|
||||
+default:nsslapd-pluginId: NSUniqueAttr
|
||||
+default:nsslapd-pluginVersion: 1.1.0
|
||||
+default:nsslapd-pluginVendor: Fedora Project
|
||||
+default:nsslapd-pluginDescription: Enforce unique attribute values
|
||||
+default:uniqueness-subtrees: $SUFFIX
|
||||
+default:uniqueness-exclude-subtrees: cn=staged users,cn=accounts,cn=provisioning,$SUFFIX
|
||||
+default:uniqueness-across-all-subtrees: on
|
||||
|
||||
# ipaUniqueID uniqueness scopes Active/Delete containers
|
||||
dn: cn=ipaUniqueID uniqueness,cn=plugins,cn=config
|
||||
--
|
||||
2.50.1
|
||||
|
||||
@ -1,230 +0,0 @@
|
||||
From 4784cb826f7cfd01471c29cfb51bdf6d34d6d643 Mon Sep 17 00:00:00 2001
|
||||
From: Julien Rische <jrische@redhat.com>
|
||||
Date: Tue, 9 Sep 2025 12:45:24 -0300
|
||||
Subject: [PATCH] ipa-kdb: enforce PAC presence on TGT for TGS-REQ
|
||||
|
||||
MS-KILE's PA-PAC-REQUEST sequence allows the Kerberos client to request
|
||||
a TGT without a PAC. At the moment, there is no way to configure the MIT
|
||||
KDC to reject such request.
|
||||
|
||||
This commit enforces the presence of the PAC when processing TGTs
|
||||
provided by TGS-REQ. It ensures the server principal of the TGT is the
|
||||
same as the one in PAC_CLIENT_INFO (i.e. enforces client principal
|
||||
canonicalization) with integrity check.
|
||||
|
||||
Only one exception is applied: this check is skipped for local TGTs on
|
||||
domain where the MS-PAC generator is not initialized (i.e. domains where
|
||||
SID generation was not executed yet).
|
||||
|
||||
Signed-off-by: Julien Rische <jrische@redhat.com>
|
||||
---
|
||||
daemons/ipa-kdb/ipa_kdb.h | 9 +++
|
||||
daemons/ipa-kdb/ipa_kdb_common.c | 18 ++++++
|
||||
daemons/ipa-kdb/ipa_kdb_kdcpolicy.c | 2 +-
|
||||
daemons/ipa-kdb/ipa_kdb_mspac.c | 87 ++++++++++++++++++++++++++++
|
||||
daemons/ipa-kdb/ipa_kdb_principals.c | 21 +------
|
||||
5 files changed, 116 insertions(+), 21 deletions(-)
|
||||
|
||||
diff --git a/daemons/ipa-kdb/ipa_kdb.h b/daemons/ipa-kdb/ipa_kdb.h
|
||||
index 85cabe142..7bad8c85f 100644
|
||||
--- a/daemons/ipa-kdb/ipa_kdb.h
|
||||
+++ b/daemons/ipa-kdb/ipa_kdb.h
|
||||
@@ -434,6 +434,14 @@ ipadb_check_for_bronze_bit_attack(krb5_context context,
|
||||
# endif
|
||||
#endif
|
||||
|
||||
+/* Check the ticket provided in a TGS-REQ. In some situations, the ticket is
|
||||
+ * expected to contain a PAC. If it is not the case, or if the function is
|
||||
+ * enable to decode an authorization-data element, it fails.
|
||||
+ * Any failure should result in the TGS-REQ to be rejected. */
|
||||
+krb5_error_code ipadb_enforce_pac(krb5_context kcontext,
|
||||
+ const krb5_ticket *ticket,
|
||||
+ const char **status);
|
||||
+
|
||||
/* DELEGATION CHECKS */
|
||||
|
||||
krb5_error_code ipadb_check_allowed_to_delegate(krb5_context kcontext,
|
||||
@@ -472,3 +480,4 @@ int ipadb_string_to_sid(const char *str, struct dom_sid *sid);
|
||||
void alloc_sid(struct dom_sid **sid);
|
||||
void free_sid(struct dom_sid **sid);
|
||||
bool dom_sid_check(const struct dom_sid *sid1, const struct dom_sid *sid2, bool exact_check);
|
||||
+bool ipadb_is_tgs_princ(krb5_context kcontext, krb5_const_principal princ);
|
||||
diff --git a/daemons/ipa-kdb/ipa_kdb_common.c b/daemons/ipa-kdb/ipa_kdb_common.c
|
||||
index 42e0856d0..eb0b0d129 100644
|
||||
--- a/daemons/ipa-kdb/ipa_kdb_common.c
|
||||
+++ b/daemons/ipa-kdb/ipa_kdb_common.c
|
||||
@@ -704,3 +704,21 @@ krb5_error_code ipadb_multibase_search(struct ipadb_context *ipactx,
|
||||
return ipadb_simple_ldap_to_kerr(ret);
|
||||
}
|
||||
|
||||
+bool
|
||||
+ipadb_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);
|
||||
+}
|
||||
diff --git a/daemons/ipa-kdb/ipa_kdb_kdcpolicy.c b/daemons/ipa-kdb/ipa_kdb_kdcpolicy.c
|
||||
index d6d618d1d..a92a9a0ad 100644
|
||||
--- a/daemons/ipa-kdb/ipa_kdb_kdcpolicy.c
|
||||
+++ b/daemons/ipa-kdb/ipa_kdb_kdcpolicy.c
|
||||
@@ -207,7 +207,7 @@ ipa_kdcpolicy_check_tgs(krb5_context context, krb5_kdcpolicy_moddata moddata,
|
||||
*lifetime_out = 0;
|
||||
*renew_lifetime_out = 0;
|
||||
|
||||
- return 0;
|
||||
+ return ipadb_enforce_pac(context, ticket, status);
|
||||
}
|
||||
|
||||
krb5_error_code kdcpolicy_ipakdb_initvt(krb5_context context,
|
||||
diff --git a/daemons/ipa-kdb/ipa_kdb_mspac.c b/daemons/ipa-kdb/ipa_kdb_mspac.c
|
||||
index 0964d112a..c4085fca5 100644
|
||||
--- a/daemons/ipa-kdb/ipa_kdb_mspac.c
|
||||
+++ b/daemons/ipa-kdb/ipa_kdb_mspac.c
|
||||
@@ -3344,6 +3344,93 @@ krb5_error_code ipadb_is_princ_from_trusted_realm(krb5_context kcontext,
|
||||
return KRB5_KDB_NOENTRY;
|
||||
}
|
||||
|
||||
+static krb5_error_code
|
||||
+check_for_pac(krb5_context kcontext, krb5_authdata **authdata, bool *pac_present)
|
||||
+{
|
||||
+ krb5_error_code kerr = ENOENT;
|
||||
+ size_t i, j;
|
||||
+ krb5_authdata **ifrel = NULL;
|
||||
+
|
||||
+ for (i = 0; authdata && authdata[i]; ++i) {
|
||||
+ if (authdata[i]->ad_type != KRB5_AUTHDATA_IF_RELEVANT) {
|
||||
+ continue;
|
||||
+ }
|
||||
+
|
||||
+ kerr = krb5_decode_authdata_container(kcontext,
|
||||
+ KRB5_AUTHDATA_IF_RELEVANT,
|
||||
+ authdata[i], &ifrel);
|
||||
+ if (kerr) {
|
||||
+ goto end;
|
||||
+ }
|
||||
+
|
||||
+ for (j = 0; ifrel[j]; ++j) {
|
||||
+ if (ifrel[j]->ad_type == KRB5_AUTHDATA_WIN2K_PAC) {
|
||||
+ break;
|
||||
+ }
|
||||
+ }
|
||||
+ if (ifrel[j]) {
|
||||
+ break;
|
||||
+ }
|
||||
+
|
||||
+ krb5_free_authdata(kcontext, ifrel);
|
||||
+ ifrel = NULL;
|
||||
+ }
|
||||
+
|
||||
+ *pac_present = ifrel;
|
||||
+ kerr = 0;
|
||||
+
|
||||
+end:
|
||||
+ krb5_free_authdata(kcontext, ifrel);
|
||||
+ return kerr;
|
||||
+}
|
||||
+
|
||||
+krb5_error_code
|
||||
+ipadb_enforce_pac(krb5_context kcontext, const krb5_ticket *ticket,
|
||||
+ const char **status)
|
||||
+{
|
||||
+ struct ipadb_context *ipactx;
|
||||
+ bool pac_present;
|
||||
+ krb5_error_code kerr;
|
||||
+
|
||||
+ /* Filter TGTs only */
|
||||
+ if (!ipadb_is_tgs_princ(kcontext, ticket->server)) {
|
||||
+ kerr = 0;
|
||||
+ goto end;
|
||||
+ }
|
||||
+
|
||||
+ /* Get IPA context */
|
||||
+ ipactx = ipadb_get_context(kcontext);
|
||||
+ if (!ipactx) {
|
||||
+ kerr = KRB5_KDB_DBNOTINITED;
|
||||
+ goto end;
|
||||
+ }
|
||||
+
|
||||
+ /* If local TGT but PAC generator not initialized, skip PAC enforcement */
|
||||
+ if (krb5_realm_compare(kcontext, ipactx->local_tgs, ticket->server) &&
|
||||
+ !ipactx->mspac)
|
||||
+ {
|
||||
+ krb5_klog_syslog(LOG_WARNING, "MS-PAC not available. This makes "
|
||||
+ "FreeIPA vulnerable to privilege escalation exploit "
|
||||
+ "(CVE-2025-7493). Please generate SIDs to enable PAC "
|
||||
+ "support.");
|
||||
+ kerr = 0;
|
||||
+ goto end;
|
||||
+ }
|
||||
+
|
||||
+ /* Search for the PAC, fail if it cannot be found */
|
||||
+ kerr = check_for_pac(kcontext, ticket->enc_part2->authorization_data,
|
||||
+ &pac_present);
|
||||
+ if (kerr) {
|
||||
+ *status = "PAC_ENFORCEMENT_CANNOT_DECODE_TGT_AUTHDATA";
|
||||
+ } else if (!pac_present) {
|
||||
+ kerr = ENOENT;
|
||||
+ *status = "PAC_ENFORCEMENT_TGT_WITHOUT_PAC";
|
||||
+ }
|
||||
+
|
||||
+end:
|
||||
+ return kerr;
|
||||
+}
|
||||
+
|
||||
#if KRB5_KDB_DAL_MAJOR_VERSION <= 8
|
||||
# ifdef HAVE_KRB5_PAC_FULL_SIGN_COMPAT
|
||||
krb5_error_code
|
||||
diff --git a/daemons/ipa-kdb/ipa_kdb_principals.c b/daemons/ipa-kdb/ipa_kdb_principals.c
|
||||
index 6ee274053..11e084739 100644
|
||||
--- a/daemons/ipa-kdb/ipa_kdb_principals.c
|
||||
+++ b/daemons/ipa-kdb/ipa_kdb_principals.c
|
||||
@@ -183,25 +183,6 @@ done:
|
||||
return ret;
|
||||
}
|
||||
|
||||
-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 ipadb_set_tl_data(krb5_db_entry *entry,
|
||||
krb5_int16 type,
|
||||
krb5_ui_2 length,
|
||||
@@ -1882,7 +1863,7 @@ krb5_error_code ipadb_get_principal(krb5_context kcontext,
|
||||
|
||||
#if KRB5_KDB_DAL_MAJOR_VERSION <= 8
|
||||
/* If TGS principal, some virtual attributes may be added */
|
||||
- if (is_tgs_princ(kcontext, (*entry)->princ)) {
|
||||
+ if (ipadb_is_tgs_princ(kcontext, (*entry)->princ)) {
|
||||
kerr = krb5_dbe_set_string(kcontext, *entry,
|
||||
KRB5_KDB_SK_OPTIONAL_AD_SIGNEDPATH,
|
||||
"true");
|
||||
--
|
||||
2.51.0
|
||||
|
||||
@ -1,93 +0,0 @@
|
||||
From d57d11974e05f84c0964ca941a6b507419b02211 Mon Sep 17 00:00:00 2001
|
||||
From: Florence Blanc-Renaud <flo@redhat.com>
|
||||
Date: Thu, 28 Aug 2025 15:31:39 +0200
|
||||
Subject: [PATCH] ipatests: extend test for unique krbcanonicalname
|
||||
|
||||
Add a test ensuring that root@REALM cannot be added as
|
||||
krbcanonicalname
|
||||
|
||||
Add a test for PAC enforcement:
|
||||
try to access a service using a TGT obtained without PAC.
|
||||
Should fail as PAC is now enforced.
|
||||
|
||||
Signed-off-by: Florence Blanc-Renaud <flo@redhat.com>
|
||||
---
|
||||
ipatests/test_integration/test_commands.py | 44 ++++++++++++++++++++--
|
||||
1 file changed, 40 insertions(+), 4 deletions(-)
|
||||
|
||||
diff --git a/ipatests/test_integration/test_commands.py b/ipatests/test_integration/test_commands.py
|
||||
index 38202c9a3fbc5e91c03a5953a5d9bec3c07117f4..c982c678aae047d5cb505889729bcb5bccbc3c20 100644
|
||||
--- a/ipatests/test_integration/test_commands.py
|
||||
+++ b/ipatests/test_integration/test_commands.py
|
||||
@@ -1563,7 +1563,7 @@ def test_unique_krbcanonicalname(self):
|
||||
hostname = master.hostname
|
||||
realm = master.domain.realm
|
||||
principal = f'test/{hostname}@{realm}'
|
||||
- entry_ldif = textwrap.dedent("""
|
||||
+ entry_ldif_template = textwrap.dedent("""
|
||||
dn: krbprincipalname={principal},cn=services,cn=accounts,{base_dn}
|
||||
changetype: add
|
||||
ipakrbprincipalalias: test/{hostname}@{realm}
|
||||
@@ -1573,13 +1573,15 @@ def test_unique_krbcanonicalname(self):
|
||||
objectclass: krbprincipal
|
||||
objectclass: krbprincipalaux
|
||||
objectclass: top
|
||||
- krbcanonicalname: admin@{realm}
|
||||
+ krbcanonicalname: {user}@{realm}
|
||||
managedby: fqdn={hostname},cn=computers,cn=accounts,{base_dn}
|
||||
- """).format(
|
||||
+ """)
|
||||
+ entry_ldif = entry_ldif_template.format(
|
||||
base_dn=base_dn,
|
||||
hostname=hostname,
|
||||
principal=principal,
|
||||
- realm=realm)
|
||||
+ realm=realm,
|
||||
+ user='admin')
|
||||
tasks.kdestroy_all(master)
|
||||
master.run_command(
|
||||
['kinit', '-kt', '/etc/krb5.keytab', f'host/{hostname}@{realm}'])
|
||||
@@ -1592,6 +1594,40 @@ def test_unique_krbcanonicalname(self):
|
||||
raiseonerr=False)
|
||||
assert "entry with the same attribute value" in result.stderr_text
|
||||
|
||||
+ # Now try with root@realm instead of admin@realm
|
||||
+ entry_ldif = entry_ldif_template.format(
|
||||
+ base_dn=base_dn,
|
||||
+ hostname=hostname,
|
||||
+ principal=principal,
|
||||
+ realm=realm,
|
||||
+ user='root')
|
||||
+ args = [
|
||||
+ 'ldapmodify',
|
||||
+ '-Y',
|
||||
+ 'GSSAPI'
|
||||
+ ]
|
||||
+ result = master.run_command(args, stdin_text=entry_ldif,
|
||||
+ raiseonerr=False)
|
||||
+ assert "entry with the same attribute value" in result.stderr_text
|
||||
+ tasks.kdestroy_all(master)
|
||||
+
|
||||
+ def test_no_request_pac(self):
|
||||
+ # Try to use a TGT obtained without PAC
|
||||
+ # Should fail as the presence of the PAC when processing TGTs
|
||||
+ # provided by TGS-REQ is now enforced.
|
||||
+ hostname = self.master.hostname
|
||||
+ realm = self.master.domain.realm
|
||||
+ self.master.run_command([
|
||||
+ 'kinit', '-kt', '/etc/krb5.keytab', f'host/{hostname}@{realm}',
|
||||
+ '--no-request-pac'
|
||||
+ ])
|
||||
+ result = self.master.run_command(
|
||||
+ ['kvno', f'ldap/{hostname}@{realm}'],
|
||||
+ raiseonerr=False
|
||||
+ )
|
||||
+ assert result.returncode == 1
|
||||
+ assert "PAC_ENFORCEMENT_TGT_WITHOUT_PAC" in result.stderr_text
|
||||
+
|
||||
|
||||
class TestIPACommandWithoutReplica(IntegrationTest):
|
||||
"""
|
||||
--
|
||||
2.51.0
|
||||
|
||||
@ -1,173 +0,0 @@
|
||||
--- a/ipatests/test_integration/test_commands.py 2025-09-17 10:36:00.180673487 -0300
|
||||
+++ b/ipatests/test_integration/test_commands.py 2025-09-17 10:37:31.294681273 -0300
|
||||
@@ -1554,80 +1554,6 @@
|
||||
assert result.returncode == 1
|
||||
assert 'cannot be deleted or disabled' in result.stderr_text
|
||||
|
||||
- def test_unique_krbcanonicalname(self):
|
||||
- """Verify that the uniqueness for krbcanonicalname is working"""
|
||||
- master = self.master
|
||||
-
|
||||
- base_dn = str(master.domain.basedn)
|
||||
- hostname = master.hostname
|
||||
- realm = master.domain.realm
|
||||
- principal = f'test/{hostname}@{realm}'
|
||||
- entry_ldif_template = textwrap.dedent("""
|
||||
- dn: krbprincipalname={principal},cn=services,cn=accounts,{base_dn}
|
||||
- changetype: add
|
||||
- ipakrbprincipalalias: test/{hostname}@{realm}
|
||||
- krbprincipalname: {principal}
|
||||
- objectclass: ipakrbprincipal
|
||||
- objectclass: ipaobject
|
||||
- objectclass: ipaservice
|
||||
- objectclass: krbprincipal
|
||||
- objectclass: krbprincipalaux
|
||||
- objectclass: top
|
||||
- krbcanonicalname: {user}@{realm}
|
||||
- managedby: fqdn={hostname},cn=computers,cn=accounts,{base_dn}
|
||||
- """)
|
||||
- entry_ldif = entry_ldif_template.format(
|
||||
- base_dn=base_dn,
|
||||
- hostname=hostname,
|
||||
- principal=principal,
|
||||
- realm=realm,
|
||||
- user='admin')
|
||||
- tasks.kdestroy_all(master)
|
||||
- master.run_command(
|
||||
- ['kinit', '-kt', '/etc/krb5.keytab', f'host/{hostname}@{realm}'])
|
||||
- args = [
|
||||
- 'ldapmodify',
|
||||
- '-Y',
|
||||
- 'GSSAPI'
|
||||
- ]
|
||||
- result = master.run_command(args, stdin_text=entry_ldif,
|
||||
- raiseonerr=False)
|
||||
- assert "entry with the same attribute value" in result.stderr_text
|
||||
-
|
||||
- # Now try with root@realm instead of admin@realm
|
||||
- entry_ldif = entry_ldif_template.format(
|
||||
- base_dn=base_dn,
|
||||
- hostname=hostname,
|
||||
- principal=principal,
|
||||
- realm=realm,
|
||||
- user='root')
|
||||
- args = [
|
||||
- 'ldapmodify',
|
||||
- '-Y',
|
||||
- 'GSSAPI'
|
||||
- ]
|
||||
- result = master.run_command(args, stdin_text=entry_ldif,
|
||||
- raiseonerr=False)
|
||||
- assert "entry with the same attribute value" in result.stderr_text
|
||||
- tasks.kdestroy_all(master)
|
||||
-
|
||||
- def test_no_request_pac(self):
|
||||
- # Try to use a TGT obtained without PAC
|
||||
- # Should fail as the presence of the PAC when processing TGTs
|
||||
- # provided by TGS-REQ is now enforced.
|
||||
- hostname = self.master.hostname
|
||||
- realm = self.master.domain.realm
|
||||
- self.master.run_command([
|
||||
- 'kinit', '-kt', '/etc/krb5.keytab', f'host/{hostname}@{realm}',
|
||||
- '--no-request-pac'
|
||||
- ])
|
||||
- result = self.master.run_command(
|
||||
- ['kvno', f'ldap/{hostname}@{realm}'],
|
||||
- raiseonerr=False
|
||||
- )
|
||||
- assert result.returncode == 1
|
||||
- assert "PAC_ENFORCEMENT_TGT_WITHOUT_PAC" in result.stderr_text
|
||||
-
|
||||
|
||||
class TestIPACommandWithoutReplica(IntegrationTest):
|
||||
"""
|
||||
@@ -1749,7 +1675,7 @@
|
||||
api.bootstrap_with_global_options(context='server')
|
||||
api.finalize()
|
||||
api.Backend.ldap2.connect()
|
||||
-
|
||||
+
|
||||
api.Command["group_add"]("testgroup1", external=True)
|
||||
api.Command["group_add"]("testgroup2", external=False)
|
||||
result1 = api.Command["group_show"]("testgroup1", all=True)["result"] # noqa: E501
|
||||
@@ -1794,6 +1720,80 @@
|
||||
'/tmp/reproducer2_code.py'])
|
||||
assert "missing attribute" not in result.stdout_text
|
||||
|
||||
+ def test_unique_krbcanonicalname(self):
|
||||
+ """Verify that the uniqueness for krbcanonicalname is working"""
|
||||
+ master = self.master
|
||||
+
|
||||
+ base_dn = str(master.domain.basedn)
|
||||
+ hostname = master.hostname
|
||||
+ realm = master.domain.realm
|
||||
+ principal = f'test/{hostname}@{realm}'
|
||||
+ entry_ldif_template = textwrap.dedent("""
|
||||
+ dn: krbprincipalname={principal},cn=services,cn=accounts,{base_dn}
|
||||
+ changetype: add
|
||||
+ ipakrbprincipalalias: test/{hostname}@{realm}
|
||||
+ krbprincipalname: {principal}
|
||||
+ objectclass: ipakrbprincipal
|
||||
+ objectclass: ipaobject
|
||||
+ objectclass: ipaservice
|
||||
+ objectclass: krbprincipal
|
||||
+ objectclass: krbprincipalaux
|
||||
+ objectclass: top
|
||||
+ krbcanonicalname: {user}@{realm}
|
||||
+ managedby: fqdn={hostname},cn=computers,cn=accounts,{base_dn}
|
||||
+ """)
|
||||
+ entry_ldif = entry_ldif_template.format(
|
||||
+ base_dn=base_dn,
|
||||
+ hostname=hostname,
|
||||
+ principal=principal,
|
||||
+ realm=realm,
|
||||
+ user='admin')
|
||||
+ tasks.kdestroy_all(master)
|
||||
+ master.run_command(
|
||||
+ ['kinit', '-kt', '/etc/krb5.keytab', f'host/{hostname}@{realm}'])
|
||||
+ args = [
|
||||
+ 'ldapmodify',
|
||||
+ '-Y',
|
||||
+ 'GSSAPI'
|
||||
+ ]
|
||||
+ result = master.run_command(args, stdin_text=entry_ldif,
|
||||
+ raiseonerr=False)
|
||||
+ assert "entry with the same attribute value" in result.stderr_text
|
||||
+
|
||||
+ # Now try with root@realm instead of admin@realm
|
||||
+ entry_ldif = entry_ldif_template.format(
|
||||
+ base_dn=base_dn,
|
||||
+ hostname=hostname,
|
||||
+ principal=principal,
|
||||
+ realm=realm,
|
||||
+ user='root')
|
||||
+ args = [
|
||||
+ 'ldapmodify',
|
||||
+ '-Y',
|
||||
+ 'GSSAPI'
|
||||
+ ]
|
||||
+ result = master.run_command(args, stdin_text=entry_ldif,
|
||||
+ raiseonerr=False)
|
||||
+ assert "entry with the same attribute value" in result.stderr_text
|
||||
+ tasks.kdestroy_all(master)
|
||||
+
|
||||
+ def test_no_request_pac(self):
|
||||
+ # Try to use a TGT obtained without PAC
|
||||
+ # Should fail as the presence of the PAC when processing TGTs
|
||||
+ # provided by TGS-REQ is now enforced.
|
||||
+ hostname = self.master.hostname
|
||||
+ realm = self.master.domain.realm
|
||||
+ self.master.run_command([
|
||||
+ 'kinit', '-kt', '/etc/krb5.keytab', f'host/{hostname}@{realm}',
|
||||
+ '--no-request-pac'
|
||||
+ ])
|
||||
+ result = self.master.run_command(
|
||||
+ ['kvno', f'ldap/{hostname}@{realm}'],
|
||||
+ raiseonerr=False
|
||||
+ )
|
||||
+ assert result.returncode == 1
|
||||
+ assert "PAC_ENFORCEMENT_TGT_WITHOUT_PAC" in result.stderr_text
|
||||
+
|
||||
|
||||
class TestIPAautomount(IntegrationTest):
|
||||
@classmethod
|
||||
@ -1,911 +0,0 @@
|
||||
From aef72550a252d43423b99a179cb1e2ca3c2965e0 Mon Sep 17 00:00:00 2001
|
||||
From: Viktor Ashirov <vashirov@redhat.com>
|
||||
Date: Sat, 8 Nov 2025 00:40:42 +0100
|
||||
Subject: [PATCH 01/16] ipa-graceperiod: fix memory leaks
|
||||
|
||||
Direct return of invalid grace limit bypassed cleanup code.
|
||||
`tmpstr` variable was not freed in all code paths.
|
||||
|
||||
Related: https://pagure.io/freeipa/issue/9895
|
||||
Signed-off-by: Viktor Ashirov <vashirov@redhat.com>
|
||||
Reviewed-By: Rob Crittenden <rcritten@redhat.com>
|
||||
Reviewed-By: Alexander Bokovoy <abbra@users.noreply.github.com>
|
||||
---
|
||||
daemons/ipa-slapi-plugins/ipa-graceperiod/ipa_graceperiod.c | 4 +++-
|
||||
1 file changed, 3 insertions(+), 1 deletion(-)
|
||||
|
||||
diff --git a/daemons/ipa-slapi-plugins/ipa-graceperiod/ipa_graceperiod.c b/daemons/ipa-slapi-plugins/ipa-graceperiod/ipa_graceperiod.c
|
||||
index 345e1dee7..2912d5eb3 100644
|
||||
--- a/daemons/ipa-slapi-plugins/ipa-graceperiod/ipa_graceperiod.c
|
||||
+++ b/daemons/ipa-slapi-plugins/ipa-graceperiod/ipa_graceperiod.c
|
||||
@@ -448,7 +448,8 @@ static int ipagraceperiod_preop(Slapi_PBlock *pb)
|
||||
goto done;
|
||||
} else if (grace_limit < -1) {
|
||||
LOG_FATAL("Invalid passwordGraceLimit value %d\n", grace_limit);
|
||||
- return LDAP_OPERATIONS_ERROR;
|
||||
+ ret = LDAP_OPERATIONS_ERROR;
|
||||
+ goto done;
|
||||
}
|
||||
|
||||
grace_user_time = slapi_entry_attr_get_int(target_entry, "passwordGraceUserTime");
|
||||
@@ -500,6 +501,7 @@ done:
|
||||
slapi_vattr_values_free(&values, &actual_type_name, attr_free_flags);
|
||||
}
|
||||
if (sdn) slapi_sdn_free(&sdn);
|
||||
+ slapi_ch_free_string(&tmpstr);
|
||||
|
||||
LOG("preop returning %d: %s\n", ret, errstr ? errstr : "success\n");
|
||||
|
||||
--
|
||||
2.51.0
|
||||
|
||||
|
||||
From 21471d2c34942bd2ef00850f22102f2006ec62ee Mon Sep 17 00:00:00 2001
|
||||
From: Viktor Ashirov <vashirov@redhat.com>
|
||||
Date: Sat, 8 Nov 2025 00:44:35 +0100
|
||||
Subject: [PATCH 02/16] ipa-lockout: fix memory leaks
|
||||
|
||||
Move cleanup of `unlock_time` to `done` label to ensure cleanup in all code paths.
|
||||
|
||||
Related: https://pagure.io/freeipa/issue/9895
|
||||
Signed-off-by: Viktor Ashirov <vashirov@redhat.com>
|
||||
Reviewed-By: Rob Crittenden <rcritten@redhat.com>
|
||||
Reviewed-By: Alexander Bokovoy <abbra@users.noreply.github.com>
|
||||
---
|
||||
daemons/ipa-slapi-plugins/ipa-lockout/ipa_lockout.c | 2 +-
|
||||
1 file changed, 1 insertion(+), 1 deletion(-)
|
||||
|
||||
diff --git a/daemons/ipa-slapi-plugins/ipa-lockout/ipa_lockout.c b/daemons/ipa-slapi-plugins/ipa-lockout/ipa_lockout.c
|
||||
index a8095ccd3..9b157bcd3 100644
|
||||
--- a/daemons/ipa-slapi-plugins/ipa-lockout/ipa_lockout.c
|
||||
+++ b/daemons/ipa-slapi-plugins/ipa-lockout/ipa_lockout.c
|
||||
@@ -812,7 +812,6 @@ static int ipalockout_preop(Slapi_PBlock *pb)
|
||||
goto done;
|
||||
}
|
||||
}
|
||||
- slapi_ch_free_string(&unlock_time);
|
||||
}
|
||||
|
||||
max_fail = slapi_entry_attr_get_uint(policy_entry, "krbPwdMaxFailure");
|
||||
@@ -837,6 +836,7 @@ static int ipalockout_preop(Slapi_PBlock *pb)
|
||||
|
||||
done:
|
||||
if (lastfail) slapi_ch_free_string(&lastfail);
|
||||
+ if (unlock_time) slapi_ch_free_string(&unlock_time);
|
||||
slapi_entry_free(target_entry);
|
||||
slapi_entry_free(policy_entry);
|
||||
if (values != NULL) {
|
||||
--
|
||||
2.51.0
|
||||
|
||||
|
||||
From c84d394fc273baeafd6e56b2d2fc1b5f3a0c363b Mon Sep 17 00:00:00 2001
|
||||
From: Viktor Ashirov <vashirov@redhat.com>
|
||||
Date: Sat, 8 Nov 2025 00:46:35 +0100
|
||||
Subject: [PATCH 03/16] ipa-pwd-extop: fix memory leaks
|
||||
|
||||
`cur_pw` was allocated but not freed after password validation.
|
||||
`principal_expire` was allocated but not freed in all code paths.
|
||||
|
||||
Related: https://pagure.io/freeipa/issue/9895
|
||||
Signed-off-by: Viktor Ashirov <vashirov@redhat.com>
|
||||
Reviewed-By: Rob Crittenden <rcritten@redhat.com>
|
||||
Reviewed-By: Alexander Bokovoy <abbra@users.noreply.github.com>
|
||||
---
|
||||
daemons/ipa-slapi-plugins/ipa-pwd-extop/ipa_pwd_extop.c | 1 +
|
||||
daemons/ipa-slapi-plugins/ipa-pwd-extop/prepost.c | 4 ++++
|
||||
2 files changed, 5 insertions(+)
|
||||
|
||||
diff --git a/daemons/ipa-slapi-plugins/ipa-pwd-extop/ipa_pwd_extop.c b/daemons/ipa-slapi-plugins/ipa-pwd-extop/ipa_pwd_extop.c
|
||||
index 989f2a02e..94e0f9c70 100644
|
||||
--- a/daemons/ipa-slapi-plugins/ipa-pwd-extop/ipa_pwd_extop.c
|
||||
+++ b/daemons/ipa-slapi-plugins/ipa-pwd-extop/ipa_pwd_extop.c
|
||||
@@ -483,6 +483,7 @@ parse_req_done:
|
||||
|
||||
slapi_value_free(&cpw[0]);
|
||||
slapi_value_free(&pw);
|
||||
+ slapi_ch_free_string(&cur_pw);
|
||||
|
||||
if (ret != 0) {
|
||||
LOG_TRACE("Invalid password!\n");
|
||||
diff --git a/daemons/ipa-slapi-plugins/ipa-pwd-extop/prepost.c b/daemons/ipa-slapi-plugins/ipa-pwd-extop/prepost.c
|
||||
index 45626523f..7fab55c84 100644
|
||||
--- a/daemons/ipa-slapi-plugins/ipa-pwd-extop/prepost.c
|
||||
+++ b/daemons/ipa-slapi-plugins/ipa-pwd-extop/prepost.c
|
||||
@@ -1489,6 +1489,7 @@ static int ipapwd_pre_bind(Slapi_PBlock *pb)
|
||||
|
||||
if (current_time > expire_time && expire_time > 0) {
|
||||
LOG_FATAL("kerberos principal in %s is expired\n", dn);
|
||||
+ slapi_ch_free_string(&principal_expire);
|
||||
slapi_entry_free(entry);
|
||||
slapi_sdn_free(&sdn);
|
||||
slapi_send_ldap_result(pb, LDAP_UNWILLING_TO_PERFORM, NULL,
|
||||
@@ -1521,6 +1522,7 @@ static int ipapwd_pre_bind(Slapi_PBlock *pb)
|
||||
/* Authenticate the user. */
|
||||
ret = ipapwd_authenticate(dn, entry, credentials);
|
||||
if (ret) {
|
||||
+ slapi_ch_free_string(&principal_expire);
|
||||
slapi_entry_free(entry);
|
||||
slapi_sdn_free(&sdn);
|
||||
return 0;
|
||||
@@ -1533,11 +1535,13 @@ static int ipapwd_pre_bind(Slapi_PBlock *pb)
|
||||
/* Attempt to write out kerberos keys for the user. */
|
||||
ipapwd_write_krb_keys(pb, discard_const(dn), entry, credentials);
|
||||
|
||||
+ slapi_ch_free_string(&principal_expire);
|
||||
slapi_entry_free(entry);
|
||||
slapi_sdn_free(&sdn);
|
||||
return 0;
|
||||
|
||||
invalid_creds:
|
||||
+ slapi_ch_free_string(&principal_expire);
|
||||
slapi_entry_free(entry);
|
||||
slapi_sdn_free(&sdn);
|
||||
slapi_send_ldap_result(pb, rc, NULL, errMesg, 0, NULL);
|
||||
--
|
||||
2.51.0
|
||||
|
||||
|
||||
From d26414276d678b0624904ad472a6c8b6e7dde980 Mon Sep 17 00:00:00 2001
|
||||
From: Viktor Ashirov <vashirov@redhat.com>
|
||||
Date: Sat, 8 Nov 2025 00:48:33 +0100
|
||||
Subject: [PATCH 04/16] ipa-sidgen: fix memory leaks
|
||||
|
||||
In various code paths the `ctx` structure was freed, but not
|
||||
`ctx->base_dn` which may have been allocated.
|
||||
|
||||
`sid` was duplicated, but the original memory was never freed.
|
||||
|
||||
Related: https://pagure.io/freeipa/issue/9895
|
||||
Signed-off-by: Viktor Ashirov <vashirov@redhat.com>
|
||||
Reviewed-By: Rob Crittenden <rcritten@redhat.com>
|
||||
Reviewed-By: Alexander Bokovoy <abbra@users.noreply.github.com>
|
||||
---
|
||||
daemons/ipa-slapi-plugins/ipa-sidgen/ipa_sidgen.c | 9 ++++++++-
|
||||
daemons/ipa-slapi-plugins/ipa-sidgen/ipa_sidgen.h | 2 +-
|
||||
daemons/ipa-slapi-plugins/ipa-sidgen/ipa_sidgen_common.c | 3 ++-
|
||||
3 files changed, 11 insertions(+), 3 deletions(-)
|
||||
|
||||
diff --git a/daemons/ipa-slapi-plugins/ipa-sidgen/ipa_sidgen.c b/daemons/ipa-slapi-plugins/ipa-sidgen/ipa_sidgen.c
|
||||
index 99e6b850b..9418ec303 100644
|
||||
--- a/daemons/ipa-slapi-plugins/ipa-sidgen/ipa_sidgen.c
|
||||
+++ b/daemons/ipa-slapi-plugins/ipa-sidgen/ipa_sidgen.c
|
||||
@@ -66,6 +66,8 @@ static int ipa_sidgen_close(Slapi_PBlock *pb)
|
||||
if (ret == 0) {
|
||||
free_ranges(&ctx->ranges);
|
||||
slapi_ch_free_string(&ctx->dom_sid);
|
||||
+ slapi_ch_free_string(&ctx->base_dn);
|
||||
+ free(ctx);
|
||||
} else {
|
||||
LOG_FATAL("Missing private plugin context.\n");
|
||||
}
|
||||
@@ -204,7 +206,10 @@ static int ipa_sidgen_init_ctx(Slapi_PBlock *pb, struct ipa_sidgen_ctx **_ctx)
|
||||
|
||||
done:
|
||||
if (ret != 0) {
|
||||
- free(ctx);
|
||||
+ if (ctx) {
|
||||
+ slapi_ch_free_string(&ctx->base_dn);
|
||||
+ free(ctx);
|
||||
+ }
|
||||
} else {
|
||||
*_ctx = ctx;
|
||||
}
|
||||
@@ -237,6 +242,8 @@ int ipa_sidgen_init(Slapi_PBlock *pb)
|
||||
(void *) ipa_sidgen_add_post_op) != 0 ||
|
||||
slapi_pblock_set(pb, SLAPI_PLUGIN_PRIVATE, ctx) != 0) {
|
||||
LOG_FATAL("failed to register plugin\n");
|
||||
+ slapi_ch_free_string(&ctx->base_dn);
|
||||
+ free(ctx);
|
||||
ret = EFAIL;
|
||||
}
|
||||
|
||||
diff --git a/daemons/ipa-slapi-plugins/ipa-sidgen/ipa_sidgen.h b/daemons/ipa-slapi-plugins/ipa-sidgen/ipa_sidgen.h
|
||||
index aec862796..fbae87e4d 100644
|
||||
--- a/daemons/ipa-slapi-plugins/ipa-sidgen/ipa_sidgen.h
|
||||
+++ b/daemons/ipa-slapi-plugins/ipa-sidgen/ipa_sidgen.h
|
||||
@@ -74,7 +74,7 @@ struct range_info {
|
||||
|
||||
struct ipa_sidgen_ctx {
|
||||
Slapi_ComponentId *plugin_id;
|
||||
- const char *base_dn;
|
||||
+ char *base_dn;
|
||||
char *dom_sid;
|
||||
struct range_info **ranges;
|
||||
};
|
||||
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 13f4de541..b26c2df52 100644
|
||||
--- a/daemons/ipa-slapi-plugins/ipa-sidgen/ipa_sidgen_common.c
|
||||
+++ b/daemons/ipa-slapi-plugins/ipa-sidgen/ipa_sidgen_common.c
|
||||
@@ -51,7 +51,7 @@ int get_dom_sid(Slapi_ComponentId *plugin_id, const char *base_dn, char **_sid)
|
||||
int search_result;
|
||||
Slapi_Entry **search_entries = NULL;
|
||||
int ret;
|
||||
- const char *sid;
|
||||
+ char *sid = NULL;
|
||||
|
||||
search_pb = slapi_pblock_new();
|
||||
if (search_pb == NULL) {
|
||||
@@ -114,6 +114,7 @@ int get_dom_sid(Slapi_ComponentId *plugin_id, const char *base_dn, char **_sid)
|
||||
ret = 0;
|
||||
|
||||
done:
|
||||
+ slapi_ch_free_string(&sid);
|
||||
slapi_free_search_results_internal(search_pb);
|
||||
slapi_pblock_destroy(search_pb);
|
||||
|
||||
--
|
||||
2.51.0
|
||||
|
||||
|
||||
From 144b7c97aa29e8e99fe065d62643848a4eacc515 Mon Sep 17 00:00:00 2001
|
||||
From: Viktor Ashirov <vashirov@redhat.com>
|
||||
Date: Sat, 8 Nov 2025 00:57:49 +0100
|
||||
Subject: [PATCH 05/16] ipa-range-check: fix memory leak
|
||||
|
||||
`ipa_range_check_close` function didn't do any cleanup.
|
||||
The `ctx` structure was freed, but not `ctx->base_dn` which may have
|
||||
been allocated.
|
||||
|
||||
Related: https://pagure.io/freeipa/issue/9895
|
||||
Signed-off-by: Viktor Ashirov <vashirov@redhat.com>
|
||||
Reviewed-By: Rob Crittenden <rcritten@redhat.com>
|
||||
Reviewed-By: Alexander Bokovoy <abbra@users.noreply.github.com>
|
||||
---
|
||||
.../ipa-range-check/ipa_range_check.c | 18 ++++++++++++++++--
|
||||
1 file changed, 16 insertions(+), 2 deletions(-)
|
||||
|
||||
diff --git a/daemons/ipa-slapi-plugins/ipa-range-check/ipa_range_check.c b/daemons/ipa-slapi-plugins/ipa-range-check/ipa_range_check.c
|
||||
index 5b53a2fe5..37840cd47 100644
|
||||
--- a/daemons/ipa-slapi-plugins/ipa-range-check/ipa_range_check.c
|
||||
+++ b/daemons/ipa-slapi-plugins/ipa-range-check/ipa_range_check.c
|
||||
@@ -76,7 +76,7 @@ Slapi_PluginDesc ipa_range_check_plugin_desc = {
|
||||
|
||||
struct ipa_range_check_ctx {
|
||||
Slapi_ComponentId *plugin_id;
|
||||
- const char *base_dn;
|
||||
+ char *base_dn;
|
||||
};
|
||||
|
||||
typedef enum {
|
||||
@@ -469,6 +469,15 @@ static int ipa_range_check_start(Slapi_PBlock *pb)
|
||||
|
||||
static int ipa_range_check_close(Slapi_PBlock *pb)
|
||||
{
|
||||
+ int ret;
|
||||
+ struct ipa_range_check_ctx *ctx;
|
||||
+
|
||||
+ ret = slapi_pblock_get(pb, SLAPI_PLUGIN_PRIVATE, &ctx);
|
||||
+ if (ret == 0 && ctx != NULL) {
|
||||
+ slapi_ch_free_string(&ctx->base_dn);
|
||||
+ free(ctx);
|
||||
+ }
|
||||
+
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -752,7 +761,10 @@ static int ipa_range_check_init_ctx(Slapi_PBlock *pb,
|
||||
|
||||
done:
|
||||
if (ret != 0) {
|
||||
- free(ctx);
|
||||
+ if (ctx) {
|
||||
+ slapi_ch_free_string(&ctx->base_dn);
|
||||
+ free(ctx);
|
||||
+ }
|
||||
} else {
|
||||
*_ctx = ctx;
|
||||
}
|
||||
@@ -787,6 +799,8 @@ int ipa_range_check_init(Slapi_PBlock *pb)
|
||||
(void *) ipa_range_check_add_pre_op) != 0 ||
|
||||
slapi_pblock_set(pb, SLAPI_PLUGIN_PRIVATE, rc_ctx) != 0) {
|
||||
LOG_FATAL("failed to register plugin\n");
|
||||
+ slapi_ch_free_string(&rc_ctx->base_dn);
|
||||
+ free(rc_ctx);
|
||||
ret = EFAIL;
|
||||
}
|
||||
|
||||
--
|
||||
2.51.0
|
||||
|
||||
|
||||
From 13277d0b4a060d934946ee34ba69a82f93f6f083 Mon Sep 17 00:00:00 2001
|
||||
From: Viktor Ashirov <vashirov@redhat.com>
|
||||
Date: Sat, 8 Nov 2025 00:59:58 +0100
|
||||
Subject: [PATCH 06/16] ipa-extdom-extop: fix memory leaks
|
||||
|
||||
In various code paths the `ctx` structure was freed, but not `ctx`
|
||||
resources (`base_dn`, `nss_ctx`, `extdom_instance_counter`) which may
|
||||
have been allocated.
|
||||
|
||||
Plugin didn't have SLAPI_PLUGIN_CLOSE_FN registered, so context was
|
||||
never freed on server shutdown.
|
||||
|
||||
Related: https://pagure.io/freeipa/issue/9895
|
||||
Signed-off-by: Viktor Ashirov <vashirov@redhat.com>
|
||||
Reviewed-By: Rob Crittenden <rcritten@redhat.com>
|
||||
Reviewed-By: Alexander Bokovoy <abbra@users.noreply.github.com>
|
||||
---
|
||||
.../ipa-extdom-extop/ipa_extdom_extop.c | 39 ++++++++++++++++++-
|
||||
1 file changed, 38 insertions(+), 1 deletion(-)
|
||||
|
||||
diff --git a/daemons/ipa-slapi-plugins/ipa-extdom-extop/ipa_extdom_extop.c b/daemons/ipa-slapi-plugins/ipa-extdom-extop/ipa_extdom_extop.c
|
||||
index 5d22f9f2d..a180e3307 100644
|
||||
--- a/daemons/ipa-slapi-plugins/ipa-extdom-extop/ipa_extdom_extop.c
|
||||
+++ b/daemons/ipa-slapi-plugins/ipa-extdom-extop/ipa_extdom_extop.c
|
||||
@@ -171,6 +171,26 @@ static int ipa_extdom_start(Slapi_PBlock *pb)
|
||||
return LDAP_SUCCESS;
|
||||
}
|
||||
|
||||
+static int ipa_extdom_close(Slapi_PBlock *pb)
|
||||
+{
|
||||
+ int ret;
|
||||
+ struct ipa_extdom_ctx *ctx;
|
||||
+
|
||||
+ ret = slapi_pblock_get(pb, SLAPI_PLUGIN_PRIVATE, &ctx);
|
||||
+ if (ret == 0 && ctx != NULL) {
|
||||
+ if (ctx->extdom_instance_counter) {
|
||||
+ slapi_counter_destroy(&ctx->extdom_instance_counter);
|
||||
+ }
|
||||
+ if (ctx->nss_ctx) {
|
||||
+ back_extdom_free_context(&ctx->nss_ctx);
|
||||
+ }
|
||||
+ slapi_ch_free_string(&ctx->base_dn);
|
||||
+ free(ctx);
|
||||
+ }
|
||||
+
|
||||
+ return 0;
|
||||
+}
|
||||
+
|
||||
static int ipa_extdom_extop(Slapi_PBlock *pb)
|
||||
{
|
||||
char *oid = NULL;
|
||||
@@ -360,7 +380,16 @@ static int ipa_extdom_init_ctx(Slapi_PBlock *pb, struct ipa_extdom_ctx **_ctx)
|
||||
|
||||
done:
|
||||
if (ret) {
|
||||
- free(ctx);
|
||||
+ if (ctx) {
|
||||
+ if (ctx->extdom_instance_counter) {
|
||||
+ slapi_counter_destroy(&ctx->extdom_instance_counter);
|
||||
+ }
|
||||
+ if (ctx->nss_ctx) {
|
||||
+ back_extdom_free_context(&ctx->nss_ctx);
|
||||
+ }
|
||||
+ slapi_ch_free_string(&ctx->base_dn);
|
||||
+ free(ctx);
|
||||
+ }
|
||||
} else {
|
||||
*_ctx = ctx;
|
||||
}
|
||||
@@ -388,6 +417,10 @@ int ipa_extdom_init(Slapi_PBlock *pb)
|
||||
ret = slapi_pblock_set(pb, SLAPI_PLUGIN_START_FN,
|
||||
(void *)ipa_extdom_start);
|
||||
}
|
||||
+ if (!ret) {
|
||||
+ ret = slapi_pblock_set(pb, SLAPI_PLUGIN_CLOSE_FN,
|
||||
+ (void *)ipa_extdom_close);
|
||||
+ }
|
||||
if (!ret) {
|
||||
ret = slapi_pblock_set(pb, SLAPI_PLUGIN_EXT_OP_OIDLIST,
|
||||
ipa_extdom_oid_list);
|
||||
@@ -405,6 +438,10 @@ int ipa_extdom_init(Slapi_PBlock *pb)
|
||||
}
|
||||
if (ret) {
|
||||
LOG("Failed to set plug-in version, function, and OID.\n" );
|
||||
+ slapi_counter_destroy(&extdom_ctx->extdom_instance_counter);
|
||||
+ back_extdom_free_context(&extdom_ctx->nss_ctx);
|
||||
+ slapi_ch_free_string(&extdom_ctx->base_dn);
|
||||
+ free(extdom_ctx);
|
||||
return -1;
|
||||
}
|
||||
|
||||
--
|
||||
2.51.0
|
||||
|
||||
|
||||
From a203332f0689dae88aa4e31f42eb22416e28ada5 Mon Sep 17 00:00:00 2001
|
||||
From: Viktor Ashirov <vashirov@redhat.com>
|
||||
Date: Sat, 8 Nov 2025 01:03:52 +0100
|
||||
Subject: [PATCH 07/16] ipa-enrollment: fix memory leaks
|
||||
|
||||
`smods`, `fqdn`, `sdn` were not freed.
|
||||
|
||||
Related: https://pagure.io/freeipa/issue/9895
|
||||
Signed-off-by: Viktor Ashirov <vashirov@redhat.com>
|
||||
Reviewed-By: Rob Crittenden <rcritten@redhat.com>
|
||||
Reviewed-By: Alexander Bokovoy <abbra@users.noreply.github.com>
|
||||
---
|
||||
daemons/ipa-slapi-plugins/ipa-enrollment/ipa_enrollment.c | 7 ++++++-
|
||||
1 file changed, 6 insertions(+), 1 deletion(-)
|
||||
|
||||
diff --git a/daemons/ipa-slapi-plugins/ipa-enrollment/ipa_enrollment.c b/daemons/ipa-slapi-plugins/ipa-enrollment/ipa_enrollment.c
|
||||
index 26cbb69d7..70a297da0 100644
|
||||
--- a/daemons/ipa-slapi-plugins/ipa-enrollment/ipa_enrollment.c
|
||||
+++ b/daemons/ipa-slapi-plugins/ipa-enrollment/ipa_enrollment.c
|
||||
@@ -136,7 +136,7 @@ ipa_join(Slapi_PBlock *pb)
|
||||
int is_root=0;
|
||||
char *krbLastPwdChange = NULL;
|
||||
char *fqdn = NULL;
|
||||
- Slapi_Mods *smods;
|
||||
+ Slapi_Mods *smods = NULL;
|
||||
char *attrlist[] = {"fqdn", "krbPrincipalKey", "krbLastPwdChange", "krbPrincipalName", NULL };
|
||||
char * filter;
|
||||
|
||||
@@ -328,8 +328,13 @@ free_and_return:
|
||||
if (pbtm) {
|
||||
slapi_pblock_destroy(pbtm);
|
||||
}
|
||||
+ if (smods) {
|
||||
+ slapi_mods_free(&smods);
|
||||
+ }
|
||||
|
||||
if (krbLastPwdChange) slapi_ch_free_string(&krbLastPwdChange);
|
||||
+ if (fqdn) slapi_ch_free_string(&fqdn);
|
||||
+ if (sdn) slapi_sdn_free(&sdn);
|
||||
|
||||
LOG("%s", errMesg ? errMesg : "success\n");
|
||||
slapi_send_ldap_result(pb, rc, NULL, errMesg, 0, NULL);
|
||||
--
|
||||
2.51.0
|
||||
|
||||
|
||||
From d7ba4663a72bc0aacff6cea38f83561d39c7b365 Mon Sep 17 00:00:00 2001
|
||||
From: Viktor Ashirov <vashirov@redhat.com>
|
||||
Date: Sat, 8 Nov 2025 01:05:15 +0100
|
||||
Subject: [PATCH 08/16] topology: fix memory leaks
|
||||
|
||||
`agmt_attr_val`, `targetHost` and internal search results pblock were
|
||||
not freed.
|
||||
|
||||
Related: https://pagure.io/freeipa/issue/9895
|
||||
Signed-off-by: Viktor Ashirov <vashirov@redhat.com>
|
||||
Reviewed-By: Rob Crittenden <rcritten@redhat.com>
|
||||
Reviewed-By: Alexander Bokovoy <abbra@users.noreply.github.com>
|
||||
---
|
||||
daemons/ipa-slapi-plugins/topology/topology_util.c | 4 +++-
|
||||
1 file changed, 3 insertions(+), 1 deletion(-)
|
||||
|
||||
diff --git a/daemons/ipa-slapi-plugins/topology/topology_util.c b/daemons/ipa-slapi-plugins/topology/topology_util.c
|
||||
index de8147a4a..f8da3b073 100644
|
||||
--- a/daemons/ipa-slapi-plugins/topology/topology_util.c
|
||||
+++ b/daemons/ipa-slapi-plugins/topology/topology_util.c
|
||||
@@ -678,6 +678,7 @@ ipa_topo_util_update_agmt_list(TopoReplica *conf, TopoReplicaSegmentList *repl_s
|
||||
mattrs[i],
|
||||
segm_attr_val);
|
||||
}
|
||||
+ slapi_ch_free_string(&agmt_attr_val);
|
||||
|
||||
}
|
||||
}
|
||||
@@ -711,10 +712,10 @@ ipa_topo_util_update_agmt_list(TopoReplica *conf, TopoReplicaSegmentList *repl_s
|
||||
ipa_topo_cfg_segment_set_visited(conf, topo_segm);
|
||||
}
|
||||
}
|
||||
+ slapi_ch_free_string(&targetHost);
|
||||
|
||||
repl_agmt = entries[++nentries];
|
||||
}
|
||||
- slapi_free_search_results_internal(pb);
|
||||
|
||||
update_only:
|
||||
/* check if segments not covered by agreement exist
|
||||
@@ -724,6 +725,7 @@ update_only:
|
||||
ipa_topo_get_plugin_hostname());
|
||||
|
||||
error_return:
|
||||
+ slapi_free_search_results_internal(pb);
|
||||
slapi_ch_free_string(&filter);
|
||||
slapi_pblock_destroy(pb);
|
||||
return rc;
|
||||
--
|
||||
2.51.0
|
||||
|
||||
|
||||
From a1ebdf556d31f8ed5b1159674df9aff16f97ab2a Mon Sep 17 00:00:00 2001
|
||||
From: Viktor Ashirov <vashirov@redhat.com>
|
||||
Date: Fri, 28 Nov 2025 12:19:05 +0100
|
||||
Subject: [PATCH 09/16] ipa-pwd-extop: fix memory leaks
|
||||
|
||||
In `ipapwd_set_extradata` free `xdata` after it's not longer needed. It
|
||||
was leaked because `slapi_value_new_berval()` makes a copy of the data.
|
||||
|
||||
In `ipapwd_free_slapi_value_array` free `svals` (caller's pointer)
|
||||
instead of `sv` (local pointer).
|
||||
|
||||
Related: https://pagure.io/freeipa/issue/9895
|
||||
Signed-off-by: Viktor Ashirov <vashirov@redhat.com>
|
||||
Reviewed-By: Rob Crittenden <rcritten@redhat.com>
|
||||
Reviewed-By: Alexander Bokovoy <abbra@users.noreply.github.com>
|
||||
---
|
||||
daemons/ipa-slapi-plugins/ipa-pwd-extop/common.c | 3 ++-
|
||||
1 file changed, 2 insertions(+), 1 deletion(-)
|
||||
|
||||
diff --git a/daemons/ipa-slapi-plugins/ipa-pwd-extop/common.c b/daemons/ipa-slapi-plugins/ipa-pwd-extop/common.c
|
||||
index 5251713c6..204b2c6d9 100644
|
||||
--- a/daemons/ipa-slapi-plugins/ipa-pwd-extop/common.c
|
||||
+++ b/daemons/ipa-slapi-plugins/ipa-pwd-extop/common.c
|
||||
@@ -1076,6 +1076,7 @@ int ipapwd_set_extradata(const char *dn,
|
||||
|
||||
slapi_value_free(&va[0]);
|
||||
slapi_mods_free(&smods);
|
||||
+ free(xdata);
|
||||
|
||||
return ret;
|
||||
}
|
||||
@@ -1091,7 +1092,7 @@ void ipapwd_free_slapi_value_array(Slapi_Value ***svals)
|
||||
}
|
||||
}
|
||||
|
||||
- slapi_ch_free((void **)sv);
|
||||
+ slapi_ch_free((void **)svals);
|
||||
}
|
||||
|
||||
void free_ipapwd_krbcfg(struct ipapwd_krbcfg **cfg)
|
||||
--
|
||||
2.51.0
|
||||
|
||||
|
||||
From 7dd85cabad5b53f893cb1d1f4485608d54ee7c18 Mon Sep 17 00:00:00 2001
|
||||
From: Viktor Ashirov <vashirov@redhat.com>
|
||||
Date: Thu, 11 Dec 2025 10:08:35 +0100
|
||||
Subject: [PATCH 10/16] ipa-pwd-extop: fix memory leaks of bind DN
|
||||
|
||||
In `ipapwd_chpwop()`, `ipapwd_setkeytab()`, and `ipapwd_getkeytab()`
|
||||
functions, `bindDN`/`bind_dn` is obtained via `slapi_pblock_get()` with
|
||||
SLAPI_CONN_DN which returns an allocated string. This string was never
|
||||
freed in the cleanup sections of these functions.
|
||||
|
||||
Add `slapi_ch_free_string()` calls for the bind DN variables in the
|
||||
`free_and_return` sections of all three functions.
|
||||
|
||||
Related: https://pagure.io/freeipa/issue/9895
|
||||
Signed-off-by: Viktor Ashirov <vashirov@redhat.com>
|
||||
Reviewed-By: Alexander Bokovoy <abokovoy@redhat.com>
|
||||
---
|
||||
daemons/ipa-slapi-plugins/ipa-pwd-extop/ipa_pwd_extop.c | 3 +++
|
||||
1 file changed, 3 insertions(+)
|
||||
|
||||
diff --git a/daemons/ipa-slapi-plugins/ipa-pwd-extop/ipa_pwd_extop.c b/daemons/ipa-slapi-plugins/ipa-pwd-extop/ipa_pwd_extop.c
|
||||
index 94e0f9c70..eb441003e 100644
|
||||
--- a/daemons/ipa-slapi-plugins/ipa-pwd-extop/ipa_pwd_extop.c
|
||||
+++ b/daemons/ipa-slapi-plugins/ipa-pwd-extop/ipa_pwd_extop.c
|
||||
@@ -647,6 +647,7 @@ free_and_return:
|
||||
}
|
||||
slapi_pblock_destroy(chpwop_pb);
|
||||
}
|
||||
+ slapi_ch_free_string(&bindDN);
|
||||
slapi_ch_free_string(&oldPasswd);
|
||||
slapi_ch_free_string(&newPasswd);
|
||||
/* Either this is the same pointer that we allocated and set above,
|
||||
@@ -1364,6 +1365,7 @@ static int ipapwd_setkeytab(Slapi_PBlock *pb, struct ipapwd_krbcfg *krbcfg)
|
||||
|
||||
/* Free anything that we allocated above */
|
||||
free_and_return:
|
||||
+ slapi_ch_free_string(&bindDN);
|
||||
free(serviceName);
|
||||
if (kset) ipapwd_keyset_free(&kset);
|
||||
|
||||
@@ -1781,6 +1783,7 @@ free_and_return:
|
||||
slapi_send_ldap_result(pb, rc, NULL, err_msg, 0, NULL);
|
||||
|
||||
/* Free anything that we allocated above */
|
||||
+ slapi_ch_free_string(&bind_dn);
|
||||
if (krbctx) krb5_free_context(krbctx);
|
||||
free(kenctypes);
|
||||
free(service_name);
|
||||
--
|
||||
2.51.0
|
||||
|
||||
|
||||
From 207f296ee4721d1fa51b03c3ecc843ef04fa2b5d Mon Sep 17 00:00:00 2001
|
||||
From: Rafael Guterres Jeffman <rjeffman@redhat.com>
|
||||
Date: Fri, 30 Jan 2026 17:01:06 -0300
|
||||
Subject: [PATCH 11/16] ipa-pwd-extop: fix memory leaks in `ipapwd_pre_add()`
|
||||
|
||||
In `ipapwd_pre_add()`, when processing password from entry extension,
|
||||
`userpw` was reassigned without freeing the previous value.
|
||||
Additionally, `enabled` obtained from `ipapwd_getIpaConfigAttr()` was
|
||||
never freed, and early returns bypassed the cleanup section causing
|
||||
memory leaks.
|
||||
|
||||
Free `userpw` before reassigning it.
|
||||
Free `enabled` after use.
|
||||
Replace early `return 0` statements with `goto done` to ensure proper
|
||||
cleanup of all allocated resources.
|
||||
|
||||
Related: https://pagure.io/freeipa/issue/9895
|
||||
Signed-off-by: Viktor Ashirov <vashirov@redhat.com>
|
||||
Reviewed-By: Alexander Bokovoy <abokovoy@redhat.com>
|
||||
---
|
||||
daemons/ipa-slapi-plugins/ipa-pwd-extop/prepost.c | 9 +++++++--
|
||||
1 file changed, 7 insertions(+), 2 deletions(-)
|
||||
|
||||
diff --git a/daemons/ipa-slapi-plugins/ipa-pwd-extop/prepost.c b/daemons/ipa-slapi-plugins/ipa-pwd-extop/prepost.c
|
||||
index 7fab55c84..6fb6856b8 100644
|
||||
--- a/daemons/ipa-slapi-plugins/ipa-pwd-extop/prepost.c
|
||||
+++ b/daemons/ipa-slapi-plugins/ipa-pwd-extop/prepost.c
|
||||
@@ -282,6 +282,7 @@ static int ipapwd_pre_add(Slapi_PBlock *pb)
|
||||
if (rc) {
|
||||
goto done;
|
||||
}
|
||||
+ slapi_ch_free_string(&userpw);
|
||||
userpw = slapi_ch_strdup(userpw_clear);
|
||||
}
|
||||
|
||||
@@ -293,8 +294,11 @@ static int ipapwd_pre_add(Slapi_PBlock *pb)
|
||||
if (NULL == enabled) {
|
||||
LOG("no ipaMigrationEnabled in config, assuming FALSE\n");
|
||||
} else if (0 == strcmp(enabled, "TRUE")) {
|
||||
- return 0;
|
||||
+ slapi_ch_free_string(&enabled);
|
||||
+ rc = LDAP_SUCCESS;
|
||||
+ goto done;
|
||||
}
|
||||
+ slapi_ch_free_string(&enabled);
|
||||
|
||||
/* With User Life Cycle, it could be a stage user that is activated.
|
||||
* The userPassword and krb keys were set while the user was a stage user.
|
||||
@@ -306,7 +310,8 @@ static int ipapwd_pre_add(Slapi_PBlock *pb)
|
||||
LOG("User Life Cycle: %s is a activated stage user "
|
||||
"(with prehashed password and krb keys)\n",
|
||||
sdn ? slapi_sdn_get_dn(sdn) : "unknown");
|
||||
- return 0;
|
||||
+ rc = LDAP_SUCCESS;
|
||||
+ goto done;
|
||||
}
|
||||
|
||||
LOG("pre-hashed passwords are not valid\n");
|
||||
--
|
||||
2.51.0
|
||||
|
||||
|
||||
From a3d6e658196665996932dd0d3f606673df8cee22 Mon Sep 17 00:00:00 2001
|
||||
From: Rafael Guterres Jeffman <rjeffman@redhat.com>
|
||||
Date: Fri, 30 Jan 2026 17:03:18 -0300
|
||||
Subject: [PATCH 12/16] ipa-pwd-extop: fix bind DN memory leaks in pre-op
|
||||
handlers
|
||||
|
||||
In `ipapwd_pre_add()` and `ipapwd_pre_mod()`, `binddn` is obtained via
|
||||
`slapi_pblock_get()` with SLAPI_CONN_DN which returns an allocated
|
||||
string. This string was never freed after use.
|
||||
|
||||
Add `slapi_ch_free_string(&binddn)` calls after the bind DN is no longer
|
||||
needed in both functions.
|
||||
|
||||
Related: https://pagure.io/freeipa/issue/9895
|
||||
Signed-off-by: Viktor Ashirov <vashirov@redhat.com>
|
||||
Reviewed-By: Alexander Bokovoy <abokovoy@redhat.com>
|
||||
---
|
||||
daemons/ipa-slapi-plugins/ipa-pwd-extop/prepost.c | 2 ++
|
||||
1 file changed, 2 insertions(+)
|
||||
|
||||
diff --git a/daemons/ipa-slapi-plugins/ipa-pwd-extop/prepost.c b/daemons/ipa-slapi-plugins/ipa-pwd-extop/prepost.c
|
||||
index 6fb6856b8..51c39bbfc 100644
|
||||
--- a/daemons/ipa-slapi-plugins/ipa-pwd-extop/prepost.c
|
||||
+++ b/daemons/ipa-slapi-plugins/ipa-pwd-extop/prepost.c
|
||||
@@ -378,6 +378,7 @@ static int ipapwd_pre_add(Slapi_PBlock *pb)
|
||||
break;
|
||||
}
|
||||
}
|
||||
+ slapi_ch_free_string(&binddn);
|
||||
}
|
||||
|
||||
pwdop->pwdata.dn = slapi_ch_strdup(slapi_sdn_get_dn(sdn));
|
||||
@@ -873,6 +874,7 @@ static int ipapwd_pre_mod(Slapi_PBlock *pb)
|
||||
|
||||
slapi_sdn_free(&bdn);
|
||||
slapi_sdn_free(&tdn);
|
||||
+ slapi_ch_free_string(&binddn);
|
||||
|
||||
}
|
||||
|
||||
--
|
||||
2.51.0
|
||||
|
||||
|
||||
From 549494b8d381a7960e232148ed676416dacad6fc Mon Sep 17 00:00:00 2001
|
||||
From: Viktor Ashirov <vashirov@redhat.com>
|
||||
Date: Thu, 11 Dec 2025 13:13:45 +0100
|
||||
Subject: [PATCH 13/16] ipa-pwd-extop: fix NT hash string memory leak
|
||||
|
||||
In `ipapwd_pre_add()` and `ipapwd_pre_mod()`, the `nt` string returned
|
||||
by `ipapwd_gen_hashes()` was only freed when `is_smb` was true. When NT
|
||||
hashes are generated for `is_ipant` entries but `is_smb` is false, the
|
||||
`nt` string was leaked.
|
||||
|
||||
Free `nt`, `ntvals` and `svals` unconditionally.
|
||||
|
||||
Fix the error path in `ipapwd_pre_add()` where `nt` and `ntvals` were
|
||||
leaked when `slapi_entry_attr_replace_sv()` failed for `svals`.
|
||||
|
||||
Related: https://pagure.io/freeipa/issue/9895
|
||||
Signed-off-by: Viktor Ashirov <vashirov@redhat.com>
|
||||
Reviewed-By: Alexander Bokovoy <abokovoy@redhat.com>
|
||||
---
|
||||
daemons/ipa-slapi-plugins/ipa-pwd-extop/prepost.c | 15 ++++++++-------
|
||||
1 file changed, 8 insertions(+), 7 deletions(-)
|
||||
|
||||
diff --git a/daemons/ipa-slapi-plugins/ipa-pwd-extop/prepost.c b/daemons/ipa-slapi-plugins/ipa-pwd-extop/prepost.c
|
||||
index 51c39bbfc..02c7ed3c6 100644
|
||||
--- a/daemons/ipa-slapi-plugins/ipa-pwd-extop/prepost.c
|
||||
+++ b/daemons/ipa-slapi-plugins/ipa-pwd-extop/prepost.c
|
||||
@@ -417,22 +417,23 @@ static int ipapwd_pre_add(Slapi_PBlock *pb)
|
||||
LOG_FATAL("failed to set encoded values in entry\n");
|
||||
rc = LDAP_OPERATIONS_ERROR;
|
||||
ipapwd_free_slapi_value_array(&svals);
|
||||
+ slapi_ch_free_string(&nt);
|
||||
+ ipapwd_free_slapi_value_array(&ntvals);
|
||||
goto done;
|
||||
}
|
||||
-
|
||||
- ipapwd_free_slapi_value_array(&svals);
|
||||
}
|
||||
+ ipapwd_free_slapi_value_array(&svals);
|
||||
|
||||
if (nt && is_smb) {
|
||||
/* set value */
|
||||
slapi_entry_attr_set_charptr(e, "sambaNTPassword", nt);
|
||||
- slapi_ch_free_string(&nt);
|
||||
}
|
||||
+ slapi_ch_free_string(&nt);
|
||||
|
||||
if (ntvals && is_ipant) {
|
||||
slapi_entry_attr_replace_sv(e, "ipaNTHash", ntvals);
|
||||
- ipapwd_free_slapi_value_array(&ntvals);
|
||||
}
|
||||
+ ipapwd_free_slapi_value_array(&ntvals);
|
||||
|
||||
if (is_smb) {
|
||||
/* with samba integration we need to also set sambaPwdLastSet or
|
||||
@@ -913,21 +914,21 @@ static int ipapwd_pre_mod(Slapi_PBlock *pb)
|
||||
/* replace values */
|
||||
slapi_mods_add_mod_values(smods, LDAP_MOD_REPLACE,
|
||||
"krbPrincipalKey", svals);
|
||||
- ipapwd_free_slapi_value_array(&svals);
|
||||
}
|
||||
+ ipapwd_free_slapi_value_array(&svals);
|
||||
|
||||
if (nt && is_smb) {
|
||||
/* replace value */
|
||||
slapi_mods_add_string(smods, LDAP_MOD_REPLACE,
|
||||
"sambaNTPassword", nt);
|
||||
- slapi_ch_free_string(&nt);
|
||||
}
|
||||
+ slapi_ch_free_string(&nt);
|
||||
|
||||
if (ntvals && is_ipant) {
|
||||
slapi_mods_add_mod_values(smods, LDAP_MOD_REPLACE,
|
||||
"ipaNTHash", ntvals);
|
||||
- ipapwd_free_slapi_value_array(&ntvals);
|
||||
}
|
||||
+ ipapwd_free_slapi_value_array(&ntvals);
|
||||
|
||||
if (is_smb) {
|
||||
/* with samba integration we need to also set sambaPwdLastSet or
|
||||
--
|
||||
2.51.0
|
||||
|
||||
|
||||
From dc07404f435b9efe3da1b3ba2d81a0fe3ab608cc Mon Sep 17 00:00:00 2001
|
||||
From: Viktor Ashirov <vashirov@redhat.com>
|
||||
Date: Thu, 11 Dec 2025 14:34:47 +0100
|
||||
Subject: [PATCH 14/16] ipa-pwd-extop: fix password history values memory leak
|
||||
|
||||
In `ipapwd_post_modadd()`, the `pwvals` array returned by
|
||||
`ipapwd_setPasswordHistory()` was passed to `slapi_mods_add_mod_values()`
|
||||
but never freed. The `slapi_mods_add_mod_values()` function makes a copy
|
||||
of the values, so the original array still needs to be freed.
|
||||
|
||||
Add `ipapwd_free_slapi_value_array()` call in the cleanup section to
|
||||
free the array.
|
||||
|
||||
Related: https://pagure.io/freeipa/issue/9895
|
||||
Signed-off-by: Viktor Ashirov <vashirov@redhat.com>
|
||||
Reviewed-By: Alexander Bokovoy <abokovoy@redhat.com>
|
||||
---
|
||||
daemons/ipa-slapi-plugins/ipa-pwd-extop/prepost.c | 3 ++-
|
||||
1 file changed, 2 insertions(+), 1 deletion(-)
|
||||
|
||||
diff --git a/daemons/ipa-slapi-plugins/ipa-pwd-extop/prepost.c b/daemons/ipa-slapi-plugins/ipa-pwd-extop/prepost.c
|
||||
index 02c7ed3c6..74688ac39 100644
|
||||
--- a/daemons/ipa-slapi-plugins/ipa-pwd-extop/prepost.c
|
||||
+++ b/daemons/ipa-slapi-plugins/ipa-pwd-extop/prepost.c
|
||||
@@ -1073,7 +1073,7 @@ static int ipapwd_post_modadd(Slapi_PBlock *pb)
|
||||
void *op;
|
||||
struct ipapwd_operation *pwdop = NULL;
|
||||
Slapi_Mods *smods;
|
||||
- Slapi_Value **pwvals;
|
||||
+ Slapi_Value **pwvals = NULL;
|
||||
int ret;
|
||||
char *errMsg = "Internal operations error\n";
|
||||
struct ipapwd_krbcfg *krbcfg = NULL;
|
||||
@@ -1203,6 +1203,7 @@ done:
|
||||
slapi_mods_free(&smods);
|
||||
slapi_ch_free_string(&principal);
|
||||
free_ipapwd_krbcfg(&krbcfg);
|
||||
+ ipapwd_free_slapi_value_array(&pwvals);
|
||||
return 0;
|
||||
}
|
||||
|
||||
--
|
||||
2.51.0
|
||||
|
||||
|
||||
From e9978a6a1e3997329b54573dce9f460a26284302 Mon Sep 17 00:00:00 2001
|
||||
From: Viktor Ashirov <vashirov@redhat.com>
|
||||
Date: Thu, 11 Dec 2025 15:45:27 +0100
|
||||
Subject: [PATCH 15/16] ipa-pwd-extop: fix memory leaks in
|
||||
`ipapwd_gen_hashes()` error path
|
||||
|
||||
In `ipapwd_gen_hashes()`, when an error occurred after allocating output
|
||||
parameters, `*ntvals` was freed but `*nthash` was not.
|
||||
|
||||
Add `slapi_ch_free_string(nthash)` to the error cleanup section.
|
||||
|
||||
Related: https://pagure.io/freeipa/issue/9895
|
||||
Signed-off-by: Viktor Ashirov <vashirov@redhat.com>
|
||||
Reviewed-By: Alexander Bokovoy <abokovoy@redhat.com>
|
||||
---
|
||||
daemons/ipa-slapi-plugins/ipa-pwd-extop/encoding.c | 2 ++
|
||||
1 file changed, 2 insertions(+)
|
||||
|
||||
diff --git a/daemons/ipa-slapi-plugins/ipa-pwd-extop/encoding.c b/daemons/ipa-slapi-plugins/ipa-pwd-extop/encoding.c
|
||||
index 7b2f34122..05b317e93 100644
|
||||
--- a/daemons/ipa-slapi-plugins/ipa-pwd-extop/encoding.c
|
||||
+++ b/daemons/ipa-slapi-plugins/ipa-pwd-extop/encoding.c
|
||||
@@ -220,6 +220,7 @@ int ipapwd_gen_hashes(struct ipapwd_krbcfg *krbcfg,
|
||||
|
||||
*svals = NULL;
|
||||
*nthash = NULL;
|
||||
+ *ntvals = NULL;
|
||||
*errMesg = NULL;
|
||||
|
||||
if (is_krb) {
|
||||
@@ -281,6 +282,7 @@ done:
|
||||
if (rc) {
|
||||
ipapwd_free_slapi_value_array(svals);
|
||||
ipapwd_free_slapi_value_array(ntvals);
|
||||
+ slapi_ch_free_string(nthash);
|
||||
}
|
||||
|
||||
return rc;
|
||||
--
|
||||
2.51.0
|
||||
|
||||
|
||||
From 6809fe26c08a3eca33be82448b248b7c330f5877 Mon Sep 17 00:00:00 2001
|
||||
From: Viktor Ashirov <vashirov@redhat.com>
|
||||
Date: Thu, 11 Dec 2025 16:16:02 +0100
|
||||
Subject: [PATCH 16/16] ipa-pwd-extop: fix valueset memory leak in
|
||||
`ipapwd_get_cur_kvno()`
|
||||
|
||||
In `ipapwd_get_cur_kvno()`, the `Slapi_ValueSet` obtained via
|
||||
`slapi_attr_get_valueset()` was never freed. This function returns a
|
||||
copy of the valueset that must be freed by the caller using
|
||||
`slapi_valueset_free()`.
|
||||
|
||||
Add `slapi_valueset_free(svs)` before returning from the function.
|
||||
|
||||
Related: https://pagure.io/freeipa/issue/9895
|
||||
Signed-off-by: Viktor Ashirov <vashirov@redhat.com>
|
||||
Reviewed-By: Alexander Bokovoy <abokovoy@redhat.com>
|
||||
---
|
||||
daemons/ipa-slapi-plugins/ipa-pwd-extop/common.c | 1 +
|
||||
1 file changed, 1 insertion(+)
|
||||
|
||||
diff --git a/daemons/ipa-slapi-plugins/ipa-pwd-extop/common.c b/daemons/ipa-slapi-plugins/ipa-pwd-extop/common.c
|
||||
index 204b2c6d9..4c4240a98 100644
|
||||
--- a/daemons/ipa-slapi-plugins/ipa-pwd-extop/common.c
|
||||
+++ b/daemons/ipa-slapi-plugins/ipa-pwd-extop/common.c
|
||||
@@ -755,6 +755,7 @@ next:
|
||||
hint = slapi_valueset_next_value(svs, hint, &sv);
|
||||
}
|
||||
|
||||
+ slapi_valueset_free(svs);
|
||||
return kvno;
|
||||
}
|
||||
|
||||
--
|
||||
2.51.0
|
||||
|
||||
@ -1,70 +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
|
||||
@@ -258,8 +258,7 @@ 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"
|
||||
USERMOD = "/usr/sbin/usermod"
|
||||
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.13.1.tar.gz.asc
Normal file
16
SOURCES/freeipa-4.13.1.tar.gz.asc
Normal file
@ -0,0 +1,16 @@
|
||||
-----BEGIN PGP SIGNATURE-----
|
||||
|
||||
iQIzBAABCgAdFiEE11Z2TU1+KXxtrRFyaYdvcqbi008FAmlqBAcACgkQaYdvcqbi
|
||||
009J4Q//cZi2ZS9RNBkU/v7v7Uvfb7WbsTAUQ2V5EJlqQ7BXbn2tkLtACbzeRn6X
|
||||
aLECdZK+0etdyjbFlb7CvR6GUKw3BCZXpV62XXSQ3/EOhGRtGbrzRiIgaYOmFjzb
|
||||
Hs4Ini6x/QClb0iA478ajG3YHrNYJhy3fFj+s3xzNGZuhJLvhlg3C2VDG7AgrzEx
|
||||
RPiK7DIPY3SVUayxGuPLkNxsZiEQ1IcYtIu++Wp9QHmgPlAdP5aZ2TcEeotgUq4Z
|
||||
+1LXEsDywEdQpg2iO92rja01q+zlcxzuQVjgdEh0G9lIw6f/GtB5WTdm2SER26oq
|
||||
tld6MI6vrWZY+zMSc6FVjy0UB1Tk1kjjp7dp8OQhgzpe2i7LLKZl8bwRhvV6XlZT
|
||||
+ylGHNK5cFglgtOjxIyLVGy6p6PIM+FtEFigUHB5faH+UrWTIL1+zrqWEhhiykI+
|
||||
TT7/plOxG+tALr3ZDOZVqn1Idh9UQNQ8YdPzzKQMKAx5bbm5uHJXR6FQWt/P1QVc
|
||||
S2N1Jlgi8IzbrnVPqN04BDCKsbOOWK6hP8a/SxoGW2IgTd7QWXalfRkeD4vdnM/1
|
||||
9RRWRGuWWKuk2EUlKK202za+wJ7oOY4JSLXC3zxo1bTsHnUN+SYun+5BgYaiKLbM
|
||||
dXZ4K+9Fs4clmT38W3I2LWPFoKcZVczN6/3MpQbTvmgEOkx8LZ0=
|
||||
=jpva
|
||||
-----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-----
|
||||
3774
SPECS/freeipa.spec
Normal file
3774
SPECS/freeipa.spec
Normal file
File diff suppressed because it is too large
Load Diff
5458
SPECS/ipa.spec
5458
SPECS/ipa.spec
File diff suppressed because it is too large
Load Diff
Loading…
Reference in New Issue
Block a user