diff --git a/.gitignore b/.gitignore index 34c1a4e..d9c893f 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1 @@ -SOURCES/freeipa-4.9.6.tar.gz +SOURCES/freeipa-4.9.8.tar.gz diff --git a/.ipa.metadata b/.ipa.metadata index ab790ce..15ef586 100644 --- a/.ipa.metadata +++ b/.ipa.metadata @@ -1 +1 @@ -b7b91082908db35e4acbcd0221b8df4044913dc1 SOURCES/freeipa-4.9.6.tar.gz +38641a7f95779ba35089fcc10e25ec82a9b0248e SOURCES/freeipa-4.9.8.tar.gz diff --git a/README.debrand b/README.debrand deleted file mode 100644 index 01c46d2..0000000 --- a/README.debrand +++ /dev/null @@ -1,2 +0,0 @@ -Warning: This package was configured for automatic debranding, but the changes -failed to apply. diff --git a/SOURCES/0001-Remove-unneeded-dependency-on-python-coverage.patch b/SOURCES/0001-Remove-unneeded-dependency-on-python-coverage.patch deleted file mode 100644 index d27c680..0000000 --- a/SOURCES/0001-Remove-unneeded-dependency-on-python-coverage.patch +++ /dev/null @@ -1,30 +0,0 @@ -From 01f4b9d7935ca41c93b17e28543054f36e5baf46 Mon Sep 17 00:00:00 2001 -From: Florence Blanc-Renaud -Date: Wed, 30 Jun 2021 14:57:32 +0200 -Subject: [PATCH] Remove unneeded dependency on python-coverage - -The spec file requires python3-coverage although it is not -used in the project. - -Fixes: https://pagure.io/freeipa/issue/8905 -Signed-off-by: Florence Blanc-Renaud -Reviewed-By: Francois Cami ---- - freeipa.spec.in | 1 - - 1 file changed, 1 deletion(-) - -diff --git a/freeipa.spec.in b/freeipa.spec.in -index fdca43a24a6e07f77b9cd8a0feec940a0366f128..fbfe4d09eedc169112dcdc18a953134de67b7731 100755 ---- a/freeipa.spec.in -+++ b/freeipa.spec.in -@@ -872,7 +872,6 @@ BuildArch: noarch - Requires: python3-ipaclient = %{version}-%{release} - Requires: python3-ipaserver = %{version}-%{release} - Requires: iptables --Requires: python3-coverage - Requires: python3-cryptography >= 1.6 - Requires: python3-pexpect - %if 0%{?fedora} --- -2.26.3 - diff --git a/SOURCES/0001-ipa-kdb-issue-PAC_REQUESTER_SID-only-for-TGTs.patch b/SOURCES/0001-ipa-kdb-issue-PAC_REQUESTER_SID-only-for-TGTs.patch new file mode 100644 index 0000000..e303e04 --- /dev/null +++ b/SOURCES/0001-ipa-kdb-issue-PAC_REQUESTER_SID-only-for-TGTs.patch @@ -0,0 +1,36 @@ +From 669f3d71161741c676ddd6a08bd08d4a4ccd495b Mon Sep 17 00:00:00 2001 +From: Alexander Bokovoy +Date: Fri, 26 Nov 2021 17:40:54 +0200 +Subject: [PATCH] ipa-kdb: issue PAC_REQUESTER_SID only for TGTs + +MS-KILE 3.3.5.6.4.8 in revision after Windows Server November 2021 +security fixes added the following requirement: + +- PAC_REQUESTER_SID is only added in TGT case (including referrals and + tickets to RODCs) + +Fixes: https://pagure.io/freeipa/issue/9031 + +Signed-off-by: Alexander Bokovoy +Reviewed-By: Rob Crittenden +--- + daemons/ipa-kdb/ipa_kdb_mspac.c | 3 ++- + 1 file changed, 2 insertions(+), 1 deletion(-) + +diff --git a/daemons/ipa-kdb/ipa_kdb_mspac.c b/daemons/ipa-kdb/ipa_kdb_mspac.c +index 6f7d1ac15daf17dfca36ebd3265c866725d24717..538cfbba958068bd2ee0aaae7a2743ae82237898 100644 +--- a/daemons/ipa-kdb/ipa_kdb_mspac.c ++++ b/daemons/ipa-kdb/ipa_kdb_mspac.c +@@ -1148,7 +1148,8 @@ static krb5_error_code ipadb_get_pac(krb5_context kcontext, + #endif + + #ifdef HAVE_PAC_REQUESTER_SID +- { ++ /* MS-KILE 3.3.5.6.4.8: add PAC_REQUESTER_SID only in TGT case */ ++ if ((flags & KRB5_KDB_FLAG_CLIENT_REFERRALS_ONLY) != 0) { + union PAC_INFO pac_requester_sid; + /* == Package PAC_REQUESTER_SID == */ + memset(&pac_requester_sid, 0, sizeof(pac_requester_sid)); +-- +2.31.1 + diff --git a/SOURCES/0002-Add-checks-to-prevent-adding-auth-indicators-to-inte.patch b/SOURCES/0002-Add-checks-to-prevent-adding-auth-indicators-to-inte.patch deleted file mode 100644 index 411d510..0000000 --- a/SOURCES/0002-Add-checks-to-prevent-adding-auth-indicators-to-inte.patch +++ /dev/null @@ -1,134 +0,0 @@ -From dffccae7193b0616cb84792edec480f5f67e1fc6 Mon Sep 17 00:00:00 2001 -From: Antonio Torres -Date: Mon, 8 Mar 2021 18:15:50 +0100 -Subject: [PATCH] Add checks to prevent adding auth indicators to internal IPA - services - -Authentication indicators should not be enforced against internal -IPA services, since not all users of those services are able to produce -Kerberos tickets with all the auth indicator options. This includes -host, ldap, HTTP and cifs in IPA server and cifs in IPA clients. -If a client that is being promoted to replica has an auth indicator -in its host principal then the promotion is aborted. - -Fixes: https://pagure.io/freeipa/issue/8206 -Signed-off-by: Antonio Torres ---- - ipaserver/install/server/replicainstall.py | 13 ++++++++++++ - ipaserver/plugins/host.py | 5 ++++- - ipaserver/plugins/service.py | 24 ++++++++++++++++++++++ - 3 files changed, 41 insertions(+), 1 deletion(-) - -diff --git a/ipaserver/install/server/replicainstall.py b/ipaserver/install/server/replicainstall.py -index 73967a2249d5c8944d70c5c3ca9a9d3b3bfc6b73..f1fb9103687ce9719ef24c8cb3c41088a4003b25 100644 ---- a/ipaserver/install/server/replicainstall.py -+++ b/ipaserver/install/server/replicainstall.py -@@ -770,6 +770,15 @@ def promotion_check_ipa_domain(master_ldap_conn, basedn): - )) - - -+def promotion_check_host_principal_auth_ind(conn, hostdn): -+ entry = conn.get_entry(hostdn, ['krbprincipalauthind']) -+ if 'krbprincipalauthind' in entry: -+ raise RuntimeError( -+ "Client cannot be promoted to a replica if the host principal " -+ "has an authentication indicator set." -+ ) -+ -+ - @common_cleanup - @preserve_enrollment_state - def promote_check(installer): -@@ -956,6 +965,10 @@ def promote_check(installer): - config.master_host_name, None) - - promotion_check_ipa_domain(conn, remote_api.env.basedn) -+ hostdn = DN(('fqdn', api.env.host), -+ api.env.container_host, -+ api.env.basedn) -+ promotion_check_host_principal_auth_ind(conn, hostdn) - - # Make sure that domain fulfills minimal domain level - # requirement -diff --git a/ipaserver/plugins/host.py b/ipaserver/plugins/host.py -index eb1f8ef042faf4b0deadfd5cef47f7688836506e..41fa933e2422184eafc4eae185a163082b96e045 100644 ---- a/ipaserver/plugins/host.py -+++ b/ipaserver/plugins/host.py -@@ -38,7 +38,7 @@ from .baseldap import (LDAPQuery, LDAPObject, LDAPCreate, - LDAPAddAttributeViaOption, - LDAPRemoveAttributeViaOption) - from .service import ( -- validate_realm, normalize_principal, -+ validate_realm, validate_auth_indicator, normalize_principal, - set_certificate_attrs, ticket_flags_params, update_krbticketflags, - set_kerberos_attrs, rename_ipaallowedtoperform_from_ldap, - rename_ipaallowedtoperform_to_ldap, revoke_certs) -@@ -735,6 +735,8 @@ class host_add(LDAPCreate): - update_krbticketflags(ldap, entry_attrs, attrs_list, options, False) - if 'krbticketflags' in entry_attrs: - entry_attrs['objectclass'].append('krbticketpolicyaux') -+ validate_auth_indicator(entry_attrs) -+ - return dn - - def post_callback(self, ldap, dn, entry_attrs, *keys, **options): -@@ -993,6 +995,7 @@ class host_mod(LDAPUpdate): - if 'krbprincipalaux' not in (item.lower() for item in - entry_attrs['objectclass']): - entry_attrs['objectclass'].append('krbprincipalaux') -+ validate_auth_indicator(entry_attrs) - - add_sshpubkey_to_attrs_pre(self.context, attrs_list) - -diff --git a/ipaserver/plugins/service.py b/ipaserver/plugins/service.py -index 1c93478049f5bdfdaf8503e459bd962dbbee9b44..cfbbff3c69c6a92535df58c51767c3d0952c7b0b 100644 ---- a/ipaserver/plugins/service.py -+++ b/ipaserver/plugins/service.py -@@ -201,6 +201,28 @@ def validate_realm(ugettext, principal): - raise errors.RealmMismatch() - - -+def validate_auth_indicator(entry): -+ new_value = entry.get('krbprincipalauthind', None) -+ if not new_value: -+ return -+ # The following services are considered internal IPA services -+ # and shouldn't be allowed to have auth indicators. -+ # https://pagure.io/freeipa/issue/8206 -+ pkey = api.Object['service'].get_primary_key_from_dn(entry.dn) -+ principal = kerberos.Principal(pkey) -+ server = api.Command.server_find(principal.hostname)['result'] -+ if server: -+ prefixes = ("host", "cifs", "ldap", "HTTP") -+ else: -+ prefixes = ("cifs",) -+ if principal.service_name in prefixes: -+ raise errors.ValidationError( -+ name='krbprincipalauthind', -+ error=_('authentication indicators not allowed ' -+ 'in service "%s"' % principal.service_name) -+ ) -+ -+ - def normalize_principal(value): - """ - Ensure that the name in the principal is lower-case. The realm is -@@ -652,6 +674,7 @@ class service_add(LDAPCreate): - hostname) - - self.obj.validate_ipakrbauthzdata(entry_attrs) -+ validate_auth_indicator(entry_attrs) - - if not options.get('force', False): - # We know the host exists if we've gotten this far but we -@@ -846,6 +869,7 @@ class service_mod(LDAPUpdate): - assert isinstance(dn, DN) - - self.obj.validate_ipakrbauthzdata(entry_attrs) -+ validate_auth_indicator(entry_attrs) - - # verify certificates - certs = entry_attrs.get('usercertificate') or [] --- -2.26.3 - diff --git a/SOURCES/0002-ipa-kdb-fix-requester-SID-check-according-to-MS-KILE.patch b/SOURCES/0002-ipa-kdb-fix-requester-SID-check-according-to-MS-KILE.patch new file mode 100644 index 0000000..b220cc9 --- /dev/null +++ b/SOURCES/0002-ipa-kdb-fix-requester-SID-check-according-to-MS-KILE.patch @@ -0,0 +1,122 @@ +From 7d93bda31ce0b4e0e22c6e464c9138800dcf8b1c Mon Sep 17 00:00:00 2001 +From: Alexander Bokovoy +Date: Fri, 26 Nov 2021 11:13:51 +0200 +Subject: [PATCH] ipa-kdb: fix requester SID check according to MS-KILE and + MS-SFU updates + +New versions of MS-KILE and MS-SFU after Windows Server November 2021 +security updates add PAC_REQUESTER_SID buffer check behavior: + + - PAC_REQUESTER_SID should only be added for TGT requests + + - if PAC_REQUESTER_SID is present, KDC must verify that the cname on + the ticket resolves to the account with the same SID as the + PAC_REQUESTER_SID. If it doesn't KDC must respond with + KDC_ERR_TKT_REVOKED + +Change requester SID check to skip exact check for non-local +PAC_REQUESTER_SID but harden to ensure it comes from the trusted domains +we know about. + +If requester SID is the same as in PAC, we already do cname vs PAC SID +verification. + +With these changes FreeIPA works against Windows Server 2019 with +November 2021 security fixes in cross-realm S4U2Self operations. + +Fixes: https://pagure.io/freeipa/issue/9031 + +Signed-off-by: Alexander Bokovoy +Reviewed-By: Rob Crittenden +--- + daemons/ipa-kdb/ipa_kdb_mspac.c | 47 ++++++++++++++++++++++++--------- + 1 file changed, 34 insertions(+), 13 deletions(-) + +diff --git a/daemons/ipa-kdb/ipa_kdb_mspac.c b/daemons/ipa-kdb/ipa_kdb_mspac.c +index 538cfbba958068bd2ee0aaae7a2743ae82237898..1b972c167dd50619c7a6bd78eb5c81b0e05a4832 100644 +--- a/daemons/ipa-kdb/ipa_kdb_mspac.c ++++ b/daemons/ipa-kdb/ipa_kdb_mspac.c +@@ -1697,7 +1697,7 @@ static krb5_error_code check_logon_info_consistent(krb5_context context, + "local [%s], PAC [%s]", + dom ? dom : "", + sid ? sid : ""); +- return KRB5KDC_ERR_POLICY; ++ return KRB5KDC_ERR_TGT_REVOKED; + } + } + +@@ -1709,7 +1709,7 @@ static krb5_error_code check_logon_info_consistent(krb5_context context, + kerr = ipadb_get_principal(context, client_princ, flags, &client_actual); + if (kerr != 0) { + krb5_klog_syslog(LOG_ERR, "PAC issue: ipadb_get_principal failed."); +- return KRB5KDC_ERR_POLICY; ++ return KRB5KDC_ERR_TGT_REVOKED; + } + + ied = (struct ipadb_e_data *)client_actual->e_data; +@@ -1743,7 +1743,7 @@ static krb5_error_code check_logon_info_consistent(krb5_context context, + "local [%s] vs PAC [%s]", + local_sid ? local_sid : "", + pac_sid ? pac_sid : ""); +- kerr = KRB5KDC_ERR_POLICY; ++ kerr = KRB5KDC_ERR_TGT_REVOKED; + goto done; + } + +@@ -2005,22 +2005,43 @@ static krb5_error_code ipadb_check_logon_info(krb5_context context, + /* Check that requester SID is the same as in the PAC entry */ + if (requester_sid != NULL) { + struct dom_sid client_sid; ++ bool is_from_trusted_domain = false; + kerr = ipadb_get_sid_from_pac(tmpctx, info.info, &client_sid); + if (kerr) { + goto done; + } + result = dom_sid_check(&client_sid, requester_sid, true); + if (!result) { +- /* memctx is freed by the caller */ +- char *pac_sid = dom_sid_string(tmpctx, &client_sid); +- char *req_sid = dom_sid_string(tmpctx, requester_sid); +- krb5_klog_syslog(LOG_ERR, "PAC issue: PAC has a SID " +- "different from what PAC requester claims. " +- "PAC [%s] vs PAC requester [%s]", +- pac_sid ? pac_sid : "", +- req_sid ? req_sid : ""); +- kerr = KRB5KDC_ERR_POLICY; +- goto done; ++ struct ipadb_context *ipactx = ipadb_get_context(context); ++ if (!ipactx || !ipactx->mspac) { ++ return KRB5_KDB_DBNOTINITED; ++ } ++ /* In S4U case we might be dealing with the PAC issued by the trusted domain */ ++ 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++) { ++ result = dom_sid_check(&ipactx->mspac->trusts[i].domsid, ++ requester_sid, false); ++ if (result) { ++ is_from_trusted_domain = true; ++ break; ++ } ++ } ++ } ++ ++ if (!is_from_trusted_domain) { ++ /* memctx is freed by the caller */ ++ char *pac_sid = dom_sid_string(tmpctx, &client_sid); ++ char *req_sid = dom_sid_string(tmpctx, requester_sid); ++ krb5_klog_syslog(LOG_ERR, "PAC issue: PAC has a SID " ++ "different from what PAC requester claims. " ++ "PAC [%s] vs PAC requester [%s]", ++ pac_sid ? pac_sid : "", ++ req_sid ? req_sid : ""); ++ kerr = KRB5KDC_ERR_TGT_REVOKED; ++ goto done; ++ } + } + } + +-- +2.31.1 + diff --git a/SOURCES/0003-ipatests-ensure-auth-indicators-can-t-be-added-to-in.patch b/SOURCES/0003-ipatests-ensure-auth-indicators-can-t-be-added-to-in.patch deleted file mode 100644 index 681a79d..0000000 --- a/SOURCES/0003-ipatests-ensure-auth-indicators-can-t-be-added-to-in.patch +++ /dev/null @@ -1,138 +0,0 @@ -From 538a9992fd1394ed24cbcdf2a2a27694ac28da55 Mon Sep 17 00:00:00 2001 -From: Antonio Torres -Date: Mon, 8 Mar 2021 18:20:35 +0100 -Subject: [PATCH] ipatests: ensure auth indicators can't be added to internal - IPA services - -Authentication indicators should not be added to internal IPA services, -since this can lead to a broken IPA setup. In case a client with -an auth indicator set in its host principal, promoting it to a replica -should fail. - -Related: https://pagure.io/freeipa/issue/8206 -Signed-off-by: Antonio Torres ---- - .../test_replica_promotion.py | 38 +++++++++++++++++++ - ipatests/test_xmlrpc/test_host_plugin.py | 10 +++++ - ipatests/test_xmlrpc/test_service_plugin.py | 21 ++++++++++ - 3 files changed, 69 insertions(+) - -diff --git a/ipatests/test_integration/test_replica_promotion.py b/ipatests/test_integration/test_replica_promotion.py -index 0a137dbdcb068811899e7ff7914730f14ea651c1..b9c56f775d08885cb6b1226eeb7bcf105f87cdc1 100644 ---- a/ipatests/test_integration/test_replica_promotion.py -+++ b/ipatests/test_integration/test_replica_promotion.py -@@ -101,6 +101,44 @@ class TestReplicaPromotionLevel1(ReplicaPromotionBase): - assert result.returncode == 1 - assert expected_err in result.stderr_text - -+ @replicas_cleanup -+ def test_install_with_host_auth_ind_set(self): -+ """ A client shouldn't be able to be promoted if it has -+ any auth indicator set in the host principal. -+ https://pagure.io/freeipa/issue/8206 -+ """ -+ -+ client = self.replicas[0] -+ # Configure firewall first -+ Firewall(client).enable_services(["freeipa-ldap", -+ "freeipa-ldaps"]) -+ -+ client.run_command(['ipa-client-install', '-U', -+ '--domain', self.master.domain.name, -+ '--realm', self.master.domain.realm, -+ '-p', 'admin', -+ '-w', self.master.config.admin_password, -+ '--server', self.master.hostname, -+ '--force-join']) -+ -+ tasks.kinit_admin(client) -+ -+ client.run_command(['ipa', 'host-mod', '--auth-ind=otp', -+ client.hostname]) -+ -+ res = client.run_command(['ipa-replica-install', '-U', '-w', -+ self.master.config.dirman_password], -+ raiseonerr=False) -+ -+ client.run_command(['ipa', 'host-mod', '--auth-ind=', -+ client.hostname]) -+ -+ expected_err = ("Client cannot be promoted to a replica if the host " -+ "principal has an authentication indicator set.") -+ assert res.returncode == 1 -+ assert expected_err in res.stderr_text -+ -+ - @replicas_cleanup - def test_one_command_installation(self): - """ -diff --git a/ipatests/test_xmlrpc/test_host_plugin.py b/ipatests/test_xmlrpc/test_host_plugin.py -index c66bbc865cd5e1ee5ee5e1874c177a3ea9b08c93..9cfde3565d48e103a0549e2bfb7579e07668f41b 100644 ---- a/ipatests/test_xmlrpc/test_host_plugin.py -+++ b/ipatests/test_xmlrpc/test_host_plugin.py -@@ -605,6 +605,16 @@ class TestProtectedMaster(XMLRPC_test): - error=u'An IPA master host cannot be deleted or disabled')): - command() - -+ def test_try_add_auth_ind_master(self, this_host): -+ command = this_host.make_update_command({ -+ u'krbprincipalauthind': u'radius'}) -+ with raises_exact(errors.ValidationError( -+ name='krbprincipalauthind', -+ error=u'authentication indicators not allowed ' -+ 'in service "host"' -+ )): -+ command() -+ - - @pytest.mark.tier1 - class TestValidation(XMLRPC_test): -diff --git a/ipatests/test_xmlrpc/test_service_plugin.py b/ipatests/test_xmlrpc/test_service_plugin.py -index 4c845938c33e2eca4235d53c4f4644c2fcdeda9c..ed634a0455a41dce367ed638634d1fc6d9e47553 100644 ---- a/ipatests/test_xmlrpc/test_service_plugin.py -+++ b/ipatests/test_xmlrpc/test_service_plugin.py -@@ -25,6 +25,7 @@ from ipalib import api, errors - from ipatests.test_xmlrpc.xmlrpc_test import Declarative, fuzzy_uuid, fuzzy_hash - from ipatests.test_xmlrpc.xmlrpc_test import fuzzy_digits, fuzzy_date, fuzzy_issuer - from ipatests.test_xmlrpc.xmlrpc_test import fuzzy_hex, XMLRPC_test -+from ipatests.test_xmlrpc.xmlrpc_test import raises_exact - from ipatests.test_xmlrpc import objectclasses - from ipatests.test_xmlrpc.testcert import get_testcert, subject_base - from ipatests.test_xmlrpc.test_user_plugin import get_user_result, get_group_dn -@@ -1552,6 +1553,15 @@ def indicators_host(request): - return tracker.make_fixture(request) - - -+@pytest.fixture(scope='function') -+def this_host(request): -+ """Fixture for the current master""" -+ tracker = HostTracker(name=api.env.host.partition('.')[0], -+ fqdn=api.env.host) -+ tracker.exists = True -+ return tracker -+ -+ - @pytest.fixture(scope='function') - def indicators_service(request): - tracker = ServiceTracker( -@@ -1587,6 +1597,17 @@ class TestAuthenticationIndicators(XMLRPC_test): - expected_updates={u'krbprincipalauthind': [u'radius']} - ) - -+ def test_update_indicator_internal_service(self, this_host): -+ command = this_host.make_command('service_mod', -+ 'ldap/' + this_host.fqdn, -+ **dict(krbprincipalauthind='otp')) -+ with raises_exact(errors.ValidationError( -+ name='krbprincipalauthind', -+ error=u'authentication indicators not allowed ' -+ 'in service "ldap"' -+ )): -+ command() -+ - - @pytest.fixture(scope='function') - def managing_host(request): --- -2.26.3 - diff --git a/SOURCES/0004-stageuser-add-ipauserauthtypeclass-when-required.patch b/SOURCES/0004-stageuser-add-ipauserauthtypeclass-when-required.patch deleted file mode 100644 index 2d9e34f..0000000 --- a/SOURCES/0004-stageuser-add-ipauserauthtypeclass-when-required.patch +++ /dev/null @@ -1,57 +0,0 @@ -From a8d6257b2cf64c3dd2b1c5d7bcf81acc3b766853 Mon Sep 17 00:00:00 2001 -From: Florence Blanc-Renaud -Date: Mon, 5 Jul 2021 09:51:41 +0200 -Subject: [PATCH] stageuser: add ipauserauthtypeclass when required - -The command -ipa stageuser-add --user-auth-type=xxx -is currently failing because the objectclass ipauserauthtypeclass -is missing from the created entry. - -There is code adding the missing objectclass in the -pre_common_callback method of user_add, and this code should -be common to user_add and stageuser_add. In order to avoid code -duplication, it makes more sense to move the existing code to -pre_common_callback of baseuser_add, that is called by both -classes. - -Fixes: https://pagure.io/freeipa/issue/8909 -Reviewed-By: Rob Crittenden -Reviewed-By: Alexander Bokovoy ---- - ipaserver/plugins/baseuser.py | 3 +++ - ipaserver/plugins/user.py | 4 ---- - 2 files changed, 3 insertions(+), 4 deletions(-) - -diff --git a/ipaserver/plugins/baseuser.py b/ipaserver/plugins/baseuser.py -index ae16a978ab01f9c5c257e9cb5567c918a7fafdc5..6035228f19ef8acaf4992490d5512c126881816d 100644 ---- a/ipaserver/plugins/baseuser.py -+++ b/ipaserver/plugins/baseuser.py -@@ -539,6 +539,9 @@ class baseuser_add(LDAPCreate): - if entry_attrs.get('ipatokenradiususername', None): - add_missing_object_class(ldap, u'ipatokenradiusproxyuser', dn, - entry_attrs, update=False) -+ if entry_attrs.get('ipauserauthtype', None): -+ add_missing_object_class(ldap, u'ipauserauthtypeclass', dn, -+ entry_attrs, update=False) - - def post_common_callback(self, ldap, dn, entry_attrs, *keys, **options): - assert isinstance(dn, DN) -diff --git a/ipaserver/plugins/user.py b/ipaserver/plugins/user.py -index 6f7facb5380ba56feab39b71cd265776f3ab57d8..e4ee572b236c288fd7dcf1d44c5adf1f836f63aa 100644 ---- a/ipaserver/plugins/user.py -+++ b/ipaserver/plugins/user.py -@@ -617,10 +617,6 @@ class user_add(baseuser_add): - 'ipauser' not in entry_attrs['objectclass']: - entry_attrs['objectclass'].append('ipauser') - -- if 'ipauserauthtype' in entry_attrs and \ -- 'ipauserauthtypeclass' not in entry_attrs['objectclass']: -- entry_attrs['objectclass'].append('ipauserauthtypeclass') -- - rcl = entry_attrs.get('ipatokenradiusconfiglink', None) - if rcl: - if 'ipatokenradiusproxyuser' not in entry_attrs['objectclass']: --- -2.26.3 - diff --git a/SOURCES/0005-XMLRPC-test-add-a-test-for-stageuser-add-user-auth-t.patch b/SOURCES/0005-XMLRPC-test-add-a-test-for-stageuser-add-user-auth-t.patch deleted file mode 100644 index d96d52f..0000000 --- a/SOURCES/0005-XMLRPC-test-add-a-test-for-stageuser-add-user-auth-t.patch +++ /dev/null @@ -1,32 +0,0 @@ -From 932910456e0269edefe396d4af96447f90ff29b3 Mon Sep 17 00:00:00 2001 -From: Florence Blanc-Renaud -Date: Mon, 5 Jul 2021 10:22:31 +0200 -Subject: [PATCH] XMLRPC test: add a test for stageuser-add --user-auth-type - -Related: https://pagure.io/freeipa/issue/8909 -Reviewed-By: Rob Crittenden -Reviewed-By: Alexander Bokovoy ---- - ipatests/test_xmlrpc/test_stageuser_plugin.py | 6 ++++++ - 1 file changed, 6 insertions(+) - -diff --git a/ipatests/test_xmlrpc/test_stageuser_plugin.py b/ipatests/test_xmlrpc/test_stageuser_plugin.py -index 5586fc607e134938225c1c982fc39d169847f549..bc606b093c98ce204ad4ea17e5c16273144fa2e7 100644 ---- a/ipatests/test_xmlrpc/test_stageuser_plugin.py -+++ b/ipatests/test_xmlrpc/test_stageuser_plugin.py -@@ -343,6 +343,12 @@ class TestStagedUser(XMLRPC_test): - result = command() - assert result['count'] == 1 - -+ def test_create_withuserauthtype(self, stageduser): -+ stageduser.ensure_missing() -+ command = stageduser.make_create_command( -+ options={u'ipauserauthtype': u'password'}) -+ command() -+ - - @pytest.mark.tier1 - class TestCreateInvalidAttributes(XMLRPC_test): --- -2.26.3 - diff --git a/SOURCES/0006-augeas-bump-version-for-rhel9.patch b/SOURCES/0006-augeas-bump-version-for-rhel9.patch deleted file mode 100644 index 35859fe..0000000 --- a/SOURCES/0006-augeas-bump-version-for-rhel9.patch +++ /dev/null @@ -1,40 +0,0 @@ -From 9144526d2d7e7dcd8503c6c38226e17ebb4ed8b9 Mon Sep 17 00:00:00 2001 -From: Florence Blanc-Renaud -Date: Wed, 7 Jul 2021 10:49:25 +0200 -Subject: [PATCH] augeas: bump version for rhel9 - -augeas 1.12.1-0.1 adds support for the new chony configuration -settings. - -Related: https://pagure.io/freeipa/issue/8676 -Reviewed-By: Francois Cami -Reviewed-By: Anuja More ---- - freeipa.spec.in | 5 ++++- - 1 file changed, 4 insertions(+), 1 deletion(-) - -diff --git a/freeipa.spec.in b/freeipa.spec.in -index fbfe4d09eedc169112dcdc18a953134de67b7731..ae4af099f39641a9f5163d61cfb37e1c3afb6f4b 100755 ---- a/freeipa.spec.in -+++ b/freeipa.spec.in -@@ -162,13 +162,16 @@ - - # augeas support for new chrony options - # see https://pagure.io/freeipa/issue/8676 --# Note: will need to be updated for RHEL9 when a fix is available for - # https://bugzilla.redhat.com/show_bug.cgi?id=1931787 - %if 0%{?fedora} >= 33 - %global augeas_version 1.12.0-6 - %else -+%if 0%{?rhel} >= 9 -+%global augeas_version 1.12.1-0 -+%else - %global augeas_version 1.12.0-3 - %endif -+%endif - - %global plugin_dir %{_libdir}/dirsrv/plugins - %global etc_systemd_dir %{_sysconfdir}/systemd/system --- -2.26.3 - diff --git a/SOURCES/0007-man-page-update-ipa-server-upgrade.1.patch b/SOURCES/0007-man-page-update-ipa-server-upgrade.1.patch deleted file mode 100644 index df25002..0000000 --- a/SOURCES/0007-man-page-update-ipa-server-upgrade.1.patch +++ /dev/null @@ -1,35 +0,0 @@ -From ecb407864fde4d917dabe0aae95881561ed384ab Mon Sep 17 00:00:00 2001 -From: Florence Blanc-Renaud -Date: Wed, 7 Jul 2021 14:11:40 +0200 -Subject: [PATCH] man page: update ipa-server-upgrade.1 - -The man page needs to clarify in which case the command needs -to be run. - -Fixes: https://pagure.io/freeipa/issue/8913 -Reviewed-By: Francois Cami ---- - install/tools/man/ipa-server-upgrade.1 | 7 ++++++- - 1 file changed, 6 insertions(+), 1 deletion(-) - -diff --git a/install/tools/man/ipa-server-upgrade.1 b/install/tools/man/ipa-server-upgrade.1 -index 3db19b0f13da1f5a36bd6e8df23fc916d0401a6d..f01e21c6b599499c4c6dbbcf120b19a3431fb3ed 100644 ---- a/install/tools/man/ipa-server-upgrade.1 -+++ b/install/tools/man/ipa-server-upgrade.1 -@@ -8,7 +8,12 @@ ipa\-server\-upgrade \- upgrade IPA server - .SH "SYNOPSIS" - ipa\-server\-upgrade [options] - .SH "DESCRIPTION" --ipa\-server\-upgrade is used to upgrade IPA server when the IPA packages are being updated. It is not intended to be executed by end\-users. -+ipa\-server\-upgrade is executed automatically to upgrade IPA server when -+the IPA packages are being updated. It is not intended to be executed by -+end\-users, unless the automatic execution reports an error. In this case, -+the administrator needs to identify and fix the issue that is causing the -+upgrade failure (with the help of /var/log/ipaupgrade.log) -+and manually re\-run ipa\-server\-upgrade. - - ipa\-server\-upgrade will: - --- -2.26.3 - diff --git a/SOURCES/0008-Add-basic-support-for-subordinate-user-group-ids.patch b/SOURCES/0008-Add-basic-support-for-subordinate-user-group-ids.patch deleted file mode 100644 index 6b52566..0000000 --- a/SOURCES/0008-Add-basic-support-for-subordinate-user-group-ids.patch +++ /dev/null @@ -1,2324 +0,0 @@ -From 69cd05bf635d19b9844f65d83dace05136a40326 Mon Sep 17 00:00:00 2001 -From: Christian Heimes -Date: Fri, 19 Mar 2021 11:48:38 +0100 -Subject: [PATCH] Add basic support for subordinate user/group ids - -New LDAP object class "ipaUserSubordinate" with four new fields: -- ipasubuidnumber / ipasubuidcount -- ipasubgidnumber / ipasgbuidcount - -New self-service permission to add subids. - -New command user-auto-subid to auto-assign subid - -The code hard-codes counts to 65536, sets subgid equal to subuid, and -does not allow removal of subids. There is also a hack that emulates a -DNA plugin with step interval 65536 for testing. - -Work around problem with older SSSD clients that fail with unknown -idrange type "ipa-local-subid", see: https://github.com/SSSD/sssd/issues/5571 - -Related: https://pagure.io/freeipa/issue/8361 -Signed-off-by: Christian Heimes -Reviewed-By: Francois Cami -Reviewed-By: Rob Crittenden -Reviewed-By: Francois Cami -Reviewed-By: Rob Crittenden ---- - ACI.txt | 2 +- - API.txt | 47 ++- - Makefile.am | 2 +- - VERSION.m4 | 4 +- - doc/designs/index.rst | 1 + - doc/designs/subordinate-ids.md | 468 ++++++++++++++++++++++ - freeipa.spec.in | 1 + - install/share/60basev2.ldif | 1 + - install/share/60basev4.ldif | 19 + - install/share/Makefile.am | 1 + - install/share/bootstrap-template.ldif | 22 + - install/share/dna.ldif | 20 + - install/tools/Makefile.am | 2 + - install/tools/ipa-subids.in | 8 + - install/ui/src/freeipa/user.js | 53 ++- - install/updates/20-indices.update | 18 + - install/updates/73-subid.update | 102 +++++ - install/updates/Makefile.am | 1 + - ipalib/constants.py | 13 + - ipaserver/install/adtrustinstance.py | 29 +- - ipaserver/install/dsinstance.py | 43 +- - ipaserver/install/ipa_subids.py | 154 +++++++ - ipaserver/install/ldapupdate.py | 95 +++-- - ipaserver/plugins/baseuser.py | 274 ++++++++++++- - ipaserver/plugins/idrange.py | 10 +- - ipaserver/plugins/internal.py | 12 + - ipaserver/plugins/user.py | 17 +- - ipatests/prci_definitions/gating.yaml | 12 + - ipatests/test_integration/test_subids.py | 201 ++++++++++ - ipatests/test_xmlrpc/test_range_plugin.py | 7 + - 31 files changed, 1565 insertions(+), 75 deletions(-) - create mode 100644 doc/designs/subordinate-ids.md - create mode 100644 install/share/60basev4.ldif - create mode 100644 install/tools/ipa-subids.in - create mode 100644 install/updates/73-subid.update - create mode 100644 ipaserver/install/ipa_subids.py - create mode 100644 ipatests/test_integration/test_subids.py - -diff --git a/ACI.txt b/ACI.txt -index 05852cf6c0150db7d8de99a5f7a44e538df29e5e..fce02a333b212de9b61f920515eed3e356b1391b 100644 ---- a/ACI.txt -+++ b/ACI.txt -@@ -375,7 +375,7 @@ aci: (targetattr = "audio || businesscategory || carlicense || departmentnumber - dn: dc=ipa,dc=example - aci: (targetattr = "cn || createtimestamp || entryusn || gecos || gidnumber || homedirectory || loginshell || modifytimestamp || objectclass || uid || uidnumber")(target = "ldap:///cn=users,cn=compat,dc=ipa,dc=example")(version 3.0;acl "permission:System: Read User Compat Tree";allow (compare,read,search) userdn = "ldap:///anyone";) - dn: cn=users,cn=accounts,dc=ipa,dc=example --aci: (targetattr = "ipasshpubkey || ipauniqueid || ipauserauthtype || userclass")(targetfilter = "(objectclass=posixaccount)")(version 3.0;acl "permission:System: Read User IPA Attributes";allow (compare,read,search) userdn = "ldap:///all";) -+aci: (targetattr = "ipasshpubkey || ipasubgidcount || ipasubgidnumber || ipasubuidcount || ipasubuidnumber || ipauniqueid || ipauserauthtype || userclass")(targetfilter = "(objectclass=posixaccount)")(version 3.0;acl "permission:System: Read User IPA Attributes";allow (compare,read,search) userdn = "ldap:///all";) - dn: cn=users,cn=accounts,dc=ipa,dc=example - aci: (targetattr = "krbcanonicalname || krblastpwdchange || krbpasswordexpiration || krbprincipalaliases || krbprincipalexpiration || krbprincipalname || krbprincipaltype || nsaccountlock")(targetfilter = "(objectclass=posixaccount)")(version 3.0;acl "permission:System: Read User Kerberos Attributes";allow (compare,read,search) userdn = "ldap:///all";) - dn: cn=users,cn=accounts,dc=ipa,dc=example -diff --git a/API.txt b/API.txt -index 212ef807c771794dc2f89eb89e03b669eb49295b..262b4d6a72c7d7032a7027116f7a4f65aa620615 100644 ---- a/API.txt -+++ b/API.txt -@@ -4974,7 +4974,7 @@ output: Entry('result') - output: Output('summary', type=[, ]) - output: PrimaryKey('value') - command: stageuser_add/1 --args: 1,45,3 -+args: 1,46,3 - arg: Str('uid', cli_name='login') - option: Str('addattr*', cli_name='addattr') - option: Flag('all', autofill=True, cli_name='all', default=False) -@@ -4992,6 +4992,7 @@ option: Str('givenname', cli_name='first') - option: Str('homedirectory?', cli_name='homedir') - option: Str('initials?', autofill=True) - option: Str('ipasshpubkey*', cli_name='sshpubkey') -+option: Int('ipasubuidnumber?', cli_name='subuid') - option: Str('ipatokenradiusconfiglink?', cli_name='radius') - option: Str('ipatokenradiususername?', cli_name='radius_username') - option: StrEnum('ipauserauthtype*', cli_name='user_auth_type', values=[u'password', u'radius', u'otp', u'pkinit', u'hardened']) -@@ -5080,7 +5081,7 @@ output: Output('result', type=[]) - output: Output('summary', type=[, ]) - output: ListOfPrimaryKeys('value') - command: stageuser_find/1 --args: 1,58,4 -+args: 1,60,4 - arg: Str('criteria?') - option: Flag('all', autofill=True, cli_name='all', default=False) - option: Str('carlicense*', autofill=False) -@@ -5104,6 +5105,8 @@ option: Str('ipanthomedirectory?', autofill=False, cli_name='smb_home_dir') - option: StrEnum('ipanthomedirectorydrive?', autofill=False, cli_name='smb_home_drive', values=[u'A:', u'B:', u'C:', u'D:', u'E:', u'F:', u'G:', u'H:', u'I:', u'J:', u'K:', u'L:', u'M:', u'N:', u'O:', u'P:', u'Q:', u'R:', u'S:', u'T:', u'U:', u'V:', u'W:', u'X:', u'Y:', u'Z:']) - option: Str('ipantlogonscript?', autofill=False, cli_name='smb_logon_script') - option: Str('ipantprofilepath?', autofill=False, cli_name='smb_profile_path') -+option: Int('ipasubgidnumber?', autofill=False, cli_name='subgid') -+option: Int('ipasubuidnumber?', autofill=False, cli_name='subuid') - option: Str('ipatokenradiusconfiglink?', autofill=False, cli_name='radius') - option: Str('ipatokenradiususername?', autofill=False, cli_name='radius_username') - option: StrEnum('ipauserauthtype*', autofill=False, cli_name='user_auth_type', values=[u'password', u'radius', u'otp', u'pkinit', u'hardened']) -@@ -5145,7 +5148,7 @@ output: ListOfEntries('result') - output: Output('summary', type=[, ]) - output: Output('truncated', type=[]) - command: stageuser_mod/1 --args: 1,51,3 -+args: 1,52,3 - arg: Str('uid', cli_name='login') - option: Str('addattr*', cli_name='addattr') - option: Flag('all', autofill=True, cli_name='all', default=False) -@@ -5167,6 +5170,7 @@ option: StrEnum('ipanthomedirectorydrive?', autofill=False, cli_name='smb_home_d - option: Str('ipantlogonscript?', autofill=False, cli_name='smb_logon_script') - option: Str('ipantprofilepath?', autofill=False, cli_name='smb_profile_path') - option: Str('ipasshpubkey*', autofill=False, cli_name='sshpubkey') -+option: Int('ipasubuidnumber?', autofill=False, cli_name='subuid') - option: Str('ipatokenradiusconfiglink?', autofill=False, cli_name='radius') - option: Str('ipatokenradiususername?', autofill=False, cli_name='radius_username') - option: StrEnum('ipauserauthtype*', autofill=False, cli_name='user_auth_type', values=[u'password', u'radius', u'otp', u'pkinit', u'hardened']) -@@ -6058,7 +6062,7 @@ output: Entry('result') - output: Output('summary', type=[, ]) - output: PrimaryKey('value') - command: user_add/1 --args: 1,46,3 -+args: 1,47,3 - arg: Str('uid', cli_name='login') - option: Str('addattr*', cli_name='addattr') - option: Flag('all', autofill=True, cli_name='all', default=False) -@@ -6075,6 +6079,7 @@ option: Str('givenname', cli_name='first') - option: Str('homedirectory?', cli_name='homedir') - option: Str('initials?', autofill=True) - option: Str('ipasshpubkey*', cli_name='sshpubkey') -+option: Int('ipasubuidnumber?', cli_name='subuid') - option: Str('ipatokenradiusconfiglink?', cli_name='radius') - option: Str('ipatokenradiususername?', cli_name='radius_username') - option: StrEnum('ipauserauthtype*', cli_name='user_auth_type', values=[u'password', u'radius', u'otp', u'pkinit', u'hardened']) -@@ -6156,6 +6161,16 @@ option: Str('version?') - output: Entry('result') - output: Output('summary', type=[, ]) - output: PrimaryKey('value') -+command: user_auto_subid/1 -+args: 1,4,3 -+arg: Str('uid', cli_name='login') -+option: Flag('all', autofill=True, cli_name='all', default=False) -+option: Flag('no_members', autofill=True, default=False) -+option: Flag('raw', autofill=True, cli_name='raw', default=False) -+option: Str('version?') -+output: Entry('result') -+output: Output('summary', type=[, ]) -+output: PrimaryKey('value') - command: user_del/1 - args: 1,3,3 - arg: Str('uid+', cli_name='login') -@@ -6180,7 +6195,7 @@ output: Output('result', type=[]) - output: Output('summary', type=[, ]) - output: PrimaryKey('value') - command: user_find/1 --args: 1,61,4 -+args: 1,63,4 - arg: Str('criteria?') - option: Flag('all', autofill=True, cli_name='all', default=False) - option: Str('carlicense*', autofill=False) -@@ -6204,6 +6219,8 @@ option: Str('ipanthomedirectory?', autofill=False, cli_name='smb_home_dir') - option: StrEnum('ipanthomedirectorydrive?', autofill=False, cli_name='smb_home_drive', values=[u'A:', u'B:', u'C:', u'D:', u'E:', u'F:', u'G:', u'H:', u'I:', u'J:', u'K:', u'L:', u'M:', u'N:', u'O:', u'P:', u'Q:', u'R:', u'S:', u'T:', u'U:', u'V:', u'W:', u'X:', u'Y:', u'Z:']) - option: Str('ipantlogonscript?', autofill=False, cli_name='smb_logon_script') - option: Str('ipantprofilepath?', autofill=False, cli_name='smb_profile_path') -+option: Int('ipasubgidnumber?', autofill=False, cli_name='subgid') -+option: Int('ipasubuidnumber?', autofill=False, cli_name='subuid') - option: Str('ipatokenradiusconfiglink?', autofill=False, cli_name='radius') - option: Str('ipatokenradiususername?', autofill=False, cli_name='radius_username') - option: StrEnum('ipauserauthtype*', autofill=False, cli_name='user_auth_type', values=[u'password', u'radius', u'otp', u'pkinit', u'hardened']) -@@ -6247,8 +6264,23 @@ output: Output('count', type=[]) - output: ListOfEntries('result') - output: Output('summary', type=[, ]) - output: Output('truncated', type=[]) -+command: user_match_subid/1 -+args: 1,8,4 -+arg: Str('criteria?') -+option: Flag('all', autofill=True, cli_name='all', default=False) -+option: Int('ipasubuidnumber', autofill=False, cli_name='subuid') -+option: Flag('no_members', autofill=True, default=True) -+option: Flag('pkey_only?', autofill=True, default=False) -+option: Flag('raw', autofill=True, cli_name='raw', default=False) -+option: Int('sizelimit?', autofill=False) -+option: Int('timelimit?', autofill=False) -+option: Str('version?') -+output: Output('count', type=[]) -+output: ListOfEntries('result') -+output: Output('summary', type=[, ]) -+output: Output('truncated', type=[]) - command: user_mod/1 --args: 1,52,3 -+args: 1,53,3 - arg: Str('uid', cli_name='login') - option: Str('addattr*', cli_name='addattr') - option: Flag('all', autofill=True, cli_name='all', default=False) -@@ -6270,6 +6302,7 @@ option: StrEnum('ipanthomedirectorydrive?', autofill=False, cli_name='smb_home_d - option: Str('ipantlogonscript?', autofill=False, cli_name='smb_logon_script') - option: Str('ipantprofilepath?', autofill=False, cli_name='smb_profile_path') - option: Str('ipasshpubkey*', autofill=False, cli_name='sshpubkey') -+option: Int('ipasubuidnumber?', autofill=False, cli_name='subuid') - option: Str('ipatokenradiusconfiglink?', autofill=False, cli_name='radius') - option: Str('ipatokenradiususername?', autofill=False, cli_name='radius_username') - option: StrEnum('ipauserauthtype*', autofill=False, cli_name='user_auth_type', values=[u'password', u'radius', u'otp', u'pkinit', u'hardened']) -@@ -7183,10 +7216,12 @@ default: user_add_cert/1 - default: user_add_certmapdata/1 - default: user_add_manager/1 - default: user_add_principal/1 -+default: user_auto_subid/1 - default: user_del/1 - default: user_disable/1 - default: user_enable/1 - default: user_find/1 -+default: user_match_subid/1 - default: user_mod/1 - default: user_remove_cert/1 - default: user_remove_certmapdata/1 -diff --git a/Makefile.am b/Makefile.am -index c5a33e67f56b2c6f9efb5b4c6af3f7a44ccbdb3c..321df05a7c44f32929a2c5ec45341a42105a8e2f 100644 ---- a/Makefile.am -+++ b/Makefile.am -@@ -229,7 +229,7 @@ fasttest: $(GENERATED_PYTHON_FILES) ipasetup.py - --ignore $(abspath $(top_srcdir))/ipatests/test_integration \ - --ignore $(abspath $(top_srcdir))/ipatests/test_xmlrpc - --fastlint: $(GENERATED_PYTHON_FILES) ipasetup.py -+fastlint: $(GENERATED_PYTHON_FILES) ipasetup.py acilint apilint - if ! WITH_PYLINT - @echo "ERROR: pylint not available"; exit 1 - endif -diff --git a/VERSION.m4 b/VERSION.m4 -index 9f024675f905a1ee771b6ff293c25b2ac46d92df..1c1e0d56c0eb5c15be0887fae9f90e399757acc7 100644 ---- a/VERSION.m4 -+++ b/VERSION.m4 -@@ -86,8 +86,8 @@ define(IPA_DATA_VERSION, 20100614120000) - # # - ######################################################## - define(IPA_API_VERSION_MAJOR, 2) --define(IPA_API_VERSION_MINOR, 242) --# Last change: add status options for cert-find -+# Last change: add subordinate id feature -+define(IPA_API_VERSION_MINOR, 243) - - - ######################################################## -diff --git a/doc/designs/index.rst b/doc/designs/index.rst -index cbec1096c363c9c31656b05f22c50321cd45e073..6dd0edff3004fd0d19208f0c063d4156bde3bf91 100644 ---- a/doc/designs/index.rst -+++ b/doc/designs/index.rst -@@ -17,3 +17,4 @@ FreeIPA design documentation - membermanager.md - hidden-replicas.md - disable-stale-users.md -+ subordinate-ids.md -diff --git a/doc/designs/subordinate-ids.md b/doc/designs/subordinate-ids.md -new file mode 100644 -index 0000000000000000000000000000000000000000..1b578667a8cfdda223af38a14d142c72a5d5c073 ---- /dev/null -+++ b/doc/designs/subordinate-ids.md -@@ -0,0 +1,468 @@ -+# Central management of subordinate user and group ids -+ -+Subordinate ids are a Linux Kernel feature to grant a user additional -+user and group id ranges. Amongst others the feature can be used -+by container runtime engies to implement rootless containers. -+Traditionally subordinate id ranges are configured in ``/etc/subuid`` -+and ``/etc/subgid``. -+ -+To make rootless containers in a large environment as easy as pie, IPA -+gains the ability to centrally manage and assign subordinate id ranges. -+SSSD and shadow-util are extended to read subordinate ids from IPA and -+provide them to userspace tools. -+ -+## Overview -+ -+Feature requests -+ -+* [FreeIPA feature request #8361](https://pagure.io/freeipa/issue/8361) -+* [SSSD feature request #5197](https://github.com/SSSD/sssd/issues/5197) -+* [shadow-util feature request #154](https://github.com/shadow-maint/shadow/issues/154) -+* [389-DS RFE for DNA plugin rhbz#1938239](https://bugzilla.redhat.com/show_bug.cgi?id=1938239) -+ -+Man pages -+ -+* [man subuid(5)](https://man7.org/linux/man-pages/man5/subuid.5.html) -+* [man subgid(5)](https://man7.org/linux/man-pages/man5/subgid.5.html) -+* [man user_namespaces(7)](https://man7.org/linux/man-pages/man7/user_namespaces.7.html) -+* [man newuidmap(1)](https://man7.org/linux/man-pages/man1/newuidmap.1.html) -+ -+Articles / blog posts -+* [Basic Setup and Use of Podman in a Rootless environment](https://github.com/containers/podman/blob/master/docs/tutorials/rootless_tutorial.md) -+* [How does rootless Podman work](https://opensource.com/article/19/2/how-does-rootless-podman-work) -+ -+## Design choices -+ -+Some design choices are owed to the circumstance that uids and gids -+are limited datatypes. The Linux Kernel and userland defines -+``uid_t`` and ``gid_t`` as unsigned 32bit integers (``uint32_t``), which -+limits possible values for numeric user and group ids to -+``0 .. 2^32-2``. ``(uid_t)-1`` is reserved for error reporting. On the -+other hand the user ``nobody`` typically has uid 65534 / gid 65534. This -+means we need to assign 65,536 subordinate ids to every user. The -+theoretical maximum amount of subordinate ranges is less than 65,536 -+(``65536 * 65536 == 2^32``). [``logins.def``](https://man7.org/linux/man-pages/man5/login.defs.5.html) -+also uses 65536 as default setting for ``SUB_UID_COUNT``. -+ -+The practical limit is far smaller. Subordinate ids should not overlap -+with system accounts, local user accounts, IPA user accounts, and -+mapped accounts from Active Directory. Therefore IPA uses the upper -+half of the uid_t range (>= 2^31 == 2,147,483,648) for subordinate ids. -+The high bit is rarely used. IPA limits general numeric ids -+(``uidNumber``, ``gidNumber``, ID ranges) to maximum values of signed -+32bit integer (2^31-1) for backwards compatibility with XML-RPC. -+``logins.def`` defaults to ``SUB_UID_MAX`` 600,100,000. -+ -+A default subordinate id count of 65,536 and a total range of approx. -+2.1 billion limits IPA to slightly more than 32,000 possible ranges. It -+may sound like a lot of users, but there are much bigger installations -+of IPA. For comparison Fedora Accounts has over 120,000 users stored in -+IPA. -+ -+For that reason we treat subordinate id space as premium real estate -+and don't auto-map or auto-assign subordinate ids by default. Instead -+we give the admin several options to assign them manually, semi-manual, -+or automatically. -+ -+### Revision 1 limitation -+ -+The first revision of the feature is deliberately limited and -+restricted. We are aiming for a simple implementation that covers -+basic use cases. Some restrictions may be lifted in the future. -+ -+* subuid and subgids cannot be set independently. They are always set -+ to the same value. -+* counts are hard-coded to value 65536 -+* once assigned subids cannot be removed -+* IPA does not support multiple subordinate id ranges. Contrary to -+ ``/etc/subuid``, users are limited to one set of subordinate ids. -+* subids are auto-assigned. Auto-assignment is currently emulated -+ until 389-DS has been extended to support DNA with step interval. -+* subids are allocated from hard-coded range -+ ``[2147483648..4294901767]`` (``2^31`` to ``2^32-1-65536``), which -+ is the upper 2.1 billion uids of ``uid_t`` (``uint32_t``). The range -+ can hold little 32,767 subordinate id ranges. -+* Active Directory support is out of scope and may be provided in the -+ future. -+ -+### Subid assignment example -+ -+``` -+>>> import itertools -+>>> def subids(): -+... for n in itertools.count(start=0): -+... start = SUBID_RANGE_START + (n * SUBID_COUNT) -+... last = start + SUBID_COUNT - 1 -+... yield (start, last) -+... -+>>> gen = subids() -+>>> next(gen) -+(2147483648, 2147549183) -+>>> next(gen) -+(2147549184, 2147614719) -+>>> next(gen) -+(2147614720, 2147680255) -+``` -+ -+The first user has 65565 subordinate ids from uid/gid ``2147483648`` -+to ``2147549183``, the next user has ``2147549184`` to ``2147614719``, -+and so on. The range count includes the start value. -+ -+An installation with multiple servers, 389-DS' -+[DNA](https://directory.fedoraproject.org/docs/389ds/design/dna-plugin.html) -+plug-in takes care of delegating and assigning chunks of subid ranges -+to servers. The DNA plug-in guarantees uniqueness across servers. -+ -+## LDAP -+ -+### LDAP schema extension -+ -+The subordinate id feature introduces a new auxiliar object class -+``ipaSubordinateId`` with four required attributes ``ipaSubUidNumber``, -+``ipaSubUidCount``, ``ipaSubGidNumber``, and ``ipaSubGidCount``. The -+attributes with ``number`` suffix store the start value of the interval. -+The ``count`` attributes contain the size of the interval including the -+start value. The maximum subid is -+``ipaSubUidNumber + ipaSubUidCount - 1``. -+ -+All four attributes are single-value ``INTEGER`` type with standard -+integer matching rules. OIDs ``2.16.840.1.113730.3.8.23.8`` and -+``2.16.840.1.113730.3.8.23.11`` are reserved for future use. -+ -+```raw -+attributeTypes: ( -+ 2.16.840.1.113730.3.8.23.6 -+ NAME 'ipaSubUidNumber' -+ DESC 'Numerical subordinate user ID (range start value)' -+ EQUALITY integerMatch ORDERING integerOrderingMatch -+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE -+ X-ORIGIN 'IPA v4.9' -+) -+attributeTypes: ( -+ 2.16.840.1.113730.3.8.23.7 -+ NAME 'ipaSubUidCount' -+ DESC 'Subordinate user ID count (range size)' -+ EQUALITY integerMatch ORDERING integerOrderingMatch -+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE -+ X-ORIGIN 'IPA v4.9' -+) -+attributeTypes: ( -+ 2.16.840.1.113730.3.8.23.9 -+ NAME 'ipaSubGidNumber' -+ DESC 'Numerical subordinate group ID (range start value)' -+ EQUALITY integerMatch ORDERING integerOrderingMatch -+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE -+ X-ORIGIN 'IPA v4.9' -+) -+attributeTypes: ( -+ 2.16.840.1.113730.3.8.23.10 -+ NAME 'ipaSubGidCount' -+ DESC 'Subordinate group ID count (range size)' -+ EQUALITY integerMatch ORDERING integerOrderingMatch -+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE -+ X-ORIGIN 'IPA v4.9' -+) -+``` -+ -+The ``ipaSubordinateId`` object class is an auxiliar subclass of -+``top`` and requires all four subordinate id attributes as well as -+``uidNumber``. It does not subclass ``posixAccount`` to make -+the class reusable in idview overrides later. -+ -+```raw -+objectClasses: ( -+ 2.16.840.1.113730.3.8.24.4 -+ NAME 'ipaSubordinateId' -+ DESC 'Subordinate uid and gid for users' -+ SUP top AUXILIARY -+ MUST ( uidNumber $ ipaSubUidNumber $ ipaSubUidCount $ ipaSubGidNumber $ ipaSubGidCount ) -+ X-ORIGIN 'IPA v4.9' -+) -+``` -+ -+The ``ipaSubordinateGid`` and ``ipaSubordinateUid`` are defined for -+future use. IPA always assumes the presence of ``ipaSubordinateId`` and -+does not use these object classes. -+ -+```raw -+objectClasses: ( -+ 2.16.840.1.113730.3.8.24.2 -+ NAME 'ipaSubordinateUid' -+ DESC 'Subordinate uids for users, see subuid(5)' -+ SUP top AUXILIARY -+ MUST ( uidNumber $ ipaSubUidNumber $ ipaSubUidCount ) -+ X-ORIGIN 'IPA v4.9' -+ ) -+objectClasses: ( -+ 2.16.840.1.113730.3.8.24.3 -+ NAME 'ipaSubordinateGid' -+ DESC 'Subordinate gids for users, see subgid(5)' -+ SUP top AUXILIARY -+ MUST ( uidNumber $ ipaSubGidNumber $ ipaSubGidCount ) -+ X-ORIGIN 'IPA v4.9' -+) -+``` -+ -+### Index -+ -+The attributes ``ipaSubUidNumber`` and ``ipaSubGidNumber`` are index -+for ``pres`` and ``eq`` with ``nsMatchingRule: integerOrderingMatch`` -+to enable efficient ``=``, ``>=``, and ``<=`` searches. -+ -+### Distributed numeric assignment (DNA) plug-in extension -+ -+Subordinate id auto-assignment requires an extension of 389-DS' -+[DNA](https://directory.fedoraproject.org/docs/389ds/design/dna-plugin.html) -+plug-in. The DNA plug-in is responsible for safely assigning unique -+numeric ids across all replicas. -+ -+Currently the DNA plug-in only supports a step size of ``1``. A new -+option ``dnaStepAttr`` (name is tentative) will tell the DNA plug-in -+to use the value of entry attributes as step size. -+ -+ -+## Permissions, Privileges, Roles -+ -+### Self-servive RBAC -+ -+The self-service permission enables users to request auto-assignment -+of subordinate uid and gid ranges for themselves. Subordinate ids cannot -+be modified or deleted. -+ -+* ACI: *selfservice: Add subordinate id* -+* Permission: *Self-service subordinate ID* -+* Privilege: *Subordinate ID Selfservice User* -+* Role: *Subordinate ID Selfservice Users* -+* role default member: n/a -+ -+### Administrator RBAC -+ -+The administrator permission allows privileged users to auto-assign -+subordinate ids to users. Once assigned subordinate ids cannot -+be modified or deleted. -+ -+* ACI: *Add subordinate ids to any user* -+* Permission: *Manage subordinate ID* -+* Privilege: *Subordinate ID Administrators* -+* default privilege role: *User Administrator* -+ -+ -+## Workflows -+ -+In the default configuration of IPA, neither existing users nor new -+users will have subordinate ids assigned. There are a couple of ways -+to assign subordinate ids to users. -+ -+### User administrator -+ -+Users with *User Administrator* role and members of the *admins* group -+have permission to auto-assign new subordinate ids to any user. Auto -+assignment can be performed with new ``user-auto-subid`` command on the -+command line or with the *Auto assign subordinate ids* action in the -+*Actions* drop-down menu in the web UI. -+ -+```shell -+$ ipa user-auto-subid someusername -+``` -+ -+### Self-service for group members -+ -+Ordinary users cannot self-service subordinate ids by default. Admins -+can assign the new *Subordinate ID Selfservice User* to users group to -+enable self-service for members of the group. -+ -+For example to enable self-service for all members of the default user -+group ``ipausers``, do: -+ -+```shell -+$ ipa role-add-member "Subordinate ID Selfservice User" --groups=ipausers -+``` -+ -+This allows members of ``ipausers`` to request subordinate ids with -+the ``user-auto-subid`` command or the *Auto assign subordinate ids* -+action in the web UI. -+ -+```shell -+$ ipa user-auto-subid myusername -+``` -+ -+### Auto assignment with user default object class -+ -+Admins can also enable auto-assignment of subordinate ids for all new -+users by adding ``ipasubordinateid`` as a default user objectclass. -+This can be accomplished in the web UI under "IPA Server" / -+"Configuration" / "Default user objectclasses" or on the command line -+with: -+ -+```shell -+$ ipa config-mod --addattr="ipaUserObjectClasses=ipasubordinateid" -+``` -+ -+**NOTE:** The objectclass must be written all lower case. -+ -+### ipa-subid tool -+ -+Finally IPA includes a new tool for mass-assignment of subordinate ids. -+The command uses automatic LDAPI EXTERNAL bind when it's executed as -+root user. Other it requires valid Kerberos TGT of an admin or user -+administrator. -+ -+```raw -+ -+# /usr/libexec/ipa/ipa-subids --help -+Usage: ipa-subids -+ -+Mass-assign subordinate ids -+ -+Options: -+ --version show program's version number and exit -+ -h, --help show this help message and exit -+ --group=GROUP Filter by group membership -+ --filter=USER_FILTER Raw LDAP filter -+ --dry-run Dry run mode. -+ --all-users All users -+ -+ Logging and output options: -+ -v, --verbose print debugging information -+ -d, --debug alias for --verbose (deprecated) -+ -q, --quiet output only errors -+ --log-file=FILE log to the given file -+ -+# # /usr/libexec/ipa/ipa-subids --group ipausers -+Processing user 'testsubordinated1' (1/15) -+Processing user 'testsubordinated2' (2/15) -+Processing user 'testsubordinated3' (3/15) -+Processing user 'testsubordinated4' (4/15) -+Processing user 'testsubordinated5' (5/15) -+Processing user 'testsubordinated6' (6/15) -+Processing user 'testsubordinated7' (7/15) -+Processing user 'testsubordinated8' (8/15) -+Processing user 'testsubordinated9' (9/15) -+Processing user 'testsubordinated10' (10/15) -+Processing user 'testsubordinated11' (11/15) -+Processing user 'testsubordinated12' (12/15) -+Processing user 'testsubordinated13' (13/15) -+Processing user 'testsubordinated14' (14/15) -+Processing user 'testsubordinated15' (15/15) -+Processed 15 user(s) -+The ipa-subids command was successful -+``` -+ -+### Find and match users by any subordinate id -+ -+The ``user-find`` command search by start value of subordinate uid and -+gid range. The new command ``user-match-subid`` can be used to find a -+user by any subordinate id in their range. -+ -+```raw -+$ ipa user-match-subid --subuid=2153185287 -+ User login: asmith -+ First name: Alice -+ Last name: Smith -+ ... -+ SubUID range start: 2153185280 -+ SubUID range size: 65536 -+ SubGID range start: 2153185280 -+ SubGID range size: 65536 -+---------------------------- -+Number of entries returned 1 -+---------------------------- -+$ ipa user-match-subid --subuid=2153185279 -+ User login: bjones -+ First name: Bob -+ Last name: Jones -+ ... -+ SubUID range start: 2153119744 -+ SubUID range size: 65536 -+ SubGID range start: 2153119744 -+ SubGID range size: 65536 -+---------------------------- -+Number of entries returned 1 -+---------------------------- -+``` -+ -+## SSSD integration -+ -+* base: ``cn=accounts,$SUFFIX`` / ``cn=users,cn=accounts,$SUFFIX`` -+* scope: ``SCOPE_SUBTREE`` (2) / ``SCOPE_ONELEVEL`` (1) -+* user filter: should include ``(objectClass=posixAccount)`` -+* attributes: ``uidNumber ipaSubUidNumber ipaSubUidCount ipaSubGidNumber ipaSubGidCount`` -+ -+SSSD can safely assume that only *user accounts* of type ``posixAccount`` -+have subordinate ids. In the first revision there are no other entries -+with subordinate ids. The ``posixAccount`` object class has ``uid`` -+(user login name) and ``uidNumber`` (numeric user id) as mandatory -+attributes. The ``uid`` attribute is guaranteed to be unique across -+all user accounts in an IPA domain. -+ -+The ``uidNumber`` attribute is commonly unique, too. However it's -+technically possible that an administrator has assigned the same -+numeric user id to multiple users. Automatically assigned uid numbers -+don't conflict. SSSD should treat multiple users with same numeric -+user id as an error. -+ -+The attribute ``ipaSubUidNumber`` is always accompanied by -+``ipaSubUidCount`` and ``ipaSubGidNumber`` is always accompanied -+by ``ipaSubGidCount``. In revision 1 the presence of -+``ipaSubUidNumber`` implies presence of the other three attributes. -+All four subordinate id attributes and ``uidNumber`` are single-value -+``INTEGER`` types. Any value outside of range of ``uint32_t`` must -+treated as invalid. SSSD will never see the DNA magic value ``-1`` -+in ``cn=accounts,$SUFFIX`` subtree. -+ -+IPA recommends that SSSD simply extends its existing query for user -+accounts and requests the four subordinate attributes additionally to -+RFC 2307 attributes ``rfc2307_user_map``. SSSD can directly take the -+values and return them without further processing, e.g. -+``uidNumber:ipaSubUidNumber:ipaSubUidCount`` for ``/etc/subuid``. -+ -+Filters for additional cases: -+ -+* subuid filter (find user with subuid by numeric uid): -+ ``&((objectClass=posixAccount)(ipaSubUidNumber=*)(uidNumber=$UID))``, -+ ``(&(objectClass=ipaSubordinateId)(uidNumber=$UID))``, or similar -+* subuid enumeration filter: -+ ``&((objectClass=posixAccount)(ipaSubUidNumber=*)(uidNumber=*))``, -+ ``(objectClass=ipaSubordinateId)``, or similar -+* subgid filter (find user with subgid by numeric uid): -+ ``&((objectClass=posixAccount)(ipaSubGidNumber=*)(uidNumber=$UID))``, -+ ``(&(objectClass=ipaSubordinateId)(uidNumber=$UID))``, or similar -+* subgid enumeration filter: -+ ``&((objectClass=posixAccount)(ipaSubGidNumber=*)(uidNumber=*))``, -+ ``(objectClass=ipaSubordinateId)``, or similar -+ -+## Implementation details -+ -+* The four subid attributes are not included in -+ ``baseuser.default_attributes`` on purpose. The ``config-mod`` -+ command does not permit removal of a user default objectclasses -+ when the class is the last provider of an attribute in -+ ``default_attributes``. -+* ``ipaSubordinateId`` object class does not subclass the other two -+ object classes. LDAP supports -+ ``SUP ( ipaSubordinateGid $ ipaSubordinateUid )`` but 389-DS only -+ auto-inherits from first object class. -+* The idrange entry ``$REALM_subid_range`` has preconfigured base RIDs -+ and SID so idrange plug-in and sidgen task ignore the entry. It's the -+ simplest approach to ensure backwards compatibility with older IPA -+ server versions that don't know how to handle the new range. -+ The SID is ``S-1-5-21-738065-838566-$DOMAIN_HASH``. ``S-1-5-21`` -+ is the well-known SID prefix for domain SIDs. ``738065-838566`` is -+ the decimal representation of the string ``IPA-SUB``. ``DOMAIN_HASH`` -+ is the MURMUR-3 hash of the domain name for key ``0xdeadbeef``. SSSD -+ rejects SIDs unless they are prefixed with ``S-1-5-21`` (see -+ ``sss_idmap.c:is_domain_sid()``). -+* The new ``$REALM_subid_range`` entry uses range type ``ipa-ad-trust`` -+ instead of range type ``ipa-local-subid`` for backwards compatibility -+ with older SSSD clients, see -+ [SSSD #5571](https://github.com/SSSD/sssd/issues/5571). -+* Shared DNA configuration entries in ``cn=dna,cn=ipa,cn=etc,$SUFFIX`` -+ are automatically removed by existing code. Server and replication -+ plug-ins search and delete entries by ``dnaHostname`` attribute. -+ -+### TODO -+ -+* enable configuration for ``dnaStepAttr`` -+* remove ``fake_dna_plugin`` hack from ``baseuser`` plug-in. -+* add custom range type for idranges and teach AD trust, sidgen, and -+ range overlap check code to deal with new range type. -diff --git a/freeipa.spec.in b/freeipa.spec.in -index ae4af099f39641a9f5163d61cfb37e1c3afb6f4b..044e3559975c399f6697d4da94b5a059eb5b407c 100755 ---- a/freeipa.spec.in -+++ b/freeipa.spec.in -@@ -1361,6 +1361,7 @@ fi - %{_libexecdir}/ipa/ipa-pki-wait-running - %{_libexecdir}/ipa/ipa-otpd - %{_libexecdir}/ipa/ipa-print-pac -+%{_libexecdir}/ipa/ipa-subids - %dir %{_libexecdir}/ipa/custodia - %attr(755,root,root) %{_libexecdir}/ipa/custodia/ipa-custodia-dmldap - %attr(755,root,root) %{_libexecdir}/ipa/custodia/ipa-custodia-pki-tomcat -diff --git a/install/share/60basev2.ldif b/install/share/60basev2.ldif -index f253f30c91350c1358b24986806efea7768ea9ce..952755309d13d7df1806a52af351df250185b16d 100644 ---- a/install/share/60basev2.ldif -+++ b/install/share/60basev2.ldif -@@ -3,6 +3,7 @@ - ## Attributes: 2.16.840.1.113730.3.8.3 - V2 base attributres - ## ObjectClasses: 2.16.840.1.113730.3.8.4 - V2 base objectclasses - ## Attributes: 2.16.840.1.113730.3.8.23 - V4 base attributes -+## ObjectClasses: 2.16.840.1.113730.3.8.24 - V4 base objectclasses - ## - dn: cn=schema - attributeTypes: (2.16.840.1.113730.3.8.3.1 NAME 'ipaUniqueID' DESC 'Unique identifier' EQUALITY caseIgnoreMatch ORDERING caseIgnoreOrderingMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 X-ORIGIN 'IPA v2' ) -diff --git a/install/share/60basev4.ldif b/install/share/60basev4.ldif -new file mode 100644 -index 0000000000000000000000000000000000000000..7f5173e593ff68a03d4005957b1dc9b9eb489dc5 ---- /dev/null -+++ b/install/share/60basev4.ldif -@@ -0,0 +1,19 @@ -+## IPA Base OID: 2.16.840.1.113730.3.8 -+## -+## Attributes: 2.16.840.1.113730.3.8.23 - V4 base attributes -+## ObjectClasses: 2.16.840.1.113730.3.8.24 - V4 base objectclasses -+## -+dn: cn=schema -+# subordinate ids -+# range ceiling OIDs are reserved for future use (operational attribute?) -+# object class requires uidNumber but does not subclass posixAccount so we -+# can re-use the object class in idview overrides later. -+attributeTypes: ( 2.16.840.1.113730.3.8.23.6 NAME 'ipaSubUidNumber' DESC 'Numerical subordinate user ID (range start value)' EQUALITY integerMatch ORDERING integerOrderingMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE X-ORIGIN 'IPA v4.9') -+attributeTypes: ( 2.16.840.1.113730.3.8.23.7 NAME 'ipaSubUidCount' DESC 'Subordinate user ID count (range size)' EQUALITY integerMatch ORDERING integerOrderingMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE X-ORIGIN 'IPA v4.9') -+# attributeTypes: ( 2.16.840.1.113730.3.8.23.8 NAME 'ipaSubUidCeiling' DESC 'Numerical subordinate user ID ceiling (largest value in range)' EQUALITY integerMatch ORDERING integerOrderingMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE X-ORIGIN 'IPA v4.9') -+attributeTypes: ( 2.16.840.1.113730.3.8.23.9 NAME 'ipaSubGidNumber' DESC 'Numerical subordinate group ID (range start value)' EQUALITY integerMatch ORDERING integerOrderingMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE X-ORIGIN 'IPA v4.9') -+attributeTypes: ( 2.16.840.1.113730.3.8.23.10 NAME 'ipaSubGidCount' DESC 'Subordinate group ID count (range size)' EQUALITY integerMatch ORDERING integerOrderingMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE X-ORIGIN 'IPA v4.9') -+# attributeTypes: ( 2.16.840.1.113730.3.8.23.11 NAME 'ipaSubGidCeiling' DESC 'Numerical subordinate user ID ceiling (largest value in range)' EQUALITY integerMatch ORDERING integerOrderingMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE X-ORIGIN 'IPA v4.9') -+objectClasses: (2.16.840.1.113730.3.8.24.2 NAME 'ipaSubordinateUid' DESC 'Subordinate uids for users, see subuid(5)' SUP top AUXILIARY MUST ( uidNumber $ ipaSubUidNumber $ ipaSubUidCount ) X-ORIGIN 'IPA v4.9') -+objectClasses: (2.16.840.1.113730.3.8.24.3 NAME 'ipaSubordinateGid' DESC 'Subordinate gids for users, see subgid(5)' SUP top AUXILIARY MUST ( uidNumber $ ipaSubGidNumber $ ipaSubGidCount ) X-ORIGIN 'IPA v4.9') -+objectClasses: (2.16.840.1.113730.3.8.24.4 NAME 'ipaSubordinateId' DESC 'Subordinate uid and gid for users' SUP top AUXILIARY MUST ( uidNumber $ ipaSubUidNumber $ ipaSubUidCount $ ipaSubGidNumber $ ipaSubGidCount ) X-ORIGIN 'IPA v4.9') -diff --git a/install/share/Makefile.am b/install/share/Makefile.am -index 0f1a6975fc3394316769295e67ac3c2e05ee9cee..e0fe4b7d1756bd05f060a92ab52f910b4bd3adc8 100644 ---- a/install/share/Makefile.am -+++ b/install/share/Makefile.am -@@ -16,6 +16,7 @@ dist_app_DATA = \ - 60ipaconfig.ldif \ - 60basev2.ldif \ - 60basev3.ldif \ -+ 60basev4.ldif \ - 60ipadns.ldif \ - 60ipapk11.ldif \ - 60certificate-profiles.ldif \ -diff --git a/install/share/bootstrap-template.ldif b/install/share/bootstrap-template.ldif -index 6a689798451e8cc072284065849f9a95635f8069..16f2ef822eaf56dd68d4140b22a607539645b151 100644 ---- a/install/share/bootstrap-template.ldif -+++ b/install/share/bootstrap-template.ldif -@@ -167,6 +167,12 @@ objectClass: nsContainer - objectClass: top - cn: posix-ids - -+dn: cn=subordinate-ids,cn=dna,cn=ipa,cn=etc,$SUFFIX -+changetype: add -+objectClass: nsContainer -+objectClass: top -+cn: subordinate-ids -+ - dn: cn=ca_renewal,cn=ipa,cn=etc,$SUFFIX - changetype: add - objectClass: nsContainer -@@ -476,6 +482,22 @@ ipaBaseID: $IDSTART - ipaIDRangeSize: $IDRANGE_SIZE - ipaRangeType: ipa-local - -+dn: cn=${REALM}_subid_range,cn=ranges,cn=etc,$SUFFIX -+changetype: add -+objectClass: top -+objectClass: ipaIDrange -+objectClass: ipaTrustedADDomainRange -+cn: ${REALM}_subid_range -+ipaBaseID: eval($SUBID_RANGE_START) -+ipaIDRangeSize: eval($SUBID_RANGE_SIZE) -+# HACK: RIDs to work around adtrust sidgen issue -+ipaBaseRID: eval($SUBID_RANGE_START - $IDRANGE_SIZE) -+# 738065-838566 = IPA-SUB -+ipaNTTrustedDomainSID: S-1-5-21-738065-838566-$DOMAIN_HASH -+# HACK: "ipa-local-subid" range type causes issues with older SSSD clients -+# see https://github.com/SSSD/sssd/issues/5571 -+ipaRangeType: ipa-ad-trust -+ - dn: cn=ca,$SUFFIX - changetype: add - objectClass: nsContainer -diff --git a/install/share/dna.ldif b/install/share/dna.ldif -index f4bff3691570eb1fe028b13b69d2cc175c7df174..649313e72fc58112865e5901125923b3704276b1 100644 ---- a/install/share/dna.ldif -+++ b/install/share/dna.ldif -@@ -16,6 +16,26 @@ dnaThreshold: 500 - dnaSharedCfgDN: cn=posix-ids,cn=dna,cn=ipa,cn=etc,$SUFFIX - dnaExcludeScope: cn=provisioning,$SUFFIX - -+dn: cn=Subordinate IDs,cn=Distributed Numeric Assignment Plugin,cn=plugins,cn=config -+changetype: add -+objectclass: top -+objectclass: extensibleObject -+cn: Subordinate IDs -+dnaType: ipasubuidnumber -+dnaType: ipasubgidnumber -+dnaNextValue: eval($SUBID_RANGE_START) -+dnaMaxValue: eval($SUBID_RANGE_MAX) -+dnaMagicRegen: -1 -+dnaFilter: (objectClass=ipaSubordinateId) -+dnaScope: $SUFFIX -+dnaThreshold: eval($SUBID_DNA_THRESHOLD) -+# TODO: enable when 389-DS' DNA plugin supports dnaStepAttr -+# dnaStepAttr: ipaSubUidCount -+# dnaStepAttr: ipaSubGidCount -+# dnaStepAllowedValues: eval($SUBID_COUNT) -+dnaSharedCfgDN: cn=subordinate-ids,cn=dna,cn=ipa,cn=etc,$SUFFIX -+dnaExcludeScope: cn=provisioning,$SUFFIX -+ - # Enable the DNA plugin - dn: cn=Distributed Numeric Assignment Plugin,cn=plugins,cn=config - changetype: modify -diff --git a/install/tools/Makefile.am b/install/tools/Makefile.am -index d6fbf9e3bc84bc475d7a797ff663df40da0a0efa..5f36742957505f6d695097c8aab6c73f9d59e146 100644 ---- a/install/tools/Makefile.am -+++ b/install/tools/Makefile.am -@@ -38,6 +38,7 @@ dist_noinst_DATA = \ - ipa-pki-retrieve-key.in \ - ipa-pki-wait-running.in \ - ipa-acme-manage.in \ -+ ipa-subids.in \ - $(NULL) - - nodist_sbin_SCRIPTS = \ -@@ -78,6 +79,7 @@ nodist_app_SCRIPTS = \ - ipa-httpd-pwdreader \ - ipa-pki-retrieve-key \ - ipa-pki-wait-running \ -+ ipa-subids \ - $(NULL) - - PYTHON_SHEBANG = \ -diff --git a/install/tools/ipa-subids.in b/install/tools/ipa-subids.in -new file mode 100644 -index 0000000000000000000000000000000000000000..5c7b9f8f788e3c230253e86151cff8234161909b ---- /dev/null -+++ b/install/tools/ipa-subids.in -@@ -0,0 +1,8 @@ -+#!/usr/bin/python3 -+# -+# Copyright (C) 2021 FreeIPA Contributors see COPYING for license -+# -+ -+from ipaserver.install.ipa_subids import IPASubids -+ -+IPASubids.run_cli() -diff --git a/install/ui/src/freeipa/user.js b/install/ui/src/freeipa/user.js -index a4eb390b7d9ca0fb8f50245cfedec27ca2607cdd..5b49b0f6edbbbb6c802afb803a6406a0ab796c44 100644 ---- a/install/ui/src/freeipa/user.js -+++ b/install/ui/src/freeipa/user.js -@@ -259,6 +259,33 @@ return { - } - ] - }, -+ { -+ name: 'subordinate', -+ label: '@i18n:objects.subordinate.identity', -+ fields: [ -+ { -+ name: 'ipasubuidnumber', -+ label: '@i18n:objects.subordinate.subuidnumber', -+ read_only: true -+ }, -+ { -+ name: 'ipasubuidcount', -+ label: '@i18n:objects.subordinate.subuidcount', -+ read_only: true -+ -+ }, -+ { -+ name: 'ipasubgidnumber', -+ label: '@i18n:objects.subordinate.subgidnumber', -+ read_only: true -+ }, -+ { -+ name: 'ipasubgidcount', -+ label: '@i18n:objects.subordinate.subgidcount', -+ read_only: true -+ } -+ ] -+ }, - { - name: 'pwpolicy', - label: '@i18n:objects.pwpolicy.identity', -@@ -451,6 +478,16 @@ return { - enable_cond: ['is-locked'], - confirm_msg: '@i18n:objects.user.unlock_confirm' - }, -+ { -+ $factory: IPA.object_action, -+ name: 'auto_subid', -+ method: 'auto_subid', -+ label: '@i18n:objects.user.auto_subid', -+ needs_confirm: true, -+ hide_cond: ['preserved-user'], -+ enable_cond: ['no-subid'], -+ confirm_msg: '@i18n:objects.user.auto_subid_confirm' -+ }, - { - $type: 'automember_rebuild', - name: 'automember_rebuild', -@@ -461,12 +498,22 @@ return { - $type: 'cert_request', - hide_cond: ['preserved-user'], - title: '@i18n:objects.cert.issue_for_user' -+ }, -+ { -+ $factory: IPA.object_action, -+ name: 'auto_subid', -+ method: 'auto_subid', -+ label: '@i18n:objects.user.auto_subid', -+ needs_confirm: true, -+ hide_cond: ['preserved-user'], -+ enable_cond: ['no-subid'], -+ confirm_msg: '@i18n:objects.user.auto_subid_confirm' - } - ], - header_actions: [ - 'reset_password', 'enable', 'disable', 'stage', 'undel', - 'delete_active_user', 'delete', 'unlock', 'add_otptoken', -- 'automember_rebuild', 'request_cert' -+ 'automember_rebuild', 'request_cert', 'auto_subid' - ], - state: { - evaluators: [ -@@ -1159,6 +1206,10 @@ IPA.user.is_locked_evaluator = function(spec) { - } - } - -+ if (!user.ipasubuidnumber) { -+ that.state.push('no-subid'); -+ } -+ - that.notify_on_change(old_state); - }; - -diff --git a/install/updates/20-indices.update b/install/updates/20-indices.update -index 6632f105a98276d0d7e63ce249ade15501c3b673..7f83ab9f04c565a59efdd2f41c3e7ee30f5da9c7 100644 ---- a/install/updates/20-indices.update -+++ b/install/updates/20-indices.update -@@ -272,6 +272,24 @@ add:nsIndexType: eq - add:nsIndexType: pres - add:nsIndexType: sub - -+dn: cn=ipaSubGidNumber,cn=index,cn=userRoot,cn=ldbm database,cn=plugins,cn=config -+only:cn: ipaSubGidNumber -+default:objectClass: nsIndex -+default:objectClass: top -+default:nsSystemIndex: false -+add:nsIndexType: eq -+add:nsIndexType: pres -+add:nsMatchingRule: integerOrderingMatch -+ -+dn: cn=ipaSubUidNumber,cn=index,cn=userRoot,cn=ldbm database,cn=plugins,cn=config -+only:cn: ipaSubUidNumber -+default:objectClass: nsIndex -+default:objectClass: top -+default:nsSystemIndex: false -+add:nsIndexType: eq -+add:nsIndexType: pres -+add:nsMatchingRule: integerOrderingMatch -+ - dn: cn=ipasudorunasgroup,cn=index,cn=userRoot,cn=ldbm database,cn=plugins,cn=config - only:cn: ipasudorunasgroup - default:objectClass: nsIndex -diff --git a/install/updates/73-subid.update b/install/updates/73-subid.update -new file mode 100644 -index 0000000000000000000000000000000000000000..2aab3d445a33ae1663f81ca2d61b62ebc94aa37d ---- /dev/null -+++ b/install/updates/73-subid.update -@@ -0,0 +1,102 @@ -+# subordinate ids -+ -+# self-service RBAC -+dn: cn=Subordinate ID Selfservice User,cn=roles,cn=accounts,$SUFFIX -+default:objectClass: groupofnames -+default:objectClass: nestedgroup -+default:objectClass: top -+default:cn: Subordinate ID Selfservice User -+default:description: User that can self-request subordiante ids -+# default: member: cn=ipausers,cn=groups,cn=accounts,$SUFFIX -+ -+dn: cn=Subordinate ID Selfservice Users,cn=privileges,cn=pbac,$SUFFIX -+default:objectClass: top -+default:objectClass: groupofnames -+default:objectClass: nestedgroup -+default:cn: Subordinate ID Selfservice Users -+default:description: Subordinate ID Selfservice User -+default:member: cn=Subordinate ID Selfservice User,cn=roles,cn=accounts,$SUFFIX -+ -+dn: cn=Self-service subordinate ID,cn=permissions,cn=pbac,$SUFFIX -+default:objectClass: top -+default:objectClass: groupofnames -+default:objectClass: ipapermission -+default:cn: Self-service subordinate ID -+default:ipapermissiontype: SYSTEM -+default:member: cn=Subordinate ID Selfservice Users,cn=privileges,cn=pbac,$SUFFIX -+ -+# Administrator RBAC -+dn: cn=Subordinate ID Administrators,cn=privileges,cn=pbac,$SUFFIX -+default:objectClass: top -+default:objectClass: groupofnames -+default:objectClass: nestedgroup -+default:cn: Subordinate ID Administrators -+default:description: Subordinate ID Administrators -+default:member: cn=User Administrator,cn=roles,cn=accounts,$SUFFIX -+ -+dn: cn=Manage subordinate ID,cn=permissions,cn=pbac,$SUFFIX -+default:objectClass: top -+default:objectClass: groupofnames -+default:objectClass: ipapermission -+default:cn: Manage subordinate ID -+default:ipapermissiontype: SYSTEM -+default:member: cn=Subordinate ID Administrators,cn=privileges,cn=pbac,$SUFFIX -+ -+# ACIs (in domain database root so they also apply to staging area) -+# -+# - allow users to request new subid with DNA_MAGIC value, subid count=65536, -+# and subgid == subuid. -+# - allow user admins to set subids. count=65536 and subgid == subuid -+# properties are enforced as wel. -+# -+# The delete-when-empty check is required because IPA uses MOD_REPLACE to -+# set attributes, see https://github.com/389ds/389-ds-base/issues/4597. -+# -+# TODO: remove (ipasubuidnumber>=eval($SUBID_RANGE_START) from -+# self-service permission when 389-DS' DNA plugin supports dnaStepAttr and -+# fake_dna_plugin hack has been removed. -+# -+dn: $SUFFIX -+add: aci: (targetfilter = "(objectclass=posixaccount)")(targattrfilters = "add=objectClass:(|(objectClass=ipasubordinateid)(objectClass=ipasubordinategid)(objectClass=ipasubordinateuid)) && ipasubuidnumber:(|(ipasubuidnumber>=eval($SUBID_RANGE_START))(ipasubuidnumber=-1)) && ipasubuidcount:(ipasubuidcount=eval($SUBID_COUNT)) && ipasubgidnumber:(|(ipasubgidnumber>=eval($SUBID_RANGE_START))(ipasubgidnumber=-1)) && ipasubgidcount:(ipasubgidcount=eval($SUBID_COUNT)), del=ipasubuidnumber:(!(ipasubuidnumber=*)) && ipasubuidcount:(!(ipasubuidcount=*)) && ipasubgidnumber:(!(ipasubgidnumber=*)) && ipasubgidcount:(!(ipasubgidcount=*))")(version 3.0;acl "selfservice: Add subordinate id";allow (write) userdn = "ldap:///self" and groupdn="ldap:///cn=Self-service subordinate ID,cn=permissions,cn=pbac,$SUFFIX";) -+add: aci: (targetfilter = "(objectclass=posixaccount)")(targattrfilters = "add=objectClass:(|(objectClass=ipasubordinateid)(objectClass=ipasubordinategid)(objectClass=ipasubordinateuid)) && ipasubuidnumber:(|(ipasubuidnumber>=1)(ipasubuidnumber=-1)) && ipasubuidcount:(ipasubuidcount=eval($SUBID_COUNT)) && ipasubgidnumber:(|(ipasubgidnumber>=1)(ipasubgidnumber=-1)) && ipasubgidcount:(ipasubgidcount=eval($SUBID_COUNT)), del=ipasubuidnumber:(!(ipasubuidnumber=*)) && ipasubuidcount:(!(ipasubuidcount=*)) && ipasubgidnumber:(!(ipasubgidnumber=*)) && ipasubgidcount:(!(ipasubgidcount=*))")(version 3.0;acl "Add subordinate ids to any user";allow (write) groupdn="ldap:///cn=Subordinate ID Administrators,cn=privileges,cn=pbac,$SUFFIX";) -+ -+# DNA plugin and idrange configuration -+dn: cn=subordinate-ids,cn=dna,cn=ipa,cn=etc,$SUFFIX -+default: objectClass: nsContainer -+default: objectClass: top -+default: cn: subordinate-ids -+ -+dn: cn=Subordinate IDs,cn=Distributed Numeric Assignment Plugin,cn=plugins,cn=config -+default: objectclass: top -+default: objectclass: extensibleObject -+default: cn: Subordinate IDs -+default: dnaType: ipasubuidnumber -+default: dnaType: ipasubgidnumber -+default: dnaNextValue: eval($SUBID_RANGE_START) -+default: dnaMaxValue: eval($SUBID_RANGE_MAX) -+default: dnaMagicRegen: -1 -+default: dnaFilter: (objectClass=ipaSubordinateId) -+default: dnaScope: $SUFFIX -+default: dnaThreshold: eval($SUBID_DNA_THRESHOLD) -+# TODO: enable when 389-DS' DNA plugin supports dnaStepAttr -+# default: dnaStepAttr: ipaSubUidCount -+# default: dnaStepAttr: ipaSubGidCount -+# default: dnaStepAllowedValues: eval($SUBID_COUNT) -+default: dnaSharedCfgDN: cn=subordinate-ids,cn=dna,cn=ipa,cn=etc,$SUFFIX -+default: dnaExcludeScope: cn=provisioning,$SUFFIX -+default: aci: (targetattr = "dnaNextRange || dnaNextValue || dnaMaxValue")(version 3.0;acl "permission:Modify DNA Range";allow (write) groupdn = "ldap:///cn=Modify DNA Range,cn=permissions,cn=pbac,$SUFFIX";) -+default: aci: (targetattr = "cn || dnaMaxValue || dnaNextRange || dnaNextValue || dnaThreshold || dnaType || objectclass")(version 3.0;acl "permission:Read DNA Range";allow (read, search, compare) groupdn = "ldap:///cn=Read DNA Range,cn=permissions,cn=pbac,$SUFFIX";) -+ -+dn: cn=${REALM}_subid_range,cn=ranges,cn=etc,$SUFFIX -+default: objectClass: top -+default: objectClass: ipaIDrange -+default: objectClass: ipaTrustedADDomainRange -+default: cn: ${REALM}_subid_range -+default: ipaBaseID: $SUBID_RANGE_START -+default: ipaIDRangeSize: $SUBID_RANGE_SIZE -+# HACK: RIDs to work around adtrust sidgen issue -+default: ipaBaseRID: eval($SUBID_RANGE_START - $IDRANGE_SIZE) -+default: ipaNTTrustedDomainSID: S-1-5-21-738065-838566-$DOMAIN_HASH -+# HACK: "ipa-local-subid" range type causes issues with older SSSD clients -+# see https://github.com/SSSD/sssd/issues/5571 -+default: ipaRangeType: ipa-ad-trust -diff --git a/install/updates/Makefile.am b/install/updates/Makefile.am -index 5741805a65a09c4c00ea47bf437c8821373d1e80..d4f6acba0dc83e4692edd10b8a7617915bd49e84 100644 ---- a/install/updates/Makefile.am -+++ b/install/updates/Makefile.am -@@ -61,6 +61,7 @@ app_DATA = \ - 71-idviews-sasl-mapping.update \ - 72-domainlevels.update \ - 73-custodia.update \ -+ 73-subid.update \ - 73-winsync.update \ - 73-certmap.update \ - 75-user-trust-attributes.update \ -diff --git a/ipalib/constants.py b/ipalib/constants.py -index 79ea36f08cb0108a7434bc58cf0a764e9e15a7af..bee4c92fb39769d427e315116575f217924915be 100644 ---- a/ipalib/constants.py -+++ b/ipalib/constants.py -@@ -343,3 +343,16 @@ SOFTHSM_DNSSEC_TOKEN_LABEL = u'ipaDNSSEC' - # Apache's mod_ssl SSLVerifyDepth value (Maximum depth of CA - # Certificates in Client Certificate verification) - MOD_SSL_VERIFY_DEPTH = '5' -+ -+# subuid / subgid counts are hard-coded -+# An interval of 65536 uids/gids is required to map nobody (65534). -+SUBID_COUNT = 65536 -+ -+# upper half of uid_t (uint32_t) -+SUBID_RANGE_START = 2 ** 31 -+# theoretical max limit is UINT32_MAX-1 ((2 ** 32) - 2) -+# We use a smaller value to keep the topmost subid interval unused. -+SUBID_RANGE_MAX = (2 ** 32) - (2 * SUBID_COUNT) -+SUBID_RANGE_SIZE = SUBID_RANGE_MAX - SUBID_RANGE_START -+# threshold before DNA plugin requests a new range -+SUBID_DNA_THRESHOLD = 500 * SUBID_COUNT -diff --git a/ipaserver/install/adtrustinstance.py b/ipaserver/install/adtrustinstance.py -index a7a403f37db13b7cccf74dff1b92b22529170b8a..24e90f3ecf5b4669f162e1bc68a33ef9d6094514 100644 ---- a/ipaserver/install/adtrustinstance.py -+++ b/ipaserver/install/adtrustinstance.py -@@ -36,6 +36,7 @@ from ipaserver.install import service - from ipaserver.install import installutils - from ipaserver.install.replication import wait_for_task - from ipalib import errors, api -+from ipalib.constants import SUBID_RANGE_START - from ipalib.util import normalize_zone - from ipapython.dn import DN - from ipapython import ipachangeconf -@@ -352,12 +353,19 @@ class ADTRUSTInstance(service.Service): - DN(api.env.container_ranges, self.suffix), - ldap.SCOPE_ONELEVEL, "(objectclass=ipaDomainIDRange)") - -- # Filter out ranges where RID base is already set -- no_rid_base_set = lambda r: not any(( -- r.single_value.get('ipaBaseRID'), -- r.single_value.get('ipaSecondaryBaseRID'))) -+ ranges_with_no_rid_base = [] -+ for entry in ranges: -+ sv = entry.single_value -+ if sv.get('ipaBaseRID') or sv.get('ipaSecondaryBaseRID'): -+ # skip range where RID base is already set -+ continue -+ if sv.get('ipaRangeType') == 'ipa-local-subid': -+ # ignore subid ranges -+ continue -+ ranges_with_no_rid_base.append(entry) - -- ranges_with_no_rid_base = [r for r in ranges if no_rid_base_set(r)] -+ logger.debug(repr(ranges)) -+ logger.debug(repr(ranges_with_no_rid_base)) - - # Return if no range is without RID base - if len(ranges_with_no_rid_base) == 0: -@@ -384,6 +392,17 @@ class ADTRUSTInstance(service.Service): - "They have to differ at least by %d." % size) - raise RuntimeError("RID bases too close.\n") - -+ # values above -+ if any( -+ v + size >= SUBID_RANGE_START -+ for v in (self.rid_base, self.secondary_rid_base) -+ ): -+ self.print_msg( -+ "Ceiling of primary or secondary base is larger than " -+ f"start of subordinate id range {SUBID_RANGE_START}." -+ ) -+ raise RuntimeError("RID bases overlap with SUBID range.\n") -+ - # Modify the range - # If the RID bases would cause overlap with some other range, - # this will be detected by ipa-range-check DS plugin -diff --git a/ipaserver/install/dsinstance.py b/ipaserver/install/dsinstance.py -index 6033c04109f6278cb7b6015becd507b2b4699e02..ac9e131bb1b8c6ff8aff911cb257fbb03406d603 100644 ---- a/ipaserver/install/dsinstance.py -+++ b/ipaserver/install/dsinstance.py -@@ -23,7 +23,6 @@ from __future__ import print_function, absolute_import - import logging - import shutil - import os --import time - import tempfile - import fnmatch - -@@ -46,6 +45,7 @@ from ipaserver.install import certs - from ipaserver.install import replication - from ipaserver.install import sysupgrade - from ipaserver.install import upgradeinstance -+from ipaserver.install import ldapupdate - from ipalib import api - from ipalib import errors - from ipalib import constants -@@ -66,6 +66,7 @@ IPA_SCHEMA_FILES = ("60kerberos.ldif", - "60ipaconfig.ldif", - "60basev2.ldif", - "60basev3.ldif", -+ "60basev4.ldif", - "60ipapk11.ldif", - "60ipadns.ldif", - "60certificate-profiles.ldif", -@@ -214,6 +215,8 @@ class DsInstance(service.Service): - if realm_name: - self.suffix = ipautil.realm_to_suffix(self.realm) - self.serverid = ipaldap.realm_to_serverid(self.realm) -+ if self.domain is None: -+ self.domain = self.realm.lower() - self.__setup_sub_dict() - else: - self.suffix = DN() -@@ -497,34 +500,22 @@ class DsInstance(service.Service): - - def __setup_sub_dict(self): - server_root = find_server_root() -- try: -- idrange_size = self.idmax - self.idstart + 1 -- except TypeError: -- idrange_size = None -- self.sub_dict = dict( -- FQDN=self.fqdn, SERVERID=self.serverid, -+ self.sub_dict = ldapupdate.get_sub_dict( -+ realm=self.realm, -+ domain=self.domain, -+ suffix=self.suffix, -+ fqdn=self.fqdn, -+ idstart=self.idstart, -+ idmax=self.idmax, -+ ) -+ self.sub_dict.update( -+ DOMAIN_LEVEL=self.domainlevel, -+ SERVERID=self.serverid, - PASSWORD=self.dm_password, - RANDOM_PASSWORD=ipautil.ipa_generate_password(), -- SUFFIX=self.suffix, -- REALM=self.realm, USER=DS_USER, -- SERVER_ROOT=server_root, DOMAIN=self.domain, -- TIME=int(time.time()), IDSTART=self.idstart, -- IDMAX=self.idmax, HOST=self.fqdn, -- ESCAPED_SUFFIX=str(self.suffix), -+ USER=DS_USER, - GROUP=DS_GROUP, -- IDRANGE_SIZE=idrange_size, -- DOMAIN_LEVEL=self.domainlevel, -- MAX_DOMAIN_LEVEL=constants.MAX_DOMAIN_LEVEL, -- MIN_DOMAIN_LEVEL=constants.MIN_DOMAIN_LEVEL, -- STRIP_ATTRS=" ".join(replication.STRIP_ATTRS), -- EXCLUDES='(objectclass=*) $ EXCLUDE ' + -- ' '.join(replication.EXCLUDES), -- TOTAL_EXCLUDES='(objectclass=*) $ EXCLUDE ' + -- ' '.join(replication.TOTAL_EXCLUDES), -- DEFAULT_SHELL=platformconstants.DEFAULT_SHELL, -- DEFAULT_ADMIN_SHELL=platformconstants.DEFAULT_ADMIN_SHELL, -- SELINUX_USERMAP_DEFAULT=platformconstants.SELINUX_USERMAP_DEFAULT, -- SELINUX_USERMAP_ORDER=platformconstants.SELINUX_USERMAP_ORDER, -+ SERVER_ROOT=server_root, - ) - - def __create_instance(self): -diff --git a/ipaserver/install/ipa_subids.py b/ipaserver/install/ipa_subids.py -new file mode 100644 -index 0000000000000000000000000000000000000000..ac77a4008aec58d92c8b24df5e00b83c6998401f ---- /dev/null -+++ b/ipaserver/install/ipa_subids.py -@@ -0,0 +1,154 @@ -+# -+# Copyright (C) 2021 FreeIPA Contributors see COPYING for license -+# -+ -+import logging -+ -+from ipalib import api -+from ipalib import errors -+from ipalib.facts import is_ipa_configured -+from ipaplatform.paths import paths -+from ipapython.admintool import AdminTool, ScriptError -+from ipapython.dn import DN -+from ipaserver.plugins.baseldap import DNA_MAGIC -+ -+logger = logging.getLogger(__name__) -+ -+ -+class IPASubids(AdminTool): -+ command_name = "ipa-subids" -+ usage = "%prog [--group GROUP|--all-users]" -+ description = "Mass-assign subordinate ids to users" -+ -+ @classmethod -+ def add_options(cls, parser): -+ super(IPASubids, cls).add_options(parser, debug_option=True) -+ parser.add_option( -+ "--group", -+ dest="group", -+ action="store", -+ default=None, -+ help="Updates members of a user group.", -+ ) -+ parser.add_option( -+ "--all-users", -+ dest="all_users", -+ action="store_true", -+ default=False, -+ help="Update all users.", -+ ) -+ parser.add_option( -+ "--filter", -+ dest="user_filter", -+ action="store", -+ default="(!(nsaccountlock=TRUE))", -+ help="Additional raw LDAP filter (default: active users).", -+ ) -+ parser.add_option( -+ "--dry-run", -+ dest="dry_run", -+ action="store_true", -+ default=False, -+ help="Dry run mode.", -+ ) -+ -+ def validate_options(self, neends_root=False): -+ super().validate_options(needs_root=True) -+ opt = self.safe_options -+ -+ if opt.all_users and opt.group: -+ raise ScriptError("--group and --all-users are mutually exclusive") -+ if not opt.all_users and not opt.group: -+ raise ScriptError("Either --group or --all-users required") -+ -+ def get_group_info(self): -+ assert api.isdone("finalize") -+ group = self.safe_options.group -+ if group is None: -+ return None -+ try: -+ result = api.Command.group_show(group, no_members=True) -+ return result["result"] -+ except errors.NotFound: -+ raise ScriptError(f"Unknown users group '{group}'.") -+ -+ def make_filter(self, groupinfo, user_filter): -+ filters = [ -+ # only users with posixAccount -+ "(objectClass=posixAccount)", -+ # without subordinate ids -+ "(!(objectClass=ipaSubordinateId))", -+ ] -+ if groupinfo is not None: -+ filters.append( -+ self.ldap2.make_filter({"memberof": groupinfo["dn"]}) -+ ) -+ if user_filter: -+ filters.append(user_filter) -+ return self.ldap2.combine_filters(filters, self.ldap2.MATCH_ALL) -+ -+ def search_users(self, filters): -+ users_dn = DN(api.env.container_user, api.env.basedn) -+ attrs = ["objectclass", "uid", "uidnumber"] -+ -+ logger.debug("basedn: %s", users_dn) -+ logger.debug("attrs: %s", attrs) -+ logger.debug("filter: %s", filters) -+ -+ try: -+ entries = self.ldap2.get_entries( -+ base_dn=users_dn, -+ filter=filters, -+ attrs_list=attrs, -+ ) -+ except errors.NotFound: -+ logger.debug("No entries found") -+ return [] -+ else: -+ return entries -+ -+ def run(self): -+ if not is_ipa_configured(): -+ print("IPA is not configured.") -+ return 2 -+ -+ api.bootstrap(in_server=True, confdir=paths.ETC_IPA) -+ api.finalize() -+ api.Backend.ldap2.connect() -+ self.ldap2 = api.Backend.ldap2 -+ user_obj = api.Object["user"] -+ -+ dry_run = self.safe_options.dry_run -+ group_info = self.get_group_info() -+ filters = self.make_filter( -+ group_info, self.safe_options.user_filter -+ ) -+ -+ entries = self.search_users(filters) -+ total = len(entries) -+ logger.info("Found %i user(s) without subordinate ids", total) -+ -+ total = len(entries) -+ for i, entry in enumerate(entries, start=1): -+ logger.info( -+ " Processing user '%s' (%i/%i)", -+ entry.single_value["uid"], -+ i, -+ total -+ ) -+ user_obj.set_subordinate_ids( -+ self.ldap2, entry.dn, entry, DNA_MAGIC -+ ) -+ if not dry_run: -+ self.ldap2.update_entry(entry) -+ -+ if dry_run: -+ logger.info("Dry run mode, no user was modified") -+ else: -+ logger.info("Updated %s user(s)", total) -+ -+ return 0 -+ -+ -+if __name__ == "__main__": -+ IPASubids.run_cli() -diff --git a/ipaserver/install/ldapupdate.py b/ipaserver/install/ldapupdate.py -index f21e5a5af465be37541b9fbdddaf800b73f80b71..d0516dc3028366df5d03a960866abe72601aa4b6 100644 ---- a/ipaserver/install/ldapupdate.py -+++ b/ipaserver/install/ldapupdate.py -@@ -32,9 +32,9 @@ import os - import fnmatch - import warnings - -+from pysss_murmur import murmurhash3 # pylint: disable=no-name-in-module - import six - --from ipaserver.install import installutils - from ipapython import ipautil, ipaldap - from ipalib import errors - from ipalib import api, create_api -@@ -43,6 +43,7 @@ from ipaplatform.constants import constants as platformconstants - from ipaplatform.paths import paths - from ipaplatform.tasks import tasks - from ipapython.dn import DN -+from ipaserver.install import installutils, replication - - if six.PY3: - unicode = str -@@ -53,6 +54,54 @@ UPDATES_DIR=paths.UPDATES_DIR - UPDATE_SEARCH_TIME_LIMIT = 30 # seconds - - -+def get_sub_dict(realm, domain, suffix, fqdn, idstart=None, idmax=None): -+ """LDAP template substitution dict for installer and updater -+ """ -+ if idstart is None: -+ idrange_size = None -+ else: -+ idrange_size = idmax - idstart + 1 -+ -+ return dict( -+ REALM=realm, -+ DOMAIN=domain, -+ SUFFIX=suffix, -+ ESCAPED_SUFFIX=str(suffix), -+ FQDN=fqdn, -+ HOST=fqdn, -+ LIBARCH=paths.LIBARCH, -+ TIME=int(time.time()), -+ FIPS="#" if tasks.is_fips_enabled() else "", -+ # idstart, idmax, and idrange_size may be None -+ IDSTART=idstart, -+ IDMAX=idmax, -+ IDRANGE_SIZE=idrange_size, -+ SUBID_COUNT=constants.SUBID_COUNT, -+ SUBID_RANGE_START=constants.SUBID_RANGE_START, -+ SUBID_RANGE_SIZE=constants.SUBID_RANGE_SIZE, -+ SUBID_RANGE_MAX=constants.SUBID_RANGE_MAX, -+ SUBID_DNA_THRESHOLD=constants.SUBID_DNA_THRESHOLD, -+ DOMAIN_HASH=murmurhash3(domain, len(domain), 0xdeadbeef), -+ MAX_DOMAIN_LEVEL=constants.MAX_DOMAIN_LEVEL, -+ MIN_DOMAIN_LEVEL=constants.MIN_DOMAIN_LEVEL, -+ STRIP_ATTRS=" ".join(replication.STRIP_ATTRS), -+ EXCLUDES=( -+ '(objectclass=*) $ EXCLUDE ' + ' '.join(replication.EXCLUDES) -+ ), -+ TOTAL_EXCLUDES=( -+ '(objectclass=*) $ EXCLUDE ' -+ + ' '.join(replication.TOTAL_EXCLUDES) -+ ), -+ DEFAULT_SHELL=platformconstants.DEFAULT_SHELL, -+ DEFAULT_ADMIN_SHELL=platformconstants.DEFAULT_ADMIN_SHELL, -+ SELINUX_USERMAP_DEFAULT=platformconstants.SELINUX_USERMAP_DEFAULT, -+ SELINUX_USERMAP_ORDER=platformconstants.SELINUX_USERMAP_ORDER, -+ # uid / gid for autobind -+ NAMED_UID=platformconstants.NAMED_USER.uid, -+ NAMED_GID=platformconstants.NAMED_GROUP.gid, -+ ) -+ -+ - def connect(ldapi=False, realm=None, fqdn=None): - """Create a connection for updates""" - if ldapi: -@@ -284,35 +333,33 @@ class LDAPUpdate: - ldap_uri=self.ldapuri - ) - self.api.finalize() -- - self.create_connection() - -+ # get ipa-local domain idrange settings -+ domain_range = f"{self.api.env.realm}_id_range" -+ try: -+ result = self.api.Command.idrange_show(domain_range)["result"] -+ except errors.NotFound: -+ idstart = None -+ idmax = None -+ else: -+ idstart = int(result['ipabaseid'][0]) -+ idrange_size = int(result['ipaidrangesize'][0]) -+ idmax = idstart + idrange_size - 1 -+ -+ default_sub = get_sub_dict( -+ realm=api.env.realm, -+ domain=api.env.domain, -+ suffix=api.env.basedn, -+ fqdn=api.env.host, -+ idstart=idstart, -+ idmax=idmax, -+ ) - replication_plugin = ( - installutils.get_replication_plugin_name(self.conn.get_entry) - ) -+ default_sub["REPLICATION_PLUGIN"] = replication_plugin - -- default_sub = dict( -- REALM=api.env.realm, -- DOMAIN=api.env.domain, -- SUFFIX=api.env.basedn, -- ESCAPED_SUFFIX=str(api.env.basedn), -- FQDN=api.env.host, -- LIBARCH=paths.LIBARCH, -- TIME=int(time.time()), -- MIN_DOMAIN_LEVEL=str(constants.MIN_DOMAIN_LEVEL), -- MAX_DOMAIN_LEVEL=str(constants.MAX_DOMAIN_LEVEL), -- STRIP_ATTRS=" ".join(constants.REPL_AGMT_STRIP_ATTRS), -- EXCLUDES="(objectclass=*) $ EXCLUDE %s" % ( -- " ".join(constants.REPL_AGMT_EXCLUDES) -- ), -- TOTAL_EXCLUDES="(objectclass=*) $ EXCLUDE %s" % ( -- " ".join(constants.REPL_AGMT_TOTAL_EXCLUDES) -- ), -- SELINUX_USERMAP_DEFAULT=platformconstants.SELINUX_USERMAP_DEFAULT, -- SELINUX_USERMAP_ORDER=platformconstants.SELINUX_USERMAP_ORDER, -- FIPS="#" if tasks.is_fips_enabled() else "", -- REPLICATION_PLUGIN=replication_plugin, -- ) - for k, v in default_sub.items(): - self.sub_dict.setdefault(k, v) - -diff --git a/ipaserver/plugins/baseuser.py b/ipaserver/plugins/baseuser.py -index 6035228f19ef8acaf4992490d5512c126881816d..12ff03c2302ff08aabb9369306965e0c125724f8 100644 ---- a/ipaserver/plugins/baseuser.py -+++ b/ipaserver/plugins/baseuser.py -@@ -17,9 +17,10 @@ - # You should have received a copy of the GNU General Public License - # along with this program. If not, see . - -+import random - import six - --from ipalib import api, errors -+from ipalib import api, errors, output, constants - from ipalib import ( - Flag, Int, Password, Str, Bool, StrEnum, DateTime, DNParam) - from ipalib.parameters import Principal, Certificate -@@ -27,13 +28,13 @@ from ipalib.plugable import Registry - from .baseldap import ( - DN, LDAPObject, LDAPCreate, LDAPUpdate, LDAPSearch, LDAPDelete, - LDAPRetrieve, LDAPAddAttribute, LDAPModAttribute, LDAPRemoveAttribute, -- LDAPAddMember, LDAPRemoveMember, -+ LDAPQuery, LDAPAddMember, LDAPRemoveMember, - LDAPAddAttributeViaOption, LDAPRemoveAttributeViaOption, -- add_missing_object_class) -+ add_missing_object_class, DNA_MAGIC, pkey_to_value, entry_to_dict -+) - from ipaserver.plugins.service import (validate_realm, normalize_principal) - from ipalib.request import context - from ipalib import _ --from ipalib.constants import PATTERN_GROUPUSER_NAME - from ipapython import kerberos - from ipapython.ipautil import ipa_generate_password, TMP_PWD_ENTROPY_BITS - from ipapython.ipavalidate import Email -@@ -161,7 +162,7 @@ class baseuser(LDAPObject): - possible_objectclasses = [ - 'meporiginentry', 'ipauserauthtypeclass', 'ipauser', - 'ipatokenradiusproxyuser', 'ipacertmapobject', -- 'ipantuserattrs' -+ 'ipantuserattrs', 'ipasubordinateid', - ] - disallow_object_classes = ['krbticketpolicyaux'] - permission_filter_objectclasses = ['posixaccount'] -@@ -175,13 +176,15 @@ class baseuser(LDAPObject): - 'krbprincipalexpiration', 'usercertificate;binary', - 'krbprincipalname', 'krbcanonicalname', - 'ipacertmapdata', 'ipantlogonscript', 'ipantprofilepath', -- 'ipanthomedirectory', 'ipanthomedirectorydrive' -+ 'ipanthomedirectory', 'ipanthomedirectorydrive', - ] - search_display_attributes = [ - 'uid', 'givenname', 'sn', 'homedirectory', 'krbcanonicalname', - 'krbprincipalname', 'loginshell', - 'mail', 'telephonenumber', 'title', 'nsaccountlock', - 'uidnumber', 'gidnumber', 'sshpubkeyfp', -+ 'ipasubuidnumber', 'ipasubuidcount', 'ipasubgidnumber', -+ 'ipasubgidcount', - ] - uuid_attribute = 'ipauniqueid' - attribute_members = { -@@ -198,7 +201,7 @@ class baseuser(LDAPObject): - - takes_params = ( - Str('uid', -- pattern=PATTERN_GROUPUSER_NAME, -+ pattern=constants.PATTERN_GROUPUSER_NAME, - pattern_errmsg='may only include letters, numbers, _, -, . and $', - maxlength=255, - cli_name='login', -@@ -429,6 +432,41 @@ class baseuser(LDAPObject): - 'J:', 'K:', 'L:', 'M:', 'N:', 'O:', 'P:', 'Q:', 'R:', - 'S:', 'T:', 'U:', 'V:', 'W:', 'X:', 'Y:', 'Z:'), - ), -+ Int( -+ 'ipasubuidnumber?', -+ label=_('SubUID range start'), -+ cli_name='subuid', -+ doc=_('Start value for subordinate user ID (subuid) range'), -+ minvalue=constants.SUBID_RANGE_START, -+ maxvalue=constants.SUBID_RANGE_MAX, -+ ), -+ Int( -+ 'ipasubuidcount?', -+ label=_('SubUID range size'), -+ cli_name='subuidcount', -+ doc=_('Subordinate user ID count'), -+ flags={'no_create', 'no_update', 'no_search'}, -+ minvalue=constants.SUBID_COUNT, -+ maxvalue=constants.SUBID_COUNT, -+ ), -+ Int( -+ 'ipasubgidnumber?', -+ label=_('SubGID range start'), -+ cli_name='subgid', -+ doc=_('Start value for subordinate group ID (subgid) range'), -+ flags={'no_create', 'no_update'}, -+ minvalue=constants.SUBID_RANGE_START, -+ maxvalue=constants.SUBID_RANGE_MAX, -+ ), -+ Int( -+ 'ipasubgidcount?', -+ label=_('SubGID range size'), -+ cli_name='subgidcount', -+ doc=_('Subordinate group ID count'), -+ flags={'no_create', 'no_update', 'no_search'}, -+ minvalue=constants.SUBID_COUNT, -+ maxvalue=constants.SUBID_COUNT, -+ ), - ) - - def normalize_and_validate_email(self, email, config=None): -@@ -526,6 +564,131 @@ class baseuser(LDAPObject): - except KeyError: - pass - -+ def handle_subordinate_ids(self, ldap, dn, entry_attrs): -+ """Handle ipaSubordinateId object class -+ """ -+ obj_classes = entry_attrs.get("objectclass") -+ new_subuid = entry_attrs.single_value.get("ipasubuidnumber") -+ new_subgid = entry_attrs.single_value.get("ipasubgidnumber") -+ -+ # entry has object class ipaSubordinateId -+ # default to auto-assigment of subuids -+ if ( -+ new_subuid is None -+ and obj_classes is not None -+ and self.has_objectclass(obj_classes, "ipasubordinateid") -+ ): -+ new_subuid = DNA_MAGIC -+ -+ # neither auto-assignment nor explicit assignment -+ if new_subuid is None: -+ # nothing to do -+ return False -+ -+ # enforce subuid == subgid -+ if new_subgid is not None and new_subgid != new_subuid: -+ raise errors.ValidationError( -+ name="ipasubgidnumber", -+ error=_("subgidnumber must be equal to subuidnumber") -+ ) -+ -+ self.set_subordinate_ids(ldap, dn, entry_attrs, new_subuid) -+ return True -+ -+ def set_subordinate_ids(self, ldap, dn, entry_attrs, subuid): -+ """Set subuid value of an entry -+ -+ Takes care of objectclass and sibbling attributes -+ """ -+ if "objectclass" in entry_attrs: -+ obj_classes = entry_attrs["objectclass"] -+ else: -+ _entry_attrs = ldap.get_entry(dn, ["objectclass"]) -+ entry_attrs["objectclass"] = _entry_attrs["objectclass"] -+ obj_classes = entry_attrs["objectclass"] -+ -+ if not self.has_objectclass(obj_classes, "ipasubordinateid"): -+ # could append ipasubordinategid and ipasubordinateuid, too -+ obj_classes.append("ipasubordinateid") -+ -+ # XXX HACK, remove later -+ if subuid == DNA_MAGIC: -+ subuid = self._fake_dna_plugin(ldap, dn, entry_attrs) -+ -+ entry_attrs["ipasubuidnumber"] = subuid -+ # enforice subuid == subgid for now -+ entry_attrs["ipasubgidnumber"] = subuid -+ # hard-coded constants -+ entry_attrs["ipasubuidcount"] = constants.SUBID_COUNT -+ entry_attrs["ipasubgidcount"] = constants.SUBID_COUNT -+ -+ def get_subid_match_candidate_filter( -+ self, ldap, *, subuid, subgid, extra_filters=(), offset=None, -+ ): -+ """Create LDAP filter to locate matching/overlapping subids -+ """ -+ if subuid is None and subgid is None: -+ raise ValueError("subuid and subgid are both None") -+ if offset is None: -+ # assumes that no subordinate count is larger than SUBID_COUNT -+ offset = constants.SUBID_COUNT - 1 -+ -+ class_filters = "(objectclass=ipasubordinateid)" -+ subid_filters = [] -+ if subuid is not None: -+ subid_filters.append( -+ ldap.combine_filters( -+ [ -+ f"(ipasubuidnumber>={subuid - offset})", -+ f"(ipasubuidnumber<={subuid + offset})", -+ ], -+ rules=ldap.MATCH_ALL -+ ) -+ ) -+ if subgid is not None: -+ subid_filters.append( -+ ldap.combine_filters( -+ [ -+ f"(ipasubgidnumber>={subgid - offset})", -+ f"(ipasubgidnumber<={subgid + offset})", -+ ], -+ rules=ldap.MATCH_ALL -+ ) -+ ) -+ -+ subid_filters = ldap.combine_filters( -+ subid_filters, rules=ldap.MATCH_ANY -+ ) -+ filters = [class_filters, subid_filters] -+ filters.extend(extra_filters) -+ return ldap.combine_filters(filters, rules=ldap.MATCH_ALL) -+ -+ def _fake_dna_plugin(self, ldap, dn, entry_attrs): -+ """XXX HACK, remove when 389-DS DNA plugin supports steps""" -+ uidnumber = entry_attrs.single_value.get("uidnumber") -+ if uidnumber is None: -+ entry = ldap.get_entry(dn, ["uidnumber"]) -+ uidnumber = entry.single_value["uidnumber"] -+ uidnumber = int(uidnumber) -+ -+ if uidnumber == DNA_MAGIC: -+ return ( -+ 3221225472 -+ + random.randint(1, 16382) * constants.SUBID_COUNT -+ ) -+ -+ if not hasattr(context, "idrange_ipabaseid"): -+ range_name = f"{self.api.env.realm}_id_range" -+ range = self.api.Command.idrange_show(range_name)["result"] -+ context.idrange_ipabaseid = int(range["ipabaseid"][0]) -+ -+ range_start = context.idrange_ipabaseid -+ -+ assert uidnumber >= range_start -+ assert uidnumber < range_start + 2**14 -+ -+ return (uidnumber - range_start) * constants.SUBID_COUNT + 2**31 -+ - - class baseuser_add(LDAPCreate): - """ -@@ -536,6 +699,7 @@ class baseuser_add(LDAPCreate): - assert isinstance(dn, DN) - set_krbcanonicalname(entry_attrs) - self.obj.convert_usercertificate_pre(entry_attrs) -+ self.obj.handle_subordinate_ids(ldap, dn, entry_attrs) - if entry_attrs.get('ipatokenradiususername', None): - add_missing_object_class(ldap, u'ipatokenradiusproxyuser', dn, - entry_attrs, update=False) -@@ -688,6 +852,7 @@ class baseuser_mod(LDAPUpdate): - - self.check_objectclass(ldap, dn, entry_attrs) - self.obj.convert_usercertificate_pre(entry_attrs) -+ self.obj.handle_subordinate_ids(ldap, dn, entry_attrs) - self.preserve_krbprincipalname_pre(ldap, entry_attrs, *keys, **options) - update_samba_attrs(ldap, dn, entry_attrs, **options) - -@@ -968,3 +1133,98 @@ class baseuser_remove_certmapdata(ModCertMapData, - LDAPRemoveAttribute): - __doc__ = _("Remove one or more certificate mappings from the user entry.") - msg_summary = _('Removed certificate mappings from user "%(value)s"') -+ -+ -+class baseuser_auto_subid(LDAPQuery): -+ __doc__ = _("Auto-assign subuid and subgid range to user entry") -+ -+ has_output = output.standard_entry -+ -+ def execute(self, cn, **options): -+ ldap = self.obj.backend -+ dn = self.obj.get_dn(cn) -+ -+ try: -+ entry_attrs = ldap.get_entry( -+ dn, ["objectclass", "ipasubuidnumber"] -+ ) -+ except errors.NotFound: -+ raise self.obj.handle_not_found(cn) -+ -+ if "ipasubuidnumber" in entry_attrs: -+ raise errors.AlreadyContainsValueError(attr="ipasubuidnumber") -+ -+ self.obj.set_subordinate_ids(ldap, dn, entry_attrs, subuid=DNA_MAGIC) -+ ldap.update_entry(entry_attrs) -+ -+ # fetch updated entry (use search display attribute to show subids) -+ if options.get('all', False): -+ attrs_list = ['*'] + self.obj.search_display_attributes -+ else: -+ attrs_list = set(self.obj.search_display_attributes) -+ attrs_list.update(entry_attrs.keys()) -+ if options.get('no_members', False): -+ attrs_list.difference_update(self.obj.attribute_members) -+ attrs_list = list(attrs_list) -+ -+ entry = self._exc_wrapper((cn,), options, ldap.get_entry)( -+ dn, attrs_list -+ ) -+ entry_attrs = entry_to_dict(entry, **options) -+ entry_attrs['dn'] = dn -+ -+ return dict(result=entry_attrs, value=pkey_to_value(cn, options)) -+ -+ -+class baseuser_match_subid(baseuser_find): -+ __doc__ = _("Match users by any subordinate uid in their range") -+ -+ _subid_attrs = { -+ "ipasubuidnumber", -+ "ipasubuidcount", -+ "ipasubgidnumber", -+ "ipasubgidcount" -+ } -+ -+ def get_options(self): -+ base_options = {p.name for p in self.obj.takes_params} -+ for option in super().get_options(): -+ if option.name == "ipasubuidnumber": -+ yield option.clone( -+ label=_('SubUID match'), -+ doc=_('Match value for subordinate user ID'), -+ required=True, -+ ) -+ elif option.name not in base_options: -+ # raw, version -+ yield option.clone() -+ -+ def pre_callback( -+ self, ldap, filters, attrs_list, base_dn, scope, *args, **options -+ ): -+ # search for candidates in range -+ # Code assumes that no subordinate count is larger than SUBID_COUNT -+ filters = self.obj.get_subid_match_candidate_filter( -+ ldap, subuid=options["ipasubuidnumber"], subgid=None, -+ ) -+ # always include subid attributes -+ for missing in self._subid_attrs.difference(attrs_list): -+ attrs_list.append(missing) -+ -+ return filters, base_dn, scope -+ -+ def post_callback(self, ldap, entries, truncated, *args, **options): -+ # filter out mismatches manually -+ osubuid = options["ipasubuidnumber"] -+ new_entries = [] -+ for entry in entries: -+ esubuid = int(entry.single_value["ipasubuidnumber"]) -+ esubcount = int(entry.single_value["ipasubuidcount"]) -+ minsubuid = esubuid -+ maxsubuid = esubuid + esubcount - 1 -+ if minsubuid <= osubuid <= maxsubuid: -+ new_entries.append(entry) -+ -+ entries[:] = new_entries -+ -+ return truncated -diff --git a/ipaserver/plugins/idrange.py b/ipaserver/plugins/idrange.py -index 32b9c0c2d01b616d76505fc06fa9b6e5e209b234..3e486b8e27cfb12f2e4732fc1ee113f25dfbac5b 100644 ---- a/ipaserver/plugins/idrange.py -+++ b/ipaserver/plugins/idrange.py -@@ -205,6 +205,7 @@ class idrange(LDAPObject): - # The commented range types are planned but not yet supported - range_types = { - u'ipa-local': unicode(_('local domain range')), -+ # u'ipa-local-subid': unicode(_('local domain subid range')), - # u'ipa-ad-winsync': unicode(_('Active Directory winsync range')), - u'ipa-ad-trust': unicode(_('Active Directory domain range')), - u'ipa-ad-trust-posix': unicode(_('Active Directory trust range with ' -@@ -221,10 +222,14 @@ class idrange(LDAPObject): - Int('ipabaseid', - cli_name='base_id', - label=_("First Posix ID of the range"), -+ minvalue=1, -+ maxvalue=Int.MAX_UINT32 - ), - Int('ipaidrangesize', - cli_name='range_size', - label=_("Number of IDs in the range"), -+ minvalue=1, -+ maxvalue=Int.MAX_UINT32 - ), - Int('ipabaserid?', - cli_name='rid_base', -@@ -669,7 +674,10 @@ class idrange_mod(LDAPUpdate): - except errors.NotFound: - raise self.obj.handle_not_found(*keys) - -- if old_attrs['iparangetype'][0] == 'ipa-local': -+ if ( -+ old_attrs['iparangetype'][0] in {'ipa-local', 'ipa-local-subid'} -+ or old_attrs['cn'][0] == f'{self.api.env.realm}_subid_range' -+ ): - raise errors.ExecutionError( - message=_('This command can not be used to change ID ' - 'allocation for local IPA domain. Run ' -diff --git a/ipaserver/plugins/internal.py b/ipaserver/plugins/internal.py -index 70164eb8654d211523c98722a02b77ee13eb0009..199838b199eb4cdabf597bd34d571d05547fd32e 100644 ---- a/ipaserver/plugins/internal.py -+++ b/ipaserver/plugins/internal.py -@@ -1547,6 +1547,13 @@ class i18n_messages(Command): - "Drive to mount a home directory" - ), - }, -+ "subordinate": { -+ "identity": _("Subordinate user and group id"), -+ "subuidnumber": _("Subordinate user id"), -+ "subuidcount": _("Subordinate user id count"), -+ "subgidnumber": _("Subordinate group id"), -+ "subgidcount": _("Subordinate group id count"), -+ }, - "trustconfig": { - "options": _("Options"), - }, -@@ -1570,6 +1577,11 @@ class i18n_messages(Command): - "add_into_sudo": _( - "Add user '${primary_key}' into sudo rules" - ), -+ "auto_subid": _("Auto assign subordinate ids"), -+ "auto_subid_confirm": _( -+ "Are you sure you want to auto-assign a subordinate id " -+ "to user ${object}?" -+ ), - "contact": _("Contact Settings"), - "delete_mode": _("Delete mode"), - "employee": _("Employee Information"), -diff --git a/ipaserver/plugins/user.py b/ipaserver/plugins/user.py -index e4ee572b236c288fd7dcf1d44c5adf1f836f63aa..f89b3ad5d9c994fe1ceb3da560fde7cc5bf5155a 100644 ---- a/ipaserver/plugins/user.py -+++ b/ipaserver/plugins/user.py -@@ -50,7 +50,10 @@ from .baseuser import ( - baseuser_add_principal, - baseuser_remove_principal, - baseuser_add_certmapdata, -- baseuser_remove_certmapdata) -+ baseuser_remove_certmapdata, -+ baseuser_auto_subid, -+ baseuser_match_subid, -+) - from .idviews import remove_ipaobject_overrides - from ipalib.plugable import Registry - from .baseldap import ( -@@ -202,6 +205,8 @@ class user(baseuser): - 'ipapermright': {'read', 'search', 'compare'}, - 'ipapermdefaultattr': { - 'ipauniqueid', 'ipasshpubkey', 'ipauserauthtype', 'userclass', -+ 'ipasubuidnumber', 'ipasubuidcount', 'ipasubgidnumber', -+ 'ipasubgidcount', - }, - 'fixup_function': fix_addressbook_permission_bindrule, - }, -@@ -1306,3 +1311,13 @@ class user_add_principal(baseuser_add_principal): - class user_remove_principal(baseuser_remove_principal): - __doc__ = _('Remove principal alias from the user entry') - msg_summary = _('Removed aliases from user "%(value)s"') -+ -+ -+@register() -+class user_auto_subid(baseuser_auto_subid): -+ __doc__ = baseuser_auto_subid.__doc__ -+ -+ -+@register() -+class user_match_subid(baseuser_match_subid): -+ __doc__ = baseuser_match_subid.__doc__ -diff --git a/ipatests/prci_definitions/gating.yaml b/ipatests/prci_definitions/gating.yaml -index a66b56ad8f62a458e9cc240440e7d222c32c599f..6ddd155c9967fa248581a59c68dfe547a34be623 100644 ---- a/ipatests/prci_definitions/gating.yaml -+++ b/ipatests/prci_definitions/gating.yaml -@@ -298,3 +298,15 @@ jobs: - template: *ci-ipa-4-9-latest - timeout: 3600 - topology: *master_1repl -+ -+ fedora-latest-ipa-4-9/test_subids: -+ 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_integration/test_subids.py -+ template: *ci-ipa-4-9-latest -+ timeout: 3600 -+ topology: *master_1repl -diff --git a/ipatests/test_integration/test_subids.py b/ipatests/test_integration/test_subids.py -new file mode 100644 -index 0000000000000000000000000000000000000000..b462f22ac067f3e1e97ef3f6d63d4e14e4ae79af ---- /dev/null -+++ b/ipatests/test_integration/test_subids.py -@@ -0,0 +1,201 @@ -+# -+# Copyright (C) 2021 FreeIPA Contributors see COPYING for license -+# -+ -+"""Tests for subordinate ids -+""" -+import os -+ -+from ipalib.constants import SUBID_COUNT, SUBID_RANGE_START, SUBID_RANGE_MAX -+from ipaplatform.paths import paths -+from ipatests.pytest_ipa.integration import tasks -+from ipatests.test_integration.base import IntegrationTest -+ -+ -+class TestSubordinateId(IntegrationTest): -+ num_replicas = 0 -+ topology = "star" -+ -+ def _parse_result(self, result): -+ info = {} -+ for line in result.stdout_text.split("\n"): -+ line = line.strip() -+ if line: -+ if ":" not in line: -+ continue -+ k, v = line.split(":", 1) -+ k = k.strip() -+ v = v.strip() -+ try: -+ v = int(v, 10) -+ except ValueError: -+ if v == "FALSE": -+ v = False -+ elif v == "TRUE": -+ v = True -+ info.setdefault(k.lower(), []).append(v) -+ -+ for k, v in info.items(): -+ if len(v) == 1: -+ info[k] = v[0] -+ else: -+ info[k] = set(v) -+ return info -+ -+ def get_user(self, uid): -+ cmd = ["ipa", "user-show", "--all", "--raw", uid] -+ result = self.master.run_command(cmd) -+ return self._parse_result(result) -+ -+ def user_auto_subid(self, uid, **kwargs): -+ cmd = ["ipa", "user-auto-subid", uid] -+ return self.master.run_command(cmd, **kwargs) -+ -+ def test_auto_subid(self): -+ tasks.kinit_admin(self.master) -+ uid = "testuser_auto1" -+ tasks.user_add(self.master, uid) -+ info = self.get_user(uid) -+ assert "ipasubuidcount" not in info -+ -+ self.user_auto_subid(uid) -+ info = self.get_user(uid) -+ assert "ipasubuidcount" in info -+ -+ subuid = info["ipasubuidnumber"] -+ result = self.master.run_command( -+ ["ipa", "user-match-subid", f"--subuid={subuid}", "--raw"] -+ ) -+ match = self._parse_result(result) -+ assert match["uid"] == uid -+ assert match["ipasubuidnumber"] == info["ipasubuidnumber"] -+ assert match["ipasubuidnumber"] >= SUBID_RANGE_START -+ assert match["ipasubuidnumber"] <= SUBID_RANGE_MAX -+ assert match["ipasubuidcount"] == SUBID_COUNT -+ assert match["ipasubgidnumber"] == info["ipasubgidnumber"] -+ assert match["ipasubgidnumber"] == match["ipasubuidnumber"] -+ assert match["ipasubgidcount"] == SUBID_COUNT -+ -+ def test_ipa_subid_script(self): -+ tasks.kinit_admin(self.master) -+ -+ tool = os.path.join(paths.LIBEXEC_IPA_DIR, "ipa-subids") -+ users = [] -+ for i in range(1, 11): -+ uid = f"testuser_script{i}" -+ users.append(uid) -+ tasks.user_add(self.master, uid) -+ info = self.get_user(uid) -+ assert "ipasubuidcount" not in info -+ -+ cmd = [tool, "--verbose", "--group", "ipausers"] -+ self.master.run_command(cmd) -+ -+ for uid in users: -+ info = self.get_user(uid) -+ assert info["ipasubuidnumber"] >= SUBID_RANGE_START -+ assert info["ipasubuidnumber"] <= SUBID_RANGE_MAX -+ assert info["ipasubuidnumber"] == info["ipasubgidnumber"] -+ assert info["ipasubuidcount"] == SUBID_COUNT -+ assert info["ipasubuidcount"] == info["ipasubgidcount"] -+ -+ def test_subid_selfservice(self): -+ tasks.kinit_admin(self.master) -+ -+ uid = "testuser_selfservice1" -+ password = "Secret123" -+ role = "Subordinate ID Selfservice User" -+ -+ tasks.user_add(self.master, uid, password=password) -+ tasks.kinit_user( -+ self.master, uid, f"{password}\n{password}\n{password}\n" -+ ) -+ info = self.get_user(uid) -+ assert "ipasubuidcount" not in info -+ result = self.user_auto_subid(uid, raiseonerr=False) -+ assert result.returncode > 0 -+ -+ tasks.kinit_admin(self.master) -+ self.master.run_command( -+ ["ipa", "role-add-member", role, "--groups=ipausers"] -+ ) -+ -+ try: -+ tasks.kinit_user(self.master, uid, password) -+ self.user_auto_subid(uid) -+ info = self.get_user(uid) -+ assert "ipasubuidcount" in info -+ finally: -+ tasks.kinit_admin(self.master) -+ self.master.run_command( -+ ["ipa", "role-remove-member", role, "--groups=ipausers"] -+ ) -+ -+ def test_subid_useradmin(self): -+ tasks.kinit_admin(self.master) -+ -+ uid_useradmin = "testuser_usermgr_mgr1" -+ role = "User Administrator" -+ uid = "testuser_usermgr_user1" -+ password = "Secret123" -+ -+ # create user administrator -+ tasks.user_add(self.master, uid_useradmin, password=password) -+ # add user to user admin group -+ tasks.kinit_admin(self.master) -+ self.master.run_command( -+ ["ipa", "role-add-member", role, f"--users={uid_useradmin}"], -+ ) -+ # kinit as user admin -+ tasks.kinit_user( -+ self.master, -+ uid_useradmin, -+ f"{password}\n{password}\n{password}\n", -+ ) -+ # create new user as user admin -+ tasks.user_add(self.master, uid) -+ # assign new subid to user (with useradmin credentials) -+ self.user_auto_subid(uid) -+ -+ def test_subordinate_default_objclass(self): -+ tasks.kinit_admin(self.master) -+ -+ result = self.master.run_command( -+ ["ipa", "config-show", "--raw", "--all"] -+ ) -+ info = self._parse_result(result) -+ usercls = info["ipauserobjectclasses"] -+ assert "ipasubordinateid" not in usercls -+ -+ cmd = [ -+ "ipa", -+ "config-mod", -+ "--addattr", -+ "ipaUserObjectClasses=ipasubordinateid", -+ ] -+ self.master.run_command(cmd) -+ -+ uid = "testuser_usercls1" -+ tasks.user_add(self.master, uid) -+ info = self.get_user(uid) -+ assert "ipasubuidcount" in info -+ -+ def test_idrange_subid(self): -+ tasks.kinit_admin(self.master) -+ -+ range_name = f"{self.master.domain.realm}_subid_range" -+ -+ result = self.master.run_command( -+ ["ipa", "idrange-show", range_name, "--raw"] -+ ) -+ info = self._parse_result(result) -+ -+ # see https://github.com/SSSD/sssd/issues/5571 -+ assert info["iparangetype"] == "ipa-ad-trust" -+ assert info["ipabaseid"] == SUBID_RANGE_START -+ assert info["ipaidrangesize"] == SUBID_RANGE_MAX - SUBID_RANGE_START -+ assert info["ipabaserid"] < SUBID_RANGE_START -+ assert "ipasecondarybaserid" not in info -+ assert info["ipanttrusteddomainsid"].startswith( -+ "S-1-5-21-738065-838566-" -+ ) -diff --git a/ipatests/test_xmlrpc/test_range_plugin.py b/ipatests/test_xmlrpc/test_range_plugin.py -index c756bb7941d6c2acae89d44d6c89abc6b80ef5f7..ef683f84e97cbba61972f580e84e3587fda8c63a 100644 ---- a/ipatests/test_xmlrpc/test_range_plugin.py -+++ b/ipatests/test_xmlrpc/test_range_plugin.py -@@ -24,6 +24,7 @@ Test the `ipaserver/plugins/idrange.py` module, and XML-RPC in general. - 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 import objectclasses -@@ -46,6 +47,12 @@ rid_shift = 0 - for idrange in api.Command['idrange_find']()['result']: - size = int(idrange['ipaidrangesize'][0]) - base_id = int(idrange['ipabaseid'][0]) -+ rtype = idrange['iparangetype'][0] -+ -+ if rtype == 'ipa-local-subid' or base_id == constants.SUBID_RANGE_START: -+ # ignore subordinate id range. It would push values beyond uint32_t. -+ # There is plenty of space below SUBUID_RANGE_START. -+ continue - - id_end = base_id + size - rid_end = 0 --- -2.26.3 - diff --git a/SOURCES/0009-Redesign-subid-feature.patch b/SOURCES/0009-Redesign-subid-feature.patch deleted file mode 100644 index da0b878..0000000 --- a/SOURCES/0009-Redesign-subid-feature.patch +++ /dev/null @@ -1,2906 +0,0 @@ -From f6fd0abeaa64d927f2d993235e97ac3009e64f2c Mon Sep 17 00:00:00 2001 -From: Christian Heimes -Date: Wed, 14 Apr 2021 15:21:18 +0200 -Subject: [PATCH] Redesign subid feature - -Subordinate ids are now handled by a new plugin class and stored in -separate entries in the cn=subids,cn=accounts subtree. - -Signed-off-by: Christian Heimes -Reviewed-By: Francois Cami -Reviewed-By: Rob Crittenden -Reviewed-By: Francois Cami -Reviewed-By: Rob Crittenden ---- - ACI.txt | 12 +- - API.txt | 153 +++-- - doc/designs/subordinate-ids.md | 358 ++++++++--- - install/share/60basev4.ldif | 25 +- - install/share/60ipaconfig.ldif | 5 +- - install/share/dna.ldif | 8 +- - install/share/memberof-conf.ldif | 4 +- - install/ui/src/freeipa/app.js | 1 + - .../ui/src/freeipa/navigation/menu_spec.js | 3 +- - install/ui/src/freeipa/serverconfig.js | 4 + - install/ui/src/freeipa/subid.js | 92 +++ - install/ui/src/freeipa/user.js | 108 ++-- - install/updates/10-uniqueness.update | 19 + - install/updates/20-indices.update | 12 + - install/updates/25-referint.update | 1 + - install/updates/73-subid.update | 28 +- - ipalib/constants.py | 6 +- - ipaserver/install/ipa_subids.py | 18 +- - .../plugins/update_dna_shared_config.py | 2 +- - ipaserver/plugins/baseldap.py | 10 +- - ipaserver/plugins/baseuser.py | 272 +------- - ipaserver/plugins/config.py | 8 +- - ipaserver/plugins/internal.py | 2 +- - ipaserver/plugins/subid.py | 608 ++++++++++++++++++ - ipaserver/plugins/user.py | 39 +- - ipatests/test_integration/test_subids.py | 182 +++--- - 26 files changed, 1389 insertions(+), 591 deletions(-) - create mode 100644 install/ui/src/freeipa/subid.js - create mode 100644 ipaserver/plugins/subid.py - -diff --git a/ACI.txt b/ACI.txt -index fce02a333b212de9b61f920515eed3e356b1391b..e985461cd1c10cc98d1080daa81cfd90e2433dbb 100644 ---- a/ACI.txt -+++ b/ACI.txt -@@ -61,7 +61,7 @@ aci: (targetattr = "cn || description || ipacertprofilestoreissued")(targetfilte - dn: cn=certprofiles,cn=ca,dc=ipa,dc=example - aci: (targetattr = "cn || createtimestamp || description || entryusn || ipacertprofilestoreissued || modifytimestamp || objectclass")(targetfilter = "(objectclass=ipacertprofile)")(version 3.0;acl "permission:System: Read Certificate Profiles";allow (compare,read,search) userdn = "ldap:///all";) - dn: cn=ipaconfig,cn=etc,dc=ipa,dc=example --aci: (targetattr = "cn || createtimestamp || entryusn || ipacertificatesubjectbase || ipaconfigstring || ipacustomfields || ipadefaultemaildomain || ipadefaultloginshell || ipadefaultprimarygroup || ipadomainresolutionorder || ipagroupobjectclasses || ipagroupsearchfields || ipahomesrootdir || ipakrbauthzdata || ipamaxhostnamelength || ipamaxusernamelength || ipamigrationenabled || ipapwdexpadvnotify || ipasearchrecordslimit || ipasearchtimelimit || ipaselinuxusermapdefault || ipaselinuxusermaporder || ipauserauthtype || ipauserobjectclasses || ipausersearchfields || modifytimestamp || objectclass")(targetfilter = "(objectclass=ipaguiconfig)")(version 3.0;acl "permission:System: Read Global Configuration";allow (compare,read,search) userdn = "ldap:///all";) -+aci: (targetattr = "cn || createtimestamp || entryusn || ipacertificatesubjectbase || ipaconfigstring || ipacustomfields || ipadefaultemaildomain || ipadefaultloginshell || ipadefaultprimarygroup || ipadomainresolutionorder || ipagroupobjectclasses || ipagroupsearchfields || ipahomesrootdir || ipakrbauthzdata || ipamaxhostnamelength || ipamaxusernamelength || ipamigrationenabled || ipapwdexpadvnotify || ipasearchrecordslimit || ipasearchtimelimit || ipaselinuxusermapdefault || ipaselinuxusermaporder || ipauserauthtype || ipauserdefaultsubordinateid || ipauserobjectclasses || ipausersearchfields || modifytimestamp || objectclass")(targetfilter = "(objectclass=ipaguiconfig)")(version 3.0;acl "permission:System: Read Global Configuration";allow (compare,read,search) userdn = "ldap:///all";) - dn: cn=costemplates,cn=accounts,dc=ipa,dc=example - aci: (targetfilter = "(objectclass=costemplate)")(version 3.0;acl "permission:System: Add Group Password Policy costemplate";allow (add) groupdn = "ldap:///cn=System: Add Group Password Policy costemplate,cn=permissions,cn=pbac,dc=ipa,dc=example";) - dn: cn=costemplates,cn=accounts,dc=ipa,dc=example -@@ -318,6 +318,14 @@ dn: cn=deleted users,cn=accounts,cn=provisioning,dc=ipa,dc=example - aci: (targetattr = "krblastpwdchange || krbpasswordexpiration || krbprincipalkey || userpassword")(target = "ldap:///uid=*,cn=deleted users,cn=accounts,cn=provisioning,dc=ipa,dc=example")(targetfilter = "(objectclass=posixaccount)")(version 3.0;acl "permission:System: Reset Preserved User password";allow (read,search,write) groupdn = "ldap:///cn=System: Reset Preserved User password,cn=permissions,cn=pbac,dc=ipa,dc=example";) - dn: dc=ipa,dc=example - aci: (target_to = "ldap:///cn=users,cn=accounts,dc=ipa,dc=example")(target_from = "ldap:///cn=deleted users,cn=accounts,cn=provisioning,dc=ipa,dc=example")(targetfilter = "(objectclass=nsContainer)")(version 3.0;acl "permission:System: Undelete User";allow (moddn) groupdn = "ldap:///cn=System: Undelete User,cn=permissions,cn=pbac,dc=ipa,dc=example";) -+dn: cn=subids,cn=accounts,dc=ipa,dc=example -+aci: (targetattr = "description || ipaowner")(targetfilter = "(objectclass=ipasubordinateidentry)")(version 3.0;acl "permission:System: Manage Subordinate Ids";allow (write) groupdn = "ldap:///cn=System: Manage Subordinate Ids,cn=permissions,cn=pbac,dc=ipa,dc=example";) -+dn: cn=subids,cn=accounts,dc=ipa,dc=example -+aci: (targetattr = "createtimestamp || description || entryusn || ipaowner || ipasubgidcount || ipasubgidnumber || ipasubuidcount || ipasubuidnumber || ipauniqueid || modifytimestamp || objectclass")(targetfilter = "(objectclass=ipasubordinateidentry)")(version 3.0;acl "permission:System: Read Subordinate Id Attributes";allow (compare,read,search) userdn = "ldap:///all";) -+dn: cn=subids,cn=accounts,dc=ipa,dc=example -+aci: (targetattr = "numsubordinates")(target = "ldap:///cn=subids,cn=accounts,dc=ipa,dc=example")(version 3.0;acl "permission:System: Read Subordinate Id Count";allow (compare,read,search) userdn = "ldap:///all";) -+dn: cn=subids,cn=accounts,dc=ipa,dc=example -+aci: (targetfilter = "(objectclass=ipasubordinateidentry)")(version 3.0;acl "permission:System: Remove Subordinate Ids";allow (delete) groupdn = "ldap:///cn=System: Remove Subordinate Ids,cn=permissions,cn=pbac,dc=ipa,dc=example";) - dn: cn=sudocmds,cn=sudo,dc=ipa,dc=example - aci: (targetfilter = "(objectclass=ipasudocmd)")(version 3.0;acl "permission:System: Add Sudo Command";allow (add) groupdn = "ldap:///cn=System: Add Sudo Command,cn=permissions,cn=pbac,dc=ipa,dc=example";) - dn: cn=sudocmds,cn=sudo,dc=ipa,dc=example -@@ -375,7 +383,7 @@ aci: (targetattr = "audio || businesscategory || carlicense || departmentnumber - dn: dc=ipa,dc=example - aci: (targetattr = "cn || createtimestamp || entryusn || gecos || gidnumber || homedirectory || loginshell || modifytimestamp || objectclass || uid || uidnumber")(target = "ldap:///cn=users,cn=compat,dc=ipa,dc=example")(version 3.0;acl "permission:System: Read User Compat Tree";allow (compare,read,search) userdn = "ldap:///anyone";) - dn: cn=users,cn=accounts,dc=ipa,dc=example --aci: (targetattr = "ipasshpubkey || ipasubgidcount || ipasubgidnumber || ipasubuidcount || ipasubuidnumber || ipauniqueid || ipauserauthtype || userclass")(targetfilter = "(objectclass=posixaccount)")(version 3.0;acl "permission:System: Read User IPA Attributes";allow (compare,read,search) userdn = "ldap:///all";) -+aci: (targetattr = "ipasshpubkey || ipauniqueid || ipauserauthtype || userclass")(targetfilter = "(objectclass=posixaccount)")(version 3.0;acl "permission:System: Read User IPA Attributes";allow (compare,read,search) userdn = "ldap:///all";) - dn: cn=users,cn=accounts,dc=ipa,dc=example - aci: (targetattr = "krbcanonicalname || krblastpwdchange || krbpasswordexpiration || krbprincipalaliases || krbprincipalexpiration || krbprincipalname || krbprincipaltype || nsaccountlock")(targetfilter = "(objectclass=posixaccount)")(version 3.0;acl "permission:System: Read User Kerberos Attributes";allow (compare,read,search) userdn = "ldap:///all";) - dn: cn=users,cn=accounts,dc=ipa,dc=example -diff --git a/API.txt b/API.txt -index 262b4d6a72c7d7032a7027116f7a4f65aa620615..6c80028bfe8e9b739637fa11e015441efbf984b5 100644 ---- a/API.txt -+++ b/API.txt -@@ -1076,7 +1076,7 @@ args: 0,1,1 - option: Str('version?') - output: Output('result') - command: config_mod/1 --args: 0,28,3 -+args: 0,29,3 - option: Str('addattr*', cli_name='addattr') - option: Flag('all', autofill=True, cli_name='all', default=False) - option: Str('ca_renewal_master_server?', autofill=False) -@@ -1099,6 +1099,7 @@ option: Int('ipasearchtimelimit?', autofill=False, cli_name='searchtimelimit') - option: Str('ipaselinuxusermapdefault?', autofill=False) - option: Str('ipaselinuxusermaporder?', autofill=False) - option: StrEnum('ipauserauthtype*', autofill=False, cli_name='user_auth_type', values=[u'password', u'radius', u'otp', u'pkinit', u'hardened', u'disabled']) -+option: Bool('ipauserdefaultsubordinateid?', autofill=False, cli_name='user_default_subid') - option: Str('ipauserobjectclasses*', autofill=False, cli_name='userobjectclasses') - option: IA5Str('ipausersearchfields?', autofill=False, cli_name='usersearch') - option: Flag('raw', autofill=True, cli_name='raw', default=False) -@@ -4974,7 +4975,7 @@ output: Entry('result') - output: Output('summary', type=[, ]) - output: PrimaryKey('value') - command: stageuser_add/1 --args: 1,46,3 -+args: 1,45,3 - arg: Str('uid', cli_name='login') - option: Str('addattr*', cli_name='addattr') - option: Flag('all', autofill=True, cli_name='all', default=False) -@@ -4992,7 +4993,6 @@ option: Str('givenname', cli_name='first') - option: Str('homedirectory?', cli_name='homedir') - option: Str('initials?', autofill=True) - option: Str('ipasshpubkey*', cli_name='sshpubkey') --option: Int('ipasubuidnumber?', cli_name='subuid') - option: Str('ipatokenradiusconfiglink?', cli_name='radius') - option: Str('ipatokenradiususername?', cli_name='radius_username') - option: StrEnum('ipauserauthtype*', cli_name='user_auth_type', values=[u'password', u'radius', u'otp', u'pkinit', u'hardened']) -@@ -5099,14 +5099,13 @@ option: Str('in_group*', cli_name='in_groups') - option: Str('in_hbacrule*', cli_name='in_hbacrules') - option: Str('in_netgroup*', cli_name='in_netgroups') - option: Str('in_role*', cli_name='in_roles') -+option: Str('in_subid*', cli_name='in_subids') - option: Str('in_sudorule*', cli_name='in_sudorules') - option: Str('initials?', autofill=False) - option: Str('ipanthomedirectory?', autofill=False, cli_name='smb_home_dir') - option: StrEnum('ipanthomedirectorydrive?', autofill=False, cli_name='smb_home_drive', values=[u'A:', u'B:', u'C:', u'D:', u'E:', u'F:', u'G:', u'H:', u'I:', u'J:', u'K:', u'L:', u'M:', u'N:', u'O:', u'P:', u'Q:', u'R:', u'S:', u'T:', u'U:', u'V:', u'W:', u'X:', u'Y:', u'Z:']) - option: Str('ipantlogonscript?', autofill=False, cli_name='smb_logon_script') - option: Str('ipantprofilepath?', autofill=False, cli_name='smb_profile_path') --option: Int('ipasubgidnumber?', autofill=False, cli_name='subgid') --option: Int('ipasubuidnumber?', autofill=False, cli_name='subuid') - option: Str('ipatokenradiusconfiglink?', autofill=False, cli_name='radius') - option: Str('ipatokenradiususername?', autofill=False, cli_name='radius_username') - option: StrEnum('ipauserauthtype*', autofill=False, cli_name='user_auth_type', values=[u'password', u'radius', u'otp', u'pkinit', u'hardened']) -@@ -5123,6 +5122,7 @@ option: Str('not_in_group*', cli_name='not_in_groups') - option: Str('not_in_hbacrule*', cli_name='not_in_hbacrules') - option: Str('not_in_netgroup*', cli_name='not_in_netgroups') - option: Str('not_in_role*', cli_name='not_in_roles') -+option: Str('not_in_subid*', cli_name='not_in_subids') - option: Str('not_in_sudorule*', cli_name='not_in_sudorules') - option: Str('ou?', autofill=False, cli_name='orgunit') - option: Str('pager*', autofill=False) -@@ -5148,7 +5148,7 @@ output: ListOfEntries('result') - output: Output('summary', type=[, ]) - output: Output('truncated', type=[]) - command: stageuser_mod/1 --args: 1,52,3 -+args: 1,51,3 - arg: Str('uid', cli_name='login') - option: Str('addattr*', cli_name='addattr') - option: Flag('all', autofill=True, cli_name='all', default=False) -@@ -5170,7 +5170,6 @@ option: StrEnum('ipanthomedirectorydrive?', autofill=False, cli_name='smb_home_d - option: Str('ipantlogonscript?', autofill=False, cli_name='smb_logon_script') - option: Str('ipantprofilepath?', autofill=False, cli_name='smb_profile_path') - option: Str('ipasshpubkey*', autofill=False, cli_name='sshpubkey') --option: Int('ipasubuidnumber?', autofill=False, cli_name='subuid') - option: Str('ipatokenradiusconfiglink?', autofill=False, cli_name='radius') - option: Str('ipatokenradiususername?', autofill=False, cli_name='radius_username') - option: StrEnum('ipauserauthtype*', autofill=False, cli_name='user_auth_type', values=[u'password', u'radius', u'otp', u'pkinit', u'hardened']) -@@ -5263,6 +5262,100 @@ option: Str('version?') - output: Entry('result') - output: Output('summary', type=[, ]) - output: PrimaryKey('value') -+command: subid_add/1 -+args: 1,8,3 -+arg: Str('ipauniqueid?', cli_name='id') -+option: Str('addattr*', cli_name='addattr') -+option: Flag('all', autofill=True, cli_name='all', default=False) -+option: Str('description?', cli_name='desc') -+option: Str('ipaowner', cli_name='owner') -+option: Int('ipasubuidnumber?', cli_name='subuid') -+option: Flag('raw', autofill=True, cli_name='raw', default=False) -+option: Str('setattr*', cli_name='setattr') -+option: Str('version?') -+output: Entry('result') -+output: Output('summary', type=[, ]) -+output: PrimaryKey('value') -+command: subid_del/1 -+args: 1,2,3 -+arg: Str('ipauniqueid+', cli_name='id') -+option: Flag('continue', autofill=True, cli_name='continue', default=False) -+option: Str('version?') -+output: Output('result', type=[]) -+output: Output('summary', type=[, ]) -+output: ListOfPrimaryKeys('value') -+command: subid_find/1 -+args: 1,11,4 -+arg: Str('criteria?') -+option: Flag('all', autofill=True, cli_name='all', default=False) -+option: Str('description?', autofill=False, cli_name='desc') -+option: Str('ipaowner?', autofill=False, cli_name='owner') -+option: Int('ipasubgidnumber?', autofill=False, cli_name='subgid') -+option: Int('ipasubuidnumber?', autofill=False, cli_name='subuid') -+option: Str('ipauniqueid?', autofill=False, cli_name='id') -+option: Flag('pkey_only?', autofill=True, default=False) -+option: Flag('raw', autofill=True, cli_name='raw', default=False) -+option: Int('sizelimit?', autofill=False) -+option: Int('timelimit?', autofill=False) -+option: Str('version?') -+output: Output('count', type=[]) -+output: ListOfEntries('result') -+output: Output('summary', type=[, ]) -+output: Output('truncated', type=[]) -+command: subid_generate/1 -+args: 0,4,3 -+option: Flag('all', autofill=True, cli_name='all', default=False) -+option: Str('ipaowner?', cli_name='owner') -+option: Flag('raw', autofill=True, cli_name='raw', default=False) -+option: Str('version?') -+output: Entry('result') -+output: Output('summary', type=[, ]) -+output: PrimaryKey('value') -+command: subid_match/1 -+args: 1,7,4 -+arg: Str('criteria?') -+option: Flag('all', autofill=True, cli_name='all', default=False) -+option: Int('ipasubuidnumber', autofill=False, cli_name='subuid') -+option: Flag('pkey_only?', autofill=True, default=False) -+option: Flag('raw', autofill=True, cli_name='raw', default=False) -+option: Int('sizelimit?', autofill=False) -+option: Int('timelimit?', autofill=False) -+option: Str('version?') -+output: Output('count', type=[]) -+output: ListOfEntries('result') -+output: Output('summary', type=[, ]) -+output: Output('truncated', type=[]) -+command: subid_mod/1 -+args: 1,8,3 -+arg: Str('ipauniqueid', cli_name='id') -+option: Str('addattr*', cli_name='addattr') -+option: Flag('all', autofill=True, cli_name='all', default=False) -+option: Str('delattr*', cli_name='delattr') -+option: Str('description?', autofill=False, cli_name='desc') -+option: Flag('raw', autofill=True, cli_name='raw', default=False) -+option: Flag('rights', autofill=True, default=False) -+option: Str('setattr*', cli_name='setattr') -+option: Str('version?') -+output: Entry('result') -+output: Output('summary', type=[, ]) -+output: PrimaryKey('value') -+command: subid_show/1 -+args: 1,4,3 -+arg: Str('ipauniqueid', cli_name='id') -+option: Flag('all', autofill=True, cli_name='all', default=False) -+option: Flag('raw', autofill=True, cli_name='raw', default=False) -+option: Flag('rights', autofill=True, default=False) -+option: Str('version?') -+output: Entry('result') -+output: Output('summary', type=[, ]) -+output: PrimaryKey('value') -+command: subid_stats/1 -+args: 0,3,2 -+option: Flag('all', autofill=True, cli_name='all', default=False) -+option: Flag('raw', autofill=True, cli_name='raw', default=False) -+option: Str('version?') -+output: Entry('result') -+output: Output('summary', type=[, ]) - command: sudocmd_add/1 - args: 1,7,3 - arg: Str('sudocmd', cli_name='command') -@@ -6062,7 +6155,7 @@ output: Entry('result') - output: Output('summary', type=[, ]) - output: PrimaryKey('value') - command: user_add/1 --args: 1,47,3 -+args: 1,46,3 - arg: Str('uid', cli_name='login') - option: Str('addattr*', cli_name='addattr') - option: Flag('all', autofill=True, cli_name='all', default=False) -@@ -6079,7 +6172,6 @@ option: Str('givenname', cli_name='first') - option: Str('homedirectory?', cli_name='homedir') - option: Str('initials?', autofill=True) - option: Str('ipasshpubkey*', cli_name='sshpubkey') --option: Int('ipasubuidnumber?', cli_name='subuid') - option: Str('ipatokenradiusconfiglink?', cli_name='radius') - option: Str('ipatokenradiususername?', cli_name='radius_username') - option: StrEnum('ipauserauthtype*', cli_name='user_auth_type', values=[u'password', u'radius', u'otp', u'pkinit', u'hardened']) -@@ -6161,16 +6253,6 @@ option: Str('version?') - output: Entry('result') - output: Output('summary', type=[, ]) - output: PrimaryKey('value') --command: user_auto_subid/1 --args: 1,4,3 --arg: Str('uid', cli_name='login') --option: Flag('all', autofill=True, cli_name='all', default=False) --option: Flag('no_members', autofill=True, default=False) --option: Flag('raw', autofill=True, cli_name='raw', default=False) --option: Str('version?') --output: Entry('result') --output: Output('summary', type=[, ]) --output: PrimaryKey('value') - command: user_del/1 - args: 1,3,3 - arg: Str('uid+', cli_name='login') -@@ -6213,14 +6295,13 @@ option: Str('in_group*', cli_name='in_groups') - option: Str('in_hbacrule*', cli_name='in_hbacrules') - option: Str('in_netgroup*', cli_name='in_netgroups') - option: Str('in_role*', cli_name='in_roles') -+option: Str('in_subid*', cli_name='in_subids') - option: Str('in_sudorule*', cli_name='in_sudorules') - option: Str('initials?', autofill=False) - option: Str('ipanthomedirectory?', autofill=False, cli_name='smb_home_dir') - option: StrEnum('ipanthomedirectorydrive?', autofill=False, cli_name='smb_home_drive', values=[u'A:', u'B:', u'C:', u'D:', u'E:', u'F:', u'G:', u'H:', u'I:', u'J:', u'K:', u'L:', u'M:', u'N:', u'O:', u'P:', u'Q:', u'R:', u'S:', u'T:', u'U:', u'V:', u'W:', u'X:', u'Y:', u'Z:']) - option: Str('ipantlogonscript?', autofill=False, cli_name='smb_logon_script') - option: Str('ipantprofilepath?', autofill=False, cli_name='smb_profile_path') --option: Int('ipasubgidnumber?', autofill=False, cli_name='subgid') --option: Int('ipasubuidnumber?', autofill=False, cli_name='subuid') - option: Str('ipatokenradiusconfiglink?', autofill=False, cli_name='radius') - option: Str('ipatokenradiususername?', autofill=False, cli_name='radius_username') - option: StrEnum('ipauserauthtype*', autofill=False, cli_name='user_auth_type', values=[u'password', u'radius', u'otp', u'pkinit', u'hardened']) -@@ -6237,6 +6318,7 @@ option: Str('not_in_group*', cli_name='not_in_groups') - option: Str('not_in_hbacrule*', cli_name='not_in_hbacrules') - option: Str('not_in_netgroup*', cli_name='not_in_netgroups') - option: Str('not_in_role*', cli_name='not_in_roles') -+option: Str('not_in_subid*', cli_name='not_in_subids') - option: Str('not_in_sudorule*', cli_name='not_in_sudorules') - option: Bool('nsaccountlock?', autofill=False, cli_name='disabled', default=False) - option: Str('ou?', autofill=False, cli_name='orgunit') -@@ -6264,23 +6346,8 @@ output: Output('count', type=[]) - output: ListOfEntries('result') - output: Output('summary', type=[, ]) - output: Output('truncated', type=[]) --command: user_match_subid/1 --args: 1,8,4 --arg: Str('criteria?') --option: Flag('all', autofill=True, cli_name='all', default=False) --option: Int('ipasubuidnumber', autofill=False, cli_name='subuid') --option: Flag('no_members', autofill=True, default=True) --option: Flag('pkey_only?', autofill=True, default=False) --option: Flag('raw', autofill=True, cli_name='raw', default=False) --option: Int('sizelimit?', autofill=False) --option: Int('timelimit?', autofill=False) --option: Str('version?') --output: Output('count', type=[]) --output: ListOfEntries('result') --output: Output('summary', type=[, ]) --output: Output('truncated', type=[]) - command: user_mod/1 --args: 1,53,3 -+args: 1,52,3 - arg: Str('uid', cli_name='login') - option: Str('addattr*', cli_name='addattr') - option: Flag('all', autofill=True, cli_name='all', default=False) -@@ -6302,7 +6369,6 @@ option: StrEnum('ipanthomedirectorydrive?', autofill=False, cli_name='smb_home_d - option: Str('ipantlogonscript?', autofill=False, cli_name='smb_logon_script') - option: Str('ipantprofilepath?', autofill=False, cli_name='smb_profile_path') - option: Str('ipasshpubkey*', autofill=False, cli_name='sshpubkey') --option: Int('ipasubuidnumber?', autofill=False, cli_name='subuid') - option: Str('ipatokenradiusconfiglink?', autofill=False, cli_name='radius') - option: Str('ipatokenradiususername?', autofill=False, cli_name='radius_username') - option: StrEnum('ipauserauthtype*', autofill=False, cli_name='user_auth_type', values=[u'password', u'radius', u'otp', u'pkinit', u'hardened']) -@@ -7138,6 +7204,15 @@ default: stageuser_remove_certmapdata/1 - default: stageuser_remove_manager/1 - default: stageuser_remove_principal/1 - default: stageuser_show/1 -+default: subid/1 -+default: subid_add/1 -+default: subid_del/1 -+default: subid_find/1 -+default: subid_generate/1 -+default: subid_match/1 -+default: subid_mod/1 -+default: subid_show/1 -+default: subid_stats/1 - default: sudocmd/1 - default: sudocmd_add/1 - default: sudocmd_del/1 -@@ -7216,12 +7291,10 @@ default: user_add_cert/1 - default: user_add_certmapdata/1 - default: user_add_manager/1 - default: user_add_principal/1 --default: user_auto_subid/1 - default: user_del/1 - default: user_disable/1 - default: user_enable/1 - default: user_find/1 --default: user_match_subid/1 - default: user_mod/1 - default: user_remove_cert/1 - default: user_remove_certmapdata/1 -diff --git a/doc/designs/subordinate-ids.md b/doc/designs/subordinate-ids.md -index 1b578667a8cfdda223af38a14d142c72a5d5c073..b3be3bcfa275e836e777f807d5210a4db6be0f79 100644 ---- a/doc/designs/subordinate-ids.md -+++ b/doc/designs/subordinate-ids.md -@@ -1,5 +1,9 @@ - # Central management of subordinate user and group ids - -+## OUTDATED -+ -+**The design document does not reflect new implementation yet!** -+ - Subordinate ids are a Linux Kernel feature to grant a user additional - user and group id ranges. Amongst others the feature can be used - by container runtime engies to implement rootless containers. -@@ -74,8 +78,10 @@ basic use cases. Some restrictions may be lifted in the future. - to the same value. - * counts are hard-coded to value 65536 - * once assigned subids cannot be removed --* IPA does not support multiple subordinate id ranges. Contrary to -- ``/etc/subuid``, users are limited to one set of subordinate ids. -+* IPA does not support multiple subordinate id ranges, yet. Contrary to -+ ``/etc/subuid``, users are limited to one set of subordinate ids. The -+ limitation is implemented with a unique index on owner reference and -+ can be lifted in the future. - * subids are auto-assigned. Auto-assignment is currently emulated - until 389-DS has been extended to support DNA with step interval. - * subids are allocated from hard-coded range -@@ -118,20 +124,21 @@ to servers. The DNA plug-in guarantees uniqueness across servers. - ### LDAP schema extension - - The subordinate id feature introduces a new auxiliar object class --``ipaSubordinateId`` with four required attributes ``ipaSubUidNumber``, --``ipaSubUidCount``, ``ipaSubGidNumber``, and ``ipaSubGidCount``. The --attributes with ``number`` suffix store the start value of the interval. --The ``count`` attributes contain the size of the interval including the --start value. The maximum subid is --``ipaSubUidNumber + ipaSubUidCount - 1``. -- --All four attributes are single-value ``INTEGER`` type with standard --integer matching rules. OIDs ``2.16.840.1.113730.3.8.23.8`` and -+``ipaSubordinateId`` with five required attributes ``ipaOwner``, -+``ipaSubUidNumber``, ``ipaSubUidCount``, ``ipaSubGidNumber``, and -+``ipaSubGidCount``. The attributes with ``number`` suffix store the -+start value of the interval. The ``count`` attributes contain the -+size of the interval including the start value. The maximum subid is -+``ipaSubUidNumber + ipaSubUidCount - 1``. The ``ipaOwner`` attribute -+is a reference to the owning user. -+ -+All count and number attributes are single-value ``INTEGER`` type with -+standard integer matching rules. OIDs ``2.16.840.1.113730.3.8.23.8`` and - ``2.16.840.1.113730.3.8.23.11`` are reserved for future use. - - ```raw - attributeTypes: ( -- 2.16.840.1.113730.3.8.23.6 -+ 2.16.840.1.113730.3.8.23.7 - NAME 'ipaSubUidNumber' - DESC 'Numerical subordinate user ID (range start value)' - EQUALITY integerMatch ORDERING integerOrderingMatch -@@ -139,7 +146,7 @@ attributeTypes: ( - X-ORIGIN 'IPA v4.9' - ) - attributeTypes: ( -- 2.16.840.1.113730.3.8.23.7 -+ 2.16.840.1.113730.3.8.23.8 - NAME 'ipaSubUidCount' - DESC 'Subordinate user ID count (range size)' - EQUALITY integerMatch ORDERING integerOrderingMatch -@@ -147,7 +154,7 @@ attributeTypes: ( - X-ORIGIN 'IPA v4.9' - ) - attributeTypes: ( -- 2.16.840.1.113730.3.8.23.9 -+ 2.16.840.1.113730.3.8.23.10 - NAME 'ipaSubGidNumber' - DESC 'Numerical subordinate group ID (range start value)' - EQUALITY integerMatch ORDERING integerOrderingMatch -@@ -155,7 +162,7 @@ attributeTypes: ( - X-ORIGIN 'IPA v4.9' - ) - attributeTypes: ( -- 2.16.840.1.113730.3.8.23.10 -+ 2.16.840.1.113730.3.8.23.11 - NAME 'ipaSubGidCount' - DESC 'Subordinate group ID count (range size)' - EQUALITY integerMatch ORDERING integerOrderingMatch -@@ -164,51 +171,96 @@ attributeTypes: ( - ) - ``` - --The ``ipaSubordinateId`` object class is an auxiliar subclass of -+The ``ipaOwner`` attribute is a single-value DN attribute that refers -+to user entry that owns the subordinate ID entry. The proposal does not -+reuse any of the existing attributes like ``owner`` or ``member``, -+because they are all multi-valued. -+ -+``` -+attributeTypes: ( -+ 2.16.840.1.113730.3.8.23.13 -+ NAME 'ipaOwner' -+ DESC 'Owner of an entry' -+ SUP distinguishedName -+ EQUALITY distinguishedNameMatch -+ SYNTAX 1.3.6.1.4.1.1466.115.121.1.12 -+ SINGLE-VALUE -+ X-ORIGIN 'IPA v4.9') -+``` -+ -+The ``ipaSubordinateId`` object class is an auxiliary subclass of - ``top`` and requires all four subordinate id attributes as well as --``uidNumber``. It does not subclass ``posixAccount`` to make --the class reusable in idview overrides later. -+``ipaOwner`` attribute`` - - ```raw - objectClasses: ( - 2.16.840.1.113730.3.8.24.4 - NAME 'ipaSubordinateId' - DESC 'Subordinate uid and gid for users' -- SUP top AUXILIARY -- MUST ( uidNumber $ ipaSubUidNumber $ ipaSubUidCount $ ipaSubGidNumber $ ipaSubGidCount ) -+ SUP top -+ AUXILIARY -+ MUST ( ipaOwner $ ipaSubUidNumber $ ipaSubUidCount $ ipaSubGidNumber $ ipaSubGidCount ) - X-ORIGIN 'IPA v4.9' - ) - ``` - - The ``ipaSubordinateGid`` and ``ipaSubordinateUid`` are defined for --future use. IPA always assumes the presence of ``ipaSubordinateId`` and --does not use these object classes. -+future use. IPA always assumes the presence of ``ipaSubordinateId``. - - ```raw - objectClasses: ( - 2.16.840.1.113730.3.8.24.2 - NAME 'ipaSubordinateUid' - DESC 'Subordinate uids for users, see subuid(5)' -- SUP top AUXILIARY -- MUST ( uidNumber $ ipaSubUidNumber $ ipaSubUidCount ) -+ SUP top -+ AUXILIARY -+ MUST ( ipaOwner $ ipaSubUidNumber $ ipaSubUidCount ) - X-ORIGIN 'IPA v4.9' - ) - objectClasses: ( - 2.16.840.1.113730.3.8.24.3 - NAME 'ipaSubordinateGid' - DESC 'Subordinate gids for users, see subgid(5)' -- SUP top AUXILIARY -- MUST ( uidNumber $ ipaSubGidNumber $ ipaSubGidCount ) -+ SUP top -+ AUXILIARY -+ MUST ( ipaOwner $ ipaSubGidNumber $ ipaSubGidCount ) - X-ORIGIN 'IPA v4.9' - ) - ``` - --### Index -+Subordinate id entries have the structural object class -+``ipaSubordinateIdEntry`` and one or more of the auxiliary object -+classes ``ipaSubordinateId``, ``ipaSubordinateGid``, or -+``ipaSubordinateUid``. ``ipaUniqueId`` is used as a primary key (RDN). -+ -+```raw -+objectClasses: ( -+ 2.16.840.1.113730.3.8.24.5 -+ NAME 'ipaSubordinateIdEntry' -+ DESC 'Subordinate uid and gid entry' -+ SUP top -+ STRUCTURAL -+ MUST ( ipaUniqueId ) MAY ( description ) X-ORIGIN 'IPA v4.9' -+) -+``` -+ -+### cn=subids,cn=accounts,$SUFFIX -+ -+Subordiante ids and ACIs are stored in the new subtree -+``cn=subids,cn=accounts,$SUFFIX``. -+ -+### Index, integrity, memberOf - - The attributes ``ipaSubUidNumber`` and ``ipaSubGidNumber`` are index - for ``pres`` and ``eq`` with ``nsMatchingRule: integerOrderingMatch`` - to enable efficient ``=``, ``>=``, and ``<=`` searches. - -+The attribute ``ipaOwner`` is indexed for ``pres`` and ``eq``. This DN -+attribute is also checked for referential integrity and uniqueness -+within the ``cn=subids,cn=accounts,$SUFFIX`` subtree. The memberOf -+plugin creates back-references for ``ipaOwner`` references. -+ -+ - ### Distributed numeric assignment (DNA) plug-in extension - - Subordinate id auto-assignment requires an extension of 389-DS' -@@ -221,6 +273,85 @@ option ``dnaStepAttr`` (name is tentative) will tell the DNA plug-in - to use the value of entry attributes as step size. - - -+## IPA plugins and commands -+ -+The config plugin has a new option to enable or disable generation of -+subordinate id entries for new users: -+ -+```raw -+$ ipa config-mod --user-default-subid=true -+``` -+ -+Subordinate ids are managed by a new plugin class. The ``subid-add`` -+and ``subid-del`` commands are hidden from command line. New subordinate -+ids are generated and auto-assigned with ``subid-generate``. -+ -+```raw -+$ ipa help subid -+Topic commands: -+ subid-find Search for subordinate id. -+ subid-generate Generate and auto-assign subuid and subgid range to user entry -+ subid-match Match users by any subordinate uid in their range -+ subid-mod Modify a subordinate id. -+ subid-show Display information about a subordinate id. -+ subid-stats Subordinate id statistics -+``` -+ -+```raw -+$ ipa subid-generate --owner testuser9 -+----------------------------------------------------------- -+Added subordinate id "aa28f132-457c-488b-82e1-d123727e4f81" -+----------------------------------------------------------- -+ Unique ID: aa28f132-457c-488b-82e1-d123727e4f81 -+ Description: auto-assigned subid -+ Owner: testuser9 -+ SubUID range start: 3922132992 -+ SubUID range size: 65536 -+ SubGID range start: 3922132992 -+ SubGID range size: 65536 -+``` -+ -+ -+```raw -+$ ipa subid-find --owner testuser9 -+------------------------ -+1 subordinate id matched -+------------------------ -+ Unique ID: aa28f132-457c-488b-82e1-d123727e4f81 -+ Owner: testuser9 -+ SubUID range start: 3922132992 -+ SubUID range size: 65536 -+ SubGID range start: 3922132992 -+ SubGID range size: 65536 -+---------------------------- -+Number of entries returned 1 -+---------------------------- -+``` -+ -+```raw -+$ ipa -vv subid-stats -+... -+ipa: INFO: Response: { -+ "error": null, -+ "id": 0, -+ "principal": "admin@IPASUBID.TEST", -+ "result": { -+ "result": { -+ "assigned_subids": 20, -+ "baseid": 2147483648, -+ "dna_remaining": 4293394434, -+ "rangesize": 2147352576, -+ "remaining_subids": 65512 -+ }, -+ "summary": "65532 remaining subordinate id ranges" -+ }, -+ "version": "4.10.0.dev" -+} -+------------------------------------- -+65532 remaining subordinate id ranges -+------------------------------------- -+``` -+ - ## Permissions, Privileges, Roles - - ### Self-servive RBAC -@@ -246,6 +377,13 @@ be modified or deleted. - * Privilege: *Subordinate ID Administrators* - * default privilege role: *User Administrator* - -+### Managed permissions -+ -+* *System: Read Subordinate Id Attributes* (all authenticated users) -+* *System: Read Subordinate Id Count* (all authenticated usrs) -+* *System: Manage Subordinate Ids* (User Administrators) -+* *System: Remove Subordinate Ids* (User Administrators) -+ - - ## Workflows - -@@ -257,12 +395,12 @@ to assign subordinate ids to users. - - Users with *User Administrator* role and members of the *admins* group - have permission to auto-assign new subordinate ids to any user. Auto --assignment can be performed with new ``user-auto-subid`` command on the -+assignment can be performed with new ``subid-generate`` command on the - command line or with the *Auto assign subordinate ids* action in the - *Actions* drop-down menu in the web UI. - - ```shell --$ ipa user-auto-subid someusername -+$ ipa subid-generate --owner myusername - ``` - - ### Self-service for group members -@@ -279,27 +417,14 @@ $ ipa role-add-member "Subordinate ID Selfservice User" --groups=ipausers - ``` - - This allows members of ``ipausers`` to request subordinate ids with --the ``user-auto-subid`` command or the *Auto assign subordinate ids* --action in the web UI. -- --```shell --$ ipa user-auto-subid myusername --``` -- --### Auto assignment with user default object class -- --Admins can also enable auto-assignment of subordinate ids for all new --users by adding ``ipasubordinateid`` as a default user objectclass. --This can be accomplished in the web UI under "IPA Server" / --"Configuration" / "Default user objectclasses" or on the command line --with: -+the ``subid-generate`` command or the *Auto assign subordinate ids* -+action in the web UI (**TODO** not implemented yet). The command picks -+the name of the current user principal automatically. - - ```shell --$ ipa config-mod --addattr="ipaUserObjectClasses=ipasubordinateid" -+$ ipa subid-generate - ``` - --**NOTE:** The objectclass must be written all lower case. -- - ### ipa-subid tool - - Finally IPA includes a new tool for mass-assignment of subordinate ids. -@@ -355,26 +480,25 @@ gid range. The new command ``user-match-subid`` can be used to find a - user by any subordinate id in their range. - - ```raw --$ ipa user-match-subid --subuid=2153185287 -- User login: asmith -- First name: Alice -- Last name: Smith -- ... -- SubUID range start: 2153185280 -+$ ipa subid-match --subuid=2147549183 -+------------------------ -+1 subordinate id matched -+------------------------ -+ Name: asmith-auto -+ Owner: asmith -+ SubUID range start: 2147483648 - SubUID range size: 65536 -- SubGID range start: 2153185280 -+ SubGID range start: 2147483648 - SubGID range size: 65536 - ---------------------------- - Number of entries returned 1 - ---------------------------- --$ ipa user-match-subid --subuid=2153185279 -- User login: bjones -- First name: Bob -- Last name: Jones -- ... -- SubUID range start: 2153119744 -+$ ipa user-match-subid --subuid=2147549184 -+ Name: bjones-auto -+ Owner: bjones -+ SubUID range start: 2147549184 - SubUID range size: 65536 -- SubGID range start: 2153119744 -+ SubGID range start: 2147549184 - SubGID range size: 65536 - ---------------------------- - Number of entries returned 1 -@@ -383,61 +507,54 @@ Number of entries returned 1 - - ## SSSD integration - --* base: ``cn=accounts,$SUFFIX`` / ``cn=users,cn=accounts,$SUFFIX`` --* scope: ``SCOPE_SUBTREE`` (2) / ``SCOPE_ONELEVEL`` (1) --* user filter: should include ``(objectClass=posixAccount)`` --* attributes: ``uidNumber ipaSubUidNumber ipaSubUidCount ipaSubGidNumber ipaSubGidCount`` -- --SSSD can safely assume that only *user accounts* of type ``posixAccount`` --have subordinate ids. In the first revision there are no other entries --with subordinate ids. The ``posixAccount`` object class has ``uid`` --(user login name) and ``uidNumber`` (numeric user id) as mandatory --attributes. The ``uid`` attribute is guaranteed to be unique across --all user accounts in an IPA domain. -- --The ``uidNumber`` attribute is commonly unique, too. However it's --technically possible that an administrator has assigned the same --numeric user id to multiple users. Automatically assigned uid numbers --don't conflict. SSSD should treat multiple users with same numeric --user id as an error. -+* search base: ``cn=subids,cn=accounts,$SUFFIX`` -+* scope: ``SCOPE_ONELEVEL`` (1) -+* filter: ``(objectClass=ipaSubordinateId)`` -+* attributes: ``ipaOwner ipaSubUidNumber ipaSubUidCount ipaSubGidNumber ipaSubGidCount`` - - The attribute ``ipaSubUidNumber`` is always accompanied by - ``ipaSubUidCount`` and ``ipaSubGidNumber`` is always accompanied - by ``ipaSubGidCount``. In revision 1 the presence of - ``ipaSubUidNumber`` implies presence of the other three attributes. --All four subordinate id attributes and ``uidNumber`` are single-value --``INTEGER`` types. Any value outside of range of ``uint32_t`` must --treated as invalid. SSSD will never see the DNA magic value ``-1`` --in ``cn=accounts,$SUFFIX`` subtree. -- --IPA recommends that SSSD simply extends its existing query for user --accounts and requests the four subordinate attributes additionally to --RFC 2307 attributes ``rfc2307_user_map``. SSSD can directly take the --values and return them without further processing, e.g. --``uidNumber:ipaSubUidNumber:ipaSubUidCount`` for ``/etc/subuid``. -- --Filters for additional cases: -- --* subuid filter (find user with subuid by numeric uid): -- ``&((objectClass=posixAccount)(ipaSubUidNumber=*)(uidNumber=$UID))``, -- ``(&(objectClass=ipaSubordinateId)(uidNumber=$UID))``, or similar --* subuid enumeration filter: -- ``&((objectClass=posixAccount)(ipaSubUidNumber=*)(uidNumber=*))``, -- ``(objectClass=ipaSubordinateId)``, or similar --* subgid filter (find user with subgid by numeric uid): -- ``&((objectClass=posixAccount)(ipaSubGidNumber=*)(uidNumber=$UID))``, -- ``(&(objectClass=ipaSubordinateId)(uidNumber=$UID))``, or similar --* subgid enumeration filter: -- ``&((objectClass=posixAccount)(ipaSubGidNumber=*)(uidNumber=*))``, -- ``(objectClass=ipaSubordinateId)``, or similar -+All four subordinate id attributes are single-value ``INTEGER`` types. -+Any value outside of range of ``uint32_t`` must treated as invalid. -+SSSD will never see the DNA magic value ``-1`` in -+``cn=accounts,$SUFFIX`` subtree. In revision 1 each user subordinate -+id entry is assigned to exactly one user and each user has either 0 -+or 1 subid. -+ -+IPA recommends that SSSD uses LDAP deref controls for ``ipaOwner`` -+attribute to fetch ``uidNumber`` from the user object. -+ -+### Deref control example -+ -+```raw -+$ ldapsearch -L -E '!deref=ipaOwner:uid,uidNumber' \ -+ -b 'cn=subids,cn=accounts,dc=ipasubid,dc=test' \ -+ '(ipaOwner=uid=testuser10,cn=users,cn=accounts,dc=ipasubid,dc=test)' -+ -+dn: ipauniqueid=35c02c93-3799-4551-a355-ebbf042e431c,cn=subids,cn=accounts,dc=ipasubid,dc=test -+# control: 1.3.6.1.4.1.4203.666.5.16 false MIQAAABxMIQAAABrBAhpcGFPd25lcgQ3dWlk -+ PXRlc3R1c2VyMTAsY249dXNlcnMsY249YWNjb3VudHMsZGM9aXBhc3ViaWQsZGM9dGVzdKCEAAAAIj -+ CEAAAAHAQJdWlkTnVtYmVyMYQAAAALBAk2MjgwMDAwMTE= -+# ipaOwner: ;uid=testuser10,cn=users,cn=accounts,dc=ipasubid,dc=test -+ -+ipaOwner: uid=testuser10,cn=users,cn=accounts,dc=ipasubid,dc=test -+ipaUniqueID: 35c02c93-3799-4551-a355-ebbf042e431c -+description: auto-assigned subid -+objectClass: ipasubordinateidentry -+objectClass: ipasubordinategid -+objectClass: ipasubordinateuid -+objectClass: ipasubordinateid -+objectClass: top -+ipaSubUidNumber: 3434020864 -+ipaSubGidNumber: 3434020864 -+ipaSubUidCount: 65536 -+ipaSubGidCount: 65536 -+``` - - ## Implementation details - --* The four subid attributes are not included in -- ``baseuser.default_attributes`` on purpose. The ``config-mod`` -- command does not permit removal of a user default objectclasses -- when the class is the last provider of an attribute in -- ``default_attributes``. - * ``ipaSubordinateId`` object class does not subclass the other two - object classes. LDAP supports - ``SUP ( ipaSubordinateGid $ ipaSubordinateUid )`` but 389-DS only -@@ -459,6 +576,13 @@ Filters for additional cases: - * Shared DNA configuration entries in ``cn=dna,cn=ipa,cn=etc,$SUFFIX`` - are automatically removed by existing code. Server and replication - plug-ins search and delete entries by ``dnaHostname`` attribute. -+* ``ipaSubordinateId`` entries no longer contains ``uidNumber`` -+ attribute. I considered to use CoS plugin to provide ``uidNumber`` -+ as virtual attribute. However it's not possible to -+ ``objectClass: cosIndirectDefinition`` with -+ ``cosIndirectSpecifier: ipaOwner`` and -+ ``cosAttribute: uidNumber override`` for the task. Indexes and -+ searches don't work with virtual attributes. - - ### TODO - -@@ -466,3 +590,23 @@ Filters for additional cases: - * remove ``fake_dna_plugin`` hack from ``baseuser`` plug-in. - * add custom range type for idranges and teach AD trust, sidgen, and - range overlap check code to deal with new range type. -+ -+#### user-del --preserve -+ -+Preserving a user with ``ipa user-del --preserve`` currently fails with -+an ObjectclassViolation error (err=65). The problem is caused by -+configuration of referential integrity postoperation plug-in. The -+plug-in excludes the subtree -+``nsslapd-pluginexcludeentryscope: cn=provisioning,$SUFFX``. Preserved -+users are moved into the staging area of the provisioning subtree. -+Since the ``ipaOwner`` DN target is now out of scope, the plug-in -+attempts to delete references. However ``ipaOwner`` is a required -+attribute, which triggers the objectclass violation. -+ -+Possible solutions -+ -+* Don't preserve subid entries -+* Implement preserve feature for subid entries and move subids of -+ preserved users into -+ ``cn=deleted subids,cn=accounts,cn=provisioning,$SUFFIX`` subtree. -+* Change ``nsslapd-pluginexcludeentryscope`` setting -diff --git a/install/share/60basev4.ldif b/install/share/60basev4.ldif -index 7f5173e593ff68a03d4005957b1dc9b9eb489dc5..c48b0c36a3012d86a74e12a77695e29cceafb698 100644 ---- a/install/share/60basev4.ldif -+++ b/install/share/60basev4.ldif -@@ -5,15 +5,16 @@ - ## - dn: cn=schema - # subordinate ids --# range ceiling OIDs are reserved for future use (operational attribute?) --# object class requires uidNumber but does not subclass posixAccount so we --# can re-use the object class in idview overrides later. --attributeTypes: ( 2.16.840.1.113730.3.8.23.6 NAME 'ipaSubUidNumber' DESC 'Numerical subordinate user ID (range start value)' EQUALITY integerMatch ORDERING integerOrderingMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE X-ORIGIN 'IPA v4.9') --attributeTypes: ( 2.16.840.1.113730.3.8.23.7 NAME 'ipaSubUidCount' DESC 'Subordinate user ID count (range size)' EQUALITY integerMatch ORDERING integerOrderingMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE X-ORIGIN 'IPA v4.9') --# attributeTypes: ( 2.16.840.1.113730.3.8.23.8 NAME 'ipaSubUidCeiling' DESC 'Numerical subordinate user ID ceiling (largest value in range)' EQUALITY integerMatch ORDERING integerOrderingMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE X-ORIGIN 'IPA v4.9') --attributeTypes: ( 2.16.840.1.113730.3.8.23.9 NAME 'ipaSubGidNumber' DESC 'Numerical subordinate group ID (range start value)' EQUALITY integerMatch ORDERING integerOrderingMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE X-ORIGIN 'IPA v4.9') --attributeTypes: ( 2.16.840.1.113730.3.8.23.10 NAME 'ipaSubGidCount' DESC 'Subordinate group ID count (range size)' EQUALITY integerMatch ORDERING integerOrderingMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE X-ORIGIN 'IPA v4.9') --# attributeTypes: ( 2.16.840.1.113730.3.8.23.11 NAME 'ipaSubGidCeiling' DESC 'Numerical subordinate user ID ceiling (largest value in range)' EQUALITY integerMatch ORDERING integerOrderingMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE X-ORIGIN 'IPA v4.9') --objectClasses: (2.16.840.1.113730.3.8.24.2 NAME 'ipaSubordinateUid' DESC 'Subordinate uids for users, see subuid(5)' SUP top AUXILIARY MUST ( uidNumber $ ipaSubUidNumber $ ipaSubUidCount ) X-ORIGIN 'IPA v4.9') --objectClasses: (2.16.840.1.113730.3.8.24.3 NAME 'ipaSubordinateGid' DESC 'Subordinate gids for users, see subgid(5)' SUP top AUXILIARY MUST ( uidNumber $ ipaSubGidNumber $ ipaSubGidCount ) X-ORIGIN 'IPA v4.9') --objectClasses: (2.16.840.1.113730.3.8.24.4 NAME 'ipaSubordinateId' DESC 'Subordinate uid and gid for users' SUP top AUXILIARY MUST ( uidNumber $ ipaSubUidNumber $ ipaSubUidCount $ ipaSubGidNumber $ ipaSubGidCount ) X-ORIGIN 'IPA v4.9') -+# range ceiling OIDs are reserved for future use -+attributeTypes: ( 2.16.840.1.113730.3.8.23.7 NAME 'ipaSubUidNumber' DESC 'Numerical subordinate user ID (range start value)' EQUALITY integerMatch ORDERING integerOrderingMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE X-ORIGIN 'IPA v4.9') -+attributeTypes: ( 2.16.840.1.113730.3.8.23.8 NAME 'ipaSubUidCount' DESC 'Subordinate user ID count (range size)' EQUALITY integerMatch ORDERING integerOrderingMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE X-ORIGIN 'IPA v4.9') -+# attributeTypes: ( 2.16.840.1.113730.3.8.23.9 NAME 'ipaSubUidCeiling' DESC 'Numerical subordinate user ID ceiling (largest value in range)' EQUALITY integerMatch ORDERING integerOrderingMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE X-ORIGIN 'IPA v4.9') -+attributeTypes: ( 2.16.840.1.113730.3.8.23.10 NAME 'ipaSubGidNumber' DESC 'Numerical subordinate group ID (range start value)' EQUALITY integerMatch ORDERING integerOrderingMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE X-ORIGIN 'IPA v4.9') -+attributeTypes: ( 2.16.840.1.113730.3.8.23.11 NAME 'ipaSubGidCount' DESC 'Subordinate group ID count (range size)' EQUALITY integerMatch ORDERING integerOrderingMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE X-ORIGIN 'IPA v4.9') -+# attributeTypes: ( 2.16.840.1.113730.3.8.23.12 NAME 'ipaSubGidCeiling' DESC 'Numerical subordinate user ID ceiling (largest value in range)' EQUALITY integerMatch ORDERING integerOrderingMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE X-ORIGIN 'IPA v4.9') -+attributeTypes: ( 2.16.840.1.113730.3.8.23.13 NAME 'ipaOwner' DESC 'Owner of an entry' SUP distinguishedName EQUALITY distinguishedNameMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.12 SINGLE-VALUE X-ORIGIN 'IPA v4.9') -+# attribute 2.16.840.1.113730.3.8.23.14 'ipaUserDefaultSubordinateId' is defined in 60ipaconfig.ldif -+objectClasses: (2.16.840.1.113730.3.8.24.2 NAME 'ipaSubordinateUid' DESC 'Subordinate uids for users, see subuid(5)' SUP top AUXILIARY MUST ( ipaOwner $ ipaSubUidNumber $ ipaSubUidCount ) X-ORIGIN 'IPA v4.9') -+objectClasses: (2.16.840.1.113730.3.8.24.3 NAME 'ipaSubordinateGid' DESC 'Subordinate gids for users, see subgid(5)' SUP top AUXILIARY MUST ( ipaOwner $ ipaSubGidNumber $ ipaSubGidCount ) X-ORIGIN 'IPA v4.9') -+objectClasses: (2.16.840.1.113730.3.8.24.4 NAME 'ipaSubordinateId' DESC 'Subordinate uid and gid for users' SUP top AUXILIARY MUST ( ipaOwner $ ipaSubUidNumber $ ipaSubUidCount $ ipaSubGidNumber $ ipaSubGidCount ) X-ORIGIN 'IPA v4.9') -+objectClasses: (2.16.840.1.113730.3.8.24.5 NAME 'ipaSubordinateIdEntry' DESC 'Subordinate uid and gid entry' SUP top STRUCTURAL MUST ( ipaUniqueId ) MAY ( description ) X-ORIGIN 'IPA v4.9') -diff --git a/install/share/60ipaconfig.ldif b/install/share/60ipaconfig.ldif -index dbcf9ee603b361f6aac1413d0a53fff5561b6f89..f84b38ead1d70ff408f5669029f1517b0c98ecf1 100644 ---- a/install/share/60ipaconfig.ldif -+++ b/install/share/60ipaconfig.ldif -@@ -6,6 +6,7 @@ - ## ObjectClasses: 2.16.840.1.113730.3.8.2 - V1 - ## Attributes: 2.16.840.1.113730.3.8.3 - V2 - ## ObjectClasses: 2.16.840.1.113730.3.8.4 - V2 -+## Attributes: 2.16.840.1.113730.3.8.23 - V4 base attributes - dn: cn=schema - ############################################### - ## -@@ -45,11 +46,13 @@ attributeTypes: ( 2.16.840.1.113730.3.8.3.26 NAME 'ipaSELinuxUserMapDefault' DES - attributeTypes: ( 2.16.840.1.113730.3.8.3.27 NAME 'ipaSELinuxUserMapOrder' DESC 'Available SELinux user context ordering' EQUALITY caseIgnoreMatch ORDERING caseIgnoreMatch SUBSTR caseIgnoreSubstringsMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 SINGLE-VALUE X-ORIGIN 'IPA v3') - ## ipaMaxHostnameLength - maximum hostname length to allow - attributeTypes: ( 2.16.840.1.113730.3.8.1.28 NAME 'ipaMaxHostnameLength' EQUALITY integerMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE) -+# ipaUserDefaultSubordinateId - if TRUE new user entries gain subordinate id by default -+attributeTypes: ( 2.16.840.1.113730.3.8.3.23.14 NAME 'ipaUserDefaultSubordinateId' DESC 'Enable adding user entries with subordinate id' SYNTAX 1.3.6.1.4.1.1466.115.121.1.7 SINGLE-VALUE X-ORIGIN 'IPA v4.9') - ############################################### - ## - ## ObjectClasses - ## - ## ipaGuiConfig - GUI config parameters objectclass --objectClasses: ( 2.16.840.1.113730.3.8.2.1 NAME 'ipaGuiConfig' AUXILIARY MAY ( ipaUserSearchFields $ ipaGroupSearchFields $ ipaSearchTimeLimit $ ipaSearchRecordsLimit $ ipaCustomFields $ ipaHomesRootDir $ ipaDefaultLoginShell $ ipaDefaultPrimaryGroup $ ipaMaxUsernameLength $ ipaPwdExpAdvNotify $ ipaUserObjectClasses $ ipaGroupObjectClasses $ ipaDefaultEmailDomain $ ipaMigrationEnabled $ ipaCertificateSubjectBase $ ipaSELinuxUserMapDefault $ ipaSELinuxUserMapOrder $ ipaKrbAuthzData $ ipaMaxHostnameLength) ) -+objectClasses: ( 2.16.840.1.113730.3.8.2.1 NAME 'ipaGuiConfig' AUXILIARY MAY ( ipaUserSearchFields $ ipaGroupSearchFields $ ipaSearchTimeLimit $ ipaSearchRecordsLimit $ ipaCustomFields $ ipaHomesRootDir $ ipaDefaultLoginShell $ ipaDefaultPrimaryGroup $ ipaMaxUsernameLength $ ipaPwdExpAdvNotify $ ipaUserObjectClasses $ ipaGroupObjectClasses $ ipaDefaultEmailDomain $ ipaMigrationEnabled $ ipaCertificateSubjectBase $ ipaSELinuxUserMapDefault $ ipaSELinuxUserMapOrder $ ipaKrbAuthzData $ ipaMaxHostnameLength $ ipaUserDefaultSubordinateId) ) - ## ipaConfigObject - Generic config strings object holder - objectClasses: (2.16.840.1.113730.3.8.4.13 NAME 'ipaConfigObject' DESC 'generic config object for IPA' AUXILIARY MAY ( ipaConfigString ) X-ORIGIN 'IPA v2' ) -diff --git a/install/share/dna.ldif b/install/share/dna.ldif -index 649313e72fc58112865e5901125923b3704276b1..735faab8261feef59486f7c933b01c57ad511166 100644 ---- a/install/share/dna.ldif -+++ b/install/share/dna.ldif -@@ -29,12 +29,12 @@ dnaMagicRegen: -1 - dnaFilter: (objectClass=ipaSubordinateId) - dnaScope: $SUFFIX - dnaThreshold: eval($SUBID_DNA_THRESHOLD) --# TODO: enable when 389-DS' DNA plugin supports dnaStepAttr --# dnaStepAttr: ipaSubUidCount --# dnaStepAttr: ipaSubGidCount --# dnaStepAllowedValues: eval($SUBID_COUNT) - dnaSharedCfgDN: cn=subordinate-ids,cn=dna,cn=ipa,cn=etc,$SUFFIX - dnaExcludeScope: cn=provisioning,$SUFFIX -+# TODO: enable when 389-DS' DNA plugin supports dnaStepAttr -+# dnaIntervalAttr: ipasubuidcount -+# dnaIntervalAttr: ipasubgidcount -+# dnaMaxInterval: eval($SUBID_COUNT) - - # Enable the DNA plugin - dn: cn=Distributed Numeric Assignment Plugin,cn=plugins,cn=config -diff --git a/install/share/memberof-conf.ldif b/install/share/memberof-conf.ldif -index 79ad647e76feb6647524553a634c91c66ebd178e..3c22dfa9e52b8005bac88cd0962571c6fea18e7b 100644 ---- a/install/share/memberof-conf.ldif -+++ b/install/share/memberof-conf.ldif -@@ -8,4 +8,6 @@ memberofgroupattr: memberUser - - - add: memberofgroupattr - memberofgroupattr: memberHost -- -+- -+add: memberofgroupattr -+memberofgroupattr: ipaOwner -diff --git a/install/ui/src/freeipa/app.js b/install/ui/src/freeipa/app.js -index 093737b8f923b41e8a1eabc90f66c6709991e239..9e0007528febb3d6641d152c72c51328d2d72cf6 100644 ---- a/install/ui/src/freeipa/app.js -+++ b/install/ui/src/freeipa/app.js -@@ -52,6 +52,7 @@ define([ - './serverconfig', - './service', - './stageuser', -+ './subid', - './sudo', - './trust', - './topology', -diff --git a/install/ui/src/freeipa/navigation/menu_spec.js b/install/ui/src/freeipa/navigation/menu_spec.js -index 0c30459691d8f652dc35ccf74ed27fae7654020d..6ccd06919fbe04c7e8d2034ff7a1f644f373c607 100644 ---- a/install/ui/src/freeipa/navigation/menu_spec.js -+++ b/install/ui/src/freeipa/navigation/menu_spec.js -@@ -103,7 +103,8 @@ var nav = {}; - ] - } - ] -- } -+ }, -+ { entity: 'subid' } - ] - }, - { -diff --git a/install/ui/src/freeipa/serverconfig.js b/install/ui/src/freeipa/serverconfig.js -index bb26b107317ae12ee032fb767fcfc9060690c66e..9de0bab4b64abf1094e52dffda8add5349dc0e3c 100644 ---- a/install/ui/src/freeipa/serverconfig.js -+++ b/install/ui/src/freeipa/serverconfig.js -@@ -123,6 +123,10 @@ return { - $type: 'checkbox', - name: 'ipamigrationenabled' - }, -+ { -+ $type: 'checkbox', -+ name: 'ipauserdefaultsubordinateid' -+ }, - { - $type: 'multivalued', - name: 'ipauserobjectclasses' -diff --git a/install/ui/src/freeipa/subid.js b/install/ui/src/freeipa/subid.js -new file mode 100644 -index 0000000000000000000000000000000000000000..f286165070b08badf77cac6c30e93cab916c2acc ---- /dev/null -+++ b/install/ui/src/freeipa/subid.js -@@ -0,0 +1,92 @@ -+/* -+ * Copyright (C) 2021 FreeIPA Contributors see COPYING for license -+ */ -+ -+define([ -+ 'dojo/on', -+ './ipa', -+ './jquery', -+ './phases', -+ './reg', -+ './details', -+ './search', -+ './association', -+ './entity'], -+ function(on, IPA, $, phases, reg) { -+ -+var exp = IPA.subid = {}; -+ -+var make_spec = function() { -+return { -+ name: 'subid', -+ facets: [ -+ { -+ $type: 'search', -+ columns: [ -+ 'ipauniqueid', -+ 'ipaowner', -+ 'ipasubgidnumber', -+ 'ipasubuidnumber' -+ ] -+ }, -+ { -+ $type: 'details', -+ sections: [ -+ { -+ name: 'details', -+ fields: [ -+ 'ipauniqueid', -+ 'description', -+ { -+ name: 'ipaowner', -+ label: '@i18n:objects.subid.ipaowner', -+ title: '@mo-param:subid:ipaowner:label' -+ }, -+ { -+ name: 'ipasubgidnumber', -+ label: '@i18n:objects.subid.ipasubgidnumber', -+ title: '@mo-param:subid:ipasubgidnumber:label' -+ }, -+ { -+ name: 'ipasubgidcount', -+ label: '@i18n:objects.subid.ipasubgidcount', -+ title: '@mo-param:subid:ipasubgidcount:label' -+ }, -+ { -+ name: 'ipasubuidnumber', -+ label: '@i18n:objects.subid.ipasubuidnumber', -+ title: '@mo-param:subid:ipasubuidnumber:label' -+ }, -+ { -+ name: 'ipasubuidcount', -+ label: '@i18n:objects.subid.ipasubuidcount', -+ title: '@mo-param:subid:ipasubuidcount:label' -+ } -+ ] -+ } -+ ] -+ } -+ ], -+ adder_dialog: { -+ title: '@i18n:objects.subid.add', -+ method: 'generate', -+ fields: [ -+ { -+ $type: 'entity_select', -+ name: 'ipaowner', -+ other_entity: 'user', -+ other_field: 'uid' -+ } -+ ] -+ } -+};}; -+ -+exp.entity_spec = make_spec(); -+exp.register = function() { -+ var e = reg.entity; -+ e.register({type: 'subid', spec: exp.entity_spec}); -+}; -+phases.on('registration', exp.register); -+ -+return {}; -+}); -diff --git a/install/ui/src/freeipa/user.js b/install/ui/src/freeipa/user.js -index 5b49b0f6edbbbb6c802afb803a6406a0ab796c44..56bb6f4feffb637d33a57aecf9a98f08d4639550 100644 ---- a/install/ui/src/freeipa/user.js -+++ b/install/ui/src/freeipa/user.js -@@ -259,33 +259,6 @@ return { - } - ] - }, -- { -- name: 'subordinate', -- label: '@i18n:objects.subordinate.identity', -- fields: [ -- { -- name: 'ipasubuidnumber', -- label: '@i18n:objects.subordinate.subuidnumber', -- read_only: true -- }, -- { -- name: 'ipasubuidcount', -- label: '@i18n:objects.subordinate.subuidcount', -- read_only: true -- -- }, -- { -- name: 'ipasubgidnumber', -- label: '@i18n:objects.subordinate.subgidnumber', -- read_only: true -- }, -- { -- name: 'ipasubgidcount', -- label: '@i18n:objects.subordinate.subgidcount', -- read_only: true -- } -- ] -- }, - { - name: 'pwpolicy', - label: '@i18n:objects.pwpolicy.identity', -@@ -478,16 +451,6 @@ return { - enable_cond: ['is-locked'], - confirm_msg: '@i18n:objects.user.unlock_confirm' - }, -- { -- $factory: IPA.object_action, -- name: 'auto_subid', -- method: 'auto_subid', -- label: '@i18n:objects.user.auto_subid', -- needs_confirm: true, -- hide_cond: ['preserved-user'], -- enable_cond: ['no-subid'], -- confirm_msg: '@i18n:objects.user.auto_subid_confirm' -- }, - { - $type: 'automember_rebuild', - name: 'automember_rebuild', -@@ -500,20 +463,15 @@ return { - title: '@i18n:objects.cert.issue_for_user' - }, - { -- $factory: IPA.object_action, -- name: 'auto_subid', -- method: 'auto_subid', -- label: '@i18n:objects.user.auto_subid', -- needs_confirm: true, -+ $type: 'subid_generate', - hide_cond: ['preserved-user'], -- enable_cond: ['no-subid'], -- confirm_msg: '@i18n:objects.user.auto_subid_confirm' -+ enable_cond: ['no-subid'] - } - ], - header_actions: [ - 'reset_password', 'enable', 'disable', 'stage', 'undel', - 'delete_active_user', 'delete', 'unlock', 'add_otptoken', -- 'automember_rebuild', 'request_cert', 'auto_subid' -+ 'automember_rebuild', 'request_cert', 'subid_generate' - ], - state: { - evaluators: [ -@@ -532,7 +490,8 @@ return { - IPA.user.self_service_other_user_evaluator, - IPA.user.preserved_user_evaluator, - IPA.user.is_locked_evaluator, -- IPA.cert.certificate_evaluator -+ IPA.cert.certificate_evaluator, -+ IPA.user.has_subid_evaluator - ], - summary_conditions: [ - { -@@ -593,6 +552,12 @@ return { - add_title: '@i18n:objects.user.add_into_sudo', - remove_method: 'remove_user', - remove_title: '@i18n:objects.user.remove_from_sudo' -+ }, -+ { -+ $type: 'association', -+ name: 'memberof_subid', -+ associator: IPA.serial_associator, -+ read_only: true - } - ], - standard_association_facets: { -@@ -1206,7 +1171,31 @@ IPA.user.is_locked_evaluator = function(spec) { - } - } - -- if (!user.ipasubuidnumber) { -+ that.notify_on_change(old_state); -+ }; -+ -+ return that; -+}; -+ -+IPA.user.has_subid_evaluator = function(spec) { -+ -+ spec = spec || {}; -+ spec.event = spec.event || 'post_load'; -+ -+ var that = IPA.state_evaluator(spec); -+ that.name = spec.name || 'has_subid_evaluator'; -+ that.param = spec.param || 'memberof_subid'; -+ -+ /** -+ * Evaluates if user already has a subid -+ */ -+ that.on_event = function(data) { -+ -+ var old_state = that.state; -+ that.state = []; -+ -+ var value = that.adapter.load(data); -+ if (value.length === 0) { - that.state.push('no-subid'); - } - -@@ -1216,6 +1205,30 @@ IPA.user.is_locked_evaluator = function(spec) { - return that; - }; - -+IPA.user.subid_generate_action = function(spec) { -+ -+ spec = spec || {}; -+ spec.name = spec.name || 'subid_generate'; -+ spec.label = spec.label || '@i18n:objects.user.auto_subid'; -+ spec.hide_cond = spec.hide_cond || ['preserved-user']; -+ spec.confirm_msg = spec.confirm_msg || '@i18n:objects.user.auto_subid_confirm'; -+ -+ var that = IPA.action(spec); -+ -+ that.execute_action = function(facet) { -+ -+ var subid_e = reg.entity.get('subid'); -+ var dialog = subid_e.get_dialog('add'); -+ dialog.open(); -+ if (!IPA.is_selfservice) { -+ var owner = facet.get_pkey(); -+ dialog.get_field('ipaowner').set_value([owner]); -+ } -+ }; -+ -+ return that; -+}; -+ - exp.entity_spec = make_spec(); - exp.register = function() { - var e = reg.entity; -@@ -1225,6 +1238,7 @@ exp.register = function() { - a.register('reset_password', IPA.user.reset_password_action); - a.register('add_otptoken', IPA.user.add_otptoken_action); - a.register('delete_active_user', IPA.user.delete_active_user_action); -+ a.register('subid_generate', IPA.user.subid_generate_action); - d.copy('password', 'user_password', { - factory: IPA.user.password_dialog, - pre_ops: [IPA.user.password_dialog_pre_op] -diff --git a/install/updates/10-uniqueness.update b/install/updates/10-uniqueness.update -index 77facba195cb5a1564818010f97afdd15d65a274..699de3b4d3305def5d81aeb945106b80eef0ef40 100644 ---- a/install/updates/10-uniqueness.update -+++ b/install/updates/10-uniqueness.update -@@ -109,3 +109,22 @@ default:nsslapd-plugin-depends-on-type: database - default:nsslapd-pluginId: NSUniqueAttr - default:nsslapd-pluginVersion: 1.1.0 - default:nsslapd-pluginVendor: Fedora Project -+ -+dn: cn=ipaSubordinateIdEntry ipaOwner uniqueness,cn=plugins,cn=config -+default:objectClass: top -+default:objectClass: nsSlapdPlugin -+default:objectClass: extensibleObject -+default:cn: ipaSubordinateIdEntry ipaOwner uniqueness -+default:nsslapd-pluginDescription: Enforce unique attribute values of ipaOwner -+default:nsslapd-pluginPath: libattr-unique-plugin -+default:nsslapd-pluginInitfunc: NSUniqueAttr_Init -+default:nsslapd-pluginType: preoperation -+default:nsslapd-pluginEnabled: on -+default:uniqueness-attribute-name: ipaOwner -+default:uniqueness-subtrees: cn=subids,cn=accounts,$SUFFIX -+default:uniqueness-across-all-subtrees: on -+default:uniqueness-subtree-entries-oc: ipaSubordinateIdEntry -+default:nsslapd-plugin-depends-on-type: database -+default:nsslapd-pluginId: NSUniqueAttr -+default:nsslapd-pluginVersion: 1.1.0 -+default:nsslapd-pluginVendor: Fedora Project -diff --git a/install/updates/20-indices.update b/install/updates/20-indices.update -index 7f83ab9f04c565a59efdd2f41c3e7ee30f5da9c7..d6df5b37d0a9092e936d2c6002bce71ed876ec27 100644 ---- a/install/updates/20-indices.update -+++ b/install/updates/20-indices.update -@@ -263,6 +263,14 @@ default:nsSystemIndex: false - add:nsIndexType: eq - add:nsIndexType: pres - -+dn: cn=ipaOwner,cn=index,cn=userRoot,cn=ldbm database,cn=plugins,cn=config -+only:cn: ipaOwner -+default:objectClass: nsIndex -+default:objectClass: top -+default:nsSystemIndex: false -+add:nsIndexType: eq -+add:nsIndexType: pres -+ - dn: cn=ipasudorunas,cn=index,cn=userRoot,cn=ldbm database,cn=plugins,cn=config - only:cn: ipasudorunas - default:objectClass: nsIndex -@@ -425,6 +433,10 @@ default:nsSystemIndex: false - add:nsIndexType: eq - add:nsIndexType: pres - -+dn: cn=memberOf,cn=index,cn=userRoot,cn=ldbm database,cn=plugins,cn=config -+only:cn: member -+add:nsIndexType: sub -+ - dn: cn=memberPrincipal,cn=index,cn=userRoot,cn=ldbm database,cn=plugins,cn=config - only:cn: memberPrincipal - default:objectClass: nsIndex -diff --git a/install/updates/25-referint.update b/install/updates/25-referint.update -index 89bc5ef91b2f5c85ee3a4a2c7d112a0549d4e1da..b29926a4e3bd410115cde4ede3ed7886a412d300 100644 ---- a/install/updates/25-referint.update -+++ b/install/updates/25-referint.update -@@ -21,3 +21,4 @@ add: referint-membership-attr: ipamemberca - add: referint-membership-attr: ipamembercertprofile - add: referint-membership-attr: ipalocation - add: referint-membership-attr: membermanager -+add: referint-membership-attr: ipaowner -diff --git a/install/updates/73-subid.update b/install/updates/73-subid.update -index 2aab3d445a33ae1663f81ca2d61b62ebc94aa37d..1aa43822a8b8c220583b81e08d70b648ca594363 100644 ---- a/install/updates/73-subid.update -+++ b/install/updates/73-subid.update -@@ -1,5 +1,15 @@ - # subordinate ids - -+# create memberOf attributes for ipaOwner -+dn: cn=MemberOf Plugin,cn=plugins,cn=config -+add: memberofgroupattr: ipaOwner -+ -+# container -+dn: cn=subids,cn=accounts,$SUFFIX -+default: objectClass: top -+default: objectClass: nsContainer -+default: cn: subids -+ - # self-service RBAC - dn: cn=Subordinate ID Selfservice User,cn=roles,cn=accounts,$SUFFIX - default:objectClass: groupofnames -@@ -56,9 +66,9 @@ default:member: cn=Subordinate ID Administrators,cn=privileges,cn=pbac,$SUFFIX - # self-service permission when 389-DS' DNA plugin supports dnaStepAttr and - # fake_dna_plugin hack has been removed. - # --dn: $SUFFIX --add: aci: (targetfilter = "(objectclass=posixaccount)")(targattrfilters = "add=objectClass:(|(objectClass=ipasubordinateid)(objectClass=ipasubordinategid)(objectClass=ipasubordinateuid)) && ipasubuidnumber:(|(ipasubuidnumber>=eval($SUBID_RANGE_START))(ipasubuidnumber=-1)) && ipasubuidcount:(ipasubuidcount=eval($SUBID_COUNT)) && ipasubgidnumber:(|(ipasubgidnumber>=eval($SUBID_RANGE_START))(ipasubgidnumber=-1)) && ipasubgidcount:(ipasubgidcount=eval($SUBID_COUNT)), del=ipasubuidnumber:(!(ipasubuidnumber=*)) && ipasubuidcount:(!(ipasubuidcount=*)) && ipasubgidnumber:(!(ipasubgidnumber=*)) && ipasubgidcount:(!(ipasubgidcount=*))")(version 3.0;acl "selfservice: Add subordinate id";allow (write) userdn = "ldap:///self" and groupdn="ldap:///cn=Self-service subordinate ID,cn=permissions,cn=pbac,$SUFFIX";) --add: aci: (targetfilter = "(objectclass=posixaccount)")(targattrfilters = "add=objectClass:(|(objectClass=ipasubordinateid)(objectClass=ipasubordinategid)(objectClass=ipasubordinateuid)) && ipasubuidnumber:(|(ipasubuidnumber>=1)(ipasubuidnumber=-1)) && ipasubuidcount:(ipasubuidcount=eval($SUBID_COUNT)) && ipasubgidnumber:(|(ipasubgidnumber>=1)(ipasubgidnumber=-1)) && ipasubgidcount:(ipasubgidcount=eval($SUBID_COUNT)), del=ipasubuidnumber:(!(ipasubuidnumber=*)) && ipasubuidcount:(!(ipasubuidcount=*)) && ipasubgidnumber:(!(ipasubgidnumber=*)) && ipasubgidcount:(!(ipasubgidcount=*))")(version 3.0;acl "Add subordinate ids to any user";allow (write) groupdn="ldap:///cn=Subordinate ID Administrators,cn=privileges,cn=pbac,$SUFFIX";) -+dn: cn=subids,cn=accounts,$SUFFIX -+add: aci: (targetfilter = "(objectclass=ipasubordinateidentry)")(targetattr="description || ipaowner || ipauniqueid")(targattrfilters = "add=objectClass:(|(objectClass=top)(objectClass=ipasubordinateid)(objectClass=ipasubordinateidentry)(objectClass=ipasubordinategid)(objectClass=ipasubordinateuid)) && ipasubuidnumber:(|(ipasubuidnumber>=eval($SUBID_RANGE_START))(ipasubuidnumber=-1)) && ipasubuidcount:(ipasubuidcount=eval($SUBID_COUNT)) && ipasubgidnumber:(|(ipasubgidnumber>=eval($SUBID_RANGE_START))(ipasubgidnumber=-1)) && ipasubgidcount:(ipasubgidcount=eval($SUBID_COUNT)), del=ipasubuidnumber:(!(ipasubuidnumber=*)) && ipasubuidcount:(!(ipasubuidcount=*)) && ipasubgidnumber:(!(ipasubgidnumber=*)) && ipasubgidcount:(!(ipasubgidcount=*))")(version 3.0;acl "selfservice: Add subordinate id";allow (add, write) userattr = "ipaowner#SELFDN" and groupdn="ldap:///cn=Self-service subordinate ID,cn=permissions,cn=pbac,$SUFFIX";) -+add: aci: (targetfilter = "(objectclass=ipasubordinateidentry)")(targetattr="description || ipaowner || ipauniqueid")(targattrfilters = "add=objectClass:(|(objectClass=top)(objectClass=ipasubordinateid)(objectClass=ipasubordinateidentry)(objectClass=ipasubordinategid)(objectClass=ipasubordinateuid)) && ipasubuidnumber:(|(ipasubuidnumber>=1)(ipasubuidnumber=-1)) && ipasubuidcount:(ipasubuidcount=eval($SUBID_COUNT)) && ipasubgidnumber:(|(ipasubgidnumber>=1)(ipasubgidnumber=-1)) && ipasubgidcount:(ipasubgidcount=eval($SUBID_COUNT)), del=ipasubuidnumber:(!(ipasubuidnumber=*)) && ipasubuidcount:(!(ipasubuidcount=*)) && ipasubgidnumber:(!(ipasubgidnumber=*)) && ipasubgidcount:(!(ipasubgidcount=*))")(version 3.0;acl "Add subordinate ids to any user";allow (add, write) groupdn="ldap:///cn=Subordinate ID Administrators,cn=privileges,cn=pbac,$SUFFIX";) - - # DNA plugin and idrange configuration - dn: cn=subordinate-ids,cn=dna,cn=ipa,cn=etc,$SUFFIX -@@ -78,14 +88,14 @@ default: dnaMagicRegen: -1 - default: dnaFilter: (objectClass=ipaSubordinateId) - default: dnaScope: $SUFFIX - default: dnaThreshold: eval($SUBID_DNA_THRESHOLD) --# TODO: enable when 389-DS' DNA plugin supports dnaStepAttr --# default: dnaStepAttr: ipaSubUidCount --# default: dnaStepAttr: ipaSubGidCount --# default: dnaStepAllowedValues: eval($SUBID_COUNT) - default: dnaSharedCfgDN: cn=subordinate-ids,cn=dna,cn=ipa,cn=etc,$SUFFIX - default: dnaExcludeScope: cn=provisioning,$SUFFIX --default: aci: (targetattr = "dnaNextRange || dnaNextValue || dnaMaxValue")(version 3.0;acl "permission:Modify DNA Range";allow (write) groupdn = "ldap:///cn=Modify DNA Range,cn=permissions,cn=pbac,$SUFFIX";) --default: aci: (targetattr = "cn || dnaMaxValue || dnaNextRange || dnaNextValue || dnaThreshold || dnaType || objectclass")(version 3.0;acl "permission:Read DNA Range";allow (read, search, compare) groupdn = "ldap:///cn=Read DNA Range,cn=permissions,cn=pbac,$SUFFIX";) -+# TODO: enable when 389-DS' DNA plugin supports dnaStepAttr -+# add: dnaIntervalAttr: ipasubuidcount -+# add: dnaIntervalAttr: ipasubgidcount -+# addifnew: dnaMaxInterval: eval($SUBID_COUNT) -+add: aci: (targetattr = "dnaNextRange || dnaNextValue || dnaMaxValue")(version 3.0;acl "permission:Modify DNA Range";allow (write) groupdn = "ldap:///cn=Modify DNA Range,cn=permissions,cn=pbac,$SUFFIX";) -+add: aci: (targetattr = "cn || dnaMaxValue || dnaNextRange || dnaNextValue || dnaThreshold || dnaType || objectclass")(version 3.0;acl "permission:Read DNA Range";allow (read, search, compare) groupdn = "ldap:///cn=Read DNA Range,cn=permissions,cn=pbac,$SUFFIX";) - - dn: cn=${REALM}_subid_range,cn=ranges,cn=etc,$SUFFIX - default: objectClass: top -diff --git a/ipalib/constants.py b/ipalib/constants.py -index bee4c92fb39769d427e315116575f217924915be..bff899ba64c75832e2037870ccbca4587458d97b 100644 ---- a/ipalib/constants.py -+++ b/ipalib/constants.py -@@ -131,6 +131,9 @@ DEFAULT_CONFIG = ( - ('container_ranges', DN(('cn', 'ranges'), ('cn', 'etc'))), - ('container_dna', DN(('cn', 'dna'), ('cn', 'ipa'), ('cn', 'etc'))), - ('container_dna_posix_ids', DN(('cn', 'posix-ids'), ('cn', 'dna'), ('cn', 'ipa'), ('cn', 'etc'))), -+ ('container_dna_subordinate_ids', DN( -+ ('cn', 'subordinate-ids'), ('cn', 'dna'), ('cn', 'ipa'), ('cn', 'etc') -+ )), - ('container_realm_domains', DN(('cn', 'Realm Domains'), ('cn', 'ipa'), ('cn', 'etc'))), - ('container_otp', DN(('cn', 'otp'))), - ('container_radiusproxy', DN(('cn', 'radiusproxy'))), -@@ -148,6 +151,7 @@ DEFAULT_CONFIG = ( - ('container_certmaprules', DN(('cn', 'certmaprules'), ('cn', 'certmap'))), - ('container_ca_renewal', - DN(('cn', 'ca_renewal'), ('cn', 'ipa'), ('cn', 'etc'))), -+ ('container_subids', DN(('cn', 'subids'), ('cn', 'accounts'))), - - # Ports, hosts, and URIs: - # Following values do not have any reasonable default. -@@ -355,4 +359,4 @@ SUBID_RANGE_START = 2 ** 31 - SUBID_RANGE_MAX = (2 ** 32) - (2 * SUBID_COUNT) - SUBID_RANGE_SIZE = SUBID_RANGE_MAX - SUBID_RANGE_START - # threshold before DNA plugin requests a new range --SUBID_DNA_THRESHOLD = 500 * SUBID_COUNT -+SUBID_DNA_THRESHOLD = 500 -diff --git a/ipaserver/install/ipa_subids.py b/ipaserver/install/ipa_subids.py -index ac77a4008aec58d92c8b24df5e00b83c6998401f..2b33b667084f529fa50e2f11eeefda8a8927f68c 100644 ---- a/ipaserver/install/ipa_subids.py -+++ b/ipaserver/install/ipa_subids.py -@@ -10,7 +10,7 @@ from ipalib.facts import is_ipa_configured - from ipaplatform.paths import paths - from ipapython.admintool import AdminTool, ScriptError - from ipapython.dn import DN --from ipaserver.plugins.baseldap import DNA_MAGIC -+from ipapython.version import API_VERSION - - logger = logging.getLogger(__name__) - -@@ -77,7 +77,7 @@ class IPASubids(AdminTool): - # only users with posixAccount - "(objectClass=posixAccount)", - # without subordinate ids -- "(!(objectClass=ipaSubordinateId))", -+ f"(!(memberOf=*,cn=subids,cn=accounts,{api.env.basedn}))", - ] - if groupinfo is not None: - filters.append( -@@ -89,7 +89,7 @@ class IPASubids(AdminTool): - - def search_users(self, filters): - users_dn = DN(api.env.container_user, api.env.basedn) -- attrs = ["objectclass", "uid", "uidnumber"] -+ attrs = ["objectclass", "uid"] - - logger.debug("basedn: %s", users_dn) - logger.debug("attrs: %s", attrs) -@@ -116,7 +116,7 @@ class IPASubids(AdminTool): - api.finalize() - api.Backend.ldap2.connect() - self.ldap2 = api.Backend.ldap2 -- user_obj = api.Object["user"] -+ subid_generate = api.Command.subid_generate - - dry_run = self.safe_options.dry_run - group_info = self.get_group_info() -@@ -136,11 +136,13 @@ class IPASubids(AdminTool): - i, - total - ) -- user_obj.set_subordinate_ids( -- self.ldap2, entry.dn, entry, DNA_MAGIC -- ) - if not dry_run: -- self.ldap2.update_entry(entry) -+ # TODO: check for duplicate entry (race condition) -+ # TODO: log new subid -+ subid_generate( -+ ipaowner=entry.single_value["uid"], -+ version=API_VERSION -+ ) - - if dry_run: - logger.info("Dry run mode, no user was modified") -diff --git a/ipaserver/install/plugins/update_dna_shared_config.py b/ipaserver/install/plugins/update_dna_shared_config.py -index 0f704c68a0551a7b7db6d42275498d02885b70fc..955bee5dd830f0dcad3f0810e7e2f1a1c725a0aa 100644 ---- a/ipaserver/install/plugins/update_dna_shared_config.py -+++ b/ipaserver/install/plugins/update_dna_shared_config.py -@@ -17,7 +17,7 @@ register = Registry() - - @register() - class update_dna_shared_config(Updater): -- dna_plugin_names = ('posix IDs',) -+ dna_plugin_names = ('posix IDs', 'Subordinate IDs') - - dna_plugin_dn = DN( - ('cn', 'Distributed Numeric Assignment Plugin'), -diff --git a/ipaserver/plugins/baseldap.py b/ipaserver/plugins/baseldap.py -index 3ccd2e38a254e274ba3685b9233f23b2313f8eec..0b7839536f66740a60377460c7432ade7c0654c2 100644 ---- a/ipaserver/plugins/baseldap.py -+++ b/ipaserver/plugins/baseldap.py -@@ -121,6 +121,8 @@ global_output_params = ( - Str('memberof_hbacrule?', - label='Member of HBAC rule', - ), -+ Str('memberof_subid?', -+ label='Subordinate ids',), - Str('member_idoverrideuser?', - label=_('Member ID user overrides'),), - Str('memberindirect_idoverrideuser?', -@@ -795,7 +797,13 @@ class LDAPObject(Object): - - dn = entry.dn - filter = self.backend.make_filter( -- {'member': dn, 'memberuser': dn, 'memberhost': dn}) -+ { -+ 'member': dn, -+ 'memberuser': dn, -+ 'memberhost': dn, -+ 'ipaowner': dn -+ } -+ ) - try: - result = self.backend.get_entries( - self.api.env.basedn, -diff --git a/ipaserver/plugins/baseuser.py b/ipaserver/plugins/baseuser.py -index 12ff03c2302ff08aabb9369306965e0c125724f8..14a71b2217d3370701662b35c43f4207c1ab9b95 100644 ---- a/ipaserver/plugins/baseuser.py -+++ b/ipaserver/plugins/baseuser.py -@@ -17,10 +17,9 @@ - # You should have received a copy of the GNU General Public License - # along with this program. If not, see . - --import random - import six - --from ipalib import api, errors, output, constants -+from ipalib import api, errors, constants - from ipalib import ( - Flag, Int, Password, Str, Bool, StrEnum, DateTime, DNParam) - from ipalib.parameters import Principal, Certificate -@@ -28,9 +27,9 @@ from ipalib.plugable import Registry - from .baseldap import ( - DN, LDAPObject, LDAPCreate, LDAPUpdate, LDAPSearch, LDAPDelete, - LDAPRetrieve, LDAPAddAttribute, LDAPModAttribute, LDAPRemoveAttribute, -- LDAPQuery, LDAPAddMember, LDAPRemoveMember, -+ LDAPAddMember, LDAPRemoveMember, - LDAPAddAttributeViaOption, LDAPRemoveAttributeViaOption, -- add_missing_object_class, DNA_MAGIC, pkey_to_value, entry_to_dict -+ add_missing_object_class - ) - from ipaserver.plugins.service import (validate_realm, normalize_principal) - from ipalib.request import context -@@ -162,7 +161,7 @@ class baseuser(LDAPObject): - possible_objectclasses = [ - 'meporiginentry', 'ipauserauthtypeclass', 'ipauser', - 'ipatokenradiusproxyuser', 'ipacertmapobject', -- 'ipantuserattrs', 'ipasubordinateid', -+ 'ipantuserattrs', - ] - disallow_object_classes = ['krbticketpolicyaux'] - permission_filter_objectclasses = ['posixaccount'] -@@ -183,13 +182,13 @@ class baseuser(LDAPObject): - 'krbprincipalname', 'loginshell', - 'mail', 'telephonenumber', 'title', 'nsaccountlock', - 'uidnumber', 'gidnumber', 'sshpubkeyfp', -- 'ipasubuidnumber', 'ipasubuidcount', 'ipasubgidnumber', -- 'ipasubgidcount', - ] - uuid_attribute = 'ipauniqueid' - attribute_members = { - 'manager': ['user'], -- 'memberof': ['group', 'netgroup', 'role', 'hbacrule', 'sudorule'], -+ 'memberof': [ -+ 'group', 'netgroup', 'role', 'hbacrule', 'sudorule', 'subid' -+ ], - 'memberofindirect': ['group', 'netgroup', 'role', 'hbacrule', 'sudorule'], - } - allow_rename = True -@@ -432,41 +431,6 @@ class baseuser(LDAPObject): - 'J:', 'K:', 'L:', 'M:', 'N:', 'O:', 'P:', 'Q:', 'R:', - 'S:', 'T:', 'U:', 'V:', 'W:', 'X:', 'Y:', 'Z:'), - ), -- Int( -- 'ipasubuidnumber?', -- label=_('SubUID range start'), -- cli_name='subuid', -- doc=_('Start value for subordinate user ID (subuid) range'), -- minvalue=constants.SUBID_RANGE_START, -- maxvalue=constants.SUBID_RANGE_MAX, -- ), -- Int( -- 'ipasubuidcount?', -- label=_('SubUID range size'), -- cli_name='subuidcount', -- doc=_('Subordinate user ID count'), -- flags={'no_create', 'no_update', 'no_search'}, -- minvalue=constants.SUBID_COUNT, -- maxvalue=constants.SUBID_COUNT, -- ), -- Int( -- 'ipasubgidnumber?', -- label=_('SubGID range start'), -- cli_name='subgid', -- doc=_('Start value for subordinate group ID (subgid) range'), -- flags={'no_create', 'no_update'}, -- minvalue=constants.SUBID_RANGE_START, -- maxvalue=constants.SUBID_RANGE_MAX, -- ), -- Int( -- 'ipasubgidcount?', -- label=_('SubGID range size'), -- cli_name='subgidcount', -- doc=_('Subordinate group ID count'), -- flags={'no_create', 'no_update', 'no_search'}, -- minvalue=constants.SUBID_COUNT, -- maxvalue=constants.SUBID_COUNT, -- ), - ) - - def normalize_and_validate_email(self, email, config=None): -@@ -564,131 +528,6 @@ class baseuser(LDAPObject): - except KeyError: - pass - -- def handle_subordinate_ids(self, ldap, dn, entry_attrs): -- """Handle ipaSubordinateId object class -- """ -- obj_classes = entry_attrs.get("objectclass") -- new_subuid = entry_attrs.single_value.get("ipasubuidnumber") -- new_subgid = entry_attrs.single_value.get("ipasubgidnumber") -- -- # entry has object class ipaSubordinateId -- # default to auto-assigment of subuids -- if ( -- new_subuid is None -- and obj_classes is not None -- and self.has_objectclass(obj_classes, "ipasubordinateid") -- ): -- new_subuid = DNA_MAGIC -- -- # neither auto-assignment nor explicit assignment -- if new_subuid is None: -- # nothing to do -- return False -- -- # enforce subuid == subgid -- if new_subgid is not None and new_subgid != new_subuid: -- raise errors.ValidationError( -- name="ipasubgidnumber", -- error=_("subgidnumber must be equal to subuidnumber") -- ) -- -- self.set_subordinate_ids(ldap, dn, entry_attrs, new_subuid) -- return True -- -- def set_subordinate_ids(self, ldap, dn, entry_attrs, subuid): -- """Set subuid value of an entry -- -- Takes care of objectclass and sibbling attributes -- """ -- if "objectclass" in entry_attrs: -- obj_classes = entry_attrs["objectclass"] -- else: -- _entry_attrs = ldap.get_entry(dn, ["objectclass"]) -- entry_attrs["objectclass"] = _entry_attrs["objectclass"] -- obj_classes = entry_attrs["objectclass"] -- -- if not self.has_objectclass(obj_classes, "ipasubordinateid"): -- # could append ipasubordinategid and ipasubordinateuid, too -- obj_classes.append("ipasubordinateid") -- -- # XXX HACK, remove later -- if subuid == DNA_MAGIC: -- subuid = self._fake_dna_plugin(ldap, dn, entry_attrs) -- -- entry_attrs["ipasubuidnumber"] = subuid -- # enforice subuid == subgid for now -- entry_attrs["ipasubgidnumber"] = subuid -- # hard-coded constants -- entry_attrs["ipasubuidcount"] = constants.SUBID_COUNT -- entry_attrs["ipasubgidcount"] = constants.SUBID_COUNT -- -- def get_subid_match_candidate_filter( -- self, ldap, *, subuid, subgid, extra_filters=(), offset=None, -- ): -- """Create LDAP filter to locate matching/overlapping subids -- """ -- if subuid is None and subgid is None: -- raise ValueError("subuid and subgid are both None") -- if offset is None: -- # assumes that no subordinate count is larger than SUBID_COUNT -- offset = constants.SUBID_COUNT - 1 -- -- class_filters = "(objectclass=ipasubordinateid)" -- subid_filters = [] -- if subuid is not None: -- subid_filters.append( -- ldap.combine_filters( -- [ -- f"(ipasubuidnumber>={subuid - offset})", -- f"(ipasubuidnumber<={subuid + offset})", -- ], -- rules=ldap.MATCH_ALL -- ) -- ) -- if subgid is not None: -- subid_filters.append( -- ldap.combine_filters( -- [ -- f"(ipasubgidnumber>={subgid - offset})", -- f"(ipasubgidnumber<={subgid + offset})", -- ], -- rules=ldap.MATCH_ALL -- ) -- ) -- -- subid_filters = ldap.combine_filters( -- subid_filters, rules=ldap.MATCH_ANY -- ) -- filters = [class_filters, subid_filters] -- filters.extend(extra_filters) -- return ldap.combine_filters(filters, rules=ldap.MATCH_ALL) -- -- def _fake_dna_plugin(self, ldap, dn, entry_attrs): -- """XXX HACK, remove when 389-DS DNA plugin supports steps""" -- uidnumber = entry_attrs.single_value.get("uidnumber") -- if uidnumber is None: -- entry = ldap.get_entry(dn, ["uidnumber"]) -- uidnumber = entry.single_value["uidnumber"] -- uidnumber = int(uidnumber) -- -- if uidnumber == DNA_MAGIC: -- return ( -- 3221225472 -- + random.randint(1, 16382) * constants.SUBID_COUNT -- ) -- -- if not hasattr(context, "idrange_ipabaseid"): -- range_name = f"{self.api.env.realm}_id_range" -- range = self.api.Command.idrange_show(range_name)["result"] -- context.idrange_ipabaseid = int(range["ipabaseid"][0]) -- -- range_start = context.idrange_ipabaseid -- -- assert uidnumber >= range_start -- assert uidnumber < range_start + 2**14 -- -- return (uidnumber - range_start) * constants.SUBID_COUNT + 2**31 -- - - class baseuser_add(LDAPCreate): - """ -@@ -699,7 +538,6 @@ class baseuser_add(LDAPCreate): - assert isinstance(dn, DN) - set_krbcanonicalname(entry_attrs) - self.obj.convert_usercertificate_pre(entry_attrs) -- self.obj.handle_subordinate_ids(ldap, dn, entry_attrs) - if entry_attrs.get('ipatokenradiususername', None): - add_missing_object_class(ldap, u'ipatokenradiusproxyuser', dn, - entry_attrs, update=False) -@@ -852,7 +690,6 @@ class baseuser_mod(LDAPUpdate): - - self.check_objectclass(ldap, dn, entry_attrs) - self.obj.convert_usercertificate_pre(entry_attrs) -- self.obj.handle_subordinate_ids(ldap, dn, entry_attrs) - self.preserve_krbprincipalname_pre(ldap, entry_attrs, *keys, **options) - update_samba_attrs(ldap, dn, entry_attrs, **options) - -@@ -1133,98 +970,3 @@ class baseuser_remove_certmapdata(ModCertMapData, - LDAPRemoveAttribute): - __doc__ = _("Remove one or more certificate mappings from the user entry.") - msg_summary = _('Removed certificate mappings from user "%(value)s"') -- -- --class baseuser_auto_subid(LDAPQuery): -- __doc__ = _("Auto-assign subuid and subgid range to user entry") -- -- has_output = output.standard_entry -- -- def execute(self, cn, **options): -- ldap = self.obj.backend -- dn = self.obj.get_dn(cn) -- -- try: -- entry_attrs = ldap.get_entry( -- dn, ["objectclass", "ipasubuidnumber"] -- ) -- except errors.NotFound: -- raise self.obj.handle_not_found(cn) -- -- if "ipasubuidnumber" in entry_attrs: -- raise errors.AlreadyContainsValueError(attr="ipasubuidnumber") -- -- self.obj.set_subordinate_ids(ldap, dn, entry_attrs, subuid=DNA_MAGIC) -- ldap.update_entry(entry_attrs) -- -- # fetch updated entry (use search display attribute to show subids) -- if options.get('all', False): -- attrs_list = ['*'] + self.obj.search_display_attributes -- else: -- attrs_list = set(self.obj.search_display_attributes) -- attrs_list.update(entry_attrs.keys()) -- if options.get('no_members', False): -- attrs_list.difference_update(self.obj.attribute_members) -- attrs_list = list(attrs_list) -- -- entry = self._exc_wrapper((cn,), options, ldap.get_entry)( -- dn, attrs_list -- ) -- entry_attrs = entry_to_dict(entry, **options) -- entry_attrs['dn'] = dn -- -- return dict(result=entry_attrs, value=pkey_to_value(cn, options)) -- -- --class baseuser_match_subid(baseuser_find): -- __doc__ = _("Match users by any subordinate uid in their range") -- -- _subid_attrs = { -- "ipasubuidnumber", -- "ipasubuidcount", -- "ipasubgidnumber", -- "ipasubgidcount" -- } -- -- def get_options(self): -- base_options = {p.name for p in self.obj.takes_params} -- for option in super().get_options(): -- if option.name == "ipasubuidnumber": -- yield option.clone( -- label=_('SubUID match'), -- doc=_('Match value for subordinate user ID'), -- required=True, -- ) -- elif option.name not in base_options: -- # raw, version -- yield option.clone() -- -- def pre_callback( -- self, ldap, filters, attrs_list, base_dn, scope, *args, **options -- ): -- # search for candidates in range -- # Code assumes that no subordinate count is larger than SUBID_COUNT -- filters = self.obj.get_subid_match_candidate_filter( -- ldap, subuid=options["ipasubuidnumber"], subgid=None, -- ) -- # always include subid attributes -- for missing in self._subid_attrs.difference(attrs_list): -- attrs_list.append(missing) -- -- return filters, base_dn, scope -- -- def post_callback(self, ldap, entries, truncated, *args, **options): -- # filter out mismatches manually -- osubuid = options["ipasubuidnumber"] -- new_entries = [] -- for entry in entries: -- esubuid = int(entry.single_value["ipasubuidnumber"]) -- esubcount = int(entry.single_value["ipasubuidcount"]) -- minsubuid = esubuid -- maxsubuid = esubuid + esubcount - 1 -- if minsubuid <= osubuid <= maxsubuid: -- new_entries.append(entry) -- -- entries[:] = new_entries -- -- return truncated -diff --git a/ipaserver/plugins/config.py b/ipaserver/plugins/config.py -index ace66e589e50dac098aefd6b393b5e835cac9d7f..3526153ec117a05846daca7d42447ff50b5b7934 100644 ---- a/ipaserver/plugins/config.py -+++ b/ipaserver/plugins/config.py -@@ -121,6 +121,7 @@ class config(LDAPObject): - 'ipapwdexpadvnotify', 'ipaselinuxusermaporder', - 'ipaselinuxusermapdefault', 'ipaconfigstring', 'ipakrbauthzdata', - 'ipauserauthtype', 'ipadomainresolutionorder', 'ipamaxhostnamelength', -+ 'ipauserdefaultsubordinateid', - ] - container_dn = DN(('cn', 'ipaconfig'), ('cn', 'etc')) - permission_filter_objectclasses = ['ipaguiconfig'] -@@ -142,7 +143,7 @@ class config(LDAPObject): - 'ipasearchrecordslimit', 'ipasearchtimelimit', - 'ipauserauthtype', 'ipauserobjectclasses', - 'ipausersearchfields', 'ipacustomfields', -- 'ipamaxhostnamelength', -+ 'ipamaxhostnamelength', 'ipauserdefaultsubordinateid', - }, - }, - } -@@ -261,6 +262,11 @@ class config(LDAPObject): - values=(u'password', u'radius', u'otp', - u'pkinit', u'hardened', u'disabled'), - ), -+ Bool('ipauserdefaultsubordinateid?', -+ cli_name='user_default_subid', -+ label=_('Enable adding subids to new users'), -+ doc=_('Enable adding subids to new users'), -+ ), - Str( - 'ipa_master_server*', - label=_('IPA masters'), -diff --git a/ipaserver/plugins/internal.py b/ipaserver/plugins/internal.py -index 199838b199eb4cdabf597bd34d571d05547fd32e..5ef940c2b88cc2b132a15d619772349b30731306 100644 ---- a/ipaserver/plugins/internal.py -+++ b/ipaserver/plugins/internal.py -@@ -1547,7 +1547,7 @@ class i18n_messages(Command): - "Drive to mount a home directory" - ), - }, -- "subordinate": { -+ "subid": { - "identity": _("Subordinate user and group id"), - "subuidnumber": _("Subordinate user id"), - "subuidcount": _("Subordinate user id count"), -diff --git a/ipaserver/plugins/subid.py b/ipaserver/plugins/subid.py -new file mode 100644 -index 0000000000000000000000000000000000000000..7d9a2f33e84bc7cdf17900346343e49d5eda0d8c ---- /dev/null -+++ b/ipaserver/plugins/subid.py -@@ -0,0 +1,608 @@ -+# -+# Copyright (C) 2021 FreeIPA Contributors see COPYING for license -+# -+ -+import random -+import uuid -+ -+from ipalib import api -+from ipalib import constants -+from ipalib import errors -+from ipalib import output -+from ipalib.plugable import Registry -+from ipalib.parameters import Int, Str -+from ipalib.request import context -+from ipalib.text import _, ngettext -+from ipapython.dn import DN -+ -+from .baseldap import ( -+ LDAPObject, -+ LDAPCreate, -+ LDAPDelete, -+ LDAPUpdate, -+ LDAPSearch, -+ LDAPRetrieve, -+ LDAPQuery, -+ DNA_MAGIC, -+) -+ -+__doc__ = _( -+ """ -+Subordinate ids -+ -+Manage subordinate user and group ids for users -+ -+EXAMPLES: -+ -+ Auto-assign a subordinate id range to current user -+ ipa subid-generate -+ -+ Auto-assign a subordinate id range to user alice: -+ ipa subid-generate --owner=alice -+ -+ Find subordinate ids for user alice: -+ ipa subid-find --owner=alice -+ -+ Match entry by any subordinate uid in range: -+ ipa subid-match --subuid=2147483649 -+""" -+) -+ -+register = Registry() -+ -+ -+@register() -+class subid(LDAPObject): -+ """Subordinate id object.""" -+ -+ container_dn = api.env.container_subids -+ -+ object_name = _("Subordinate id") -+ object_name_plural = _("Subordinate ids") -+ label = _("Subordinate ids") -+ label_singular = _("Subordinate id") -+ -+ object_class = ["ipasubordinateidentry"] -+ possible_objectclasses = [ -+ "ipasubordinategid", -+ "ipasubordinateuid", -+ "ipasubordinateid", -+ ] -+ default_attributes = [ -+ "ipauniqueid", -+ "ipaowner", -+ "ipasubuidnumber", -+ "ipasubuidcount", -+ "ipasubgidnumber", -+ "ipasubgidcount", -+ ] -+ allow_rename = False -+ -+ permission_filter_objectclasses_string = ( -+ "(objectclass=ipasubordinateidentry)" -+ ) -+ managed_permissions = { -+ # all authenticated principals can read subordinate id information -+ "System: Read Subordinate Id Attributes": { -+ "ipapermbindruletype": "all", -+ "ipapermright": {"read", "search", "compare"}, -+ "ipapermtargetfilter": [ -+ permission_filter_objectclasses_string, -+ ], -+ "ipapermdefaultattr": { -+ "objectclass", -+ "ipauniqueid", -+ "description", -+ "ipaowner", -+ "ipasubuidnumber", -+ "ipasubuidcount", -+ "ipasubgidnumber", -+ "ipasubgidcount", -+ }, -+ }, -+ "System: Read Subordinate Id Count": { -+ "ipapermbindruletype": "all", -+ "ipapermright": {"read", "search", "compare"}, -+ "ipapermtargetfilter": [], -+ "ipapermtarget": DN(container_dn, api.env.basedn), -+ "ipapermdefaultattr": {"numSubordinates"}, -+ }, -+ # user administrators can remove subordinate ids or update the -+ # ipaowner attribute. This enables user admins to remove users -+ # with assigned subids or move them to staging area (--preserve). -+ "System: Manage Subordinate Ids": { -+ "ipapermright": {"write"}, -+ "ipapermtargetfilter": [ -+ permission_filter_objectclasses_string, -+ ], -+ "ipapermdefaultattr": { -+ "description", -+ "ipaowner", # allow user admins to preserve users -+ }, -+ "default_privileges": {"User Administrators"}, -+ }, -+ "System: Remove Subordinate Ids": { -+ "ipapermright": {"delete"}, -+ "ipapermtargetfilter": [ -+ permission_filter_objectclasses_string, -+ ], -+ "default_privileges": {"User Administrators"}, -+ }, -+ } -+ -+ takes_params = ( -+ Str( -+ "ipauniqueid", -+ cli_name="id", -+ label=_("Unique ID"), -+ primary_key=True, -+ flags={"optional_create"}, -+ ), -+ Str( -+ "description?", -+ cli_name="desc", -+ label=_("Description"), -+ doc=_("Subordinate id description"), -+ ), -+ Str( -+ "ipaowner", -+ cli_name="owner", -+ label=_("Owner"), -+ doc=_("Owning user of subordinate id entry"), -+ flags={"no_update"}, -+ ), -+ Int( -+ "ipasubuidnumber?", -+ label=_("SubUID range start"), -+ cli_name="subuid", -+ doc=_("Start value for subordinate user ID (subuid) range"), -+ flags={"no_update"}, -+ minvalue=constants.SUBID_RANGE_START, -+ maxvalue=constants.SUBID_RANGE_MAX, -+ ), -+ Int( -+ "ipasubuidcount?", -+ label=_("SubUID range size"), -+ cli_name="subuidcount", -+ doc=_("Subordinate user ID count"), -+ flags={"no_create", "no_update", "no_search"}, # auto-assigned -+ minvalue=constants.SUBID_COUNT, -+ maxvalue=constants.SUBID_COUNT, -+ ), -+ Int( -+ "ipasubgidnumber?", -+ label=_("SubGID range start"), -+ cli_name="subgid", -+ doc=_("Start value for subordinate group ID (subgid) range"), -+ flags={"no_create", "no_update"}, # auto-assigned -+ minvalue=constants.SUBID_RANGE_START, -+ maxvalue=constants.SUBID_RANGE_MAX, -+ ), -+ Int( -+ "ipasubgidcount?", -+ label=_("SubGID range size"), -+ cli_name="subgidcount", -+ doc=_("Subordinate group ID count"), -+ flags={"no_create", "no_update", "no_search"}, # auto-assigned -+ minvalue=constants.SUBID_COUNT, -+ maxvalue=constants.SUBID_COUNT, -+ ), -+ ) -+ -+ def fixup_objectclass(self, entry_attrs): -+ """Add missing object classes to entry""" -+ has_subuid = "ipasubuidnumber" in entry_attrs -+ has_subgid = "ipasubgidnumber" in entry_attrs -+ -+ candicates = set(self.object_class) -+ if has_subgid: -+ candicates.add("ipasubordinategid") -+ if has_subuid: -+ candicates.add("ipasubordinateuid") -+ if has_subgid and has_subuid: -+ candicates.add("ipasubordinateid") -+ -+ entry_oc = entry_attrs.setdefault("objectclass", []) -+ current_oc = {x.lower() for x in entry_oc} -+ for oc in candicates.difference(current_oc): -+ entry_oc.append(oc) -+ -+ def handle_duplicate_entry(self, *keys): -+ if hasattr(context, "subid_owner_dn"): -+ uid = context.subid_owner_dn[0].value -+ msg = _( -+ '%(oname)s with with name "%(pkey)s" or for user "%(uid)s" ' -+ "already exists." -+ ) % { -+ "uid": uid, -+ "pkey": keys[-1] if keys else "", -+ "oname": self.object_name, -+ } -+ raise errors.DuplicateEntry(message=msg) from None -+ else: -+ super().handle_duplicate_entry(*keys) -+ -+ def convert_owner(self, entry_attrs, options): -+ """Change owner from DN to uid string""" -+ if not options.get("raw", False) and "ipaowner" in entry_attrs: -+ userobj = self.api.Object.user -+ entry_attrs["ipaowner"] = [ -+ userobj.get_primary_key_from_dn(entry_attrs["ipaowner"][0]) -+ ] -+ -+ def get_owner_dn(self, *keys, **options): -+ """Get owning user entry entry (username or DN)""" -+ owner = keys[-1] -+ userobj = self.api.Object.user -+ if isinstance(owner, DN): -+ # it's already a DN, validate it's either an active or preserved -+ # user. Ref integrity plugin checks that it's not a dangling DN. -+ user_dns = ( -+ DN(userobj.active_container_dn, self.api.env.basedn), -+ DN(userobj.delete_container_dn, self.api.env.basedn), -+ ) -+ if not owner.endswith(user_dns): -+ raise errors.ValidationError( -+ name="ipaowner", -+ error=_("'%(dn)s is not a valid user") % {"dn": owner}, -+ ) -+ return owner -+ -+ # similar to user.get_either_dn() but with error reporting and -+ # returns an entry -+ ldap = self.backend -+ try: -+ active_dn = userobj.get_dn(owner, **options) -+ entry = ldap.get_entry(active_dn, attrs_list=[]) -+ return entry.dn -+ except errors.NotFound: -+ # fall back to deleted user -+ try: -+ delete_dn = userobj.get_delete_dn(owner, **options) -+ entry = ldap.get_entry(delete_dn, attrs_list=[]) -+ return entry.dn -+ except errors.NotFound: -+ raise userobj.handle_not_found(owner) -+ -+ def handle_subordinate_ids(self, ldap, dn, entry_attrs): -+ """Handle ipaSubordinateId object class""" -+ new_subuid = entry_attrs.single_value.get("ipasubuidnumber") -+ new_subgid = entry_attrs.single_value.get("ipasubgidnumber") -+ -+ if new_subuid is None: -+ new_subuid = DNA_MAGIC -+ -+ # enforce subuid == subgid -+ if new_subgid is not None and new_subgid != new_subuid: -+ raise errors.ValidationError( -+ name="ipasubgidnumber", -+ error=_("subgidnumber must be equal to subuidnumber"), -+ ) -+ -+ self.set_subordinate_ids(ldap, dn, entry_attrs, new_subuid) -+ return True -+ -+ def set_subordinate_ids(self, ldap, dn, entry_attrs, subuid): -+ """Set subuid value of an entry -+ -+ Takes care of objectclass and sibbling attributes -+ """ -+ if "objectclass" not in entry_attrs: -+ _entry_attrs = ldap.get_entry(dn, ["objectclass"]) -+ entry_attrs["objectclass"] = _entry_attrs["objectclass"] -+ -+ # XXX HACK, remove later -+ if subuid == DNA_MAGIC: -+ subuid = self._fake_dna_plugin(ldap, dn, entry_attrs) -+ -+ entry_attrs["ipasubuidnumber"] = subuid -+ # enforice subuid == subgid for now -+ entry_attrs["ipasubgidnumber"] = subuid -+ # hard-coded constants -+ entry_attrs["ipasubuidcount"] = constants.SUBID_COUNT -+ entry_attrs["ipasubgidcount"] = constants.SUBID_COUNT -+ -+ self.fixup_objectclass(entry_attrs) -+ -+ def get_subid_match_candidate_filter( -+ self, -+ ldap, -+ *, -+ subuid, -+ subgid, -+ extra_filters=(), -+ offset=None, -+ ): -+ """Create LDAP filter to locate matching/overlapping subids""" -+ if subuid is None and subgid is None: -+ raise ValueError("subuid and subgid are both None") -+ if offset is None: -+ # assumes that no subordinate count is larger than SUBID_COUNT -+ offset = constants.SUBID_COUNT - 1 -+ -+ class_filters = "(objectclass=ipasubordinateid)" -+ subid_filters = [] -+ if subuid is not None: -+ subid_filters.append( -+ ldap.combine_filters( -+ [ -+ f"(ipasubuidnumber>={subuid - offset})", -+ f"(ipasubuidnumber<={subuid + offset})", -+ ], -+ rules=ldap.MATCH_ALL, -+ ) -+ ) -+ if subgid is not None: -+ subid_filters.append( -+ ldap.combine_filters( -+ [ -+ f"(ipasubgidnumber>={subgid - offset})", -+ f"(ipasubgidnumber<={subgid + offset})", -+ ], -+ rules=ldap.MATCH_ALL, -+ ) -+ ) -+ -+ subid_filters = ldap.combine_filters( -+ subid_filters, rules=ldap.MATCH_ANY -+ ) -+ filters = [class_filters, subid_filters] -+ filters.extend(extra_filters) -+ return ldap.combine_filters(filters, rules=ldap.MATCH_ALL) -+ -+ def _fake_dna_plugin(self, ldap, dn, entry_attrs): -+ """XXX HACK, remove when 389-DS DNA plugin supports steps""" -+ return ( -+ constants.SUBID_RANGE_START -+ + random.randint(1, 32764 - 2) * constants.SUBID_COUNT -+ ) -+ -+ -+@register() -+class subid_add(LDAPCreate): -+ __doc__ = _("Add a new subordinate id.") -+ msg_summary = _('Added subordinate id "%(value)s"') -+ -+ # internal command, use subid-auto to auto-assign subids -+ NO_CLI = True -+ -+ def pre_callback( -+ self, ldap, dn, entry_attrs, attrs_list, *keys, **options -+ ): -+ # XXX let ref integrity plugin validate DN? -+ owner_dn = self.obj.get_owner_dn(entry_attrs["ipaowner"], **options) -+ context.subid_owner_dn = owner_dn -+ entry_attrs["ipaowner"] = owner_dn -+ -+ self.obj.handle_subordinate_ids(ldap, dn, entry_attrs) -+ attrs_list.append("objectclass") -+ -+ return dn -+ -+ def execute(self, ipauniqueid=None, **options): -+ if ipauniqueid is None: -+ ipauniqueid = str(uuid.uuid4()) -+ return super().execute(ipauniqueid, **options) -+ -+ def post_callback(self, ldap, dn, entry_attrs, *keys, **options): -+ self.obj.convert_owner(entry_attrs, options) -+ return super(subid_add, self).post_callback( -+ ldap, dn, entry_attrs, *keys, **options -+ ) -+ -+ -+@register() -+class subid_del(LDAPDelete): -+ __doc__ = _("Delete a subordinate id.") -+ msg_summary = _('Deleted subordinate id "%(value)s"') -+ -+ # internal command, subids cannot be removed -+ NO_CLI = True -+ -+ -+@register() -+class subid_mod(LDAPUpdate): -+ __doc__ = _("Modify a subordinate id.") -+ msg_summary = _('Modified subordinate id "%(value)s"') -+ -+ def post_callback(self, ldap, dn, entry_attrs, *keys, **options): -+ self.obj.convert_owner(entry_attrs, options) -+ return super(subid_mod, self).post_callback( -+ ldap, dn, entry_attrs, *keys, **options -+ ) -+ -+ -+@register() -+class subid_find(LDAPSearch): -+ __doc__ = _("Search for subordinate id.") -+ msg_summary = ngettext( -+ "%(count)d subordinate id matched", -+ "%(count)d subordinate ids matched", -+ 0, -+ ) -+ -+ def pre_callback( -+ self, ldap, filters, attrs_list, base_dn, scope, *args, **options -+ ): -+ attrs_list.append("objectclass") -+ return super(subid_find, self).pre_callback( -+ ldap, filters, attrs_list, base_dn, scope, *args, **options -+ ) -+ -+ def args_options_2_entry(self, *args, **options): -+ entry_attrs = super(subid_find, self).args_options_2_entry( -+ *args, **options -+ ) -+ owner = entry_attrs.get("ipaowner") -+ if owner is not None: -+ owner_dn = self.obj.get_owner_dn(owner, **options) -+ entry_attrs["ipaowner"] = owner_dn -+ return entry_attrs -+ -+ def post_callback(self, ldap, entries, truncated, *args, **options): -+ for entry in entries: -+ self.obj.convert_owner(entry, options) -+ return super(subid_find, self).post_callback( -+ ldap, entries, truncated, *args, **options -+ ) -+ -+ -+@register() -+class subid_show(LDAPRetrieve): -+ __doc__ = _("Display information about a subordinate id.") -+ -+ def pre_callback(self, ldap, dn, attrs_list, *keys, **options): -+ attrs_list.append("objectclass") -+ return super(subid_show, self).pre_callback( -+ ldap, dn, attrs_list, *keys, **options -+ ) -+ -+ def post_callback(self, ldap, dn, entry_attrs, *keys, **options): -+ self.obj.convert_owner(entry_attrs, options) -+ return super(subid_show, self).post_callback( -+ ldap, dn, entry_attrs, *keys, **options -+ ) -+ -+ -+@register() -+class subid_generate(LDAPQuery): -+ __doc__ = _( -+ "Generate and auto-assign subuid and subgid range to user entry" -+ ) -+ -+ has_output = output.standard_entry -+ -+ takes_options = LDAPQuery.takes_options + ( -+ Str( -+ "ipaowner?", -+ cli_name="owner", -+ label=_("Owner"), -+ doc=_("Owning user of subordinate id entry"), -+ ), -+ ) -+ -+ def get_args(self): -+ return [] -+ -+ def execute(self, *keys, **options): -+ owner_uid = options.get("ipaowner") -+ # default to current user -+ if owner_uid is None: -+ owner_dn = DN(self.api.Backend.ldap2.conn.whoami_s()[4:]) -+ # validate it's a user and not a service or host -+ owner_dn = self.obj.get_owner_dn(owner_dn) -+ owner_uid = owner_dn[0].value -+ -+ return self.api.Command.subid_add( -+ description="auto-assigned subid", -+ ipaowner=owner_uid, -+ version=options["version"], -+ ) -+ -+ -+@register() -+class subid_match(subid_find): -+ __doc__ = _("Match users by any subordinate uid in their range") -+ -+ def get_options(self): -+ base_options = {p.name for p in self.obj.takes_params} -+ for option in super().get_options(): -+ if option.name == "ipasubuidnumber": -+ yield option.clone( -+ label=_("SubUID match"), -+ doc=_("Match value for subordinate user ID"), -+ required=True, -+ ) -+ elif option.name not in base_options: -+ # raw, version -+ yield option.clone() -+ -+ def pre_callback( -+ self, ldap, filters, attrs_list, base_dn, scope, *args, **options -+ ): -+ # search for candidates in range -+ # Code assumes that no subordinate count is larger than SUBID_COUNT -+ filters = self.obj.get_subid_match_candidate_filter( -+ ldap, -+ subuid=options["ipasubuidnumber"], -+ subgid=None, -+ ) -+ attrs_list.extend(self.obj.default_attributes) -+ -+ return filters, base_dn, scope -+ -+ def post_callback(self, ldap, entries, truncated, *args, **options): -+ # filter out mismatches manually -+ osubuid = options["ipasubuidnumber"] -+ new_entries = [] -+ for entry in entries: -+ esubuid = int(entry.single_value["ipasubuidnumber"]) -+ esubcount = int(entry.single_value["ipasubuidcount"]) -+ minsubuid = esubuid -+ maxsubuid = esubuid + esubcount - 1 -+ if minsubuid <= osubuid <= maxsubuid: -+ new_entries.append(entry) -+ -+ entries[:] = new_entries -+ -+ return truncated -+ -+ -+@register() -+class subid_stats(LDAPQuery): -+ __doc__ = _("Subordinate id statistics") -+ -+ takes_options = () -+ has_output = ( -+ output.summary, -+ output.Entry("result"), -+ ) -+ -+ def get_args(self): -+ return () -+ -+ def get_remaining_dna(self, ldap, **options): -+ base_dn = DN( -+ self.api.env.container_dna_subordinate_ids, self.api.env.basedn -+ ) -+ entries, _truncated = ldap.find_entries( -+ "(objectClass=dnaSharedConfig)", -+ attrs_list=["dnaRemainingValues"], -+ base_dn=base_dn, -+ scope=ldap.SCOPE_ONELEVEL, -+ ) -+ return sum( -+ int(entry.single_value["dnaRemainingValues"]) for entry in entries -+ ) -+ -+ def get_idrange(self, ldap, **options): -+ cn = f"{self.api.env.realm}_subid_range" -+ result = self.api.Command.idrange_show(cn, version=options["version"]) -+ baseid = int(result["result"]["ipabaseid"][0]) -+ rangesize = int(result["result"]["ipaidrangesize"][0]) -+ return baseid, rangesize -+ -+ def get_subid_assigned(self, ldap, **options): -+ dn = DN(self.api.env.container_subids, self.api.env.basedn) -+ entry = ldap.get_entry(dn=dn, attrs_list=["numSubordinates"]) -+ return int(entry.single_value["numSubordinates"]) -+ -+ def execute(self, *keys, **options): -+ ldap = self.obj.backend -+ dna_remaining = self.get_remaining_dna(ldap, **options) -+ baseid, rangesize = self.get_idrange(ldap, **options) -+ assigned_subids = self.get_subid_assigned(ldap, **options) -+ remaining_subids = dna_remaining // constants.SUBID_COUNT -+ return dict( -+ summary=_("%(remaining)i remaining subordinate id ranges") -+ % { -+ "remaining": remaining_subids, -+ }, -+ result=dict( -+ baseid=baseid, -+ rangesize=rangesize, -+ dna_remaining=dna_remaining, -+ assigned_subids=assigned_subids, -+ remaining_subids=remaining_subids, -+ ), -+ ) -diff --git a/ipaserver/plugins/user.py b/ipaserver/plugins/user.py -index f89b3ad5d9c994fe1ceb3da560fde7cc5bf5155a..19d07e6d61a451a0b1177adf2cf8ae1b7fceeb67 100644 ---- a/ipaserver/plugins/user.py -+++ b/ipaserver/plugins/user.py -@@ -51,8 +51,6 @@ from .baseuser import ( - baseuser_remove_principal, - baseuser_add_certmapdata, - baseuser_remove_certmapdata, -- baseuser_auto_subid, -- baseuser_match_subid, - ) - from .idviews import remove_ipaobject_overrides - from ipalib.plugable import Registry -@@ -205,8 +203,6 @@ class user(baseuser): - 'ipapermright': {'read', 'search', 'compare'}, - 'ipapermdefaultattr': { - 'ipauniqueid', 'ipasshpubkey', 'ipauserauthtype', 'userclass', -- 'ipasubuidnumber', 'ipasubuidcount', 'ipasubgidnumber', -- 'ipasubgidcount', - }, - 'fixup_function': fix_addressbook_permission_bindrule, - }, -@@ -670,6 +666,17 @@ class user_add(baseuser_add): - # if both randompassword and userpassword options were used - pass - -+ # generate subid -+ default_subid = config.single_value.get( -+ 'ipaUserDefaultSubordinateId', 'FALSE' -+ ) -+ if default_subid == 'TRUE': -+ result = self.api.Command.subid_generate( -+ ipaowner=entry_attrs.single_value['uid'], -+ version=options['version'] -+ ) -+ entry_attrs["memberOf"].append(result['result']['dn']) -+ - self.obj.get_preserved_attribute(entry_attrs, options) - - self.post_common_callback(ldap, dn, entry_attrs, *keys, **options) -@@ -757,7 +764,9 @@ class user_del(baseuser_del): - # of OTP tokens. - check_protected_member(keys[-1]) - -- if not options.get('preserve', False): -+ preserve = options.get('preserve', False) -+ -+ if not preserve: - # Remove any ID overrides tied with this user - try: - remove_ipaobject_overrides(self.obj.backend, self.obj.api, dn) -@@ -780,6 +789,15 @@ class user_del(baseuser_del): - else: - self.api.Command.otptoken_del(token) - -+ # XXX: preserving doesn't work yet, see subordinate-ids.md -+ # Delete all subid entries owned by this user. -+ results = self.api.Command.subid_find(ipaowner=owner)["result"] -+ for subid_entry in results: -+ subid_pkey = self.api.Object.subid.get_primary_key_from_dn( -+ subid_entry["dn"] -+ ) -+ self.api.Command.subid_del(subid_pkey) -+ - return dn - - def execute(self, *keys, **options): -@@ -829,6 +847,7 @@ class user_mod(baseuser_mod): - self.pre_common_callback(ldap, dn, entry_attrs, attrs_list, *keys, - **options) - validate_nsaccountlock(entry_attrs) -+ # TODO: forward uidNumber changes and rename to subids - return dn - - def post_callback(self, ldap, dn, entry_attrs, *keys, **options): -@@ -1311,13 +1330,3 @@ class user_add_principal(baseuser_add_principal): - class user_remove_principal(baseuser_remove_principal): - __doc__ = _('Remove principal alias from the user entry') - msg_summary = _('Removed aliases from user "%(value)s"') -- -- --@register() --class user_auto_subid(baseuser_auto_subid): -- __doc__ = baseuser_auto_subid.__doc__ -- -- --@register() --class user_match_subid(baseuser_match_subid): -- __doc__ = baseuser_match_subid.__doc__ -diff --git a/ipatests/test_integration/test_subids.py b/ipatests/test_integration/test_subids.py -index b462f22ac067f3e1e97ef3f6d63d4e14e4ae79af..48e58c26464f52605438afe865575e5ca4c8f1f8 100644 ---- a/ipatests/test_integration/test_subids.py -+++ b/ipatests/test_integration/test_subids.py -@@ -17,6 +17,7 @@ class TestSubordinateId(IntegrationTest): - topology = "star" - - def _parse_result(self, result): -+ # ipa CLI should get an --outform json option - info = {} - for line in result.stdout_text.split("\n"): - line = line.strip() -@@ -42,39 +43,69 @@ class TestSubordinateId(IntegrationTest): - info[k] = set(v) - return info - -- def get_user(self, uid): -- cmd = ["ipa", "user-show", "--all", "--raw", uid] -- result = self.master.run_command(cmd) -- return self._parse_result(result) -+ def assert_subid_info(self, uid, info): -+ assert info["ipauniqueid"] -+ basedn = self.master.domain.basedn -+ assert info["ipaowner"] == f"uid={uid},cn=users,cn=accounts,{basedn}" -+ assert info["ipasubuidnumber"] == info["ipasubuidnumber"] -+ assert info["ipasubuidnumber"] >= SUBID_RANGE_START -+ assert info["ipasubuidnumber"] <= SUBID_RANGE_MAX -+ assert info["ipasubuidcount"] == SUBID_COUNT -+ assert info["ipasubgidnumber"] == info["ipasubgidnumber"] -+ assert info["ipasubgidnumber"] == info["ipasubuidnumber"] -+ assert info["ipasubgidcount"] == SUBID_COUNT -+ -+ def assert_subid(self, uid, *, match): -+ cmd = ["ipa", "subid-find", "--raw", "--owner", uid] -+ result = self.master.run_command(cmd, raiseonerr=False) -+ if not match: -+ assert result.returncode >= 1 -+ if result.returncode == 1: -+ assert "0 subordinate ids matched" in result.stdout_text -+ elif result.returncode == 2: -+ assert "user not found" in result.stderr_text -+ return None -+ else: -+ assert result.returncode == 0 -+ assert "1 subordinate id matched" in result.stdout_text -+ info = self._parse_result(result) -+ self.assert_subid_info(uid, info) -+ self.master.run_command( -+ ["ipa", "subid-show", info["ipauniqueid"]] -+ ) -+ return info - -- def user_auto_subid(self, uid, **kwargs): -- cmd = ["ipa", "user-auto-subid", uid] -+ def subid_generate(self, uid, **kwargs): -+ cmd = ["ipa", "subid-generate"] -+ if uid is not None: -+ cmd.extend(("--owner", uid)) - return self.master.run_command(cmd, **kwargs) - -- def test_auto_subid(self): -- tasks.kinit_admin(self.master) -+ def test_auto_generate_subid(self): - uid = "testuser_auto1" -- tasks.user_add(self.master, uid) -- info = self.get_user(uid) -- assert "ipasubuidcount" not in info -+ passwd = "Secret123" -+ tasks.create_active_user(self.master, uid, password=passwd) - -- self.user_auto_subid(uid) -- info = self.get_user(uid) -- assert "ipasubuidcount" in info -+ tasks.kinit_admin(self.master) -+ self.assert_subid(uid, match=False) -+ -+ # add subid by name -+ self.subid_generate(uid) -+ info = self.assert_subid(uid, match=True) -+ -+ # second generate fails due to unique index on ipaowner -+ result = self.subid_generate(uid, raiseonerr=False) -+ assert result.returncode > 0 -+ assert f'for user "{uid}" already exists' in result.stderr_text - -+ # check matching - subuid = info["ipasubuidnumber"] -- result = self.master.run_command( -- ["ipa", "user-match-subid", f"--subuid={subuid}", "--raw"] -- ) -- match = self._parse_result(result) -- assert match["uid"] == uid -- assert match["ipasubuidnumber"] == info["ipasubuidnumber"] -- assert match["ipasubuidnumber"] >= SUBID_RANGE_START -- assert match["ipasubuidnumber"] <= SUBID_RANGE_MAX -- assert match["ipasubuidcount"] == SUBID_COUNT -- assert match["ipasubgidnumber"] == info["ipasubgidnumber"] -- assert match["ipasubgidnumber"] == match["ipasubuidnumber"] -- assert match["ipasubgidcount"] == SUBID_COUNT -+ for offset in (0, 1, 65535): -+ result = self.master.run_command( -+ ["ipa", "subid-match", f"--subuid={subuid + offset}", "--raw"] -+ ) -+ match = self._parse_result(result) -+ self.assert_subid_info(uid, match) - - def test_ipa_subid_script(self): - tasks.kinit_admin(self.master) -@@ -85,34 +116,28 @@ class TestSubordinateId(IntegrationTest): - uid = f"testuser_script{i}" - users.append(uid) - tasks.user_add(self.master, uid) -- info = self.get_user(uid) -- assert "ipasubuidcount" not in info -+ self.assert_subid(uid, match=False) - - cmd = [tool, "--verbose", "--group", "ipausers"] - self.master.run_command(cmd) - - for uid in users: -- info = self.get_user(uid) -- assert info["ipasubuidnumber"] >= SUBID_RANGE_START -- assert info["ipasubuidnumber"] <= SUBID_RANGE_MAX -- assert info["ipasubuidnumber"] == info["ipasubgidnumber"] -- assert info["ipasubuidcount"] == SUBID_COUNT -- assert info["ipasubuidcount"] == info["ipasubgidcount"] -+ self.assert_subid(uid, match=True) - - def test_subid_selfservice(self): -- tasks.kinit_admin(self.master) -- -- uid = "testuser_selfservice1" -+ uid1 = "testuser_selfservice1" -+ uid2 = "testuser_selfservice2" - password = "Secret123" - role = "Subordinate ID Selfservice User" - -- tasks.user_add(self.master, uid, password=password) -- tasks.kinit_user( -- self.master, uid, f"{password}\n{password}\n{password}\n" -- ) -- info = self.get_user(uid) -- assert "ipasubuidcount" not in info -- result = self.user_auto_subid(uid, raiseonerr=False) -+ tasks.create_active_user(self.master, uid1, password=password) -+ tasks.create_active_user(self.master, uid2, password=password) -+ -+ tasks.kinit_user(self.master, uid1, password=password) -+ self.assert_subid(uid1, match=False) -+ result = self.subid_generate(uid1, raiseonerr=False) -+ assert result.returncode > 0 -+ result = self.subid_generate(None, raiseonerr=False) - assert result.returncode > 0 - - tasks.kinit_admin(self.master) -@@ -121,10 +146,14 @@ class TestSubordinateId(IntegrationTest): - ) - - try: -- tasks.kinit_user(self.master, uid, password) -- self.user_auto_subid(uid) -- info = self.get_user(uid) -- assert "ipasubuidcount" in info -+ tasks.kinit_user(self.master, uid1, password) -+ self.subid_generate(uid1) -+ self.assert_subid(uid1, match=True) -+ -+ # add subid from whoami -+ tasks.kinit_as_user(self.master, uid2, password=password) -+ self.subid_generate(None) -+ self.assert_subid(uid2, match=True) - finally: - tasks.kinit_admin(self.master) - self.master.run_command( -@@ -140,45 +169,46 @@ class TestSubordinateId(IntegrationTest): - password = "Secret123" - - # create user administrator -- tasks.user_add(self.master, uid_useradmin, password=password) -+ tasks.create_active_user( -+ self.master, uid_useradmin, password=password -+ ) - # add user to user admin group - tasks.kinit_admin(self.master) - self.master.run_command( - ["ipa", "role-add-member", role, f"--users={uid_useradmin}"], - ) - # kinit as user admin -- tasks.kinit_user( -- self.master, -- uid_useradmin, -- f"{password}\n{password}\n{password}\n", -- ) -+ tasks.kinit_user(self.master, uid_useradmin, password) -+ - # create new user as user admin - tasks.user_add(self.master, uid) - # assign new subid to user (with useradmin credentials) -- self.user_auto_subid(uid) -- -- def test_subordinate_default_objclass(self): -+ self.subid_generate(uid) -+ -+ # test that user admin can preserve and delete users with subids -+ self.master.run_command(["ipa", "user-del", "--preserve", uid]) -+ # XXX does not work, see subordinate-ids.md -+ # subid should still exist -+ # self.assert_subid(uid, match=True) -+ # final delete should remove the user and subid -+ self.master.run_command(["ipa", "user-del", uid]) -+ self.assert_subid(uid, match=False) -+ -+ def tset_subid_auto_assign(self): - tasks.kinit_admin(self.master) -+ uid = "testuser_autoassign_user1" - -- result = self.master.run_command( -- ["ipa", "config-show", "--raw", "--all"] -+ self.master.run_command( -+ ["ipa", "config-mod", "--user-default-subid=true"] - ) -- info = self._parse_result(result) -- usercls = info["ipauserobjectclasses"] -- assert "ipasubordinateid" not in usercls -- -- cmd = [ -- "ipa", -- "config-mod", -- "--addattr", -- "ipaUserObjectClasses=ipasubordinateid", -- ] -- self.master.run_command(cmd) - -- uid = "testuser_usercls1" -- tasks.user_add(self.master, uid) -- info = self.get_user(uid) -- assert "ipasubuidcount" in info -+ try: -+ tasks.user_add(self.master, uid) -+ self.assert_subid(uid, match=True) -+ finally: -+ self.master.run_command( -+ ["ipa", "config-mod", "--user-default-subid=false"] -+ ) - - def test_idrange_subid(self): - tasks.kinit_admin(self.master) -@@ -199,3 +229,7 @@ class TestSubordinateId(IntegrationTest): - assert info["ipanttrusteddomainsid"].startswith( - "S-1-5-21-738065-838566-" - ) -+ -+ def test_subid_stats(self): -+ tasks.kinit_admin(self.master) -+ self.master.run_command(["ipa", "subid-stats"]) --- -2.26.3 - diff --git a/SOURCES/0010-Use-389-DS-dnaInterval-setting-to-assign-intervals.patch b/SOURCES/0010-Use-389-DS-dnaInterval-setting-to-assign-intervals.patch deleted file mode 100644 index 33c9237..0000000 --- a/SOURCES/0010-Use-389-DS-dnaInterval-setting-to-assign-intervals.patch +++ /dev/null @@ -1,113 +0,0 @@ -From c9bae715b24df0f5476bdb70a2209d5f55e46a93 Mon Sep 17 00:00:00 2001 -From: Christian Heimes -Date: Fri, 21 May 2021 09:26:33 +0200 -Subject: [PATCH] Use 389-DS' dnaInterval setting to assign intervals - -Signed-off-by: Christian Heimes -Reviewed-By: Francois Cami -Reviewed-By: Rob Crittenden -Reviewed-By: Francois Cami -Reviewed-By: Rob Crittenden ---- - freeipa.spec.in | 3 ++- - install/share/dna.ldif | 1 + - install/updates/73-subid.update | 7 ++----- - ipaserver/plugins/subid.py | 14 +------------- - 4 files changed, 6 insertions(+), 19 deletions(-) - -diff --git a/freeipa.spec.in b/freeipa.spec.in -index 044e3559975c399f6697d4da94b5a059eb5b407c..fa649cf4e1abe8e9928ef340a66d48d78f7e3521 100755 ---- a/freeipa.spec.in -+++ b/freeipa.spec.in -@@ -106,8 +106,9 @@ - %global python_ldap_version 3.1.0-1 - - # Make sure to use 389-ds-base versions that fix https://github.com/389ds/389-ds-base/issues/4700 -+# and has DNA interval enabled - %if 0%{?fedora} < 34 --%global ds_version %{lua: local v={}; v['32']='1.4.3.20-2'; v['33']='1.4.4.16-1'; print(v[rpm.expand('%{fedora}')])} -+%global ds_version 1.4.4.16-1 - %else - %global ds_version 2.0.5-1 - %endif -diff --git a/install/share/dna.ldif b/install/share/dna.ldif -index 735faab8261feef59486f7c933b01c57ad511166..9023fcd7db5a2c121c493559e2546c85c0daf69a 100644 ---- a/install/share/dna.ldif -+++ b/install/share/dna.ldif -@@ -31,6 +31,7 @@ dnaScope: $SUFFIX - dnaThreshold: eval($SUBID_DNA_THRESHOLD) - dnaSharedCfgDN: cn=subordinate-ids,cn=dna,cn=ipa,cn=etc,$SUFFIX - dnaExcludeScope: cn=provisioning,$SUFFIX -+dnaInterval: eval($SUBID_COUNT) - # TODO: enable when 389-DS' DNA plugin supports dnaStepAttr - # dnaIntervalAttr: ipasubuidcount - # dnaIntervalAttr: ipasubgidcount -diff --git a/install/updates/73-subid.update b/install/updates/73-subid.update -index 1aa43822a8b8c220583b81e08d70b648ca594363..e10703aa3f9528751233ddebe00b8c8c8fc5ed3f 100644 ---- a/install/updates/73-subid.update -+++ b/install/updates/73-subid.update -@@ -62,12 +62,8 @@ default:member: cn=Subordinate ID Administrators,cn=privileges,cn=pbac,$SUFFIX - # The delete-when-empty check is required because IPA uses MOD_REPLACE to - # set attributes, see https://github.com/389ds/389-ds-base/issues/4597. - # --# TODO: remove (ipasubuidnumber>=eval($SUBID_RANGE_START) from --# self-service permission when 389-DS' DNA plugin supports dnaStepAttr and --# fake_dna_plugin hack has been removed. --# - dn: cn=subids,cn=accounts,$SUFFIX --add: aci: (targetfilter = "(objectclass=ipasubordinateidentry)")(targetattr="description || ipaowner || ipauniqueid")(targattrfilters = "add=objectClass:(|(objectClass=top)(objectClass=ipasubordinateid)(objectClass=ipasubordinateidentry)(objectClass=ipasubordinategid)(objectClass=ipasubordinateuid)) && ipasubuidnumber:(|(ipasubuidnumber>=eval($SUBID_RANGE_START))(ipasubuidnumber=-1)) && ipasubuidcount:(ipasubuidcount=eval($SUBID_COUNT)) && ipasubgidnumber:(|(ipasubgidnumber>=eval($SUBID_RANGE_START))(ipasubgidnumber=-1)) && ipasubgidcount:(ipasubgidcount=eval($SUBID_COUNT)), del=ipasubuidnumber:(!(ipasubuidnumber=*)) && ipasubuidcount:(!(ipasubuidcount=*)) && ipasubgidnumber:(!(ipasubgidnumber=*)) && ipasubgidcount:(!(ipasubgidcount=*))")(version 3.0;acl "selfservice: Add subordinate id";allow (add, write) userattr = "ipaowner#SELFDN" and groupdn="ldap:///cn=Self-service subordinate ID,cn=permissions,cn=pbac,$SUFFIX";) -+add: aci: (targetfilter = "(objectclass=ipasubordinateidentry)")(targetattr="description || ipaowner || ipauniqueid")(targattrfilters = "add=objectClass:(|(objectClass=top)(objectClass=ipasubordinateid)(objectClass=ipasubordinateidentry)(objectClass=ipasubordinategid)(objectClass=ipasubordinateuid)) && ipasubuidnumber:(ipasubuidnumber=-1) && ipasubuidcount:(ipasubuidcount=eval($SUBID_COUNT)) && ipasubgidnumber:(ipasubgidnumber=-1) && ipasubgidcount:(ipasubgidcount=eval($SUBID_COUNT)), del=ipasubuidnumber:(!(ipasubuidnumber=*)) && ipasubuidcount:(!(ipasubuidcount=*)) && ipasubgidnumber:(!(ipasubgidnumber=*)) && ipasubgidcount:(!(ipasubgidcount=*))")(version 3.0;acl "selfservice: Add subordinate id";allow (add, write) userattr = "ipaowner#SELFDN" and groupdn="ldap:///cn=Self-service subordinate ID,cn=permissions,cn=pbac,$SUFFIX";) - add: aci: (targetfilter = "(objectclass=ipasubordinateidentry)")(targetattr="description || ipaowner || ipauniqueid")(targattrfilters = "add=objectClass:(|(objectClass=top)(objectClass=ipasubordinateid)(objectClass=ipasubordinateidentry)(objectClass=ipasubordinategid)(objectClass=ipasubordinateuid)) && ipasubuidnumber:(|(ipasubuidnumber>=1)(ipasubuidnumber=-1)) && ipasubuidcount:(ipasubuidcount=eval($SUBID_COUNT)) && ipasubgidnumber:(|(ipasubgidnumber>=1)(ipasubgidnumber=-1)) && ipasubgidcount:(ipasubgidcount=eval($SUBID_COUNT)), del=ipasubuidnumber:(!(ipasubuidnumber=*)) && ipasubuidcount:(!(ipasubuidcount=*)) && ipasubgidnumber:(!(ipasubgidnumber=*)) && ipasubgidcount:(!(ipasubgidcount=*))")(version 3.0;acl "Add subordinate ids to any user";allow (add, write) groupdn="ldap:///cn=Subordinate ID Administrators,cn=privileges,cn=pbac,$SUFFIX";) - - # DNA plugin and idrange configuration -@@ -90,6 +86,7 @@ default: dnaScope: $SUFFIX - default: dnaThreshold: eval($SUBID_DNA_THRESHOLD) - default: dnaSharedCfgDN: cn=subordinate-ids,cn=dna,cn=ipa,cn=etc,$SUFFIX - default: dnaExcludeScope: cn=provisioning,$SUFFIX -+default: dnaInterval: eval($SUBID_COUNT) - # TODO: enable when 389-DS' DNA plugin supports dnaStepAttr - # add: dnaIntervalAttr: ipasubuidcount - # add: dnaIntervalAttr: ipasubgidcount -diff --git a/ipaserver/plugins/subid.py b/ipaserver/plugins/subid.py -index 7d9a2f33e84bc7cdf17900346343e49d5eda0d8c..440f24ee627f0736100f63026158c564b04520c2 100644 ---- a/ipaserver/plugins/subid.py -+++ b/ipaserver/plugins/subid.py -@@ -2,7 +2,6 @@ - # Copyright (C) 2021 FreeIPA Contributors see COPYING for license - # - --import random - import uuid - - from ipalib import api -@@ -291,12 +290,8 @@ class subid(LDAPObject): - _entry_attrs = ldap.get_entry(dn, ["objectclass"]) - entry_attrs["objectclass"] = _entry_attrs["objectclass"] - -- # XXX HACK, remove later -- if subuid == DNA_MAGIC: -- subuid = self._fake_dna_plugin(ldap, dn, entry_attrs) -- - entry_attrs["ipasubuidnumber"] = subuid -- # enforice subuid == subgid for now -+ # enforce subuid == subgid for now - entry_attrs["ipasubgidnumber"] = subuid - # hard-coded constants - entry_attrs["ipasubuidcount"] = constants.SUBID_COUNT -@@ -350,13 +345,6 @@ class subid(LDAPObject): - filters.extend(extra_filters) - return ldap.combine_filters(filters, rules=ldap.MATCH_ALL) - -- def _fake_dna_plugin(self, ldap, dn, entry_attrs): -- """XXX HACK, remove when 389-DS DNA plugin supports steps""" -- return ( -- constants.SUBID_RANGE_START -- + random.randint(1, 32764 - 2) * constants.SUBID_COUNT -- ) -- - - @register() - class subid_add(LDAPCreate): --- -2.26.3 - diff --git a/SOURCES/0011-Fix-ipa-server-upgrade.patch b/SOURCES/0011-Fix-ipa-server-upgrade.patch deleted file mode 100644 index 055275c..0000000 --- a/SOURCES/0011-Fix-ipa-server-upgrade.patch +++ /dev/null @@ -1,68 +0,0 @@ -From 21574b261cf0d346da48e34c0a5383736ca8798b Mon Sep 17 00:00:00 2001 -From: Christian Heimes -Date: Fri, 21 May 2021 14:56:32 +0200 -Subject: [PATCH] Fix ipa-server-upgrade - -Signed-off-by: Christian Heimes -Reviewed-By: Francois Cami -Reviewed-By: Rob Crittenden -Reviewed-By: Francois Cami -Reviewed-By: Rob Crittenden ---- - install/share/bootstrap-template.ldif | 2 +- - install/updates/73-subid.update | 2 +- - ipaserver/install/ldapupdate.py | 3 +++ - 3 files changed, 5 insertions(+), 2 deletions(-) - -diff --git a/install/share/bootstrap-template.ldif b/install/share/bootstrap-template.ldif -index 16f2ef822eaf56dd68d4140b22a607539645b151..325eb8450c786899e7b5e4ae2ef8978f42a8425b 100644 ---- a/install/share/bootstrap-template.ldif -+++ b/install/share/bootstrap-template.ldif -@@ -491,7 +491,7 @@ cn: ${REALM}_subid_range - ipaBaseID: eval($SUBID_RANGE_START) - ipaIDRangeSize: eval($SUBID_RANGE_SIZE) - # HACK: RIDs to work around adtrust sidgen issue --ipaBaseRID: eval($SUBID_RANGE_START - $IDRANGE_SIZE) -+ipaBaseRID: eval($SUBID_BASE_RID) - # 738065-838566 = IPA-SUB - ipaNTTrustedDomainSID: S-1-5-21-738065-838566-$DOMAIN_HASH - # HACK: "ipa-local-subid" range type causes issues with older SSSD clients -diff --git a/install/updates/73-subid.update b/install/updates/73-subid.update -index e10703aa3f9528751233ddebe00b8c8c8fc5ed3f..890eb7f1f6f261af977f26b3457e765ee8e9791f 100644 ---- a/install/updates/73-subid.update -+++ b/install/updates/73-subid.update -@@ -102,7 +102,7 @@ default: cn: ${REALM}_subid_range - default: ipaBaseID: $SUBID_RANGE_START - default: ipaIDRangeSize: $SUBID_RANGE_SIZE - # HACK: RIDs to work around adtrust sidgen issue --default: ipaBaseRID: eval($SUBID_RANGE_START - $IDRANGE_SIZE) -+default: ipaBaseRID: eval($SUBID_BASE_RID) - default: ipaNTTrustedDomainSID: S-1-5-21-738065-838566-$DOMAIN_HASH - # HACK: "ipa-local-subid" range type causes issues with older SSSD clients - # see https://github.com/SSSD/sssd/issues/5571 -diff --git a/ipaserver/install/ldapupdate.py b/ipaserver/install/ldapupdate.py -index d0516dc3028366df5d03a960866abe72601aa4b6..06cb78e0b7dc2c82f0339c43228045d93b922288 100644 ---- a/ipaserver/install/ldapupdate.py -+++ b/ipaserver/install/ldapupdate.py -@@ -59,8 +59,10 @@ def get_sub_dict(realm, domain, suffix, fqdn, idstart=None, idmax=None): - """ - if idstart is None: - idrange_size = None -+ subid_base_rid = None - else: - idrange_size = idmax - idstart + 1 -+ subid_base_rid = constants.SUBID_RANGE_START - idrange_size - - return dict( - REALM=realm, -@@ -81,6 +83,7 @@ def get_sub_dict(realm, domain, suffix, fqdn, idstart=None, idmax=None): - SUBID_RANGE_SIZE=constants.SUBID_RANGE_SIZE, - SUBID_RANGE_MAX=constants.SUBID_RANGE_MAX, - SUBID_DNA_THRESHOLD=constants.SUBID_DNA_THRESHOLD, -+ SUBID_BASE_RID=subid_base_rid, - DOMAIN_HASH=murmurhash3(domain, len(domain), 0xdeadbeef), - MAX_DOMAIN_LEVEL=constants.MAX_DOMAIN_LEVEL, - MIN_DOMAIN_LEVEL=constants.MIN_DOMAIN_LEVEL, --- -2.26.3 - diff --git a/SOURCES/0012-Fix-oid-of-ipaUserDefaultSubordinateId.patch b/SOURCES/0012-Fix-oid-of-ipaUserDefaultSubordinateId.patch deleted file mode 100644 index c964120..0000000 --- a/SOURCES/0012-Fix-oid-of-ipaUserDefaultSubordinateId.patch +++ /dev/null @@ -1,29 +0,0 @@ -From c8b4fd5bb773a73116350bf8e853246916fe87c2 Mon Sep 17 00:00:00 2001 -From: Christian Heimes -Date: Tue, 15 Jun 2021 13:25:18 +0200 -Subject: [PATCH] Fix oid of ipaUserDefaultSubordinateId - -Reviewed-By: Francois Cami -Reviewed-By: Rob Crittenden -Reviewed-By: Francois Cami -Reviewed-By: Rob Crittenden ---- - install/share/60ipaconfig.ldif | 2 +- - 1 file changed, 1 insertion(+), 1 deletion(-) - -diff --git a/install/share/60ipaconfig.ldif b/install/share/60ipaconfig.ldif -index f84b38ead1d70ff408f5669029f1517b0c98ecf1..005c1dd11e37039132620f1d97f9662ffb8c8c59 100644 ---- a/install/share/60ipaconfig.ldif -+++ b/install/share/60ipaconfig.ldif -@@ -47,7 +47,7 @@ attributeTypes: ( 2.16.840.1.113730.3.8.3.27 NAME 'ipaSELinuxUserMapOrder' DESC - ## ipaMaxHostnameLength - maximum hostname length to allow - attributeTypes: ( 2.16.840.1.113730.3.8.1.28 NAME 'ipaMaxHostnameLength' EQUALITY integerMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE) - # ipaUserDefaultSubordinateId - if TRUE new user entries gain subordinate id by default --attributeTypes: ( 2.16.840.1.113730.3.8.3.23.14 NAME 'ipaUserDefaultSubordinateId' DESC 'Enable adding user entries with subordinate id' SYNTAX 1.3.6.1.4.1.1466.115.121.1.7 SINGLE-VALUE X-ORIGIN 'IPA v4.9') -+attributeTypes: ( 2.16.840.1.113730.3.8.23.14 NAME 'ipaUserDefaultSubordinateId' DESC 'Enable adding user entries with subordinate id' SYNTAX 1.3.6.1.4.1.1466.115.121.1.7 SINGLE-VALUE X-ORIGIN 'IPA v4.9') - ############################################### - ## - ## ObjectClasses --- -2.26.3 - diff --git a/SOURCES/0013-WebUI-Improve-subordinate-ids-user-workflow.patch b/SOURCES/0013-WebUI-Improve-subordinate-ids-user-workflow.patch deleted file mode 100644 index f1b78ec..0000000 --- a/SOURCES/0013-WebUI-Improve-subordinate-ids-user-workflow.patch +++ /dev/null @@ -1,275 +0,0 @@ -From 10418b7f3ea8c682961fc201545169663d507bf6 Mon Sep 17 00:00:00 2001 -From: Serhii Tsymbaliuk -Date: Thu, 17 Jun 2021 13:56:19 +0200 -Subject: [PATCH] WebUI: Improve subordinate ids user workflow - -- add "Subordinate ID Statistics" page -- add button for generating subid in "Subordinate ids" tab of user details page -- allow to navigate directly to owner details from subordinate id page -- adjust i18n strings - -Ticket: https://pagure.io/freeipa/issue/8361 -Signed-off-by: Serhii Tsymbaliuk -Reviewed-By: Francois Cami -Reviewed-By: Rob Crittenden -Reviewed-By: Francois Cami -Reviewed-By: Rob Crittenden ---- - install/ui/src/freeipa/details.js | 8 ++- - .../ui/src/freeipa/navigation/menu_spec.js | 19 ++++++- - install/ui/src/freeipa/subid.js | 43 +++++++++++++++- - install/ui/src/freeipa/user.js | 49 +++++++++++++++---- - ipaserver/plugins/internal.py | 22 ++++++--- - 5 files changed, 121 insertions(+), 20 deletions(-) - -diff --git a/install/ui/src/freeipa/details.js b/install/ui/src/freeipa/details.js -index b557bbcef9a427a87eee3216f4345fc853cbaaff..2704cbd0ba98efa877cf5ec8a878e688ee6807e9 100644 ---- a/install/ui/src/freeipa/details.js -+++ b/install/ui/src/freeipa/details.js -@@ -602,6 +602,12 @@ exp.details_facet = IPA.details_facet = function(spec, no_init) { - */ - that.facet_group = spec.facet_group || 'settings'; - -+ /** -+ * Indicates if the details facet depends on pkey -+ * @property {boolean} -+ */ -+ that.require_pkey = spec.require_pkey !== undefined ? spec.require_pkey : true; -+ - /** - * Widgets - * @property {IPA.widget_container} -@@ -1105,7 +1111,7 @@ exp.details_facet = IPA.details_facet = function(spec, no_init) { - */ - that.refresh = function(on_success, on_error) { - -- if (!that.get_pkey() && that.entity.redirect_facet) { -+ if (that.require_pkey && !that.get_pkey() && that.entity.redirect_facet) { - that.redirect(); - return; - } -diff --git a/install/ui/src/freeipa/navigation/menu_spec.js b/install/ui/src/freeipa/navigation/menu_spec.js -index 6ccd06919fbe04c7e8d2034ff7a1f644f373c607..a205dfade2f9508edbdc23ee6f7247508cc0479c 100644 ---- a/install/ui/src/freeipa/navigation/menu_spec.js -+++ b/install/ui/src/freeipa/navigation/menu_spec.js -@@ -104,7 +104,24 @@ var nav = {}; - } - ] - }, -- { entity: 'subid' } -+ { -+ name: 'subid', -+ label: '@i18n:tabs.subid', -+ children: [ -+ { -+ name: 'subid', -+ entity: 'subid', -+ facet: 'search', -+ label: '@i18n:tabs.subid' -+ }, -+ { -+ name: 'subid-stats', -+ entity: 'subid', -+ facet: 'stats', -+ label: '@i18n:objects.subid.stats' -+ } -+ ] -+ } - ] - }, - { -diff --git a/install/ui/src/freeipa/subid.js b/install/ui/src/freeipa/subid.js -index f286165070b08badf77cac6c30e93cab916c2acc..32f75bb7854cd3e84417a66870e99d34d49617e3 100644 ---- a/install/ui/src/freeipa/subid.js -+++ b/install/ui/src/freeipa/subid.js -@@ -31,6 +31,7 @@ return { - }, - { - $type: 'details', -+ disable_facet_tabs: true, - sections: [ - { - name: 'details', -@@ -38,9 +39,11 @@ return { - 'ipauniqueid', - 'description', - { -+ $type: 'link', - name: 'ipaowner', - label: '@i18n:objects.subid.ipaowner', -- title: '@mo-param:subid:ipaowner:label' -+ title: '@mo-param:subid:ipaowner:label', -+ other_entity: 'user' - }, - { - name: 'ipasubgidnumber', -@@ -65,6 +68,44 @@ return { - ] - } - ] -+ }, -+ { -+ $type: 'details', -+ name: 'stats', -+ label: '@i18n:objects.subid.stats', -+ refresh_command_name: 'stats', -+ check_rights: false, -+ no_update: true, -+ disable_facet_tabs: true, -+ disable_breadcrumb: true, -+ require_pkey: false, -+ fields: [ -+ { -+ name: 'assigned_subids', -+ label: '@i18n:objects.subid.assigned_subids', -+ read_only: true -+ }, -+ { -+ name: 'baseid', -+ label: '@i18n:objects.subid.baseid', -+ read_only: true -+ }, -+ { -+ name: 'dna_remaining', -+ label: '@i18n:objects.subid.dna_remaining', -+ read_only: true -+ }, -+ { -+ name: 'rangesize', -+ label: '@i18n:objects.subid.rangesize', -+ read_only: true -+ }, -+ { -+ name: 'remaining_subids', -+ label: '@i18n:objects.subid.remaining_subids', -+ read_only: true -+ } -+ ] - } - ], - adder_dialog: { -diff --git a/install/ui/src/freeipa/user.js b/install/ui/src/freeipa/user.js -index 56bb6f4feffb637d33a57aecf9a98f08d4639550..6a56320c580f58a1aba84e598736631986421113 100644 ---- a/install/ui/src/freeipa/user.js -+++ b/install/ui/src/freeipa/user.js -@@ -464,7 +464,7 @@ return { - }, - { - $type: 'subid_generate', -- hide_cond: ['preserved-user'], -+ hide_cond: ['preserved-user', 'self-service-other'], - enable_cond: ['no-subid'] - } - ], -@@ -556,8 +556,35 @@ return { - { - $type: 'association', - name: 'memberof_subid', -+ columns: [ -+ 'ipauniqueid', -+ 'ipasubuidnumber', -+ 'ipasubgidnumber' -+ ], - associator: IPA.serial_associator, -- read_only: true -+ read_only: true, -+ state: { -+ evaluators: [ -+ IPA.user.self_service_other_user_evaluator, -+ IPA.user.preserved_user_evaluator, -+ IPA.user.has_subid_evaluator -+ ] -+ }, -+ actions: [ -+ { -+ $type: 'subid_generate', -+ name: 'subid_generate', -+ hide_cond: ['preserved-user', 'self-service-other'], -+ enable_cond: ['no-subid'] -+ } -+ ], -+ control_buttons: [ -+ { -+ name: 'subid_generate', -+ label: '@i18n:objects.user.auto_subid', -+ icon: 'fa-plus' -+ } -+ ] - } - ], - standard_association_facets: { -@@ -1216,14 +1243,16 @@ IPA.user.subid_generate_action = function(spec) { - var that = IPA.action(spec); - - that.execute_action = function(facet) { -- -- var subid_e = reg.entity.get('subid'); -- var dialog = subid_e.get_dialog('add'); -- dialog.open(); -- if (!IPA.is_selfservice) { -- var owner = facet.get_pkey(); -- dialog.get_field('ipaowner').set_value([owner]); -- } -+ var owner = facet.get_pkey(); -+ var command = rpc.command({ -+ entity: 'subid', -+ method: 'generate' -+ }); -+ command.set_option('ipaowner', owner); -+ command.on_success = function(data, text_status, xhr) { -+ facet.refresh(); -+ }; -+ command.execute(); - }; - - return that; -diff --git a/ipaserver/plugins/internal.py b/ipaserver/plugins/internal.py -index 5ef940c2b88cc2b132a15d619772349b30731306..29e09f0067ec60d014e61c49313455d64478ef22 100644 ---- a/ipaserver/plugins/internal.py -+++ b/ipaserver/plugins/internal.py -@@ -1364,6 +1364,20 @@ class i18n_messages(Command): - "undel_success": _("${count} user(s) restored"), - "user_categories": _("User categories"), - }, -+ "subid": { -+ "add": _("Add subid"), -+ "assigned_subids": _("Assigned subids"), -+ "baseid": _("Base ID"), -+ "dna_remaining": _("DNA remaining"), -+ "ipaowner": _("Owner"), -+ "ipasubgidcount": _("SubGID range size"), -+ "ipasubgidnumber": _("SubGID range start"), -+ "ipasubuidcount": _("SubUID range size"), -+ "ipasubuidnumber": _("SubUID range start"), -+ "rangesize": _("Range size"), -+ "remaining_subids": _("Remaining subids"), -+ "stats": _("Subordinate ID Statistics"), -+ }, - "sudocmd": { - "add": _("Add sudo command"), - "add_into_sudocmdgroups": _( -@@ -1547,13 +1561,6 @@ class i18n_messages(Command): - "Drive to mount a home directory" - ), - }, -- "subid": { -- "identity": _("Subordinate user and group id"), -- "subuidnumber": _("Subordinate user id"), -- "subuidcount": _("Subordinate user id count"), -- "subgidnumber": _("Subordinate group id"), -- "subgidcount": _("Subordinate group id count"), -- }, - "trustconfig": { - "options": _("Options"), - }, -@@ -1942,6 +1949,7 @@ class i18n_messages(Command): - "network_services": _("Network Services"), - "policy": _("Policy"), - "role": _("Role-Based Access Control"), -+ "subid": _("Subordinate IDs"), - "sudo": _("Sudo"), - "topology": _("Topology"), - "trust": _("Trusts"), --- -2.26.3 - diff --git a/SOURCES/0014-Test-DNA-plugin-configuration.patch b/SOURCES/0014-Test-DNA-plugin-configuration.patch deleted file mode 100644 index 75244fd..0000000 --- a/SOURCES/0014-Test-DNA-plugin-configuration.patch +++ /dev/null @@ -1,57 +0,0 @@ -From b6ab27acdb07c21f43e9dcc9b777f8fd6a8925e1 Mon Sep 17 00:00:00 2001 -From: Christian Heimes -Date: Fri, 18 Jun 2021 10:51:54 +0200 -Subject: [PATCH] Test DNA plugin configuration - -Reviewed-By: Francois Cami -Reviewed-By: Rob Crittenden -Reviewed-By: Francois Cami -Reviewed-By: Rob Crittenden ---- - ipatests/test_integration/test_subids.py | 22 +++++++++++++++++++++- - 1 file changed, 21 insertions(+), 1 deletion(-) - -diff --git a/ipatests/test_integration/test_subids.py b/ipatests/test_integration/test_subids.py -index 48e58c26464f52605438afe865575e5ca4c8f1f8..28cd1f765cd63af944bce83f4676a2b1998f5f5d 100644 ---- a/ipatests/test_integration/test_subids.py -+++ b/ipatests/test_integration/test_subids.py -@@ -6,8 +6,11 @@ - """ - import os - --from ipalib.constants import SUBID_COUNT, SUBID_RANGE_START, SUBID_RANGE_MAX -+from ipalib.constants import ( -+ SUBID_COUNT, SUBID_RANGE_START, SUBID_RANGE_MAX, SUBID_DNA_THRESHOLD -+) - from ipaplatform.paths import paths -+from ipapython.dn import DN - from ipatests.pytest_ipa.integration import tasks - from ipatests.test_integration.base import IntegrationTest - -@@ -81,6 +84,23 @@ class TestSubordinateId(IntegrationTest): - cmd.extend(("--owner", uid)) - return self.master.run_command(cmd, **kwargs) - -+ def test_dna_config(self): -+ conn = self.master.ldap_connect() -+ dna_cfg = DN( -+ "cn=Subordinate IDs,cn=Distributed Numeric Assignment Plugin," -+ "cn=plugins,cn=config" -+ ) -+ entry = conn.get_entry(dna_cfg) -+ -+ def single_int(key): -+ return int(entry.single_value[key]) -+ -+ assert single_int("dnaInterval") == SUBID_COUNT -+ assert single_int("dnaThreshold") == SUBID_DNA_THRESHOLD -+ assert single_int("dnaMagicRegen") == -1 -+ assert single_int("dnaMaxValue") == SUBID_RANGE_MAX -+ assert set(entry["dnaType"]) == {"ipasubgidnumber", "ipasubuidnumber"} -+ - def test_auto_generate_subid(self): - uid = "testuser_auto1" - passwd = "Secret123" --- -2.26.3 - diff --git a/SOURCES/0015-Fall-back-to-krbprincipalname-when-validating-host-a.patch b/SOURCES/0015-Fall-back-to-krbprincipalname-when-validating-host-a.patch deleted file mode 100644 index 780d75d..0000000 --- a/SOURCES/0015-Fall-back-to-krbprincipalname-when-validating-host-a.patch +++ /dev/null @@ -1,69 +0,0 @@ -From 3b7f537dd3022ecb758b2f0f8b2aba530e74bff7 Mon Sep 17 00:00:00 2001 -From: Rob Crittenden -Date: Mon, 12 Jul 2021 11:02:10 -0400 -Subject: [PATCH] Fall back to krbprincipalname when validating host auth - indicators - -When adding a new host the principal cannot be determined because it -relies on either: - -a) an entry to already exist -b) krbprincipalname be a component of the dn - -As a result the full dn is being passed into ipapython.Kerberos -which can't parse it. - -Look into the entry in validate_validate_auth_indicator() for -krbprincipalname in this case. - -https://pagure.io/freeipa/issue/8206 - -Signed-off-by: Rob Crittenden -Reviewed-By: Alexander Bokovoy -Reviewed-By: Florence Blanc-Renaud ---- - ipaserver/plugins/service.py | 5 +++++ - ipatests/test_xmlrpc/test_host_plugin.py | 11 +++++++++++ - 2 files changed, 16 insertions(+) - -diff --git a/ipaserver/plugins/service.py b/ipaserver/plugins/service.py -index cfbbff3c69c6a92535df58c51767c3d0952c7b0b..498f5e444364c6330e053d1057b727fb5181f70b 100644 ---- a/ipaserver/plugins/service.py -+++ b/ipaserver/plugins/service.py -@@ -209,6 +209,11 @@ def validate_auth_indicator(entry): - # and shouldn't be allowed to have auth indicators. - # https://pagure.io/freeipa/issue/8206 - pkey = api.Object['service'].get_primary_key_from_dn(entry.dn) -+ if pkey == str(entry.dn): -+ # krbcanonicalname may not be set yet if this is a host entry, -+ # try krbprincipalname -+ if 'krbprincipalname' in entry: -+ pkey = entry['krbprincipalname'] - principal = kerberos.Principal(pkey) - server = api.Command.server_find(principal.hostname)['result'] - if server: -diff --git a/ipatests/test_xmlrpc/test_host_plugin.py b/ipatests/test_xmlrpc/test_host_plugin.py -index 9cfde3565d48e103a0549e2bfb7579e07668f41b..ff50e796cd19fca2c7b6c87d73940779db8daa0b 100644 ---- a/ipatests/test_xmlrpc/test_host_plugin.py -+++ b/ipatests/test_xmlrpc/test_host_plugin.py -@@ -615,6 +615,17 @@ class TestProtectedMaster(XMLRPC_test): - )): - command() - -+ def test_add_non_master_with_auth_ind(self, host5): -+ host5.ensure_missing() -+ command = host5.make_command( -+ 'host_add', host5.fqdn, krbprincipalauthind=['radius'], -+ force=True -+ ) -+ result = command() -+ # The fact that the command succeeds exercises the change but -+ # let's check the indicator as well. -+ assert result['result']['krbprincipalauthind'] == ('radius',) -+ - - @pytest.mark.tier1 - class TestValidation(XMLRPC_test): --- -2.26.3 - diff --git a/SOURCES/0016-spec-file-Trust-controller-role-should-pull-sssd-win.patch b/SOURCES/0016-spec-file-Trust-controller-role-should-pull-sssd-win.patch deleted file mode 100644 index cdb9c35..0000000 --- a/SOURCES/0016-spec-file-Trust-controller-role-should-pull-sssd-win.patch +++ /dev/null @@ -1,30 +0,0 @@ -From aa07f41769765e55c1531b52ad9ef5876e97e0e9 Mon Sep 17 00:00:00 2001 -From: Florence Blanc-Renaud -Date: Thu, 15 Jul 2021 10:06:56 +0200 -Subject: [PATCH] spec file: Trust controller role should pull - sssd-winbind-idmap package - -ipa-server-trust-ad subpackage need to pull in sssd-winbind-idmap -Fixes: https://pagure.io/freeipa/issue/8923 - -Signed-off-by: Florence Blanc-Renaud -Reviewed-By: Alexander Bokovoy ---- - freeipa.spec.in | 1 + - 1 file changed, 1 insertion(+) - -diff --git a/freeipa.spec.in b/freeipa.spec.in -index fa649cf4e1abe8e9928ef340a66d48d78f7e3521..c33d2e216e5b0f13ae4fd3f9f506d4983493f03a 100755 ---- a/freeipa.spec.in -+++ b/freeipa.spec.in -@@ -597,6 +597,7 @@ Requires: %{name}-common = %{version}-%{release} - - Requires: samba >= %{samba_version} - Requires: samba-winbind -+Requires: sssd-winbind-idmap - Requires: libsss_idmap - %if 0%{?rhel} - Obsoletes: ipa-idoverride-memberof-plugin <= 0.1 --- -2.26.3 - diff --git a/SOURCES/0017-Use-new-method-in-check-to-prevent-removal-of-last-K.patch b/SOURCES/0017-Use-new-method-in-check-to-prevent-removal-of-last-K.patch deleted file mode 100644 index 8a4d23d..0000000 --- a/SOURCES/0017-Use-new-method-in-check-to-prevent-removal-of-last-K.patch +++ /dev/null @@ -1,58 +0,0 @@ -From 0b9adf1d8d5efb48e734650e4101e8816b01e1d3 Mon Sep 17 00:00:00 2001 -From: Rob Crittenden -Date: Mon, 19 Jul 2021 17:51:44 -0400 -Subject: [PATCH] Use new method in check to prevent removal of last KRA - -It previously used a vault connection to determine if any -KRA servers were installed. This would fail if the last KRA -was not available. - -Use server roles instead to determine if the last KRA server -is to be removed. - -https://pagure.io/freeipa/issue/8397 - -Signed-off-by: Rob Crittenden -Reviewed-By: Francois Cami ---- - ipaserver/plugins/server.py | 24 +++++++++++++----------- - 1 file changed, 13 insertions(+), 11 deletions(-) - -diff --git a/ipaserver/plugins/server.py b/ipaserver/plugins/server.py -index b3dda8469..5fa7a58bd 100644 ---- a/ipaserver/plugins/server.py -+++ b/ipaserver/plugins/server.py -@@ -508,17 +508,19 @@ class server_del(LDAPDelete): - - if self.api.Command.ca_is_enabled()['result']: - try: -- vault_config = self.api.Command.vaultconfig_show()['result'] -- kra_servers = vault_config.get('kra_server_server', []) -- except errors.InvocationError: -- # KRA is not configured -- pass -- else: -- if kra_servers == [hostname]: -- handler( -- _("Deleting this server is not allowed as it would " -- "leave your installation without a KRA."), -- ignore_last_of_role) -+ roles = self.api.Command.server_role_find( -+ server_server=hostname, -+ role_servrole='KRA server', -+ status='enabled', -+ include_master=True, -+ )['result'] -+ except errors.NotFound: -+ roles = () -+ if len(roles) == 1 and roles[0]['server_server'] == hostname: -+ handler( -+ _("Deleting this server is not allowed as it would " -+ "leave your installation without a KRA."), -+ ignore_last_of_role) - - ca_servers = ipa_config.get('ca_server_server', []) - ca_renewal_master = ipa_config.get( --- -2.26.3 - diff --git a/SOURCES/0018-ipatests-test-removing-last-KRA-when-it-is-not-runni.patch b/SOURCES/0018-ipatests-test-removing-last-KRA-when-it-is-not-runni.patch deleted file mode 100644 index 5461afb..0000000 --- a/SOURCES/0018-ipatests-test-removing-last-KRA-when-it-is-not-runni.patch +++ /dev/null @@ -1,49 +0,0 @@ -From 8ea8f8b68b5a7217518f68065a5fc1df16126314 Mon Sep 17 00:00:00 2001 -From: Rob Crittenden -Date: Mon, 19 Jul 2021 21:54:22 -0400 -Subject: [PATCH] ipatests: test removing last KRA when it is not running - -Use the new role-based mechanism, one that doesn't rely -on direct communication to the server, to determine whether -the server being removed by `ipa server-del` contains the -last KRA server. - -https://pagure.io/freeipa/issue/8397 - -Signed-off-by: Rob Crittenden -Reviewed-By: Francois Cami ---- - ipatests/test_integration/test_server_del.py | 17 +++++++++++++++++ - 1 file changed, 17 insertions(+) - -diff --git a/ipatests/test_integration/test_server_del.py b/ipatests/test_integration/test_server_del.py -index 5e627d5db..9d7f5ef7a 100644 ---- a/ipatests/test_integration/test_server_del.py -+++ b/ipatests/test_integration/test_server_del.py -@@ -302,6 +302,23 @@ class TestLastServices(ServerDelBase): - 1 - ) - -+ def test_removal_of_server_raises_error_about_last_kra(self): -+ """ -+ test that removal of server fails on the last KRA -+ -+ We shut it down to verify that it can be removed if it failed. -+ """ -+ tasks.install_kra(self.master) -+ self.master.run_command(['ipactl', 'stop']) -+ tasks.assert_error( -+ tasks.run_server_del(self.replicas[0], self.master.hostname), -+ "Deleting this server is not allowed as it would leave your " -+ "installation without a KRA.", -+ 1 -+ ) -+ # Restarting the server we stopped is not necessary as it will -+ # be removed in the next test. -+ - def test_forced_removal_of_master(self): - """ - Tests that we can still force remove the master using --- -2.26.3 - diff --git a/SOURCES/0019-rhel-platform-add-a-named-crypto-policy-support.patch b/SOURCES/0019-rhel-platform-add-a-named-crypto-policy-support.patch deleted file mode 100644 index 241d293..0000000 --- a/SOURCES/0019-rhel-platform-add-a-named-crypto-policy-support.patch +++ /dev/null @@ -1,30 +0,0 @@ -From 1a5159b216455070eb51b6a11ceaf0033fc8ce4c Mon Sep 17 00:00:00 2001 -From: Alexander Bokovoy -Date: Fri, 16 Jul 2021 09:20:33 +0300 -Subject: [PATCH] rhel platform: add a named crypto-policy support - -RHEL 8+ provides bind system-wide crypto policy support, enable it. - -Fixes: https://pagure.io/freeipa/issue/8925 -Signed-off-by: Alexander Bokovoy -Reviewed-By: Florence Blanc-Renaud -Reviewed-By: Anuja More ---- - ipaplatform/rhel/paths.py | 1 + - 1 file changed, 1 insertion(+) - -diff --git a/ipaplatform/rhel/paths.py b/ipaplatform/rhel/paths.py -index c081ada32..3631550eb 100644 ---- a/ipaplatform/rhel/paths.py -+++ b/ipaplatform/rhel/paths.py -@@ -30,6 +30,7 @@ from ipaplatform.rhel.constants import HAS_NFS_CONF - - - class RHELPathNamespace(RedHatPathNamespace): -+ NAMED_CRYPTO_POLICY_FILE = "/etc/crypto-policies/back-ends/bind.config" - if HAS_NFS_CONF: - SYSCONFIG_NFS = '/etc/nfs.conf' - --- -2.26.3 - diff --git a/SOURCES/0020-Index-Fix-definition-for-memberOf.patch b/SOURCES/0020-Index-Fix-definition-for-memberOf.patch deleted file mode 100644 index 3cf9357..0000000 --- a/SOURCES/0020-Index-Fix-definition-for-memberOf.patch +++ /dev/null @@ -1,40 +0,0 @@ -From b132956e42a88ab39bb8d6a854e7c5d28d544a11 Mon Sep 17 00:00:00 2001 -From: Florence Blanc-Renaud -Date: Fri, 16 Jul 2021 09:43:54 +0200 -Subject: [PATCH] Index: Fix definition for memberOf - -The index definition for memberOf is inconsistent: - -dn: cn=memberOf,cn=index,cn=userRoot,cn=ldbm database,cn=plugins,cn=config -cn: member -nsIndexType: eq -nsIndexType: sub -nsSystemIndex: false -objectClass: top -objectClass: nsIndex - -The cn attribute should be memberOf, not member. Fix the definition. - -Fixes: https://pagure.io/freeipa/issue/8920 -Signed-off-by: Florence Blanc-Renaud -Reviewed-By: Alexander Bokovoy ---- - install/updates/20-indices.update | 2 +- - 1 file changed, 1 insertion(+), 1 deletion(-) - -diff --git a/install/updates/20-indices.update b/install/updates/20-indices.update -index d6df5b37d..cb1a11dd5 100644 ---- a/install/updates/20-indices.update -+++ b/install/updates/20-indices.update -@@ -434,7 +434,7 @@ add:nsIndexType: eq - add:nsIndexType: pres - - dn: cn=memberOf,cn=index,cn=userRoot,cn=ldbm database,cn=plugins,cn=config --only:cn: member -+only:cn: memberOf - add:nsIndexType: sub - - dn: cn=memberPrincipal,cn=index,cn=userRoot,cn=ldbm database,cn=plugins,cn=config --- -2.26.3 - diff --git a/SOURCES/0021-ipatests-use-whole-date-when-calling-journalctl-sinc.patch b/SOURCES/0021-ipatests-use-whole-date-when-calling-journalctl-sinc.patch deleted file mode 100644 index 2ec50bb..0000000 --- a/SOURCES/0021-ipatests-use-whole-date-when-calling-journalctl-sinc.patch +++ /dev/null @@ -1,35 +0,0 @@ -From b2e6292337c6f7f68ac383db8aa54a1abfa3f6b4 Mon Sep 17 00:00:00 2001 -From: Florence Blanc-Renaud -Date: Sun, 11 Jul 2021 16:29:16 +0200 -Subject: [PATCH] ipatests: use whole date when calling journalctl --since - -The test TestSelfExternalSelf::test_switch_back_to_self_signed -is checking the content of the journal using journalctl --since ... -but provides only the time, not the whole date with year-month-day. -As a consequence, if the test is executed around midnight it may -find nothing in the journal because it's looking for logs after 11:50PM, -which is a date in the future. -Fixes: https://pagure.io/freeipa/issue/8918 - -Signed-off-by: Florence Blanc-Renaud -Reviewed-By: Anuja More ---- - ipatests/test_integration/test_external_ca.py | 2 +- - 1 file changed, 1 insertion(+), 1 deletion(-) - -diff --git a/ipatests/test_integration/test_external_ca.py b/ipatests/test_integration/test_external_ca.py -index 72aa57a0b..d48d73deb 100644 ---- a/ipatests/test_integration/test_external_ca.py -+++ b/ipatests/test_integration/test_external_ca.py -@@ -301,7 +301,7 @@ class TestSelfExternalSelf(IntegrationTest): - def test_switch_back_to_self_signed(self): - - # for journalctl --since -- switch_time = time.strftime('%H:%M:%S') -+ switch_time = time.strftime('%Y-%m-%d %H:%M:%S') - # switch back to self-signed CA - result = self.master.run_command([paths.IPA_CACERT_MANAGE, 'renew', - '--self-signed']) --- -2.31.1 - diff --git a/SOURCES/0022-ipatests-Fix-for-test_source_ipahealthcheck_ipa_host.patch b/SOURCES/0022-ipatests-Fix-for-test_source_ipahealthcheck_ipa_host.patch deleted file mode 100644 index d56f3e5..0000000 --- a/SOURCES/0022-ipatests-Fix-for-test_source_ipahealthcheck_ipa_host.patch +++ /dev/null @@ -1,43 +0,0 @@ -From 26be7ffdba87e0e6294ea035ab3dc9bd933fba43 Mon Sep 17 00:00:00 2001 -From: Sudhir Menon -Date: Fri, 9 Jul 2021 13:44:12 +0530 -Subject: [PATCH] ipatests: Fix for - test_source_ipahealthcheck_ipa_host_check_ipahostkeytab - -Expected error message has been modified for -test_source_ipahealthcheck_ipa_host_check_ipahostkeytab - -Related: https://pagure.io/freeipa/issue/8889 - -Signed-off-by: Sudhir Menon -Reviewed-By: Florence Blanc-Renaud ---- - ipatests/test_integration/test_ipahealthcheck.py | 5 +---- - 1 file changed, 1 insertion(+), 4 deletions(-) - -diff --git a/ipatests/test_integration/test_ipahealthcheck.py b/ipatests/test_integration/test_ipahealthcheck.py -index 305d7b945..f6a3043f1 100644 ---- a/ipatests/test_integration/test_ipahealthcheck.py -+++ b/ipatests/test_integration/test_ipahealthcheck.py -@@ -499,9 +499,6 @@ class TestIpaHealthCheck(IntegrationTest): - from host's keytab. - """ - msg = ( -- "Failed to obtain host TGT: Major (458752): " -- "No credentials were " -- "supplied, or the credentials were unavailable or inaccessible, " - "Minor (2529639107): No credentials cache found" - ) - -@@ -514,7 +511,7 @@ class TestIpaHealthCheck(IntegrationTest): - ) - assert returncode == 1 - assert data[0]["result"] == "ERROR" -- assert data[0]["kw"]["msg"] == msg -+ assert msg in data[0]["kw"]["msg"] - - def test_source_ipahealthcheck_topology_IPATopologyDomainCheck(self): - """ --- -2.31.1 - diff --git a/SOURCES/0023-ipatests-test_ipahealthcheck-print-a-message-if-a-sy.patch b/SOURCES/0023-ipatests-test_ipahealthcheck-print-a-message-if-a-sy.patch deleted file mode 100644 index 8993bcc..0000000 --- a/SOURCES/0023-ipatests-test_ipahealthcheck-print-a-message-if-a-sy.patch +++ /dev/null @@ -1,52 +0,0 @@ -From 7f910eb2dda8595da435b4aed6e759a2916df813 Mon Sep 17 00:00:00 2001 -From: Michal Polovka -Date: Wed, 23 Jun 2021 14:53:49 +0200 -Subject: [PATCH] ipatests: test_ipahealthcheck: print a message if a system is - healthy - -Test if when the system is completely healthy, informative message is -returned and not only empty output (list or json). - -Related: https://pagure.io/freeipa/issue/8892 - -Signed-off-by: Michal Polovka -Reviewed-By: Rob Crittenden ---- - ipatests/test_integration/test_ipahealthcheck.py | 14 +++++++++++++- - 1 file changed, 13 insertions(+), 1 deletion(-) - -diff --git a/ipatests/test_integration/test_ipahealthcheck.py b/ipatests/test_integration/test_ipahealthcheck.py -index f6a3043f1..36fe72be7 100644 ---- a/ipatests/test_integration/test_ipahealthcheck.py -+++ b/ipatests/test_integration/test_ipahealthcheck.py -@@ -286,7 +286,7 @@ class TestIpaHealthCheck(IntegrationTest): - for source in sources_avail: - assert source in result.stdout_text - -- def test_human_output(self, restart_service): -+ def test_human_severity(self, restart_service): - """ - Test that in human output the severity value is correct - -@@ -306,6 +306,18 @@ class TestIpaHealthCheck(IntegrationTest): - assert output == \ - "ERROR: ipahealthcheck.meta.services.sssd: sssd: not running" - -+ def test_human_output(self): -+ """ -+ Test if in case no failures were found, informative string is printed -+ in human output. -+ -+ https://pagure.io/freeipa/issue/8892 -+ """ -+ returncode, output = run_healthcheck(self.master, output_type="human", -+ failures_only=True) -+ assert returncode == 0 -+ assert output == "No issues found." -+ - def test_ipa_healthcheck_after_certupdate(self): - """ - Verify that ipa-certupdate hasn't messed up tracking --- -2.31.1 - diff --git a/SOURCES/0024-ipatests-test_installation-move-tracking_reqs-depend.patch b/SOURCES/0024-ipatests-test_installation-move-tracking_reqs-depend.patch deleted file mode 100644 index 848571f..0000000 --- a/SOURCES/0024-ipatests-test_installation-move-tracking_reqs-depend.patch +++ /dev/null @@ -1,104 +0,0 @@ -From e5df4dc4884f1a66ccbca79b9a0d83874c996d1d Mon Sep 17 00:00:00 2001 -From: Michal Polovka -Date: Mon, 31 May 2021 14:43:28 +0200 -Subject: [PATCH] ipatests: test_installation: move tracking_reqs dependency to - ipalib constants ipaserver: krainstance: utilize moved tracking_reqs - dependency - -KRA instance import depends on lib389 package, which is not always -installed and that results in failure. Furthermore, test_installation -utilizes krainstance import. This fix moves relevant parts from -krainstance to ipalib constants where those are subsequently imported -from. - -Related: https://pagure.io/freeipa/issue/8795 - -Signed-off-by: Michal Polovka -Reviewed-By: Michal Polovka -Reviewed-By: Francois Cami -Reviewed-By: Tibor Dudlak -Reviewed-By: Rob Crittenden -Reviewed-By: Christian Heimes -Reviewed-By: Florence Blanc-Renaud ---- - ipalib/constants.py | 8 ++++++++ - ipaserver/install/krainstance.py | 7 ++----- - ipatests/test_integration/test_installation.py | 7 +++---- - 3 files changed, 13 insertions(+), 9 deletions(-) - -diff --git a/ipalib/constants.py b/ipalib/constants.py -index bff899ba6..2aeafac7a 100644 ---- a/ipalib/constants.py -+++ b/ipalib/constants.py -@@ -360,3 +360,11 @@ SUBID_RANGE_MAX = (2 ** 32) - (2 * SUBID_COUNT) - SUBID_RANGE_SIZE = SUBID_RANGE_MAX - SUBID_RANGE_START - # threshold before DNA plugin requests a new range - SUBID_DNA_THRESHOLD = 500 -+ -+# moved from ipaserver/install/krainstance.py::KRAInstance to avoid duplication -+# as per https://pagure.io/freeipa/issue/8795 -+KRA_TRACKING_REQS = { -+ 'auditSigningCert cert-pki-kra': 'caAuditSigningCert', -+ 'transportCert cert-pki-kra': 'caTransportCert', -+ 'storageCert cert-pki-kra': 'caStorageCert', -+} -diff --git a/ipaserver/install/krainstance.py b/ipaserver/install/krainstance.py -index e63db3fef..13cb2dcaa 100644 ---- a/ipaserver/install/krainstance.py -+++ b/ipaserver/install/krainstance.py -@@ -27,6 +27,7 @@ import base64 - - from ipalib import api - from ipalib import x509 -+from ipalib.constants import KRA_TRACKING_REQS - from ipaplatform.paths import paths - from ipapython import directivesetter - from ipapython import ipautil -@@ -64,11 +65,7 @@ class KRAInstance(DogtagInstance): - # Mapping of nicknames for tracking requests, and the profile to - # use for that certificate. 'configure_renewal()' reads this - # dict. The profile MUST be specified. -- tracking_reqs = { -- 'auditSigningCert cert-pki-kra': 'caAuditSigningCert', -- 'transportCert cert-pki-kra': 'caTransportCert', -- 'storageCert cert-pki-kra': 'caStorageCert', -- } -+ tracking_reqs = KRA_TRACKING_REQS - - def __init__(self, realm): - super(KRAInstance, self).__init__( -diff --git a/ipatests/test_integration/test_installation.py b/ipatests/test_integration/test_installation.py -index 0c96536f0..27f15dbe5 100644 ---- a/ipatests/test_integration/test_installation.py -+++ b/ipatests/test_integration/test_installation.py -@@ -20,7 +20,7 @@ from cryptography.hazmat.primitives import hashes - from cryptography import x509 as crypto_x509 - - from ipalib import x509 --from ipalib.constants import DOMAIN_LEVEL_0 -+from ipalib.constants import DOMAIN_LEVEL_0, KRA_TRACKING_REQS - from ipalib.constants import IPA_CA_RECORD - from ipalib.sysrestore import SYSRESTORE_STATEFILE, SYSRESTORE_INDEXFILE - from ipapython.dn import DN -@@ -34,7 +34,7 @@ from ipatests.pytest_ipa.integration.env_config import get_global_config - from ipatests.test_integration.base import IntegrationTest - from ipatests.test_integration.test_caless import CALessBase, ipa_certs_cleanup - from ipaplatform import services --from ipaserver.install import krainstance -+ - - config = get_global_config() - -@@ -1282,8 +1282,7 @@ class TestInstallMasterKRA(IntegrationTest): - """ - Test that the KRA subsystem certificates renew properly - """ -- kra = krainstance.KRAInstance(self.master.domain.realm) -- for nickname in kra.tracking_reqs: -+ for nickname in KRA_TRACKING_REQS: - cert = tasks.certutil_fetch_cert( - self.master, - paths.PKI_TOMCAT_ALIAS_DIR, --- -2.31.1 - diff --git a/SOURCES/0025-webui-tests-close-notification-when-revoking-cert.patch b/SOURCES/0025-webui-tests-close-notification-when-revoking-cert.patch deleted file mode 100644 index decc06d..0000000 --- a/SOURCES/0025-webui-tests-close-notification-when-revoking-cert.patch +++ /dev/null @@ -1,31 +0,0 @@ -From 40e4ccf1ea943aba4d10e8126ffa49feddd2e683 Mon Sep 17 00:00:00 2001 -From: Florence Blanc-Renaud -Date: Tue, 13 Jul 2021 18:38:22 +0200 -Subject: [PATCH] webui tests: close notification when revoking cert - -When a cert is revoked, a notification is displayed -and may obscure the buttons. Make sure to close the -notification before moving to the next step. - -Fixes: https://pagure.io/freeipa/issue/8911 -Signed-off-by: Florence Blanc-Renaud -Reviewed-By: Mohammad Rizwan ---- - ipatests/test_webui/test_cert.py | 1 + - 1 file changed, 1 insertion(+) - -diff --git a/ipatests/test_webui/test_cert.py b/ipatests/test_webui/test_cert.py -index 53dc76faa..7a8ffde91 100644 ---- a/ipatests/test_webui/test_cert.py -+++ b/ipatests/test_webui/test_cert.py -@@ -107,6 +107,7 @@ class test_cert(UI_driver): - self.action_list_action('revoke_cert', False) - self.select('select[name=revocation_reason]', reason) - self.dialog_button_click('ok') -+ self.close_notifications() - self.navigate_to_entity(ENTITY) - - return cert --- -2.31.1 - diff --git a/SOURCES/0026-ipatests-Test-ipa-cert-fix-warns-when-startup-direct.patch b/SOURCES/0026-ipatests-Test-ipa-cert-fix-warns-when-startup-direct.patch deleted file mode 100644 index 8d8a2d6..0000000 --- a/SOURCES/0026-ipatests-Test-ipa-cert-fix-warns-when-startup-direct.patch +++ /dev/null @@ -1,153 +0,0 @@ -From 02c0da3ef74948579106aab4b669f6e64dd60b24 Mon Sep 17 00:00:00 2001 -From: Mohammad Rizwan -Date: Thu, 24 Jun 2021 13:10:00 +0530 -Subject: [PATCH] ipatests: Test ipa-cert-fix warns when startup directive is - missing from CS.cfg - -Earlier it used to fail when startup directive missing from CS.cfg. -With https://github.com/dogtagpki/pki/pull/3466, it changed to display -a warning than failing. - -related: https://pagure.io/freeipa/issue/8890 - -Signed-off-by: Mohammad Rizwan -Reviewed-By: Florence Blanc-Renaud -Reviewed-By: Florence Blanc-Renaud ---- - .../test_integration/test_ipa_cert_fix.py | 92 ++++++++++++++++++- - 1 file changed, 90 insertions(+), 2 deletions(-) - -diff --git a/ipatests/test_integration/test_ipa_cert_fix.py b/ipatests/test_integration/test_ipa_cert_fix.py -index b2e92d4dc..394e85603 100644 ---- a/ipatests/test_integration/test_ipa_cert_fix.py -+++ b/ipatests/test_integration/test_ipa_cert_fix.py -@@ -48,6 +48,16 @@ def check_status(host, cert_count, state, timeout=600): - return count - - -+def move_date(host, chrony_state, date_str): -+ """Helper method to move the date on given host -+ :param host: The host on which date is to be moved -+ :param chrony_state: State to which chrony service to be moved -+ :param date_str: date string to move the date i.e 2years1month1days -+ """ -+ host.run_command(['systemctl', chrony_state, 'chronyd']) -+ host.run_command(['date', '-s', date_str]) -+ -+ - @pytest.fixture - def expire_cert_critical(): - """ -@@ -82,6 +92,17 @@ class TestIpaCertFix(IntegrationTest): - # the fixture - pass - -+ @pytest.fixture -+ def expire_ca_cert(self): -+ tasks.install_master(self.master, setup_dns=False, -+ extra_args=['--no-ntp']) -+ move_date(self.master, 'stop', '+20Years+1day') -+ -+ yield -+ -+ tasks.uninstall_master(self.master) -+ move_date(self.master, 'start', '-20Years-1day') -+ - def test_missing_csr(self, expire_cert_critical): - """ - Test that ipa-cert-fix succeeds when CSR is missing from CS.cfg -@@ -122,7 +143,8 @@ class TestIpaCertFix(IntegrationTest): - - # Because of BZ 1897120, pki-cert-fix fails on pki-core 10.10.0 - # https://bugzilla.redhat.com/show_bug.cgi?id=1897120 -- if tasks.get_pki_version(self.master) != tasks.parse_version('10.10.0'): -+ if (tasks.get_pki_version(self.master) -+ != tasks.parse_version('10.10.0')): - assert result.returncode == 0 - - # get the number of certs track by certmonger -@@ -180,6 +202,72 @@ class TestIpaCertFix(IntegrationTest): - raiseonerr=False) - assert result.returncode == 2 - -+ def test_missing_startup(self, expire_cert_critical): -+ """ -+ Test ipa-cert-fix fails/warns when startup directive is missing -+ -+ This test checks that if 'selftests.container.order.startup' directive -+ is missing from CS.cfg, ipa-cert-fix fails and throw proper error -+ message. It also checks that underlying command 'pki-server cert-fix' -+ should fail to renew the cert. -+ -+ related: https://pagure.io/freeipa/issue/8721 -+ -+ With https://github.com/dogtagpki/pki/pull/3466, it changed to display -+ a warning than failing. -+ -+ This test also checks that if 'selftests.container.order.startup' -+ directive is missing from CS.cfg, ipa-cert-fix dsplay proper warning -+ (depending on pki version) -+ -+ related: https://pagure.io/freeipa/issue/8890 -+ """ -+ expire_cert_critical(self.master) -+ # pki must be stopped in order to edit CS.cfg -+ self.master.run_command(['ipactl', 'stop']) -+ self.master.run_command([ -+ 'sed', '-i', r'/selftests\.container\.order\.startup/d', -+ paths.CA_CS_CFG_PATH -+ ]) -+ # dirsrv needs to be up in order to run ipa-cert-fix -+ self.master.run_command(['ipactl', 'start', -+ '--ignore-service-failures']) -+ -+ result = self.master.run_command(['ipa-cert-fix', '-v'], -+ stdin_text='yes\n', -+ raiseonerr=False) -+ -+ err_msg1 = "ERROR: 'selftests.container.order.startup'" -+ # check that pki-server cert-fix command fails -+ err_msg2 = ("ERROR: CalledProcessError(Command " -+ "['pki-server', 'cert-fix'") -+ warn_msg = ("WARNING: No selftests configured in " -+ f"{paths.CA_CS_CFG_PATH} " -+ "(selftests.container.order.startup)") -+ -+ if (tasks.get_pki_version(self.master) -+ < tasks.parse_version('10.11.0')): -+ assert (err_msg1 in result.stderr_text -+ and err_msg2 in result.stderr_text) -+ else: -+ assert warn_msg in result.stdout_text -+ -+ def test_expired_CA_cert(self, expire_ca_cert): -+ """Test to check ipa-cert-fix when CA certificate is expired -+ -+ In order to fix expired certs using ipa-cert-fix, CA cert should be -+ valid. If CA cert expired, ipa-cert-fix won't work. -+ -+ related: https://pagure.io/freeipa/issue/8721 -+ """ -+ 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'") -+ assert err_msg in result.stderr_text -+ - - class TestIpaCertFixThirdParty(CALessBase): - """ -@@ -219,7 +307,7 @@ class TestIpaCertFixThirdParty(CALessBase): - '--pin', self.master.config.admin_password, - '-d', 'server.p12'] - self.master.run_command(args) -- self.master.run_command(['ipactl', 'restart',]) -+ self.master.run_command(['ipactl', 'restart']) - - # Run ipa-cert-fix. This is basically a no-op but tests that - # the DS nickname is used and not a hardcoded value. --- -2.31.1 - diff --git a/SOURCES/0027-webui-tests-fix-algo-for-finding-available-idrange.patch b/SOURCES/0027-webui-tests-fix-algo-for-finding-available-idrange.patch deleted file mode 100644 index 92c1528..0000000 --- a/SOURCES/0027-webui-tests-fix-algo-for-finding-available-idrange.patch +++ /dev/null @@ -1,40 +0,0 @@ -From f7997ed0b7d5b915c0184bf8e8864ff935cd6232 Mon Sep 17 00:00:00 2001 -From: Florence Blanc-Renaud -Date: Fri, 16 Jul 2021 15:21:48 +0200 -Subject: [PATCH] webui tests: fix algo for finding available idrange - -The webui tests for ID range evaluate a potentially free id range -by looking for existing ranges and picking a range = max value -+ 1 million. - -With the addition of subuid range this algorithm produces values -over the limit because the subuid range goes from -2,147,483,648 to 4,294,836,224 and the max base id is 4,294,967,295. - -Ignore the subuid range when picking a potential range. -Fixes: https://pagure.io/freeipa/issue/8919 -Reviewed-By: Rob Crittenden ---- - ipatests/test_webui/task_range.py | 6 ++++++ - 1 file changed, 6 insertions(+) - -diff --git a/ipatests/test_webui/task_range.py b/ipatests/test_webui/task_range.py -index db34f6f11..f0664d35a 100644 ---- a/ipatests/test_webui/task_range.py -+++ b/ipatests/test_webui/task_range.py -@@ -64,6 +64,12 @@ class range_tasks(UI_driver): - max_rid = 0 - - for idrange in idranges: -+ # IPA.TEST_subid_range is automatically created near the end -+ # of the allowed ids, taking from 2,147,483,648 to 4,294,836,224 -+ # Ignore this range when looking for available ids otherwise -+ # we won't find any value < max baseid 4,294,967,295 -+ if idrange['cn'][0].endswith("_subid_range"): -+ continue - size = int(idrange['ipaidrangesize'][0]) - base_id = int(idrange['ipabaseid'][0]) - --- -2.31.1 - diff --git a/SOURCES/0028-ipatests-smbclient-k-use-kerberos-desired.patch b/SOURCES/0028-ipatests-smbclient-k-use-kerberos-desired.patch deleted file mode 100644 index 38d2842..0000000 --- a/SOURCES/0028-ipatests-smbclient-k-use-kerberos-desired.patch +++ /dev/null @@ -1,61 +0,0 @@ -From 161d5844eb1214e60c636bdb73713c6a43f1e75c Mon Sep 17 00:00:00 2001 -From: =?UTF-8?q?Fran=C3=A7ois=20Cami?= -Date: Mon, 19 Jul 2021 15:59:01 +0200 -Subject: [PATCH] ipatests: smbclient "-k" => "--use-kerberos=desired" -MIME-Version: 1.0 -Content-Type: text/plain; charset=UTF-8 -Content-Transfer-Encoding: 8bit - -Change documentation: -https://download.samba.org/pub/samba/rc/samba-4.15.0rc1.WHATSNEW.txt - -As of Samba 4.15rc1, smbclient does not accept "-k" anymore. -The "-k|--kerberos" option ("Try to authenticate with kerberos.") -has been replaced with "--use-kerberos=required|desired|off". - -Fixes: https://pagure.io/freeipa/issue/8926 -Signed-off-by: François Cami -Reviewed-By: Michal Polovka -Reviewed-By: Michal Polovka ---- - ipatests/test_integration/test_smb.py | 23 +++++++++++++++++++++-- - 1 file changed, 21 insertions(+), 2 deletions(-) - -diff --git a/ipatests/test_integration/test_smb.py b/ipatests/test_integration/test_smb.py -index 399ad6209..b2b7ce2e4 100644 ---- a/ipatests/test_integration/test_smb.py -+++ b/ipatests/test_integration/test_smb.py -@@ -166,9 +166,28 @@ class TestSMB(IntegrationTest): - encoding='utf-8') - assert file_contents_at_server == test_string - -- # check access using smbclient utility -+ # Detect whether smbclient uses -k or --use-kerberos=required -+ # https://pagure.io/freeipa/issue/8926 -+ # then check access using smbclient. - res = run_smb_client( -- ['smbclient', '-k', share['unc'], '-c', 'dir']) -+ [ -+ "smbclient", -+ "-h", -+ ], raiseonerr=False -+ ) -+ if "[-k|--kerberos]" in res.stderr_text: -+ smbclient_krb5_knob = "-k" -+ else: -+ smbclient_krb5_knob = "--use-kerberos=desired" -+ res = run_smb_client( -+ [ -+ "smbclient", -+ smbclient_krb5_knob, -+ share["unc"], -+ "-c", -+ "dir", -+ ] -+ ) - assert test_dir in res.stdout_text - - # check file and dir removal from client side --- -2.31.1 - diff --git a/SOURCES/0029-test_acme-refactor-with-tasks.patch b/SOURCES/0029-test_acme-refactor-with-tasks.patch deleted file mode 100644 index 5add22b..0000000 --- a/SOURCES/0029-test_acme-refactor-with-tasks.patch +++ /dev/null @@ -1,81 +0,0 @@ -From 86869364a30f071ee79974b301ff68e80c0950ba Mon Sep 17 00:00:00 2001 -From: =?UTF-8?q?Fran=C3=A7ois=20Cami?= -Date: Tue, 20 Jul 2021 20:19:16 +0200 -Subject: [PATCH] test_acme: refactor with tasks -MIME-Version: 1.0 -Content-Type: text/plain; charset=UTF-8 -Content-Transfer-Encoding: 8bit - -Signed-off-by: François Cami -Reviewed-By: Michal Polovka ---- - ipatests/pytest_ipa/integration/tasks.py | 11 +++++++++++ - ipatests/test_integration/test_acme.py | 19 ++++--------------- - 2 files changed, 15 insertions(+), 15 deletions(-) - -diff --git a/ipatests/pytest_ipa/integration/tasks.py b/ipatests/pytest_ipa/integration/tasks.py -index 22c7ba782..c2e548617 100755 ---- a/ipatests/pytest_ipa/integration/tasks.py -+++ b/ipatests/pytest_ipa/integration/tasks.py -@@ -2800,3 +2800,14 @@ def is_package_installed(host, pkg): - 'is_package_installed: unknown platform %s' % platform - ) - return result.returncode == 0 -+ -+ -+def move_date(host, chrony_cmd, date_str): -+ """Helper method to move system date -+ :param host: host on which date is to be manipulated -+ :param chrony_cmd: systemctl command to apply to -+ chrony service, for instance 'start', 'stop' -+ :param date_str: date string to change the date i.e '3years2months1day1' -+ """ -+ host.run_command(['systemctl', chrony_cmd, 'chronyd']) -+ host.run_command(['date', '-s', date_str]) -diff --git a/ipatests/test_integration/test_acme.py b/ipatests/test_integration/test_acme.py -index d90f1ff7d..b4aa1b351 100644 ---- a/ipatests/test_integration/test_acme.py -+++ b/ipatests/test_integration/test_acme.py -@@ -35,17 +35,6 @@ skip_mod_md_tests = osinfo.id not in ['rhel', 'fedora', ] - CERTBOT_DNS_IPA_SCRIPT = '/usr/libexec/ipa/acme/certbot-dns-ipa' - - --def move_date(host, chrony_cmd, date_str): -- """Helper method to move system date -- :param host: host on which date is to be manipulated -- :param chrony_cmd: systemctl command to apply to -- chrony service, for instance 'start', 'stop' -- :param date_str: date string to change the date i.e '3years2months1day1' -- """ -- host.run_command(['systemctl', chrony_cmd, 'chronyd']) -- host.run_command(['date', '-s', date_str]) -- -- - def check_acme_status(host, exp_status, timeout=60): - """Helper method to check the status of acme server""" - for _i in range(0, timeout, 5): -@@ -598,8 +587,8 @@ class TestACMERenew(IntegrationTest): - ) - # move system date to expire acme cert - for host in self.clients[0], self.master: -- host.run_command(['kdestroy', '-A']) -- move_date(host, 'stop', '+90days') -+ tasks.kdestroy_all(host) -+ tasks.move_date(host, 'stop', '+90days') - self.clients[0].run_command( - ['kinit', 'admin'], - stdin_text=cmd_input.format( -@@ -611,8 +600,8 @@ class TestACMERenew(IntegrationTest): - - # move back date - for host in self.clients[0], self.master: -- host.run_command(['kdestroy', '-A']) -- move_date(host, 'start', '-90days') -+ tasks.kdestroy_all(host) -+ tasks.move_date(host, 'start', '-90days') - tasks.kinit_admin(host) - - @pytest.mark.skipif(skip_certbot_tests, reason='certbot not available') --- -2.31.1 - diff --git a/SOURCES/0030-test_acme-make-password-renewal-more-robust.patch b/SOURCES/0030-test_acme-make-password-renewal-more-robust.patch deleted file mode 100644 index f1e3334..0000000 --- a/SOURCES/0030-test_acme-make-password-renewal-more-robust.patch +++ /dev/null @@ -1,66 +0,0 @@ -From 701adb9185c77194ba1ad0c5fd2f13484417ef6f Mon Sep 17 00:00:00 2001 -From: =?UTF-8?q?Fran=C3=A7ois=20Cami?= -Date: Tue, 20 Jul 2021 20:22:23 +0200 -Subject: [PATCH] test_acme: make password renewal more robust -MIME-Version: 1.0 -Content-Type: text/plain; charset=UTF-8 -Content-Transfer-Encoding: 8bit - -A kinit immediately following a password change can fail. -Setting KRB5_TRACE and retrieving kdcinfo will help to understand -the cause of failure. - -Fixes: https://pagure.io/freeipa/issue/8929 -Signed-off-by: François Cami -Reviewed-By: Michal Polovka ---- - ipatests/test_integration/test_acme.py | 28 +++++++++++++------------- - 1 file changed, 14 insertions(+), 14 deletions(-) - -diff --git a/ipatests/test_integration/test_acme.py b/ipatests/test_integration/test_acme.py -index b4aa1b351..10195a95f 100644 ---- a/ipatests/test_integration/test_acme.py -+++ b/ipatests/test_integration/test_acme.py -@@ -576,25 +576,25 @@ class TestACMERenew(IntegrationTest): - # request a standalone acme cert - certbot_standalone_cert(self.clients[0], self.acme_server) - -- cmd_input = ( -- # Password for admin@{REALM}: -- "{pwd}\n" -- # Password expired. You must change it now. -- # Enter new password: -- "{pwd}\n" -- # Enter it again: -- "{pwd}\n" -- ) - # move system date to expire acme cert - for host in self.clients[0], self.master: - tasks.kdestroy_all(host) - tasks.move_date(host, 'stop', '+90days') -- self.clients[0].run_command( -- ['kinit', 'admin'], -- stdin_text=cmd_input.format( -- pwd=self.clients[0].config.admin_password -- ) -+ -+ tasks.get_kdcinfo(host) -+ # Note raiseonerr=False: -+ # the assert is located after kdcinfo retrieval. -+ result = host.run_command( -+ "KRB5_TRACE=/dev/stdout kinit %s" % 'admin', -+ stdin_text='{0}\n{0}\n{0}\n'.format( -+ self.clients[0].config.admin_password -+ ), -+ raiseonerr=False - ) -+ # Retrieve kdc.$REALM after the password change, just in case SSSD -+ # domain status flipped to online during the password change. -+ tasks.get_kdcinfo(host) -+ assert result.returncode == 0 - - yield - --- -2.31.1 - diff --git a/SOURCES/0031-tasks.py-fix-flake8-reported-issues.patch b/SOURCES/0031-tasks.py-fix-flake8-reported-issues.patch deleted file mode 100644 index 548f2f5..0000000 --- a/SOURCES/0031-tasks.py-fix-flake8-reported-issues.patch +++ /dev/null @@ -1,58 +0,0 @@ -From 5b826ab3582566b15a618f57cb2e002a9c16ef64 Mon Sep 17 00:00:00 2001 -From: =?UTF-8?q?Fran=C3=A7ois=20Cami?= -Date: Tue, 20 Jul 2021 20:29:00 +0200 -Subject: [PATCH] tasks.py: fix flake8-reported issues -MIME-Version: 1.0 -Content-Type: text/plain; charset=UTF-8 -Content-Transfer-Encoding: 8bit - -Fixes: https://pagure.io/freeipa/issue/8931 -Signed-off-by: François Cami -Reviewed-By: Michal Polovka ---- - ipatests/pytest_ipa/integration/tasks.py | 14 ++++++++------ - 1 file changed, 8 insertions(+), 6 deletions(-) - -diff --git a/ipatests/pytest_ipa/integration/tasks.py b/ipatests/pytest_ipa/integration/tasks.py -index c2e548617..075c05cde 100755 ---- a/ipatests/pytest_ipa/integration/tasks.py -+++ b/ipatests/pytest_ipa/integration/tasks.py -@@ -597,7 +597,9 @@ def install_adtrust(host): - dig_command = ['dig', 'SRV', '+short', '@localhost', - '_ldap._tcp.%s' % host.domain.name] - dig_output = '0 100 389 %s.' % host.hostname -- dig_test = lambda x: re.search(re.escape(dig_output), x) -+ -+ def dig_test(x): -+ return re.search(re.escape(dig_output), x) - - run_repeatedly(host, dig_command, test=dig_test) - -@@ -2122,8 +2124,8 @@ def create_active_user(host, login, password, first='test', last='user', - result = host.run_command( - "KRB5_TRACE=/dev/stdout kinit %s" % login, - stdin_text='{0}\n{1}\n{1}\n'.format( -- temp_password, password, raiseonerr=False -- ) -+ temp_password, password -+ ), raiseonerr=False - ) - # Retrieve kdc.$REALM after the password change, just in case SSSD - # domain status flipped to online during the password change. -@@ -2264,10 +2266,10 @@ class KerberosKeyCopier: - [paths.KLIST, "-eK", "-k", keytab], log_stdout=False) - - keys_to_sync = [] -- for l in result.stdout_text.splitlines(): -- if (princ in l and any(e in l for e in self.valid_etypes)): -+ for line in result.stdout_text.splitlines(): -+ if (princ in line and any(e in line for e in self.valid_etypes)): - -- els = l.split() -+ els = line.split() - els[-2] = els[-2].strip('()') - els[-1] = els[-1].strip('()') - keys_to_sync.append(KeyEntry._make(els)) --- -2.31.1 - diff --git a/SOURCES/0032-Fix-ldapupdate.get_sub_dict-for-missing-named-user.patch b/SOURCES/0032-Fix-ldapupdate.get_sub_dict-for-missing-named-user.patch deleted file mode 100644 index e48eddf..0000000 --- a/SOURCES/0032-Fix-ldapupdate.get_sub_dict-for-missing-named-user.patch +++ /dev/null @@ -1,142 +0,0 @@ -From a1eb13cdbc109da8c028bb886a1207ea2cc23cee Mon Sep 17 00:00:00 2001 -From: Christian Heimes -Date: Tue, 27 Jul 2021 11:54:20 +0200 -Subject: [PATCH] Fix ldapupdate.get_sub_dict() for missing named user -MIME-Version: 1.0 -Content-Type: text/plain; charset=UTF-8 -Content-Transfer-Encoding: 8bit - -The named user may not be present when ipa-server-dns and bind are not -installed. NAMED_UID and NAMED_GID constants are only used with local -DNS support. - -Fixes: https://pagure.io/freeipa/issue/8936 -Signed-off-by: Christian Heimes -Co-authored-by: François Cami -Reviewed-By: Francois Cami -Reviewed-By: Rob Crittenden -Reviewed-By: Francois Cami -Reviewed-By: Rob Crittenden ---- - ipaserver/install/ldapupdate.py | 14 +++++++--- - .../nightly_ipa-4-9_latest.yaml | 12 +++++++++ - .../nightly_ipa-4-9_previous.yaml | 12 +++++++++ - .../test_integration/test_installation.py | 27 +++++++++++++++++++ - 4 files changed, 62 insertions(+), 3 deletions(-) - -diff --git a/ipaserver/install/ldapupdate.py b/ipaserver/install/ldapupdate.py -index 06cb78e0b..f0e7d6162 100644 ---- a/ipaserver/install/ldapupdate.py -+++ b/ipaserver/install/ldapupdate.py -@@ -64,6 +64,15 @@ def get_sub_dict(realm, domain, suffix, fqdn, idstart=None, idmax=None): - idrange_size = idmax - idstart + 1 - subid_base_rid = constants.SUBID_RANGE_START - idrange_size - -+ # uid / gid for autobind -+ # user is only defined when ipa-server-dns and bind are installed -+ try: -+ named_uid = platformconstants.NAMED_USER.uid -+ named_gid = platformconstants.NAMED_GROUP.gid -+ except ValueError: -+ named_uid = None -+ named_gid = None -+ - return dict( - REALM=realm, - DOMAIN=domain, -@@ -99,9 +108,8 @@ def get_sub_dict(realm, domain, suffix, fqdn, idstart=None, idmax=None): - DEFAULT_ADMIN_SHELL=platformconstants.DEFAULT_ADMIN_SHELL, - SELINUX_USERMAP_DEFAULT=platformconstants.SELINUX_USERMAP_DEFAULT, - SELINUX_USERMAP_ORDER=platformconstants.SELINUX_USERMAP_ORDER, -- # uid / gid for autobind -- NAMED_UID=platformconstants.NAMED_USER.uid, -- NAMED_GID=platformconstants.NAMED_GROUP.gid, -+ NAMED_UID=named_uid, -+ NAMED_GID=named_gid, - ) - - -diff --git a/ipatests/prci_definitions/nightly_ipa-4-9_latest.yaml b/ipatests/prci_definitions/nightly_ipa-4-9_latest.yaml -index 939ee2b7d..1c8c5ddfc 100644 ---- a/ipatests/prci_definitions/nightly_ipa-4-9_latest.yaml -+++ b/ipatests/prci_definitions/nightly_ipa-4-9_latest.yaml -@@ -547,6 +547,18 @@ jobs: - timeout: 4800 - topology: *master_1repl_1client - -+ fedora-latest-ipa-4-9/test_installation_TestInstallWithoutNamed: -+ 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_integration/test_installation.py::TestInstallWithoutNamed -+ template: *ci-ipa-4-9-latest -+ timeout: 4800 -+ topology: *master_1repl -+ - fedora-latest-ipa-4-9/test_idviews: - requires: [fedora-latest-ipa-4-9/build] - priority: 50 -diff --git a/ipatests/prci_definitions/nightly_ipa-4-9_previous.yaml b/ipatests/prci_definitions/nightly_ipa-4-9_previous.yaml -index 03658a934..6d121d59f 100644 ---- a/ipatests/prci_definitions/nightly_ipa-4-9_previous.yaml -+++ b/ipatests/prci_definitions/nightly_ipa-4-9_previous.yaml -@@ -547,6 +547,18 @@ jobs: - timeout: 4800 - topology: *master_1repl_1client - -+ fedora-previous-ipa-4-9/test_installation_TestInstallWithoutNamed: -+ 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_integration/test_installation.py::TestInstallWithoutNamed -+ template: *ci-ipa-4-9-previous -+ timeout: 4800 -+ topology: *master_1repl -+ - fedora-previous-ipa-4-9/test_idviews: - requires: [fedora-previous-ipa-4-9/build] - priority: 50 -diff --git a/ipatests/test_integration/test_installation.py b/ipatests/test_integration/test_installation.py -index e76fd0efe..e3c41eaa1 100644 ---- a/ipatests/test_integration/test_installation.py -+++ b/ipatests/test_integration/test_installation.py -@@ -1853,3 +1853,30 @@ class TestInstallWithoutSudo(IntegrationTest): - result = tasks.install_client(self.master, self.clients[0]) - assert self.no_sudo_str not in result.stderr_text - assert self.sudo_version_str not in result.stdout_text -+ -+ -+class TestInstallWithoutNamed(IntegrationTest): -+ num_replicas = 1 -+ -+ @classmethod -+ def remove_named(cls, host): -+ # remove the bind package and make sure the named user does not exist. -+ # https://pagure.io/freeipa/issue/8936 -+ result = host.run_command(['id', 'named'], raiseonerr=False) -+ if result.returncode == 0: -+ tasks.uninstall_packages(host, ['bind']) -+ host.run_command(['userdel', constants.NAMED_USER]) -+ assert host.run_command( -+ ['id', 'named'], raiseonerr=False -+ ).returncode == 1 -+ -+ @classmethod -+ def install(cls, mh): -+ for tgt in (cls.master, cls.replicas[0]): -+ cls.remove_named(tgt) -+ tasks.install_master(cls.master, setup_dns=False) -+ -+ def test_replica0_install(self): -+ tasks.install_replica( -+ self.master, self.replicas[0], setup_ca=False, setup_dns=False -+ ) --- -2.31.1 - diff --git a/SOURCES/0033-freeipa.spec.in-remove-python3-pexpect-from-Requires.patch b/SOURCES/0033-freeipa.spec.in-remove-python3-pexpect-from-Requires.patch deleted file mode 100644 index 31489e5..0000000 --- a/SOURCES/0033-freeipa.spec.in-remove-python3-pexpect-from-Requires.patch +++ /dev/null @@ -1,68 +0,0 @@ -From e0e1d6f94dd16c8066be8ce3c75ef306890a3e2b Mon Sep 17 00:00:00 2001 -From: =?UTF-8?q?Fran=C3=A7ois=20Cami?= -Date: Wed, 28 Jul 2021 18:47:02 +0200 -Subject: [PATCH] freeipa.spec.in: remove python3-pexpect from Requires -MIME-Version: 1.0 -Content-Type: text/plain; charset=UTF-8 -Content-Transfer-Encoding: 8bit - -python3-pexpect will be removed in RHEL9. -Update BuildRequires/Requires accordingly. - -Fixes: https://pagure.io/freeipa/issue/8938 -Signed-off-by: François Cami -Reviewed-By: Rob Crittenden -Reviewed-By: Antonio Torres ---- - freeipa.spec.in | 14 ++++++++++---- - 1 file changed, 10 insertions(+), 4 deletions(-) - -diff --git a/freeipa.spec.in b/freeipa.spec.in -index c33d2e216..9440f3602 100755 ---- a/freeipa.spec.in -+++ b/freeipa.spec.in -@@ -328,11 +328,18 @@ BuildRequires: python3-m2r - # Build dependencies for lint and fastcheck - # - %if %{with lint} --BuildRequires: git --%if 0%{?fedora} < 34 -+ -+# python3-pexpect might not be available in RHEL9 -+%if 0%{?fedora} || 0%{?rhel} < 9 -+BuildRequires: python3-pexpect -+%endif -+ - # jsl is orphaned in Fedora 34+ -+%if 0%{?fedora} < 34 - BuildRequires: jsl - %endif -+ -+BuildRequires: git - BuildRequires: nss-tools - BuildRequires: rpmlint - BuildRequires: softhsm -@@ -357,7 +364,6 @@ BuildRequires: python3-lxml - BuildRequires: python3-netaddr >= %{python_netaddr_version} - BuildRequires: python3-netifaces - BuildRequires: python3-paste --BuildRequires: python3-pexpect - BuildRequires: python3-pki >= %{pki_version} - BuildRequires: python3-polib - BuildRequires: python3-pyasn1 -@@ -878,11 +884,11 @@ Requires: python3-ipaclient = %{version}-%{release} - Requires: python3-ipaserver = %{version}-%{release} - Requires: iptables - Requires: python3-cryptography >= 1.6 --Requires: python3-pexpect - %if 0%{?fedora} - # These packages do not exist on RHEL and for ipatests use - # they are installed on the controller through other means - Requires: ldns-utils -+Requires: python3-pexpect - # update-crypto-policies - Requires: crypto-policies-scripts - Requires: python3-polib --- -2.31.1 - diff --git a/SOURCES/0034-ipa-getkeytab-add-option-to-discover-servers-using-D.patch b/SOURCES/0034-ipa-getkeytab-add-option-to-discover-servers-using-D.patch deleted file mode 100644 index 798f9d2..0000000 --- a/SOURCES/0034-ipa-getkeytab-add-option-to-discover-servers-using-D.patch +++ /dev/null @@ -1,365 +0,0 @@ -From 42206df69adc9c1eefa3ee576891b2ae3ac269e0 Mon Sep 17 00:00:00 2001 -From: Rob Crittenden -Date: Thu, 15 Jul 2021 15:11:28 -0400 -Subject: [PATCH] ipa-getkeytab: add option to discover servers using DNS SRV - -The basic flow is: - -- If server is provided by the user then use it -- If server the magic value '_srv', check for _ldap._tcp SRV records for - the domain in /etc/ipa/default.conf -- If no servers are found use the server from default.conf - -https://pagure.io/freeipa/issue/8478 - -Signed-off-by: Rob Crittenden -Reviewed-By: Alexander Bokovoy -Reviewed-By: Alexander Bokovoy -Reviewed-By: Florence Blanc-Renaud ---- - client/Makefile.am | 1 + - client/ipa-getkeytab.c | 221 +++++++++++++++++++++++++++++++++++++ - client/man/ipa-getkeytab.1 | 5 +- - configure.ac | 10 ++ - 4 files changed, 236 insertions(+), 1 deletion(-) - -diff --git a/client/Makefile.am b/client/Makefile.am -index 0031c04a5..72f4cb3dc 100644 ---- a/client/Makefile.am -+++ b/client/Makefile.am -@@ -66,6 +66,7 @@ ipa_getkeytab_LDADD = \ - $(SASL_LIBS) \ - $(POPT_LIBS) \ - $(LIBINTL_LIBS) \ -+ $(RESOLV_LIBS) \ - $(INI_LIBS) \ - $(NULL) - -diff --git a/client/ipa-getkeytab.c b/client/ipa-getkeytab.c -index 04786be9e..d3673eb05 100644 ---- a/client/ipa-getkeytab.c -+++ b/client/ipa-getkeytab.c -@@ -34,9 +34,11 @@ - #include - #include - #include -+#include - #include - #include - #include -+#include - - #include "config.h" - -@@ -46,6 +48,174 @@ - #include "ipa_ldap.h" - - -+struct srvrec { -+ char *host; -+ uint16_t port; -+ int priority, weight; -+ struct srvrec *next; -+}; -+ -+static int -+srvrec_priority_sort(const void *a, const void *b) -+{ -+ const struct srvrec *sa, *sb; -+ -+ sa = a; -+ sb = b; -+ return sa->priority - sb->priority; -+} -+ -+static int -+srvrec_sort_weight(const void *a, const void *b) -+{ -+ const struct srvrec *sa, *sb; -+ -+ sa = a; -+ sb = b; -+ return sa->weight - sb->weight; -+} -+ -+/* Return a uniform random number between 0 and range */ -+static double -+rand_inclusive(double range) -+{ -+ long long r; -+ -+ if (range == 0) { -+ return 0; -+ } -+ -+ if (RAND_bytes((unsigned char *) &r, sizeof(r)) == -1) { -+ return 0; -+ } -+ if (r < 0) { -+ r = -r; -+ } -+ return ((double)r / (double)LLONG_MAX) * range; -+} -+ -+static void -+sort_prio_weight(struct srvrec *res, int len) -+{ -+ int i, j; -+ double tweight; -+ struct srvrec tmp; -+ double r; -+ -+ qsort(res, len, sizeof(res[0]), srvrec_sort_weight); -+ for (i = 0; i < len - 1; i++) { -+ tweight = 0; -+ for (j = i; j < len; j++) { -+ /* Give records with 0 weight a small chance */ -+ tweight += res[j].weight ? res[j].weight : 0.01; -+ } -+ r = rand_inclusive(tweight); -+ tweight = 0; -+ for (j = i; j < len; j++) { -+ tweight += res[j].weight ? res[j].weight : 0.01; -+ if (tweight >= r) { -+ break; -+ } -+ } -+ if (j >= len) { -+ continue; -+ } -+ memcpy(&tmp, &res[i], sizeof(tmp)); -+ memcpy(&res[i], &res[j], sizeof(tmp)); -+ memcpy(&res[j], &tmp, sizeof(tmp)); -+ } -+} -+ -+/* The caller is responsible for freeing the results */ -+static int -+query_srv(const char *name, const char *domain, struct srvrec **results) -+{ -+ int i, j, len; -+ unsigned char *answer = NULL; -+ size_t answer_len = NS_MAXMSG; -+ struct srvrec *res = NULL; -+ ns_msg msg; -+ ns_rr rr; -+ int rv = -1; -+ -+ *results = NULL; -+ if ((name == NULL) || (strlen(name) == 0) || -+ (domain == NULL) || (strlen(domain) == 0)) { -+ return -1; -+ } -+ -+ res_init(); -+ answer = malloc(answer_len + 1); -+ if (answer == NULL) { -+ return -1; -+ } -+ memset(answer, 0, answer_len + 1); -+ i = res_querydomain(name, domain, C_IN, T_SRV, answer, answer_len); -+ if (i == -1) { -+ goto error; -+ } -+ answer_len = i; -+ memset(&msg, 0, sizeof(msg)); -+ if (ns_initparse(answer, answer_len, &msg) != 0) { -+ goto error; -+ } -+ memset(&rr, 0, sizeof(rr)); -+ for (i = 0; ns_parserr(&msg, ns_s_an, i, &rr) == 0; i++) { -+ continue; -+ } -+ if (i == 0) { -+ goto error; -+ } -+ len = i; -+ res = malloc(sizeof(*res) * i); -+ if (res == NULL) { -+ goto error; -+ } -+ memset(res, 0, sizeof(*res) * i); -+ for (i = 0, j = 0; i < len; i++) { -+ if (ns_parserr(&msg, ns_s_an, i, &rr) != 0) { -+ continue; -+ } -+ if (rr.rdlength < 6) { -+ continue; -+ } -+ res[j].host = malloc(rr.rdlength - 6 + 1); -+ if (res[j].host == NULL) { -+ goto error; -+ } -+ res[j].priority = ntohs(*(uint16_t *)rr.rdata); -+ res[j].weight = ntohs(*(uint16_t *)(rr.rdata + 2)); -+ res[j].port = ntohs(*(uint16_t *)(rr.rdata + 4)); -+ memcpy(res[j].host, rr.rdata + 6, rr.rdlength - 6); -+ if (ns_name_ntop(rr.rdata + 6, res[j].host, rr.rdlength - 6) == -1) { -+ continue; -+ } -+ res[j].host[rr.rdlength - 6] = '\0'; -+ j++; -+ } -+ len = j; -+ qsort(res, len, sizeof(res[0]), srvrec_priority_sort); -+ i = 0; -+ while (i < len) { -+ j = i + 1; -+ while (j < len && (res[j].priority == res[i].priority)) { -+ j++; -+ } -+ sort_prio_weight(res + i, j - i); -+ i = j; -+ } -+ /* Fixup the linked-list pointers */ -+ for (i = 0; i < len - 1; i++) { -+ res[i].next = &res[i + 1]; -+ } -+ *results = res; -+ rv = 0; -+ -+error: -+ free(answer); -+ return rv; -+} -+ - static int check_sasl_mech(const char *mech) - { - int i; -@@ -619,6 +789,7 @@ static char *ask_password(krb5_context krbctx, char *prompt1, char *prompt2, - - struct ipa_config { - const char *server_name; -+ const char *domain; - }; - - static int config_from_file(struct ini_cfgobj *cfgctx) -@@ -688,6 +859,11 @@ int read_ipa_config(struct ipa_config **ipacfg) - if (ret == 0 && obj != NULL) { - (*ipacfg)->server_name = ini_get_string_config_value(obj, &ret); - } -+ ret = ini_get_config_valueobj("global", "domain", cfgctx, -+ INI_GET_LAST_VALUE, &obj); -+ if (ret == 0 && obj != NULL) { -+ (*ipacfg)->domain = ini_get_string_config_value(obj, &ret); -+ } - - return 0; - } -@@ -754,6 +930,7 @@ int main(int argc, const char *argv[]) - static const char *sasl_mech = NULL; - static const char *ca_cert_file = NULL; - int quiet = 0; -+ int verbose = 0; - int askpass = 0; - int askbindpw = 0; - int permitted_enctypes = 0; -@@ -761,6 +938,8 @@ int main(int argc, const char *argv[]) - struct poptOption options[] = { - { "quiet", 'q', POPT_ARG_NONE, &quiet, 0, - _("Print as little as possible"), _("Output only on errors")}, -+ { "verbose", 'v', POPT_ARG_NONE, &verbose, 0, -+ _("Print debugging information"), _("Output debug info")}, - { "server", 's', POPT_ARG_STRING, &server, 0, - _("Contact this specific KDC Server"), - _("Server Name") }, -@@ -906,6 +1085,41 @@ int main(int argc, const char *argv[]) - exit(2); - } - -+ if (server && (strcasecmp(server, "_srv_") == 0)) { -+ struct srvrec *srvrecs, *srv; -+ struct ipa_config *ipacfg = NULL; -+ -+ ret = read_ipa_config(&ipacfg); -+ if (ret == 0 && ipacfg->domain && verbose) { -+ fprintf(stderr, _("DNS discovery for domain %s\n"), ipacfg->domain); -+ } -+ if (query_srv("_ldap._tcp", ipacfg->domain, &srvrecs) == 0) { -+ for (srv = srvrecs; (srv != NULL); srv = srv->next) { -+ if (verbose) { -+ fprintf(stderr, _("Discovered server %s\n"), srv->host); -+ } -+ } -+ for (srv = srvrecs; (srv != NULL); srv = srv->next) { -+ server = strdup(srv->host); -+ if (verbose) { -+ fprintf(stderr, _("Using discovered server %s\n"), server); -+ } -+ break; -+ } -+ for (srv = srvrecs; (srv != NULL); srv = srv->next) { -+ free(srv->host); -+ } -+ } else { -+ if (verbose) { -+ fprintf(stderr, _("DNS Discovery failed\n")); -+ } -+ } -+ if (strcasecmp(server, "_srv_") == 0) { -+ /* Discovery failed, fall through to option methods */ -+ server = NULL; -+ } -+ } -+ - if (!server && !ldap_uri) { - struct ipa_config *ipacfg = NULL; - -@@ -915,10 +1129,17 @@ int main(int argc, const char *argv[]) - ipacfg->server_name = NULL; - } - free(ipacfg); -+ if (verbose && server) { -+ fprintf(stderr, _("Using server from config %s\n"), server); -+ } - if (!server) { - fprintf(stderr, _("Server name not provided and unavailable\n")); - exit(2); - } -+ } else { -+ if (verbose) { -+ fprintf(stderr, _("Using provided server %s\n"), server); -+ } - } - if (server) { - ret = ipa_server_to_uri(server, sasl_mech, &ldap_uri); -diff --git a/client/man/ipa-getkeytab.1 b/client/man/ipa-getkeytab.1 -index b57c5489c..07d2d73b3 100644 ---- a/client/man/ipa-getkeytab.1 -+++ b/client/man/ipa-getkeytab.1 -@@ -78,7 +78,10 @@ arcfour\-hmac - \fB\-s ipaserver\fR - The IPA server to retrieve the keytab from (FQDN). If this option is not - provided the server name is read from the IPA configuration file --(/etc/ipa/default.conf). Cannot be used together with \fB\-H\fR. -+(/etc/ipa/default.conf). Cannot be used together with \fB\-H\fR. If the -+value is _srv_ then DNS discovery will be used to determine a server. -+If this discovery fails then it will fall back to using the configuration -+file. - .TP - \fB\-q\fR - Quiet mode. Only errors are displayed. -diff --git a/configure.ac b/configure.ac -index dc79d5dce..9d7a33825 100644 ---- a/configure.ac -+++ b/configure.ac -@@ -108,6 +108,16 @@ LDAP_CFLAGS="" - AC_SUBST(LDAP_LIBS) - AC_SUBST(LDAP_CFLAGS) - -+dnl --------------------------------------------------------------------------- -+dnl - Check for resolv library -+dnl --------------------------------------------------------------------------- -+ -+SAVE_CPPFLAGS=$CPPFLAGS -+CPPFLAGS="$NSPR_CFLAGS $NSS_CFLAGS" -+AC_CHECK_LIB(resolv,main,RESOLV_LIBS=-lresolv) -+AC_CHECK_HEADERS(resolv.h) -+AC_SUBST(RESOLV_LIBS) -+ - dnl --------------------------------------------------------------------------- - dnl - Check for OpenSSL Crypto library - dnl --------------------------------------------------------------------------- --- -2.31.1 - diff --git a/SOURCES/0035-ipa-getkeytab-fix-compiler-warnings.patch b/SOURCES/0035-ipa-getkeytab-fix-compiler-warnings.patch deleted file mode 100644 index 734e8e9..0000000 --- a/SOURCES/0035-ipa-getkeytab-fix-compiler-warnings.patch +++ /dev/null @@ -1,55 +0,0 @@ -From 0114d24ea160676b784ef7010c19bbacc67ceea0 Mon Sep 17 00:00:00 2001 -From: Rob Crittenden -Date: Thu, 15 Jul 2021 17:52:54 -0400 -Subject: [PATCH] ipa-getkeytab: fix compiler warnings - -Make read_ipa_config and filter_keys static to avoid -"no previous prototype" warnings. - -Use correct datatype of return value for ber_scanf to -correct different signedness comparision. - -Fixed while working on https://pagure.io/freeipa/issue/8478 - -Signed-off-by: Rob Crittenden -Reviewed-By: Alexander Bokovoy -Reviewed-By: Alexander Bokovoy -Reviewed-By: Florence Blanc-Renaud ---- - client/ipa-getkeytab.c | 6 +++--- - 1 file changed, 3 insertions(+), 3 deletions(-) - -diff --git a/client/ipa-getkeytab.c b/client/ipa-getkeytab.c -index d3673eb05..309b3c704 100644 ---- a/client/ipa-getkeytab.c -+++ b/client/ipa-getkeytab.c -@@ -291,7 +291,7 @@ static int ldap_sasl_interact(LDAP *ld, unsigned flags, void *priv_data, void *s - return ret; - } - --int filter_keys(krb5_context krbctx, struct keys_container *keys, -+static int filter_keys(krb5_context krbctx, struct keys_container *keys, - ber_int_t *enctypes) - { - struct krb_key_salt *ksdata; -@@ -507,7 +507,7 @@ static int ldap_set_keytab(krb5_context krbctx, - BerElement *sctrl = NULL; - struct berval *control = NULL; - LDAPControl **srvctrl = NULL; -- int ret; -+ ber_tag_t ret; - int kvno, i; - ber_tag_t rtag; - ber_int_t *encs = NULL; -@@ -826,7 +826,7 @@ static int config_from_file(struct ini_cfgobj *cfgctx) - return 0; - } - --int read_ipa_config(struct ipa_config **ipacfg) -+static int read_ipa_config(struct ipa_config **ipacfg) - { - struct ini_cfgobj *cfgctx = NULL; - struct value_obj *obj = NULL; --- -2.31.1 - diff --git a/SOURCES/0036-ipatests-test-ipa-getkeytab-server-option.patch b/SOURCES/0036-ipatests-test-ipa-getkeytab-server-option.patch deleted file mode 100644 index 0b3e60c..0000000 --- a/SOURCES/0036-ipatests-test-ipa-getkeytab-server-option.patch +++ /dev/null @@ -1,92 +0,0 @@ -From 7a13200fd8b92dd90ebc4b6416ef25659df8aa71 Mon Sep 17 00:00:00 2001 -From: Rob Crittenden -Date: Fri, 16 Jul 2021 12:59:47 -0400 -Subject: [PATCH] ipatests: test ipa-getkeytab server option - -Test various usages of the -s/--server option: -* -s is defined, use it as the server -* no -s, use the host value from /etc/ipa/default.conf -* -s is '_srv_', do DNS discovery - -https://pagure.io/freeipa/issue/8478 - -Signed-off-by: Rob Crittenden -Reviewed-By: Alexander Bokovoy -Reviewed-By: Alexander Bokovoy -Reviewed-By: Florence Blanc-Renaud ---- - ipatests/test_integration/test_commands.py | 58 ++++++++++++++++++++++ - 1 file changed, 58 insertions(+) - -diff --git a/ipatests/test_integration/test_commands.py b/ipatests/test_integration/test_commands.py -index d64519eb7..2035ced56 100644 ---- a/ipatests/test_integration/test_commands.py -+++ b/ipatests/test_integration/test_commands.py -@@ -1467,6 +1467,64 @@ class TestIPACommand(IntegrationTest): - assert 'This account is currently not available' in \ - result.stdout_text - -+ def test_ipa_getkeytab_server(self): -+ """ -+ Exercise the ipa-getkeytab server options -+ -+ This relies on the behavior that without a TGT -+ ipa-getkeytab will quit and not do much of anything. -+ -+ A bogus keytab and principal are passed in to satisfy the -+ minimum requirements. -+ """ -+ tasks.kdestroy_all(self.master) -+ -+ # Pass in a server name to use -+ result = self.master.run_command( -+ [ -+ paths.IPA_GETKEYTAB, -+ "-k", -+ "/tmp/keytab", -+ "-p", -+ "foo", -+ "-s", -+ self.master.hostname, -+ "-v", -+ ], raiseonerr=False).stderr_text -+ -+ assert 'Using provided server %s' % self.master.hostname in result -+ -+ # Don't pass in a name, should use /etc/ipa/default.conf -+ result = self.master.run_command( -+ [ -+ paths.IPA_GETKEYTAB, -+ "-k", -+ "/tmp/keytab", -+ "-p", -+ "foo", -+ "-v", -+ ], raiseonerr=False).stderr_text -+ -+ assert ( -+ 'Using server from config %s' % self.master.hostname -+ in result -+ ) -+ -+ # Use DNS SRV lookup -+ result = self.master.run_command( -+ [ -+ paths.IPA_GETKEYTAB, -+ "-k", -+ "/tmp/keytab", -+ "-p", -+ "foo", -+ "-s", -+ "_srv_", -+ "-v", -+ ], raiseonerr=False).stderr_text -+ -+ assert 'Discovered server %s' % self.master.hostname in result -+ - - class TestIPACommandWithoutReplica(IntegrationTest): - """ --- -2.31.1 - diff --git a/SOURCES/0037-ipatests-Test-for-OTP-when-the-LDAP-connection-timed.patch b/SOURCES/0037-ipatests-Test-for-OTP-when-the-LDAP-connection-timed.patch deleted file mode 100644 index d24f83c..0000000 --- a/SOURCES/0037-ipatests-Test-for-OTP-when-the-LDAP-connection-timed.patch +++ /dev/null @@ -1,91 +0,0 @@ -From 25a4acf3ad5964eacddbcb83ddf9f84432968918 Mon Sep 17 00:00:00 2001 -From: Anuja More -Date: Thu, 22 Jul 2021 14:55:50 +0530 -Subject: [PATCH] ipatests: Test for OTP when the LDAP connection timed out. - -Test to verify that when the idle timeout is exceeded (30s idle, -60s sleep) then the ipa-otpd process should exit without error. - -Related : https://pagure.io/freeipa/issue/6587 - -Signed-off-by: Anuja More -Reviewed-By: Mohammad Rizwan -Reviewed-By: Rob Crittenden ---- - ipatests/test_integration/test_otp.py | 56 +++++++++++++++++++++++++++ - 1 file changed, 56 insertions(+) - -diff --git a/ipatests/test_integration/test_otp.py b/ipatests/test_integration/test_otp.py -index b2e65af1b..fd55898ca 100644 ---- a/ipatests/test_integration/test_otp.py -+++ b/ipatests/test_integration/test_otp.py -@@ -20,6 +20,7 @@ from cryptography.hazmat.primitives.twofactor.totp import TOTP - from ipatests.test_integration.base import IntegrationTest - from ipaplatform.paths import paths - from ipatests.pytest_ipa.integration import tasks -+from ipapython.dn import DN - - - PASSWORD = "DummyPassword123" -@@ -309,3 +310,58 @@ class TestOTPToken(IntegrationTest): - master.run_command(['ipa', 'user-del', USER2]) - self.master.run_command(['semanage', 'login', '-D']) - sssd_conf_backup.restore() -+ -+ @pytest.fixture -+ def setup_otp_nsslapd(self): -+ # setting nsslapd-idletimeout -+ new_limit = 30 -+ conn = self.master.ldap_connect() -+ dn = DN(('cn', 'config')) -+ entry = conn.get_entry(dn) # pylint: disable=no-member -+ orig_limit = entry.single_value.get('nsslapd-idletimeout') -+ ldap_query = textwrap.dedent(""" -+ dn: cn=config -+ changetype: modify -+ replace: nsslapd-idletimeout -+ nsslapd-idletimeout: {limit} -+ """) -+ tasks.ldapmodify_dm(self.master, ldap_query.format(limit=new_limit)) -+ # Be sure no services are running and failed units -+ self.master.run_command(['killall', 'ipa-otpd'], raiseonerr=False) -+ check_services = self.master.run_command( -+ ['systemctl', 'list-units', '--state=failed'] -+ ) -+ assert "0 loaded units listed" in check_services.stdout_text -+ assert "ipa-otpd" not in check_services.stdout_text -+ yield -+ # cleanup -+ tasks.ldapmodify_dm(self.master, ldap_query.format(limit=orig_limit)) -+ -+ def test_check_otpd_after_idle_timeout(self, setup_otp_nsslapd): -+ """Test for OTP when the LDAP connection timed out. -+ -+ Test for : https://pagure.io/freeipa/issue/6587 -+ -+ ipa-otpd was exiting with failure when LDAP connection timed out. -+ Test to verify that when the nsslapd-idletimeout is exceeded (30s idle, -+ 60s sleep) then the ipa-otpd process should exit without error. -+ """ -+ since = time.strftime('%H:%M:%S') -+ tasks.kinit_admin(self.master) -+ otpuid, totp = add_otptoken(self.master, USER, otptype="totp") -+ try: -+ # kinit with OTP auth -+ otpvalue = totp.generate(int(time.time())).decode("ascii") -+ kinit_otp(self.master, USER, password=PASSWORD, otp=otpvalue) -+ time.sleep(60) -+ failed_services = self.master.run_command( -+ ['systemctl', 'list-units', '--state=failed'] -+ ) -+ assert "ipa-otpd" not in failed_services.stdout_text -+ cmd_jornalctl = self.master.run_command( -+ ['journalctl', '--since={}'.format(since)] -+ ) -+ regex = r".*ipa-otpd@.*\sSucceeded" -+ assert re.search(regex, cmd_jornalctl.stdout_text) -+ finally: -+ del_otptoken(self.master, otpuid) --- -2.31.1 - diff --git a/SOURCES/0038-ipatests-verify-that-getcert-output-includes-the-iss.patch b/SOURCES/0038-ipatests-verify-that-getcert-output-includes-the-iss.patch deleted file mode 100644 index af90a1f..0000000 --- a/SOURCES/0038-ipatests-verify-that-getcert-output-includes-the-iss.patch +++ /dev/null @@ -1,51 +0,0 @@ -From 826b5825bd644fc69a9bee17626d71fe03cc0190 Mon Sep 17 00:00:00 2001 -From: Rob Crittenden -Date: Mon, 26 Jul 2021 16:14:19 -0400 -Subject: [PATCH] ipatests: verify that getcert output includes the issued date - -certmonger 0.79.14 included a new feature that provides the -NotBefore (or issued) date to the certificate list output. - -Verify that it is present in the output. - -https://bugzilla.redhat.com/show_bug.cgi?id=1940261 - -Signed-off-by: Rob Crittenden -Reviewed-By: Mohammad Rizwan -Reviewed-By: Florence Blanc-Renaud ---- - ipatests/test_integration/test_cert.py | 11 +++++++++++ - 1 file changed, 11 insertions(+) - -diff --git a/ipatests/test_integration/test_cert.py b/ipatests/test_integration/test_cert.py -index b6bb2f08a..9a90db5e2 100644 ---- a/ipatests/test_integration/test_cert.py -+++ b/ipatests/test_integration/test_cert.py -@@ -19,6 +19,7 @@ from ipaplatform.paths import paths - from cryptography import x509 - from cryptography.x509.oid import ExtensionOID - from cryptography.hazmat.backends import default_backend -+from pkg_resources import parse_version - - from ipatests.pytest_ipa.integration import tasks - from ipatests.test_integration.base import IntegrationTest -@@ -257,6 +258,16 @@ class TestInstallMasterClient(IntegrationTest): - raise AssertionError("certmonger request is " - "in state {}". format(status)) - -+ def test_getcert_notafter_output(self): -+ """Test that currrent certmonger includes NotBefore in output""" -+ result = self.master.run_command(["certmonger", "-v"]).stdout_text -+ if parse_version(result.split()[1]) < parse_version('0.79.14'): -+ raise pytest.skip("not_before not provided in this version") -+ result = self.master.run_command( -+ ["getcert", "list", "-f", paths.HTTPD_CERT_FILE] -+ ).stdout_text -+ assert 'issued:' in result -+ - - class TestCertmongerRekey(IntegrationTest): - --- -2.31.1 - diff --git a/SOURCES/0039-ipatests-Look-for-warning-into-stderr-instead-of-std.patch b/SOURCES/0039-ipatests-Look-for-warning-into-stderr-instead-of-std.patch deleted file mode 100644 index a231506..0000000 --- a/SOURCES/0039-ipatests-Look-for-warning-into-stderr-instead-of-std.patch +++ /dev/null @@ -1,43 +0,0 @@ -From 96dd8ac1cd2e7fb8177d83e7ba5c6d79f4216ea3 Mon Sep 17 00:00:00 2001 -From: Mohammad Rizwan -Date: Mon, 2 Aug 2021 19:26:28 +0530 -Subject: [PATCH] ipatests: Look for warning into stderr instead of stdout - -In https://github.com/freeipa/freeipa/pull/5855 was looking -into stdout_text for warning instead of stderr_text, hence -was failing for pki version > 10.11.0. - -related: https://pagure.io/freeipa/issue/8890 - -Signed-off-by: Mohammad Rizwan -Reviewed-By: Florence Blanc-Renaud ---- - ipatests/test_integration/test_ipa_cert_fix.py | 6 ++---- - 1 file changed, 2 insertions(+), 4 deletions(-) - -diff --git a/ipatests/test_integration/test_ipa_cert_fix.py b/ipatests/test_integration/test_ipa_cert_fix.py -index 394e85603..f3cf59afc 100644 ---- a/ipatests/test_integration/test_ipa_cert_fix.py -+++ b/ipatests/test_integration/test_ipa_cert_fix.py -@@ -241,16 +241,14 @@ class TestIpaCertFix(IntegrationTest): - # check that pki-server cert-fix command fails - err_msg2 = ("ERROR: CalledProcessError(Command " - "['pki-server', 'cert-fix'") -- warn_msg = ("WARNING: No selftests configured in " -- f"{paths.CA_CS_CFG_PATH} " -- "(selftests.container.order.startup)") -+ warn_msg = "WARNING: No selftests configured in" - - if (tasks.get_pki_version(self.master) - < tasks.parse_version('10.11.0')): - assert (err_msg1 in result.stderr_text - and err_msg2 in result.stderr_text) - else: -- assert warn_msg in result.stdout_text -+ assert warn_msg in result.stderr_text - - def test_expired_CA_cert(self, expire_ca_cert): - """Test to check ipa-cert-fix when CA certificate is expired --- -2.31.1 - diff --git a/SOURCES/0040-ipatests-use-krb5_trace-in-TestIpaAdTrustInstall.patch b/SOURCES/0040-ipatests-use-krb5_trace-in-TestIpaAdTrustInstall.patch deleted file mode 100644 index 56e1db3..0000000 --- a/SOURCES/0040-ipatests-use-krb5_trace-in-TestIpaAdTrustInstall.patch +++ /dev/null @@ -1,56 +0,0 @@ -From 9ae23e1257478bfee04b08b54f36dda7f5850348 Mon Sep 17 00:00:00 2001 -From: =?UTF-8?q?Fran=C3=A7ois=20Cami?= -Date: Thu, 5 Aug 2021 11:37:35 +0200 -Subject: [PATCH] ipatests: use krb5_trace in TestIpaAdTrustInstall -MIME-Version: 1.0 -Content-Type: text/plain; charset=UTF-8 -Content-Transfer-Encoding: 8bit - -tasks.create_active_user can fail in a subtle way when there -are two IPA servers due to replication delays. -Using the debug-enabled version of create_active_user helps -determine whether there is another underlying issue and, in -general, prevents the above problem. - -Fixes: https://pagure.io/freeipa/issue/8944 -Signed-off-by: François Cami -Reviewed-By: Anuja More ---- - ipatests/test_integration/test_adtrust_install.py | 14 ++++++++++---- - 1 file changed, 10 insertions(+), 4 deletions(-) - -diff --git a/ipatests/test_integration/test_adtrust_install.py b/ipatests/test_integration/test_adtrust_install.py -index bbbb385a5..f23221186 100644 ---- a/ipatests/test_integration/test_adtrust_install.py -+++ b/ipatests/test_integration/test_adtrust_install.py -@@ -257,8 +257,11 @@ class TestIpaAdTrustInstall(IntegrationTest): - user_princ = '@'.join([user, self.master.domain.realm]) - passwd = 'Secret123' - # Create a user with a password -- tasks.create_active_user(self.master, user, passwd, extra_args=[ -- '--homedir', '/home/{}'.format(user)]) -+ tasks.create_active_user( -+ self.master, user, passwd, -+ extra_args=["--homedir", "/home/{}".format(user)], -+ krb5_trace=True -+ ) - try: - # Defaults: host/... principal for service - # keytab in /etc/krb5.keytab -@@ -282,8 +285,11 @@ class TestIpaAdTrustInstall(IntegrationTest): - user_princ = '@'.join([user, self.master.domain.realm]) - passwd = 'Secret123' - # Create a user with a password -- tasks.create_active_user(self.master, user, passwd, extra_args=[ -- '--homedir', '/home/{}'.format(user)]) -+ tasks.create_active_user( -+ self.master, user, passwd, -+ extra_args=["--homedir", "/home/{}".format(user)], -+ krb5_trace=True -+ ) - try: - # Defaults: host/... principal for service - # keytab in /etc/krb5.keytab --- -2.31.1 - diff --git a/SOURCES/0041-ipatests-Test-ldapsearch-with-base-scope-works-with-.patch b/SOURCES/0041-ipatests-Test-ldapsearch-with-base-scope-works-with-.patch deleted file mode 100644 index f616c59..0000000 --- a/SOURCES/0041-ipatests-Test-ldapsearch-with-base-scope-works-with-.patch +++ /dev/null @@ -1,45 +0,0 @@ -From a3d71eb72a6125a80a9d7b698f34dcb95dc25184 Mon Sep 17 00:00:00 2001 -From: Anuja More -Date: Thu, 5 Aug 2021 20:03:21 +0530 -Subject: [PATCH] ipatests: Test ldapsearch with base scope works with compat - tree. - -Added test to verify that ldapsearch for compat tree -with scope base and sub is not failing. - -Related: https://bugzilla.redhat.com/show_bug.cgi?id=1958909 - -Signed-off-by: Anuja More -Reviewed-By: Mohammad Rizwan -Reviewed-By: Florence Blanc-Renaud ---- - ipatests/test_integration/test_commands.py | 13 +++++++++++++ - 1 file changed, 13 insertions(+) - -diff --git a/ipatests/test_integration/test_commands.py b/ipatests/test_integration/test_commands.py -index 2035ced56..e3a0d867e 100644 ---- a/ipatests/test_integration/test_commands.py -+++ b/ipatests/test_integration/test_commands.py -@@ -1558,6 +1558,19 @@ class TestIPACommandWithoutReplica(IntegrationTest): - # Run the command again after cache is removed - self.master.run_command(['ipa', 'user-show', 'ipauser1']) - -+ def test_basesearch_compat_tree(self): -+ """Test ldapsearch against compat tree is working -+ -+ This to ensure that ldapsearch with base scope is not failing. -+ -+ related: https://bugzilla.redhat.com/show_bug.cgi?id=1958909 -+ """ -+ tasks.kinit_admin(self.master) -+ base_dn = str(self.master.domain.basedn) -+ base = "cn=admins,cn=groups,cn=compat,{basedn}".format(basedn=base_dn) -+ tasks.ldapsearch_dm(self.master, base, ldap_args=[], scope='sub') -+ tasks.ldapsearch_dm(self.master, base, ldap_args=[], scope='base') -+ - - class TestIPAautomount(IntegrationTest): - @classmethod --- -2.31.1 - diff --git a/SOURCES/0042-ipatests-skip-test_basesearch_compat_tree-on-fedora.patch b/SOURCES/0042-ipatests-skip-test_basesearch_compat_tree-on-fedora.patch deleted file mode 100644 index 012b885..0000000 --- a/SOURCES/0042-ipatests-skip-test_basesearch_compat_tree-on-fedora.patch +++ /dev/null @@ -1,44 +0,0 @@ -From d4062e407d242a72b9d4e32f4fdd6aed086ce005 Mon Sep 17 00:00:00 2001 -From: Anuja More -Date: Thu, 5 Aug 2021 20:23:15 +0530 -Subject: [PATCH] ipatests: skip test_basesearch_compat_tree on fedora. - -slapi-nis with fix is not part of fedora yet. -test requires with fix: -https://pagure.io/slapi-nis/c/61ea8f6a104da25329e301a8f56944f860de8177? - -Signed-off-by: Anuja More -Reviewed-By: Mohammad Rizwan -Reviewed-By: Florence Blanc-Renaud ---- - ipatests/test_integration/test_commands.py | 7 +++++++ - 1 file changed, 7 insertions(+) - -diff --git a/ipatests/test_integration/test_commands.py b/ipatests/test_integration/test_commands.py -index e3a0d867e..4d9a81652 100644 ---- a/ipatests/test_integration/test_commands.py -+++ b/ipatests/test_integration/test_commands.py -@@ -38,6 +38,7 @@ from ipatests.create_external_ca import ExternalCA - from ipatests.test_ipalib.test_x509 import good_pkcs7, badcert - from ipapython.ipautil import realm_to_suffix, ipa_generate_password - from ipaserver.install.installutils import realm_to_serverid -+from pkg_resources import parse_version - - logger = logging.getLogger(__name__) - -@@ -1565,6 +1566,12 @@ class TestIPACommandWithoutReplica(IntegrationTest): - - related: https://bugzilla.redhat.com/show_bug.cgi?id=1958909 - """ -+ version = self.master.run_command( -+ ["rpm", "-qa", "--qf", "%{VERSION}", "slapi-nis"] -+ ) -+ if tasks.get_platform(self.master) == "fedora" and parse_version( -+ version.stdout_text) <= parse_version("0.56.7"): -+ pytest.skip("Test requires slapi-nis with fix on fedora") - tasks.kinit_admin(self.master) - base_dn = str(self.master.domain.basedn) - base = "cn=admins,cn=groups,cn=compat,{basedn}".format(basedn=base_dn) --- -2.31.1 - diff --git a/SOURCES/0043-ipatests-Refactor-test_check_otpd_after_idle_timeout.patch b/SOURCES/0043-ipatests-Refactor-test_check_otpd_after_idle_timeout.patch deleted file mode 100644 index cbba6ba..0000000 --- a/SOURCES/0043-ipatests-Refactor-test_check_otpd_after_idle_timeout.patch +++ /dev/null @@ -1,89 +0,0 @@ -From eac03d6828d0bac1925c897090fc77e250eaee04 Mon Sep 17 00:00:00 2001 -From: Anuja More -Date: Thu, 5 Aug 2021 12:27:38 +0530 -Subject: [PATCH] ipatests: Refactor test_check_otpd_after_idle_timeout - -Use whole date when calling journalctl --since -ipa-otpd don't flush its logs to syslog immediately, -so check with run_repeatedly. -Also list failed units when ldap connection is -timed out. - -Related: https://pagure.io/freeipa/issue/6587 - -Signed-off-by: Anuja More -Reviewed-By: Florence Blanc-Renaud -Reviewed-By: Rob Crittenden ---- - ipatests/test_integration/test_otp.py | 31 ++++++++++++++++----------- - 1 file changed, 18 insertions(+), 13 deletions(-) - -diff --git a/ipatests/test_integration/test_otp.py b/ipatests/test_integration/test_otp.py -index fd55898ca..353470897 100644 ---- a/ipatests/test_integration/test_otp.py -+++ b/ipatests/test_integration/test_otp.py -@@ -313,6 +313,13 @@ class TestOTPToken(IntegrationTest): - - @pytest.fixture - def setup_otp_nsslapd(self): -+ check_services = self.master.run_command( -+ ['systemctl', 'list-units', '--state=failed'] -+ ) -+ assert "0 loaded units listed" in check_services.stdout_text -+ assert "ipa-otpd" not in check_services.stdout_text -+ # Be sure no services are running and failed units -+ self.master.run_command(['killall', 'ipa-otpd'], raiseonerr=False) - # setting nsslapd-idletimeout - new_limit = 30 - conn = self.master.ldap_connect() -@@ -326,13 +333,6 @@ class TestOTPToken(IntegrationTest): - nsslapd-idletimeout: {limit} - """) - tasks.ldapmodify_dm(self.master, ldap_query.format(limit=new_limit)) -- # Be sure no services are running and failed units -- self.master.run_command(['killall', 'ipa-otpd'], raiseonerr=False) -- check_services = self.master.run_command( -- ['systemctl', 'list-units', '--state=failed'] -- ) -- assert "0 loaded units listed" in check_services.stdout_text -- assert "ipa-otpd" not in check_services.stdout_text - yield - # cleanup - tasks.ldapmodify_dm(self.master, ldap_query.format(limit=orig_limit)) -@@ -346,7 +346,7 @@ class TestOTPToken(IntegrationTest): - Test to verify that when the nsslapd-idletimeout is exceeded (30s idle, - 60s sleep) then the ipa-otpd process should exit without error. - """ -- since = time.strftime('%H:%M:%S') -+ since = time.strftime('%Y-%m-%d %H:%M:%S') - tasks.kinit_admin(self.master) - otpuid, totp = add_otptoken(self.master, USER, otptype="totp") - try: -@@ -354,14 +354,19 @@ class TestOTPToken(IntegrationTest): - otpvalue = totp.generate(int(time.time())).decode("ascii") - kinit_otp(self.master, USER, password=PASSWORD, otp=otpvalue) - time.sleep(60) -+ -+ def test_cb(cmd_jornalctl): -+ # check if LDAP connection is timed out -+ expected_msg = "Can't contact LDAP server" -+ return expected_msg in cmd_jornalctl -+ -+ # ipa-otpd don't flush its logs to syslog immediately -+ cmd = ['journalctl', '--since={}'.format(since)] -+ tasks.run_repeatedly( -+ self.master, command=cmd, test=test_cb, timeout=90) - failed_services = self.master.run_command( - ['systemctl', 'list-units', '--state=failed'] - ) - assert "ipa-otpd" not in failed_services.stdout_text -- cmd_jornalctl = self.master.run_command( -- ['journalctl', '--since={}'.format(since)] -- ) -- regex = r".*ipa-otpd@.*\sSucceeded" -- assert re.search(regex, cmd_jornalctl.stdout_text) - finally: - del_otptoken(self.master, otpuid) --- -2.31.1 - diff --git a/SOURCES/0044-ipatests-Test-unsecure-nsupdate.patch b/SOURCES/0044-ipatests-Test-unsecure-nsupdate.patch deleted file mode 100644 index a223c94..0000000 --- a/SOURCES/0044-ipatests-Test-unsecure-nsupdate.patch +++ /dev/null @@ -1,162 +0,0 @@ -From 4fdab0c94c4e17e42e5f38a0e671bea39bcc9b74 Mon Sep 17 00:00:00 2001 -From: Anuja More -Date: Mon, 9 Aug 2021 20:57:22 +0530 -Subject: [PATCH] ipatests: Test unsecure nsupdate. - -The test configures an external bind server on the ipa-server -(not the IPA-embedded DNS server) that allows unauthenticated nsupdates. - -When the IPA client is registered using ipa-client-install, -DNS records are added for the client in the bind server using nsupdate. -The first try is using GSS-TIG but fails as expected, and the client -installer then tries with unauthenticated nsupdate. - -Related : https://pagure.io/freeipa/issue/8402 - -Signed-off-by: Anuja More -Reviewed-By: Rob Crittenden -Reviewed-By: Florence Blanc-Renaud ---- - .../test_installation_client.py | 118 ++++++++++++++++++ - 1 file changed, 118 insertions(+) - -diff --git a/ipatests/test_integration/test_installation_client.py b/ipatests/test_integration/test_installation_client.py -index fa59a5255..014b0f6ab 100644 ---- a/ipatests/test_integration/test_installation_client.py -+++ b/ipatests/test_integration/test_installation_client.py -@@ -8,10 +8,15 @@ Module provides tests for various options of ipa-client-install. - - from __future__ import absolute_import - -+import pytest -+import re - import shlex -+import textwrap - -+from ipaplatform.paths import paths - from ipatests.test_integration.base import IntegrationTest - from ipatests.pytest_ipa.integration import tasks -+from ipatests.pytest_ipa.integration.firewall import Firewall - - - class TestInstallClient(IntegrationTest): -@@ -70,3 +75,116 @@ class TestInstallClient(IntegrationTest): - extra_args=['--ssh-trust-dns']) - result = self.clients[0].run_command(['cat', '/etc/ssh/ssh_config']) - assert 'HostKeyAlgorithms' not in result.stdout_text -+ -+ -+class TestClientInstallBind(IntegrationTest): -+ """ -+ The test configures an external bind server on the ipa-server -+ (not the IPA-embedded DNS server) that allows unauthenticated nsupdates. -+ When the IPA client is registered using ipa-client-install, -+ DNS records are added for the client in the bind server using nsupdate. -+ The first try is using GSS-TIG but fails as expected, and the client -+ installer then tries with unauthenticated nsupdate. -+ """ -+ -+ num_clients = 1 -+ -+ @classmethod -+ def install(cls, mh): -+ cls.client = cls.clients[0] -+ -+ @pytest.fixture -+ def setup_bindserver(self): -+ bindserver = self.master -+ named_conf_backup = tasks.FileBackup(self.master, paths.NAMED_CONF) -+ # create a zone in the BIND server that is identical to the IPA -+ add_zone = textwrap.dedent(""" -+ zone "{domain}" IN {{ type master; -+ file "{domain}.db"; allow-query {{ any; }}; -+ allow-update {{ any; }}; }}; -+ """).format(domain=bindserver.domain.name) -+ -+ namedcfg = bindserver.get_file_contents( -+ paths.NAMED_CONF, encoding='utf-8') -+ namedcfg += '\n' + add_zone -+ bindserver.put_file_contents(paths.NAMED_CONF, namedcfg) -+ -+ def update_contents(path, pattern, replace): -+ contents = bindserver.get_file_contents(path, encoding='utf-8') -+ namedcfg_query = re.sub(pattern, replace, contents) -+ bindserver.put_file_contents(path, namedcfg_query) -+ -+ update_contents(paths.NAMED_CONF, 'localhost;', 'any;') -+ update_contents(paths.NAMED_CONF, "listen-on port 53 { 127.0.0.1; };", -+ "#listen-on port 53 { 127.0.0.1; };") -+ update_contents(paths.NAMED_CONF, "listen-on-v6 port 53 { ::1; };", -+ "#listen-on-v6 port 53 { ::1; };") -+ -+ add_records = textwrap.dedent(""" -+ @ IN SOA {fqdn}. root.{domain}. ( -+ 1001 ;Serial -+ 3H ;Refresh -+ 15M ;Retry -+ 1W ;Expire -+ 1D ;Minimum 1D -+ ) -+ @ IN NS {fqdn}. -+ ns1 IN A {bindserverip} -+ _kerberos.{domain}. IN TXT {zoneupper} -+ {fqdn}. IN A {bindserverip} -+ ipa-ca.{domain}. IN A {bindserverip} -+ _kerberos-master._tcp.{domain}. IN SRV 0 100 88 {fqdn}. -+ _kerberos-master._udp.{domain}. IN SRV 0 100 88 {fqdn}. -+ _kerberos._tcp.{domain}. IN SRV 0 100 88 {fqdn}. -+ _kerberos._udp.{domain}. IN SRV 0 100 88 {fqdn}. -+ _kpasswd._tcp.{domain}. IN SRV 0 100 464 {fqdn}. -+ _kpasswd._udp.{domain}. IN SRV 0 100 464 {fqdn}. -+ _ldap._tcp.{domain}. IN SRV 0 100 389 {fqdn}. -+ """).format( -+ fqdn=bindserver.hostname, -+ domain=bindserver.domain.name, -+ bindserverip=bindserver.ip, -+ zoneupper=bindserver.domain.name.upper() -+ ) -+ bindserverdb = "/var/named/{0}.db".format(bindserver.domain.name) -+ bindserver.put_file_contents(bindserverdb, add_records) -+ bindserver.run_command(['systemctl', 'start', 'named']) -+ Firewall(bindserver).enable_services(["dns"]) -+ yield -+ named_conf_backup.restore() -+ bindserver.run_command(['rm', '-rf', bindserverdb]) -+ -+ def test_client_nsupdate(self, setup_bindserver): -+ """Test secure nsupdate failed, then try unsecure nsupdate.. -+ -+ Test to verify when bind is configured with dynamic update policy, -+ and during client-install 'nsupdate -g' fails then it should run with -+ second call using unauthenticated nsupdate. -+ -+ Related : https://pagure.io/freeipa/issue/8402 -+ """ -+ # with pre-configured bind server, install ipa-server without dns. -+ tasks.install_master(self.master, setup_dns=False) -+ self.client.resolver.backup() -+ self.client.resolver.setup_resolver( -+ self.master.ip, self.master.domain.name) -+ try: -+ self.client.run_command(['ipa-client-install', '-U', -+ '--domain', self.client.domain.name, -+ '--realm', self.client.domain.realm, -+ '-p', self.client.config.admin_name, -+ '-w', self.client.config.admin_password, -+ '--server', self.master.hostname]) -+ # call unauthenticated nsupdate if GSS-TSIG nsupdate failed. -+ str1 = "nsupdate (GSS-TSIG) failed" -+ str2 = "'/usr/bin/nsupdate', '/etc/ipa/.dns_update.txt'" -+ client_log = self.client.get_file_contents( -+ paths.IPACLIENT_INSTALL_LOG, encoding='utf-8' -+ ) -+ assert str1 in client_log and str2 in client_log -+ dig_after = self.client.run_command( -+ ['dig', '@{0}'.format(self.master.ip), self.client.hostname, -+ '-t', 'SSHFP']) -+ assert "ANSWER: 0" not in dig_after.stdout_text.strip() -+ finally: -+ self.client.resolver.restore() --- -2.31.1 - diff --git a/SOURCES/0045-ipatests-Fix-TestAJPSecretUpgrade-tests-on-systems-w.patch b/SOURCES/0045-ipatests-Fix-TestAJPSecretUpgrade-tests-on-systems-w.patch deleted file mode 100644 index 5e978b4..0000000 --- a/SOURCES/0045-ipatests-Fix-TestAJPSecretUpgrade-tests-on-systems-w.patch +++ /dev/null @@ -1,88 +0,0 @@ -From c9bc471e063f2865d6423e4f1c9b81e73a45e43f Mon Sep 17 00:00:00 2001 -From: Stanislav Levin -Date: Wed, 4 Aug 2021 18:38:16 +0300 -Subject: [PATCH] ipatests: Fix TestAJPSecretUpgrade tests on systems without - pkiuser - -Tests in `test_ipaserver.test_secure_ajp_connector' assume that there -is pkiuser in OS, but this is not always true (for example, in systems -having minimum installed dependencies, in particular, without pki-server -RPM package). Since the tests already use the mock and pkiuser entity is -not the subject of testing the pwd.getpwnam has been mocked. - -Fixes: https://pagure.io/freeipa/issue/8942 -Signed-off-by: Stanislav Levin -Reviewed-By: Rob Crittenden ---- - .../test_secure_ajp_connector.py | 40 ++++++++++++++++--- - 1 file changed, 34 insertions(+), 6 deletions(-) - -diff --git a/ipatests/test_ipaserver/test_secure_ajp_connector.py b/ipatests/test_ipaserver/test_secure_ajp_connector.py -index 2719dbc48..35ef7407a 100644 ---- a/ipatests/test_ipaserver/test_secure_ajp_connector.py -+++ b/ipatests/test_ipaserver/test_secure_ajp_connector.py -@@ -1,5 +1,6 @@ - # Copyright (C) 2021 FreeIPA Project Contributors - see LICENSE file - -+from collections import namedtuple - from io import BytesIO - from lxml.etree import parse as myparse # pylint: disable=no-name-in-module - import pytest -@@ -32,6 +33,32 @@ def mock_etree_parse(data): - return myparse(f) - - -+def mock_pkiuser_entity(): -+ """Return struct_passwd for mocked pkiuser""" -+ StructPasswd = namedtuple( -+ "StructPasswd", -+ [ -+ "pw_name", -+ "pw_passwd", -+ "pw_uid", -+ "pw_gid", -+ "pw_gecos", -+ "pw_dir", -+ "pw_shell", -+ ] -+ ) -+ pkiuser_entity = StructPasswd( -+ constants.PKI_USER, -+ pw_passwd="x", -+ pw_uid=-1, -+ pw_gid=-1, -+ pw_gecos="", -+ pw_dir="/dev/null", -+ pw_shell="/sbin/nologin", -+ ) -+ return pkiuser_entity -+ -+ - # Format of test_data is: - # ( - # is_newer_tomcat (boolean), -@@ -148,14 +175,15 @@ test_data = ( - - - class TestAJPSecretUpgrade: -- @patch('os.chown') -- @patch('lxml.etree.parse') -- @pytest.mark.parametrize('is_newer, data, secret, expect, rewrite', -- test_data) -- def test_connecter(self, mock_parse, mock_chown, is_newer, data, secret, -- expect, rewrite): -+ @patch("ipaplatform.base.constants.pwd.getpwnam") -+ @patch("ipaplatform.base.constants.os.chown") -+ @patch("ipaserver.install.dogtaginstance.lxml.etree.parse") -+ @pytest.mark.parametrize("test_data", test_data) -+ def test_connecter(self, mock_parse, mock_chown, mock_getpwnam, test_data): -+ is_newer, data, secret, expect, rewrite = test_data - mock_chown.return_value = None - mock_parse.return_value = mock_etree_parse(data) -+ mock_getpwnam.return_value = mock_pkiuser_entity() - - dogtag = MyDogtagInstance(is_newer) - with patch('ipaserver.install.dogtaginstance.open', mock_open()) \ --- -2.31.1 - diff --git a/SOURCES/0046-ipatests-test_ipahealthcheck-Verify-permissions-for-.patch b/SOURCES/0046-ipatests-test_ipahealthcheck-Verify-permissions-for-.patch deleted file mode 100644 index 23d092b..0000000 --- a/SOURCES/0046-ipatests-test_ipahealthcheck-Verify-permissions-for-.patch +++ /dev/null @@ -1,54 +0,0 @@ -From 488ac7e3ba9f36d6b187687d120920d2d80d8b7f Mon Sep 17 00:00:00 2001 -From: Michal Polovka -Date: Tue, 10 Aug 2021 18:11:05 +0200 -Subject: [PATCH] ipatests: test_ipahealthcheck: Verify permissions for - /var/log/ files - -Test if files in /var/log are being checked with ipahealthcheck.ipa.files source. - -Resolves: https://pagure.io/freeipa/issue/8949 - -Signed-off-by: Michal Polovka -Reviewed-By: Michal Polovka -Reviewed-By: Florence Blanc-Renaud ---- - .../test_integration/test_ipahealthcheck.py | 23 +++++++++++++++++++ - 1 file changed, 23 insertions(+) - -diff --git a/ipatests/test_integration/test_ipahealthcheck.py b/ipatests/test_integration/test_ipahealthcheck.py -index 36fe72be7..089793a2f 100644 ---- a/ipatests/test_integration/test_ipahealthcheck.py -+++ b/ipatests/test_integration/test_ipahealthcheck.py -@@ -1227,6 +1227,29 @@ class TestIpaHealthCheck(IntegrationTest): - ) - assert msg in cmd.stdout_text - -+ def test_ipahealthcheck_verify_perms_for_source_files(self, -+ modify_permissions): -+ """ -+ This tests checks if files in /var/log are checked with ipa.files -+ source. -+ The test modifies permissions of ipainstall log file and checks the -+ response from healthcheck. -+ -+ https://pagure.io/freeipa/issue/8949 -+ """ -+ modify_permissions(self.master, path=paths.IPASERVER_INSTALL_LOG, -+ mode="0644") -+ returncode, data = run_healthcheck( -+ self.master, "ipahealthcheck.ipa.files", failures_only=True) -+ -+ assert returncode == 1 -+ assert len(data) == 1 -+ assert data[0]["result"] == "WARNING" -+ assert data[0]["kw"]["path"] == paths.IPASERVER_INSTALL_LOG -+ assert data[0]["kw"]["type"] == "mode" -+ assert data[0]["kw"]["expected"] == "0600" -+ -+ - @pytest.fixture - def remove_healthcheck(self): - """ --- -2.31.1 - diff --git a/SOURCES/0047-ipatests-test-to-renew-certs-on-replica-using-ipa-ce.patch b/SOURCES/0047-ipatests-test-to-renew-certs-on-replica-using-ipa-ce.patch deleted file mode 100644 index 843f0a7..0000000 --- a/SOURCES/0047-ipatests-test-to-renew-certs-on-replica-using-ipa-ce.patch +++ /dev/null @@ -1,127 +0,0 @@ -From e0aef5296b66c0b460f7e10993610fe68b312241 Mon Sep 17 00:00:00 2001 -From: Mohammad Rizwan -Date: Mon, 19 Apr 2021 12:08:28 +0530 -Subject: [PATCH] ipatests: test to renew certs on replica using ipa-cert-fix - -This test checks if ipa-cert-fix renews the certs on replica -after cert renewal on master. - -related: https://pagure.io/freeipa/issue/7885 - -ipatests: refactor expire_cert_critical fixture - -Defined method to move the date and refactor -expire_cert_critical fixture using it - -ipatests: PEP8 fixes - -Signed-off-by: Mohammad Rizwan -Reviewed-By: Florence Blanc-Renaud ---- - .../test_integration/test_ipa_cert_fix.py | 74 ++++++++++++++++++- - 1 file changed, 70 insertions(+), 4 deletions(-) - -diff --git a/ipatests/test_integration/test_ipa_cert_fix.py b/ipatests/test_integration/test_ipa_cert_fix.py -index f3cf59afc..a20996737 100644 ---- a/ipatests/test_integration/test_ipa_cert_fix.py -+++ b/ipatests/test_integration/test_ipa_cert_fix.py -@@ -6,6 +6,7 @@ - Module provides tests for ipa-cert-fix CLI. - """ - import pytest -+import re - import time - - import logging -@@ -74,15 +75,15 @@ def expire_cert_critical(): - extra_args=['--no-ntp']) - if setup_kra: - tasks.install_kra(host) -- host.run_command(['systemctl', 'stop', 'chronyd']) -- host.run_command(['date', '-s', '+3Years+1day']) -+ -+ # move date to expire certs -+ move_date(host, 'stop', '+3Years+1day') - - yield _expire_cert_critical - - host = hosts.pop('host') - tasks.uninstall_master(host) -- host.run_command(['date', '-s', '-3Years-1day']) -- host.run_command(['systemctl', 'start', 'chronyd']) -+ move_date(host, 'start', '-3Years-1day') - - - class TestIpaCertFix(IntegrationTest): -@@ -336,3 +337,68 @@ class TestCertFixKRA(IntegrationTest): - self.master.run_command(['ipa-cert-fix', '-v'], stdin_text='yes\n') - - check_status(self.master, 12, "MONITORING") -+ -+ -+class TestCertFixReplica(IntegrationTest): -+ -+ num_replicas = 1 -+ -+ @classmethod -+ def install(cls, mh): -+ tasks.install_master( -+ mh.master, setup_dns=False, extra_args=['--no-ntp'] -+ ) -+ tasks.install_replica( -+ mh.master, mh.replicas[0], -+ setup_dns=False, extra_args=['--no-ntp'] -+ ) -+ -+ def test_renew_expired_cert_replica(self): -+ """Test renewal of certificates on replica with ipa-cert-fix -+ -+ This is to check that ipa-cert-fix renews the certificates -+ on replica -+ -+ related: https://pagure.io/freeipa/issue/7885 -+ """ -+ move_date(self.master, 'stop', '+3years+1days') -+ -+ # wait for cert expiry -+ check_status(self.master, 8, "CA_UNREACHABLE") -+ -+ self.master.run_command(['ipa-cert-fix', '-v'], stdin_text='yes\n') -+ -+ check_status(self.master, 9, "MONITORING") -+ -+ # move system date to expire cert on replica -+ move_date(self.replicas[0], 'stop', '+3years+1days') -+ -+ # RA agent cert will be expired and in CA_UNREACHABLE state -+ check_status(self.replicas[0], 1, "CA_UNREACHABLE") -+ -+ # renew RA agent cert -+ self.replicas[0].run_command( -+ ['ipa-cert-fix', '-v'], stdin_text='yes\n' -+ ) -+ -+ # LDAP/HTTP/PKINIT certs will be renewed automaticaly -+ # after moving date on replica. This 3, 1 CA cert, -+ # 1 RA agent cert. Check for total 5 valid certs. -+ check_status(self.replicas[0], 5, "MONITORING") -+ -+ # get the req ids of all certs to renew remaining -+ # certs by re-submitting it -+ result = self.replicas[0].run_command(['getcert', 'list']) -+ req_ids = re.findall(r'\d{14}', result.stdout_text) -+ -+ # resubmit the certs to renew them -+ for req_id in req_ids: -+ self.replicas[0].run_command( -+ ['getcert', 'resubmit', '-i', req_id] -+ ) -+ -+ check_status(self.master, 9, "MONITORING") -+ -+ # move date back on replica and master -+ move_date(self.replicas[0], 'start', '-3years-1days') -+ move_date(self.master, 'start', '-3years-1days') --- -2.31.1 - diff --git a/SOURCES/0048-ipatests-wait-while-http-ldap-pkinit-cert-get-renew-.patch b/SOURCES/0048-ipatests-wait-while-http-ldap-pkinit-cert-get-renew-.patch deleted file mode 100644 index 910d4c8..0000000 --- a/SOURCES/0048-ipatests-wait-while-http-ldap-pkinit-cert-get-renew-.patch +++ /dev/null @@ -1,252 +0,0 @@ -From a620e5e9e152defe144705913521c3cf556faa0e Mon Sep 17 00:00:00 2001 -From: Mohammad Rizwan -Date: Mon, 26 Apr 2021 15:50:20 +0530 -Subject: [PATCH] ipatests: wait while http/ldap/pkinit cert get renew on - replica - -LDAP/HTTP/PKINIT certificates should be renewd on replica after -moving system date. Test was failing because ipa-cert-fix ran -while these cert was not renewd and it tried to fix it. - -This test adds check for replication before calling ipa-cert-fix -on replica. - -Fixes: https://pagure.io/freeipa/issue/8815 - -Signed-off-by: Mohammad Rizwan -Reviewed-By: Florence Blanc-Renaud -Reviewed-By: Sergey Orlov -Reviewed-By: Rob Crittenden -Reviewed-By: Florence Blanc-Renaud ---- - .../test_integration/test_ipa_cert_fix.py | 172 +++++++++++++++--- - 1 file changed, 144 insertions(+), 28 deletions(-) - -diff --git a/ipatests/test_integration/test_ipa_cert_fix.py b/ipatests/test_integration/test_ipa_cert_fix.py -index a20996737..fa69743e2 100644 ---- a/ipatests/test_integration/test_ipa_cert_fix.py -+++ b/ipatests/test_integration/test_ipa_cert_fix.py -@@ -5,16 +5,19 @@ - """ - Module provides tests for ipa-cert-fix CLI. - """ -+from cryptography.hazmat.backends import default_backend -+from cryptography import x509 -+from datetime import datetime, date - import pytest --import re - import time - - import logging - from ipaplatform.paths import paths -+from ipapython.ipaldap import realm_to_serverid - from ipatests.pytest_ipa.integration import tasks - from ipatests.test_integration.base import IntegrationTest - from ipatests.test_integration.test_caless import CALessBase, ipa_certs_cleanup -- -+from ipatests.test_integration.test_cert import get_certmonger_fs_id - - logger = logging.getLogger(__name__) - -@@ -59,6 +62,49 @@ def move_date(host, chrony_state, date_str): - host.run_command(['date', '-s', date_str]) - - -+def needs_resubmit(host, req_id): -+ """Helper method to identify if cert request needs to be resubmitted -+ :param host: the host -+ :param req_id: request id to perform operation for -+ -+ Returns True if resubmit needed else False -+ """ -+ # check if cert is in monitoring state -+ tasks.wait_for_certmonger_status( -+ host, ('MONITORING'), req_id, timeout=600 -+ ) -+ -+ # check if cert is valid and not expired -+ cmd = host.run_command( -+ 'getcert list -i {} | grep expires'.format(req_id) -+ ) -+ cert_expiry = cmd.stdout_text.split(' ') -+ cert_expiry = datetime.strptime(cert_expiry[1], '%Y-%m-%d').date() -+ if cert_expiry > date.today(): -+ return False -+ else: -+ return True -+ -+ -+def get_cert_expiry(host, nssdb_path, cert_nick): -+ """Method to get cert expiry date of given certificate -+ -+ :param host: the host -+ :param nssdb_path: nssdb path of certificate -+ :param cert_nick: certificate nick name for extracting cert from nssdb -+ """ -+ # get initial expiry date to compare later with renewed cert -+ host.run_command([ -+ 'certutil', '-L', '-a', -+ '-d', nssdb_path, -+ '-n', cert_nick, -+ '-o', '/root/cert.pem' -+ ]) -+ data = host.get_file_contents('/root/cert.pem') -+ cert = x509.load_pem_x509_certificate(data, backend=default_backend()) -+ return cert.not_valid_after -+ -+ - @pytest.fixture - def expire_cert_critical(): - """ -@@ -353,7 +399,19 @@ class TestCertFixReplica(IntegrationTest): - setup_dns=False, extra_args=['--no-ntp'] - ) - -- def test_renew_expired_cert_replica(self): -+ @pytest.fixture -+ def expire_certs(self): -+ # move system date to expire certs -+ for host in self.master, self.replicas[0]: -+ tasks.move_date(host, 'stop', '+3years+1days') -+ -+ yield -+ -+ # move date back on replica and master -+ for host in self.master, self.replicas[0]: -+ tasks.move_date(host, 'start', '-3years-1days') -+ -+ def test_renew_expired_cert_replica(self, expire_certs): - """Test renewal of certificates on replica with ipa-cert-fix - - This is to check that ipa-cert-fix renews the certificates -@@ -361,8 +419,6 @@ class TestCertFixReplica(IntegrationTest): - - related: https://pagure.io/freeipa/issue/7885 - """ -- move_date(self.master, 'stop', '+3years+1days') -- - # wait for cert expiry - check_status(self.master, 8, "CA_UNREACHABLE") - -@@ -370,35 +426,95 @@ class TestCertFixReplica(IntegrationTest): - - check_status(self.master, 9, "MONITORING") - -- # move system date to expire cert on replica -- move_date(self.replicas[0], 'stop', '+3years+1days') -- -- # RA agent cert will be expired and in CA_UNREACHABLE state -- check_status(self.replicas[0], 1, "CA_UNREACHABLE") -- -- # renew RA agent cert -- self.replicas[0].run_command( -- ['ipa-cert-fix', '-v'], stdin_text='yes\n' -+ # replica operations -+ # 'Server-Cert cert-pki-ca' cert will be in CA_UNREACHABLE state -+ cmd = self.replicas[0].run_command( -+ ['getcert', 'list', -+ '-d', paths.PKI_TOMCAT_ALIAS_DIR, -+ '-n', 'Server-Cert cert-pki-ca'] -+ ) -+ req_id = get_certmonger_fs_id(cmd.stdout_text) -+ tasks.wait_for_certmonger_status( -+ self.replicas[0], ('CA_UNREACHABLE'), req_id, timeout=600 -+ ) -+ # get initial expiry date to compare later with renewed cert -+ initial_expiry = get_cert_expiry( -+ self.replicas[0], -+ paths.PKI_TOMCAT_ALIAS_DIR, -+ 'Server-Cert cert-pki-ca' - ) - -- # LDAP/HTTP/PKINIT certs will be renewed automaticaly -- # after moving date on replica. This 3, 1 CA cert, -- # 1 RA agent cert. Check for total 5 valid certs. -- check_status(self.replicas[0], 5, "MONITORING") -+ # check that HTTP,LDAP,PKINIT are renewed and in MONITORING state -+ instance = realm_to_serverid(self.master.domain.realm) -+ dirsrv_cert = paths.ETC_DIRSRV_SLAPD_INSTANCE_TEMPLATE % instance -+ for cert in (paths.KDC_CERT, paths.HTTPD_CERT_FILE): -+ cmd = self.replicas[0].run_command( -+ ['getcert', 'list', '-f', cert] -+ ) -+ req_id = get_certmonger_fs_id(cmd.stdout_text) -+ tasks.wait_for_certmonger_status( -+ self.replicas[0], ('MONITORING'), req_id, timeout=600 -+ ) - -- # get the req ids of all certs to renew remaining -- # certs by re-submitting it -- result = self.replicas[0].run_command(['getcert', 'list']) -- req_ids = re.findall(r'\d{14}', result.stdout_text) -+ cmd = self.replicas[0].run_command( -+ ['getcert', 'list', '-d', dirsrv_cert] -+ ) -+ req_id = get_certmonger_fs_id(cmd.stdout_text) -+ tasks.wait_for_certmonger_status( -+ self.replicas[0], ('MONITORING'), req_id, timeout=600 -+ ) - -- # resubmit the certs to renew them -- for req_id in req_ids: -+ # check if replication working fine -+ testuser = 'testuser1' -+ password = 'Secret@123' -+ stdin = (f"{self.master.config.admin_password}\n" -+ f"{self.master.config.admin_password}\n" -+ f"{self.master.config.admin_password}\n") -+ self.master.run_command(['kinit', 'admin'], stdin_text=stdin) -+ tasks.user_add(self.master, testuser, password=password) -+ self.replicas[0].run_command(['kinit', 'admin'], stdin_text=stdin) -+ self.replicas[0].run_command(['ipa', 'user-show', testuser]) -+ -+ # renew shared certificates by resubmitting to certmonger -+ cmd = self.replicas[0].run_command( -+ ['getcert', 'list', '-f', paths.RA_AGENT_PEM] -+ ) -+ req_id = get_certmonger_fs_id(cmd.stdout_text) -+ if needs_resubmit(self.replicas[0], req_id): - self.replicas[0].run_command( - ['getcert', 'resubmit', '-i', req_id] - ) -+ tasks.wait_for_certmonger_status( -+ self.replicas[0], ('MONITORING'), req_id, timeout=600 -+ ) -+ for cert_nick in ('auditSigningCert cert-pki-ca', -+ 'ocspSigningCert cert-pki-ca', -+ 'subsystemCert cert-pki-ca'): -+ cmd = self.replicas[0].run_command( -+ ['getcert', 'list', -+ '-d', paths.PKI_TOMCAT_ALIAS_DIR, -+ '-n', cert_nick] -+ ) -+ req_id = get_certmonger_fs_id(cmd.stdout_text) -+ if needs_resubmit(self.replicas[0], req_id): -+ self.replicas[0].run_command( -+ ['getcert', 'resubmit', '-i', req_id] -+ ) -+ tasks.wait_for_certmonger_status( -+ self.replicas[0], ('MONITORING'), req_id, timeout=600 -+ ) - -- check_status(self.master, 9, "MONITORING") -+ self.replicas[0].run_command( -+ ['ipa-cert-fix', '-v'], stdin_text='yes\n' -+ ) - -- # move date back on replica and master -- move_date(self.replicas[0], 'start', '-3years-1days') -- move_date(self.master, 'start', '-3years-1days') -+ check_status(self.replicas[0], 9, "MONITORING") -+ -+ # Sometimes certmonger takes time to update the cert status -+ # So check in nssdb instead of relying on getcert command -+ renewed_expiry = get_cert_expiry( -+ self.replicas[0], -+ paths.PKI_TOMCAT_ALIAS_DIR, -+ 'Server-Cert cert-pki-ca' -+ ) -+ assert renewed_expiry > initial_expiry --- -2.31.1 - diff --git a/SOURCES/0049-ipatests-refactor-test_ipa_cert_fix-with-tasks.patch b/SOURCES/0049-ipatests-refactor-test_ipa_cert_fix-with-tasks.patch deleted file mode 100644 index 2566b9f..0000000 --- a/SOURCES/0049-ipatests-refactor-test_ipa_cert_fix-with-tasks.patch +++ /dev/null @@ -1,73 +0,0 @@ -From 4a3a15f45aad016730252c09e3e173a18184603e Mon Sep 17 00:00:00 2001 -From: =?UTF-8?q?Fran=C3=A7ois=20Cami?= -Date: Wed, 21 Jul 2021 14:29:31 +0200 -Subject: [PATCH] ipatests: refactor test_ipa_cert_fix with tasks -MIME-Version: 1.0 -Content-Type: text/plain; charset=UTF-8 -Content-Transfer-Encoding: 8bit - -Fixes: https://pagure.io/freeipa/issue/8932 -Signed-off-by: François Cami -Reviewed-By: Michal Polovka -Reviewed-By: Armando Neto -Reviewed-By: Mohammad Rizwan -Reviewed-By: Florence Blanc-Renaud ---- - ipatests/test_integration/test_ipa_cert_fix.py | 18 ++++-------------- - 1 file changed, 4 insertions(+), 14 deletions(-) - -diff --git a/ipatests/test_integration/test_ipa_cert_fix.py b/ipatests/test_integration/test_ipa_cert_fix.py -index fa69743e2..39904d5de 100644 ---- a/ipatests/test_integration/test_ipa_cert_fix.py -+++ b/ipatests/test_integration/test_ipa_cert_fix.py -@@ -52,16 +52,6 @@ def check_status(host, cert_count, state, timeout=600): - return count - - --def move_date(host, chrony_state, date_str): -- """Helper method to move the date on given host -- :param host: The host on which date is to be moved -- :param chrony_state: State to which chrony service to be moved -- :param date_str: date string to move the date i.e 2years1month1days -- """ -- host.run_command(['systemctl', chrony_state, 'chronyd']) -- host.run_command(['date', '-s', date_str]) -- -- - def needs_resubmit(host, req_id): - """Helper method to identify if cert request needs to be resubmitted - :param host: the host -@@ -123,13 +113,13 @@ def expire_cert_critical(): - tasks.install_kra(host) - - # move date to expire certs -- move_date(host, 'stop', '+3Years+1day') -+ tasks.move_date(host, 'stop', '+3Years+1day') - - yield _expire_cert_critical - - host = hosts.pop('host') - tasks.uninstall_master(host) -- move_date(host, 'start', '-3Years-1day') -+ tasks.move_date(host, 'start', '-3Years-1day') - - - class TestIpaCertFix(IntegrationTest): -@@ -143,12 +133,12 @@ class TestIpaCertFix(IntegrationTest): - def expire_ca_cert(self): - tasks.install_master(self.master, setup_dns=False, - extra_args=['--no-ntp']) -- move_date(self.master, 'stop', '+20Years+1day') -+ tasks.move_date(self.master, 'stop', '+20Years+1day') - - yield - - tasks.uninstall_master(self.master) -- move_date(self.master, 'start', '-20Years-1day') -+ tasks.move_date(self.master, 'start', '-20Years-1day') - - def test_missing_csr(self, expire_cert_critical): - """ --- -2.31.1 - diff --git a/SOURCES/0050-ipatests-use-whole-date-for-journalctl-since.patch b/SOURCES/0050-ipatests-use-whole-date-for-journalctl-since.patch deleted file mode 100644 index 97b9650..0000000 --- a/SOURCES/0050-ipatests-use-whole-date-for-journalctl-since.patch +++ /dev/null @@ -1,65 +0,0 @@ -From b5036b5ce9ae4fab011e57fe2b37a35fdd098a70 Mon Sep 17 00:00:00 2001 -From: Florence Blanc-Renaud -Date: Thu, 19 Aug 2021 10:51:01 +0200 -Subject: [PATCH] ipatests: use whole date for journalctl --since - -When a test is executed around midnight and is checking the -journal content with --since=date, it needs to specify the -whole date (with day and time) to avoid missing entries. - -If for instance --since=23:59:00 is used and the current time is -now 00:01:00, --since=23:59:00 would refer to a date in the -future and no journal entry will be found. - -Fixes: https://pagure.io/freeipa/issue/8953 -Reviewed-By: Stanislav Levin -Reviewed-By: Francois Cami ---- - ipatests/test_integration/test_cert.py | 2 +- - ipatests/test_integration/test_commands.py | 3 ++- - ipatests/test_integration/test_nfs.py | 2 +- - 3 files changed, 4 insertions(+), 3 deletions(-) - -diff --git a/ipatests/test_integration/test_cert.py b/ipatests/test_integration/test_cert.py -index 9a90db5e2..7d51b76ee 100644 ---- a/ipatests/test_integration/test_cert.py -+++ b/ipatests/test_integration/test_cert.py -@@ -69,7 +69,7 @@ class TestInstallMasterClient(IntegrationTest): - - # time to look into journal logs in - # test_certmonger_ipa_responder_jsonrpc -- cls.since = time.strftime('%H:%M:%S') -+ cls.since = time.strftime('%Y-%m-%d %H:%M:%S') - - def test_cacert_file_appear_with_option_F(self): - """Test if getcert creates cacert file with -F option -diff --git a/ipatests/test_integration/test_commands.py b/ipatests/test_integration/test_commands.py -index 4d9a81652..fd5d1b472 100644 ---- a/ipatests/test_integration/test_commands.py -+++ b/ipatests/test_integration/test_commands.py -@@ -1208,7 +1208,8 @@ class TestIPACommand(IntegrationTest): - # start to look at logs a bit before "now" - # https://pagure.io/freeipa/issue/8432 - since = time.strftime( -- '%H:%M:%S', (datetime.now() - timedelta(seconds=10)).timetuple() -+ '%Y-%m-%d %H:%M:%S', -+ (datetime.now() - timedelta(seconds=10)).timetuple() - ) - - password = 'WrongPassword' -diff --git a/ipatests/test_integration/test_nfs.py b/ipatests/test_integration/test_nfs.py -index 9a6153409..dc53a6da9 100644 ---- a/ipatests/test_integration/test_nfs.py -+++ b/ipatests/test_integration/test_nfs.py -@@ -130,7 +130,7 @@ class TestNFS(IntegrationTest): - nfsclt = self.clients[1] - - # for journalctl --since -- since = time.strftime('%H:%M:%S') -+ since = time.strftime('%Y-%m-%d %H:%M:%S') - nfsclt.run_command(["systemctl", "restart", "rpc-gssd"]) - time.sleep(WAIT_AFTER_INSTALL) - mountpoints = ("/mnt/krb", "/mnt/std", "/home") --- -2.31.1 - diff --git a/SOURCES/0051-selinux-policy-allow-custodia-to-access-proc-cpuinfo.patch b/SOURCES/0051-selinux-policy-allow-custodia-to-access-proc-cpuinfo.patch deleted file mode 100644 index d06f248..0000000 --- a/SOURCES/0051-selinux-policy-allow-custodia-to-access-proc-cpuinfo.patch +++ /dev/null @@ -1,41 +0,0 @@ -From 07e2bf732f54f936cccc4e0c7b468d77f97e911a Mon Sep 17 00:00:00 2001 -From: Florence Blanc-Renaud -Date: Mon, 30 Aug 2021 18:40:24 +0200 -Subject: [PATCH] selinux policy: allow custodia to access /proc/cpuinfo - -On aarch64, custodia creates AVC when accessing /proc/cpuinfo. - -According to gcrypt manual -(https://gnupg.org/documentation/manuals/gcrypt/Configuration.html), -/proc/cpuinfo is used on ARM architecture to read the hardware -capabilities of the CPU. This explains why the issue happens only -on aarch64. - -audit2allow suggests to add the following: -allow ipa_custodia_t proc_t:file { getattr open read }; - -but this policy would be too broad. Instead, the patch is using -the interface kernel_read_system_state. - -Fixes: https://pagure.io/freeipa/issue/8972 -Signed-off-by: Florence Blanc-Renaud -Reviewed-By: Christian Heimes ---- - selinux/ipa.te | 1 + - 1 file changed, 1 insertion(+) - -diff --git a/selinux/ipa.te b/selinux/ipa.te -index 68e10941951ac391fda7854d1403558c069dad46..7492fca04d4f0d031ecd83871078247d73cc87e0 100644 ---- a/selinux/ipa.te -+++ b/selinux/ipa.te -@@ -364,6 +364,7 @@ files_tmp_filetrans(ipa_custodia_t, ipa_custodia_tmp_t, { dir file }) - - kernel_dgram_send(ipa_custodia_t) - kernel_read_network_state(ipa_custodia_t) -+kernel_read_system_state(ipa_custodia_t) - - auth_read_passwd(ipa_custodia_t) - --- -2.31.1 - diff --git a/SOURCES/0052-extdom-return-LDAP_NO_SUCH_OBJECT-if-domains-differ.patch b/SOURCES/0052-extdom-return-LDAP_NO_SUCH_OBJECT-if-domains-differ.patch deleted file mode 100644 index e8dfa24..0000000 --- a/SOURCES/0052-extdom-return-LDAP_NO_SUCH_OBJECT-if-domains-differ.patch +++ /dev/null @@ -1,46 +0,0 @@ -From 4fca95751ca32a1ed16a6d8a4e557c5799ec5c78 Mon Sep 17 00:00:00 2001 -From: Sumit Bose -Date: Wed, 25 Aug 2021 17:10:29 +0200 -Subject: [PATCH] extdom: return LDAP_NO_SUCH_OBJECT if domains differ - -If a client sends a request to lookup an object from a given trusted -domain by UID or GID and an object with matching ID is only found in a -different domain the extdom should return LDAP_NO_SUCH_OBJECT to -indicate to the client that the requested ID does not exists in the -given domain. - -Resolves: https://pagure.io/freeipa/issue/8965 -Reviewed-By: Rob Crittenden ---- - .../ipa-extdom-extop/ipa_extdom_common.c | 8 ++++++-- - 1 file changed, 6 insertions(+), 2 deletions(-) - -diff --git a/daemons/ipa-slapi-plugins/ipa-extdom-extop/ipa_extdom_common.c b/daemons/ipa-slapi-plugins/ipa-extdom-extop/ipa_extdom_common.c -index 5d97ff6137d9d660f6121f468261c6878a9aa12a..6f646b9f49ef31e1872e87640c524db972e53b6d 100644 ---- a/daemons/ipa-slapi-plugins/ipa-extdom-extop/ipa_extdom_common.c -+++ b/daemons/ipa-slapi-plugins/ipa-extdom-extop/ipa_extdom_common.c -@@ -542,7 +542,9 @@ int pack_ber_user(struct ipa_extdom_ctx *ctx, - if (strcasecmp(locat+1, domain_name) == 0 ) { - locat[0] = '\0'; - } else { -- ret = LDAP_INVALID_SYNTAX; -+ /* The found object is from a different domain than requested, -+ * that means it does not exist in the requested domain */ -+ ret = LDAP_NO_SUCH_OBJECT; - goto done; - } - } -@@ -655,7 +657,9 @@ int pack_ber_group(enum response_types response_type, - if (strcasecmp(locat+1, domain_name) == 0 ) { - locat[0] = '\0'; - } else { -- ret = LDAP_INVALID_SYNTAX; -+ /* The found object is from a different domain than requested, -+ * that means it does not exist in the requested domain */ -+ ret = LDAP_NO_SUCH_OBJECT; - goto done; - } - } --- -2.31.1 - diff --git a/SOURCES/0053-subid-subid-match-display-the-owner-s-ID-not-DN.patch b/SOURCES/0053-subid-subid-match-display-the-owner-s-ID-not-DN.patch deleted file mode 100644 index a36c923..0000000 --- a/SOURCES/0053-subid-subid-match-display-the-owner-s-ID-not-DN.patch +++ /dev/null @@ -1,35 +0,0 @@ -From 4785a90946ec694ccc082f062b2181b23c7099e3 Mon Sep 17 00:00:00 2001 -From: =?UTF-8?q?Fran=C3=A7ois=20Cami?= -Date: Thu, 2 Sep 2021 16:17:01 +0200 -Subject: [PATCH] subid: subid-match: display the owner's ID not DN -MIME-Version: 1.0 -Content-Type: text/plain; charset=UTF-8 -Content-Transfer-Encoding: 8bit - -Previously, the subid-match command would output the full -DN of the owner of the matched range. -With this change, the UID of the owner is displayed, just like -for other subid- commands. - -Fixes: https://github.com/freeipa/freeipa/pull/6001 -Signed-off-by: François Cami -Reviewed-By: Rob Crittenden ---- - ipaserver/plugins/subid.py | 1 + - 1 file changed, 1 insertion(+) - -diff --git a/ipaserver/plugins/subid.py b/ipaserver/plugins/subid.py -index 440f24ee627f0736100f63026158c564b04520c2..132c85c7f198217ba70f2332306ee2550be86035 100644 ---- a/ipaserver/plugins/subid.py -+++ b/ipaserver/plugins/subid.py -@@ -524,6 +524,7 @@ class subid_match(subid_find): - osubuid = options["ipasubuidnumber"] - new_entries = [] - for entry in entries: -+ self.obj.convert_owner(entry, options) - esubuid = int(entry.single_value["ipasubuidnumber"]) - esubcount = int(entry.single_value["ipasubuidcount"]) - minsubuid = esubuid --- -2.31.1 - diff --git a/SOURCES/0054-migrate-ds-workaround-to-detect-compat-tree.patch b/SOURCES/0054-migrate-ds-workaround-to-detect-compat-tree.patch deleted file mode 100644 index 16dac6f..0000000 --- a/SOURCES/0054-migrate-ds-workaround-to-detect-compat-tree.patch +++ /dev/null @@ -1,37 +0,0 @@ -From 3c4f9e7347965ff9a887147df34e720224ffa7cc Mon Sep 17 00:00:00 2001 -From: Florence Blanc-Renaud -Date: Tue, 7 Sep 2021 17:06:53 +0200 -Subject: [PATCH] migrate-ds: workaround to detect compat tree - -Migrate-ds needs to check if compat tree is enabled before -migrating users and groups. The check is doing a base -search on cn=compat,$SUFFIX and considers the compat tree -enabled when the entry exists. - -Due to a bug in slapi-nis, the base search may return NotFound -even though the compat tree is enabled. The workaround is to -perform a base search on cn=users,cn=compat,$SUFFIX instead. - -Fixes: https://pagure.io/freeipa/issue/8984 -Reviewed-By: Alexander Bokovoy ---- - ipaserver/plugins/migration.py | 3 ++- - 1 file changed, 2 insertions(+), 1 deletion(-) - -diff --git a/ipaserver/plugins/migration.py b/ipaserver/plugins/migration.py -index db5241915497b14a12ed2c33003e1c4fc1a5369f..6ee205fc836a463ac250baa6131e43acb0c00efa 100644 ---- a/ipaserver/plugins/migration.py -+++ b/ipaserver/plugins/migration.py -@@ -922,7 +922,8 @@ migration process might be incomplete\n''') - # check whether the compat plugin is enabled - if not options.get('compat'): - try: -- ldap.get_entry(DN(('cn', 'compat'), (api.env.basedn))) -+ ldap.get_entry(DN(('cn', 'users'), ('cn', 'compat'), -+ (api.env.basedn))) - return dict(result={}, failed={}, enabled=True, compat=False) - except errors.NotFound: - pass --- -2.31.1 - diff --git a/SOURCES/0055-Don-t-store-entries-with-a-usercertificate-in-the-LD.patch b/SOURCES/0055-Don-t-store-entries-with-a-usercertificate-in-the-LD.patch deleted file mode 100644 index b9c02d6..0000000 --- a/SOURCES/0055-Don-t-store-entries-with-a-usercertificate-in-the-LD.patch +++ /dev/null @@ -1,60 +0,0 @@ -From be1e3bbfc13aff9a583108376f245b81cc3666fb Mon Sep 17 00:00:00 2001 -From: Rob Crittenden -Date: Thu, 9 Sep 2021 15:26:55 -0400 -Subject: [PATCH] Don't store entries with a usercertificate in the LDAP cache - -usercertificate often has a subclass and both the plain and -subclassed (binary) values are queried. I'm concerned that -they are used more or less interchangably in places so not -caching these entries is the safest path forward for now until -we can dedicate the time to find all usages, determine their -safety and/or perhaps handle this gracefully within the cache -now. - -What we see in this bug is that usercertificate;binary holds the -first certificate value but a user-mod is done with -setattr usercertificate=. Since there is no -usercertificate value (remember, it's usercertificate;binary) -a replace is done and 389-ds wipes the existing value as we've -asked it to. - -I'm not comfortable with simply treating them the same because -in LDAP they are not. - -https://pagure.io/freeipa/issue/8986 - -Signed-off-by: Rob Crittenden -Reviewed-By: Francois Cami -Reviewed-By: Fraser Tweedale ---- - ipapython/ipaldap.py | 14 +++++++++++--- - 1 file changed, 11 insertions(+), 3 deletions(-) - -diff --git a/ipapython/ipaldap.py b/ipapython/ipaldap.py -index f94b784d680f33d026e4d56ec8627d4d2ab87931..ced8f1bd66dc8f1f5c206677d2725d1e72b489f9 100644 ---- a/ipapython/ipaldap.py -+++ b/ipapython/ipaldap.py -@@ -1821,9 +1821,17 @@ class LDAPCache(LDAPClient): - entry=None, exception=None): - # idnsname - caching prevents delete when mod value to None - # cospriority - in a Class of Service object, uncacheable -- # TODO - usercertificate was banned at one point and I don't remember -- # why... -- BANNED_ATTRS = {'idnsname', 'cospriority'} -+ # usercertificate* - caching subtypes is tricky, trade less -+ # complexity for performance -+ # -+ # TODO: teach the cache about subtypes -+ -+ BANNED_ATTRS = { -+ 'idnsname', -+ 'cospriority', -+ 'usercertificate', -+ 'usercertificate;binary' -+ } - if not self._enable_cache: - return - --- -2.31.1 - diff --git a/SOURCES/0056-ipatests-Test-that-a-user-can-be-issued-multiple-cer.patch b/SOURCES/0056-ipatests-Test-that-a-user-can-be-issued-multiple-cer.patch deleted file mode 100644 index db49c3c..0000000 --- a/SOURCES/0056-ipatests-Test-that-a-user-can-be-issued-multiple-cer.patch +++ /dev/null @@ -1,68 +0,0 @@ -From 86588640137562b2016fdb0f91142d00bc38e54a Mon Sep 17 00:00:00 2001 -From: Rob Crittenden -Date: Fri, 10 Sep 2021 09:01:48 -0400 -Subject: [PATCH] ipatests: Test that a user can be issued multiple - certificates - -Prevent regressions in the LDAP cache layer that caused newly -issued certificates to overwrite existing ones. - -https://pagure.io/freeipa/issue/8986 - -Signed-off-by: Rob Crittenden -Reviewed-By: Francois Cami -Reviewed-By: Fraser Tweedale ---- - ipatests/test_integration/test_cert.py | 29 ++++++++++++++++++++++++++ - 1 file changed, 29 insertions(+) - -diff --git a/ipatests/test_integration/test_cert.py b/ipatests/test_integration/test_cert.py -index 7d51b76ee347237450b7484cf48c2e6a1bed7f7d..b4e85eadcf41212fdd16f0f3aa130a916b5019fa 100644 ---- a/ipatests/test_integration/test_cert.py -+++ b/ipatests/test_integration/test_cert.py -@@ -16,6 +16,7 @@ import string - import time - - from ipaplatform.paths import paths -+from ipapython.dn import DN - from cryptography import x509 - from cryptography.x509.oid import ExtensionOID - from cryptography.hazmat.backends import default_backend -@@ -183,6 +184,34 @@ class TestInstallMasterClient(IntegrationTest): - ) - assert "profile: caServerCert" in result.stdout_text - -+ def test_multiple_user_certificates(self): -+ """Test that a user may be issued multiple certificates""" -+ ldap = self.master.ldap_connect() -+ -+ user = 'user1' -+ -+ tasks.kinit_admin(self.master) -+ tasks.user_add(self.master, user) -+ -+ for id in (0,1): -+ csr_file = f'{id}.csr' -+ key_file = f'{id}.key' -+ cert_file = f'{id}.crt' -+ openssl_cmd = [ -+ 'openssl', 'req', '-newkey', 'rsa:2048', '-keyout', key_file, -+ '-nodes', '-out', csr_file, '-subj', '/CN=' + user] -+ self.master.run_command(openssl_cmd) -+ -+ cmd_args = ['ipa', 'cert-request', '--principal', user, -+ '--certificate-out', cert_file, csr_file] -+ self.master.run_command(cmd_args) -+ -+ # easier to count by pulling the LDAP entry -+ entry = ldap.get_entry(DN(('uid', user), ('cn', 'users'), -+ ('cn', 'accounts'), self.master.domain.basedn)) -+ -+ assert len(entry.get('usercertificate')) == 2 -+ - @pytest.fixture - def test_subca_certs(self): - """ --- -2.31.1 - diff --git a/SOURCES/0057-Parse-getStatus-as-JSON-not-XML.patch b/SOURCES/0057-Parse-getStatus-as-JSON-not-XML.patch deleted file mode 100644 index e3cae57..0000000 --- a/SOURCES/0057-Parse-getStatus-as-JSON-not-XML.patch +++ /dev/null @@ -1,56 +0,0 @@ -From 7fb95cc638b1c9b7f2e9a67dba859ef8126f2c5f Mon Sep 17 00:00:00 2001 -From: Chris Kelley -Date: Tue, 27 Jul 2021 21:57:26 +0100 -Subject: [PATCH] Parse getStatus as JSON not XML - -On dogtagpki/pki master XML is being replaced by JSON, getStatus will -return JSON in PKI 11.0+ - -The PR for dogtagpki/pki that makes this change necessary is: -https://github.com/dogtagpki/pki/pull/3674 - -Reviewed-By: Francois Cami -Reviewed-By: Rob Crittenden ---- - install/tools/ipa-pki-wait-running.in | 18 ++++++++++++++---- - 1 file changed, 14 insertions(+), 4 deletions(-) - -diff --git a/install/tools/ipa-pki-wait-running.in b/install/tools/ipa-pki-wait-running.in -index 4f0f2f34a7b0a43210676e7fd50e7029e798f301..9ca6e974e55a4d68afd06e1d9c7b67c5f926e48c 100644 ---- a/install/tools/ipa-pki-wait-running.in -+++ b/install/tools/ipa-pki-wait-running.in -@@ -13,6 +13,7 @@ import logging - import sys - import time - from xml.etree import ElementTree -+import json - - from ipalib import api - from ipaplatform.paths import paths -@@ -74,10 +75,19 @@ def get_status(conn, timeout): - """ - client = SystemStatusClient(conn) - response = client.get_status(timeout=timeout) -- root = ElementTree.fromstring(response) -- status = root.findtext("Status") -- error = root.findtext("Error") -- logging.debug("Got status '%s', error '%s'", status, error) -+ status = None -+ error = None -+ try: -+ json_response = json.loads(response) -+ status = json_response['Response']['Status'] -+ except KeyError as e: -+ error = repr(e) -+ except json.JSONDecodeError: -+ logger.debug("Response is not valid JSON, try XML") -+ root = ElementTree.fromstring(response) -+ status = root.findtext("Status") -+ error = root.findtext("Error") -+ logger.debug("Got status '%s', error '%s'", status, error) - return status, error - - --- -2.31.1 - diff --git a/SOURCES/0058-Parse-cert-chain-as-JSON-not-XML.patch b/SOURCES/0058-Parse-cert-chain-as-JSON-not-XML.patch deleted file mode 100644 index ca959dd..0000000 --- a/SOURCES/0058-Parse-cert-chain-as-JSON-not-XML.patch +++ /dev/null @@ -1,79 +0,0 @@ -From 40f76a53f78267b4d2b890defa3e4f7d27fdfb7a Mon Sep 17 00:00:00 2001 -From: Chris Kelley -Date: Thu, 5 Aug 2021 12:00:15 +0100 -Subject: [PATCH] Parse cert chain as JSON not XML - -On dogtagpki/pki master XML is being replaced by JSON in PKI 11.0+ - -The PR for dogtagpki/pki that makes this change necessary is: -https://github.com/dogtagpki/pki/pull/3677 - -Reviewed-By: Rob Crittenden ---- - ipapython/dogtag.py | 28 +++++++++++++++++++--------- - 1 file changed, 19 insertions(+), 9 deletions(-) - -diff --git a/ipapython/dogtag.py b/ipapython/dogtag.py -index 0503938fb9783d397cc7366339bb9fab48033985..8f0f0473ae313edb17e10de8b2ca7f43f231e706 100644 ---- a/ipapython/dogtag.py -+++ b/ipapython/dogtag.py -@@ -20,6 +20,7 @@ - import collections - import gzip - import io -+import json - import logging - from urllib.parse import urlencode - import xml.dom.minidom -@@ -100,6 +101,10 @@ def get_ca_certchain(ca_host=None): - data = res.read() - conn.close() - try: -+ doc = json.loads(data) -+ chain = doc['Response']['ChainBase64'] -+ except (json.JSONDecodeError, KeyError): -+ logger.debug("Response is not valid JSON, try XML") - doc = xml.dom.minidom.parseString(data) - try: - item_node = doc.getElementsByTagName("ChainBase64") -@@ -107,9 +112,9 @@ def get_ca_certchain(ca_host=None): - except IndexError: - raise error_from_xml( - doc, _("Retrieving CA cert chain failed: %s")) -- finally: -- if doc: -- doc.unlink() -+ finally: -+ if doc: -+ doc.unlink() - else: - raise errors.RemoteRetrieveError( - reason=_("request failed with HTTP status %d") % res.status) -@@ -118,13 +123,18 @@ def get_ca_certchain(ca_host=None): - - - def _parse_ca_status(body): -- doc = xml.dom.minidom.parseString(body) - try: -- item_node = doc.getElementsByTagName("XMLResponse")[0] -- item_node = item_node.getElementsByTagName("Status")[0] -- return item_node.childNodes[0].data -- except IndexError: -- raise error_from_xml(doc, _("Retrieving CA status failed: %s")) -+ doc = json.loads(body) -+ return doc['Response']['Status'] -+ except (json.JSONDecodeError, KeyError): -+ logger.debug("Response is not valid JSON, try XML") -+ doc = xml.dom.minidom.parseString(body) -+ try: -+ item_node = doc.getElementsByTagName("XMLResponse")[0] -+ item_node = item_node.getElementsByTagName("Status")[0] -+ return item_node.childNodes[0].data -+ except IndexError: -+ raise error_from_xml(doc, _("Retrieving CA status failed: %s")) - - - def ca_status(ca_host=None): --- -2.31.1 - diff --git a/SOURCES/0059-Specify-PKI-installation-log-paths.patch b/SOURCES/0059-Specify-PKI-installation-log-paths.patch deleted file mode 100644 index 44af243..0000000 --- a/SOURCES/0059-Specify-PKI-installation-log-paths.patch +++ /dev/null @@ -1,84 +0,0 @@ -From 5abf1bc79f8b32c6638ff98fbe2e4a8dec9a5010 Mon Sep 17 00:00:00 2001 -From: "Endi S. Dewata" -Date: Thu, 12 Aug 2021 13:26:42 -0500 -Subject: [PATCH] Specify PKI installation log paths - -The DogtagInstance.spawn_instance() and uninstall() have -been modified to specify the paths of PKI installation -logs using --log-file option on PKI 11.0.0 or later. - -This allows IPA to have a full control over the log files -instead of relying on PKI's default log files. - -Fixes: https://pagure.io/freeipa/issue/8966 -Signed-off-by: Endi Sukma Dewata ---- - ipaserver/install/dogtaginstance.py | 35 ++++++++++++++++++++++++++--- - 1 file changed, 32 insertions(+), 3 deletions(-) - -diff --git a/ipaserver/install/dogtaginstance.py b/ipaserver/install/dogtaginstance.py -index 644acd4eacea22f41a7cd36b54553d6d7cd22690..0d9aebb542f242b81315edd016699697f2fc4091 100644 ---- a/ipaserver/install/dogtaginstance.py -+++ b/ipaserver/install/dogtaginstance.py -@@ -36,8 +36,10 @@ from configparser import DEFAULTSECT, ConfigParser, RawConfigParser - - import six - -+import pki - from pki.client import PKIConnection - import pki.system -+import pki.util - - from ipalib import api, errors, x509 - from ipalib.install import certmonger -@@ -202,6 +204,18 @@ class DogtagInstance(service.Service): - "-f", cfg_file, - "--debug"] - -+ # specify --log-file on PKI 11.0.0 or later -+ -+ pki_version = pki.util.Version(pki.specification_version()) -+ if pki_version >= pki.util.Version("11.0.0"): -+ timestamp = time.strftime( -+ "%Y%m%d%H%M%S", -+ time.localtime(time.time())) -+ log_file = os.path.join( -+ paths.VAR_LOG_PKI_DIR, -+ "pki-%s-spawn.%s.log" % (self.subsystem.lower(), timestamp)) -+ args.extend(["--log-file", log_file]) -+ - with open(cfg_file) as f: - logger.debug( - 'Contents of pkispawn configuration file (%s):\n%s', -@@ -290,10 +304,25 @@ class DogtagInstance(service.Service): - if self.is_installed(): - self.print_msg("Unconfiguring %s" % self.subsystem) - -+ args = [paths.PKIDESTROY, -+ "-i", "pki-tomcat", -+ "-s", self.subsystem] -+ -+ # specify --log-file on PKI 11.0.0 or later -+ -+ pki_version = pki.util.Version(pki.specification_version()) -+ if pki_version >= pki.util.Version("11.0.0"): -+ timestamp = time.strftime( -+ "%Y%m%d%H%M%S", -+ time.localtime(time.time())) -+ log_file = os.path.join( -+ paths.VAR_LOG_PKI_DIR, -+ "pki-%s-destroy.%s.log" % (self.subsystem.lower(), timestamp)) -+ args.extend(["--log-file", log_file]) -+ - try: -- ipautil.run([paths.PKIDESTROY, -- "-i", 'pki-tomcat', -- "-s", self.subsystem]) -+ ipautil.run(args) -+ - except ipautil.CalledProcessError as e: - logger.critical("failed to uninstall %s instance %s", - self.subsystem, e) --- -2.31.1 - diff --git a/SOURCES/0060-Make-Dogtag-return-XML-for-ipa-cert-find.patch b/SOURCES/0060-Make-Dogtag-return-XML-for-ipa-cert-find.patch deleted file mode 100644 index 4b5a221..0000000 --- a/SOURCES/0060-Make-Dogtag-return-XML-for-ipa-cert-find.patch +++ /dev/null @@ -1,33 +0,0 @@ -From d43b513927d6dd0a12464dd24287ce40ccaf33e4 Mon Sep 17 00:00:00 2001 -From: Chris Kelley -Date: Fri, 10 Sep 2021 16:47:22 +0100 -Subject: [PATCH] Make Dogtag return XML for ipa cert-find - -Using JSON by default within Dogtag appears to cause ipa cert-find to -return JSON, when the request was made with XML. We can request that XML -is returned as before by specifying so in the request header. - -Fixes: https://pagure.io/freeipa/issue/8980 -Signed-off-by: Chris Kelley -Reviewed-By: Francois Cami ---- - ipaserver/plugins/dogtag.py | 3 ++- - 1 file changed, 2 insertions(+), 1 deletion(-) - -diff --git a/ipaserver/plugins/dogtag.py b/ipaserver/plugins/dogtag.py -index be2e4bb4e2a1b96c1bff6056da30c704c36789f3..b4feddfac19a4c5659d29bf7b6f5fd9b1247524c 100644 ---- a/ipaserver/plugins/dogtag.py -+++ b/ipaserver/plugins/dogtag.py -@@ -1832,7 +1832,8 @@ class ra(rabase.rabase, RestClient): - method='POST', - headers={'Accept-Encoding': 'gzip, deflate', - 'User-Agent': 'IPA', -- 'Content-Type': 'application/xml'}, -+ 'Content-Type': 'application/xml', -+ 'Accept': 'application/xml'}, - body=payload - ) - --- -2.31.1 - diff --git a/SOURCES/freeipa-4.9.6.tar.gz.asc b/SOURCES/freeipa-4.9.6.tar.gz.asc deleted file mode 100644 index f71d351..0000000 --- a/SOURCES/freeipa-4.9.6.tar.gz.asc +++ /dev/null @@ -1,16 +0,0 @@ ------BEGIN PGP SIGNATURE----- - -iQIzBAABCgAdFiEEhAodHH8+xLL+UwQ1RxniuKu/YhoFAmDbPRQACgkQRxniuKu/ -Yhr7uBAAnpF70nH8Cn/HhKKpfafPoN3B9fDNIfAa+jsJ52OyeNMKVNi4MEob32iN -1aMGGFCJUMle/M7v1+w8WH59eiHs1jKHcFZnl2R4Ap5SxVtypYT+ewXbNnSHII2w -qWS5PvLkJwjh6Bw/HlyBwDRSrw9Yah4oZZbJt3zE06+Imr8BpB3IWqyhuAi7FjYO -J9hHCwCvtJvWK4yplZSXCt8OS1JA68/Djgjecm5lUSamuqKaBVhDb+ZAPLDJpBf5 -Pz2JpUF/W/rplt+Q9wAFdhDB9iC0vd3MBkgs4KPsjuyS9+GGNu8LyXs0C1Wm/VgX -liX2pjZmpnTrhH3QQ2nufwH784ZpinXxS2fcbvCfX1Utgr77wNHjwqDt2NBffJl1 -BM7JJr1ZwGOGSki6yjRDXbeSAsiEX9l7f2mv2t/8ZjHMRJ7mJmBbmh5Qhk5qsMou -BptNDE20cG77xcjBtTCDpii/UatETuNAyMd/l2smfe76z8y61fQrvScxRwOCHckw -u/ERChpBZOUlQt59Efj3ja313oXZMxXRw01n/72Hh5rnk+XZf75zQ1zUDBYnwzAr -4cdqyrfpFkQu1sRQvgjT8ZLkP8istjRdVEI/Oj61zb5+6+scQ/Zh/R/mYGCV4/h+ -RzojBwUAXuwUMrj1jTbb5Lkz58+vY3Lk4xNOY2hSAc8rCcDVRZY= -=TQFs ------END PGP SIGNATURE----- diff --git a/SOURCES/freeipa-4.9.8.tar.gz.asc b/SOURCES/freeipa-4.9.8.tar.gz.asc new file mode 100644 index 0000000..c14420c --- /dev/null +++ b/SOURCES/freeipa-4.9.8.tar.gz.asc @@ -0,0 +1,16 @@ +-----BEGIN PGP SIGNATURE----- + +iQIzBAABCAAdFiEE11Z2TU1+KXxtrRFyaYdvcqbi008FAmGf1XcACgkQaYdvcqbi +00/kMQ//Vano94V0/L3YsLaqKiFcGo/py5pTq1Os3wB9zzCYSuU0P/eajuHLBYNe +MfxecZihFFlmUdNooNWbewT4CE0ey1qFLwPfGXuLrse6fXVLLaYnAv2mkPUmDSpM +XfXO0PFU0BtdkMAUsdUATngPCpQzYjVUKsAMwPovi3UcLzFZ8tWJKMA55urhwC4q +E042wPLqzcX6Ee5JBSBkfNe35vG2LY7o3Ynh8SVCee2lBJvdWiuFT5XRhybXUsOp +q3eTsVPz68p7CvOrjlLSsWPP0nbGF1O1UQsN+oaDZAav1Nx8lTOlxUCUQXWbs2X6 +BTUAOmZ6VjYu61sNgNSj+BSHlHIT3uRJ55JO5nLH/hLm0Oxn6SGRTVMueqV376QA +CsIk7UrdcX9QUtu70eRxuu1aAWJ5eaF4GDWnFP+62wzd/d6LjWEE+9kXgvrcTF0C +UzjWrmbI8x23bB4kqcROHz8lryMsBpZ94QKPHVppMiPgapDKRkculYkSeRLboADi +q4mh2prkDSq9diWV4HvZTGwPU77oiLrQsvbGuvwD62PAlyQ4rZpfW3FllTL2Lcxy +urA8a9UnQWQtDOsZIyxmMJ7R04gjI5fZfDhq6S09L9MfjFEKjsqO4FzXamj+SbAo +w25sIp1qT0sV1vOt+/R/HYSIyggQyTZpQJu5UB34QLqpfDdUwFg= +=t9up +-----END PGP SIGNATURE----- diff --git a/SPECS/freeipa.spec b/SPECS/freeipa.spec index 23476ad..3c5e656 100644 --- a/SPECS/freeipa.spec +++ b/SPECS/freeipa.spec @@ -1,3 +1,5 @@ +%define ipa_requires_gt() %(LC_ALL="C" echo '%*' | xargs -r rpm -q --qf 'Requires: %%{name} >= %%{epoch}:%%{version}-%%{release}\\n' | sed -e 's/ (none):/ /' -e 's/ 0:/ /' | grep -v "is not") + # ipatests enabled by default, can be disabled with --without ipatests %bcond_without ipatests # default to not use XML-RPC in Rawhide, can be turned around with --with ipa_join_xml @@ -69,7 +71,7 @@ # 0.7.16: https://github.com/drkjam/netaddr/issues/71 %global python_netaddr_version 0.7.19 # Require 4.7.0 which brings Python 3 bindings -%global samba_version 4.12.3-12 +%global samba_version 4.15.2-2 %global selinux_policy_version 3.14.3-52 %global slapi_nis_version 0.56.4 %global python_ldap_version 3.1.0-1 @@ -111,7 +113,7 @@ %if 0%{?fedora} < 34 %global ds_version 1.4.4.16-1 %else -%global ds_version 2.0.5-1 +%global ds_version 2.0.7-1 %endif # Fix for TLS 1.3 PHA, RHBZ#1775146 @@ -183,7 +185,7 @@ # Work-around fact that RPM SPEC parser does not accept # "Version: @VERSION@" in freeipa.spec.in used for Autoconf string replacement -%define IPA_VERSION 4.9.6 +%define IPA_VERSION 4.9.8 # Release candidate version -- uncomment with one percent for RC versions #%%global rc_version %%nil %define AT_SIGN @ @@ -196,7 +198,7 @@ Name: %{package_name} Version: %{IPA_VERSION} -Release: 9%{?rc_version:.%rc_version}%{?dist} +Release: 1%{?rc_version:.%rc_version}%{?dist} Summary: The Identity, Policy and Audit system License: GPLv3+ @@ -216,66 +218,8 @@ Source1: https://releases.pagure.org/freeipa/freeipa-%{version}%{?rc_vers # RHEL spec file only: START %if %{NON_DEVELOPER_BUILD} %if 0%{?rhel} >= 8 -Patch0001: 0001-Remove-unneeded-dependency-on-python-coverage.patch -Patch0002: 0002-Add-checks-to-prevent-adding-auth-indicators-to-inte.patch -Patch0003: 0003-ipatests-ensure-auth-indicators-can-t-be-added-to-in.patch -Patch0004: 0004-stageuser-add-ipauserauthtypeclass-when-required.patch -Patch0005: 0005-XMLRPC-test-add-a-test-for-stageuser-add-user-auth-t.patch -Patch0006: 0006-augeas-bump-version-for-rhel9.patch -Patch0007: 0007-man-page-update-ipa-server-upgrade.1.patch -Patch0008: 0008-Add-basic-support-for-subordinate-user-group-ids.patch -Patch0009: 0009-Redesign-subid-feature.patch -Patch0010: 0010-Use-389-DS-dnaInterval-setting-to-assign-intervals.patch -Patch0011: 0011-Fix-ipa-server-upgrade.patch -Patch0012: 0012-Fix-oid-of-ipaUserDefaultSubordinateId.patch -Patch0013: 0013-WebUI-Improve-subordinate-ids-user-workflow.patch -Patch0014: 0014-Test-DNA-plugin-configuration.patch -Patch0015: 0015-Fall-back-to-krbprincipalname-when-validating-host-a.patch -Patch0016: 0016-spec-file-Trust-controller-role-should-pull-sssd-win.patch -Patch0017: 0017-Use-new-method-in-check-to-prevent-removal-of-last-K.patch -Patch0018: 0018-ipatests-test-removing-last-KRA-when-it-is-not-runni.patch -Patch0019: 0019-rhel-platform-add-a-named-crypto-policy-support.patch -Patch0020: 0020-Index-Fix-definition-for-memberOf.patch -Patch0021: 0021-ipatests-use-whole-date-when-calling-journalctl-sinc.patch -Patch0022: 0022-ipatests-Fix-for-test_source_ipahealthcheck_ipa_host.patch -Patch0023: 0023-ipatests-test_ipahealthcheck-print-a-message-if-a-sy.patch -Patch0024: 0024-ipatests-test_installation-move-tracking_reqs-depend.patch -Patch0025: 0025-webui-tests-close-notification-when-revoking-cert.patch -Patch0026: 0026-ipatests-Test-ipa-cert-fix-warns-when-startup-direct.patch -Patch0027: 0027-webui-tests-fix-algo-for-finding-available-idrange.patch -Patch0028: 0028-ipatests-smbclient-k-use-kerberos-desired.patch -Patch0029: 0029-test_acme-refactor-with-tasks.patch -Patch0030: 0030-test_acme-make-password-renewal-more-robust.patch -Patch0031: 0031-tasks.py-fix-flake8-reported-issues.patch -Patch0032: 0032-Fix-ldapupdate.get_sub_dict-for-missing-named-user.patch -Patch0033: 0033-freeipa.spec.in-remove-python3-pexpect-from-Requires.patch -Patch0034: 0034-ipa-getkeytab-add-option-to-discover-servers-using-D.patch -Patch0035: 0035-ipa-getkeytab-fix-compiler-warnings.patch -Patch0036: 0036-ipatests-test-ipa-getkeytab-server-option.patch -Patch0037: 0037-ipatests-Test-for-OTP-when-the-LDAP-connection-timed.patch -Patch0038: 0038-ipatests-verify-that-getcert-output-includes-the-iss.patch -Patch0039: 0039-ipatests-Look-for-warning-into-stderr-instead-of-std.patch -Patch0040: 0040-ipatests-use-krb5_trace-in-TestIpaAdTrustInstall.patch -Patch0041: 0041-ipatests-Test-ldapsearch-with-base-scope-works-with-.patch -Patch0042: 0042-ipatests-skip-test_basesearch_compat_tree-on-fedora.patch -Patch0043: 0043-ipatests-Refactor-test_check_otpd_after_idle_timeout.patch -Patch0044: 0044-ipatests-Test-unsecure-nsupdate.patch -Patch0045: 0045-ipatests-Fix-TestAJPSecretUpgrade-tests-on-systems-w.patch -Patch0046: 0046-ipatests-test_ipahealthcheck-Verify-permissions-for-.patch -Patch0047: 0047-ipatests-test-to-renew-certs-on-replica-using-ipa-ce.patch -Patch0048: 0048-ipatests-wait-while-http-ldap-pkinit-cert-get-renew-.patch -Patch0049: 0049-ipatests-refactor-test_ipa_cert_fix-with-tasks.patch -Patch0050: 0050-ipatests-use-whole-date-for-journalctl-since.patch -Patch0051: 0051-selinux-policy-allow-custodia-to-access-proc-cpuinfo.patch -Patch0052: 0052-extdom-return-LDAP_NO_SUCH_OBJECT-if-domains-differ.patch -Patch0053: 0053-subid-subid-match-display-the-owner-s-ID-not-DN.patch -Patch0054: 0054-migrate-ds-workaround-to-detect-compat-tree.patch -Patch0055: 0055-Don-t-store-entries-with-a-usercertificate-in-the-LD.patch -Patch0056: 0056-ipatests-Test-that-a-user-can-be-issued-multiple-cer.patch -Patch0057: 0057-Parse-getStatus-as-JSON-not-XML.patch -Patch0058: 0058-Parse-cert-chain-as-JSON-not-XML.patch -Patch0059: 0059-Specify-PKI-installation-log-paths.patch -Patch0060: 0060-Make-Dogtag-return-XML-for-ipa-cert-find.patch +Patch0001: 0001-ipa-kdb-issue-PAC_REQUESTER_SID-only-for-TGTs.patch +Patch0002: 0002-ipa-kdb-fix-requester-SID-check-according-to-MS-KILE.patch Patch1001: 1001-Change-branding-to-IPA-and-Identity-Management.patch %endif %endif @@ -528,6 +472,8 @@ Requires: gssproxy >= 0.7.0-2 Requires: sssd-dbus >= %{sssd_version} Requires: libpwquality Requires: cracklib-dicts +# NDR libraries are internal in Samba and change with version without changing SONAME +%ipa_requires_gt samba-client-libs Provides: %{alt_name}-server = %{version} Conflicts: %{alt_name}-server @@ -627,9 +573,11 @@ 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} @@ -1433,6 +1381,7 @@ fi %dir %{_libexecdir}/ipa/oddjob %attr(0755,root,root) %{_libexecdir}/ipa/oddjob/org.freeipa.server.conncheck %attr(0755,root,root) %{_libexecdir}/ipa/oddjob/org.freeipa.server.trust-enable-agent +%attr(0755,root,root) %{_libexecdir}/ipa/oddjob/org.freeipa.server.config-enable-sid %config(noreplace) %{_sysconfdir}/dbus-1/system.d/org.freeipa.server.conf %config(noreplace) %{_sysconfdir}/oddjobd.conf.d/ipa-server.conf %dir %{_libexecdir}/ipa/certmonger @@ -1762,6 +1711,17 @@ fi %endif %changelog +* Thu Dec 02 2021 Florence Blanc-Renaud - 4.9.8-1 +- Resolves: rhbz#2015608 - [Rebase] Rebase ipa to latest 4.9.x release RHEL9 +- Resolves: rhbz#1825010 - Concerns regarding 'ipa pwpolicy-mod --minlife 24 --maxlife 1' +- Resolves: rhbz#1966289 - Info about searchrecordslimit set search limit to 10,000 after upgrade +- Resolves: rhbz#1980356 - reinstalling samba client causes winbindd coredump +- Resolves: rhbz#1986054 - fix automountlocation-tofiles output +- Resolves: rhbz#2020205 - Missing bind-pkcs11-utils causing failures in OpenDNSSec +- Resolves: rhbz#2021445 - CVE-2020-25719 ipa: samba: Samba AD DC did not always rely on the SID and PAC in Kerberos tickets + - ipa-kdb: issue PAC_REQUESTER_SID only for TGTs + - ipa-kdb: fix requester SID check according to MS-KILE and MS-SFU updates + * Tue Oct 5 2021 Florence Blanc-Renaud - 4.9.6-9 - Resolves: rhbz#2010701 ipa-server-install fails while 'configuring certificate server instance' - Parse getStatus as JSON not XML