[Freeipa-devel] [PATCHES] 22-24 Add initial support for ID ranges

Sumit Bose sbose at redhat.com
Wed Jun 27 19:19:36 UTC 2012


On Tue, Jun 26, 2012 at 12:30:14PM +0200, Sumit Bose wrote:
> On Sun, Jun 17, 2012 at 09:47:20PM +0200, Sumit Bose wrote:
> > On Thu, Jun 14, 2012 at 02:25:01PM +0200, Sumit Bose wrote:
> > > On Thu, Jun 14, 2012 at 07:54:40AM -0400, Simo Sorce wrote:
> > > > On Thu, 2012-06-14 at 12:35 +0200, Sumit Bose wrote:
> > > > > On Wed, Jun 13, 2012 at 08:38:23PM -0400, Simo Sorce wrote:
> > > > > > On Wed, 2012-06-13 at 21:17 +0200, Sumit Bose wrote:
> > > > > > > 
> > > > > > > to keep track of the different ranges we use for UIDs/GIDs for local
> > > > > > > users/groups and users from trusted domains new range objects are
> > > > > > > introduced which are stored below cn=range,cn=etc,$SUFFIX.
> > > > > > > 
> > > > > > > 0022: LDAP schema update
> > > > > > 
> > > > > > ack
> > > > > > 
> > > > > > > 0023: Create a range object during installation fir the local ID range
> > > > > > 
> > > > > > nack, I think we need to find a way to handle adding at least the base
> > > > > > range on update. Otherwise an updated server won't be able to have IDs
> > > > > > for most of its users.
> > > > > 
> > > > > I fully agree, but since we said that we concentrate on update issues in
> > > > > beta2 I wanted to send the version for the fresh install first to allow
> > > > > testing.
> > > > 
> > > > The reason I'd like updates is that this patchset can be installed on
> > > > top of existing servers for testing w/o having to reinstall from scratch
> > > > or manually creating the ipaDomainIDRange object :):)
> > > 
> > > ok, will do.
> > > 
> > > Do you otherwise agree with the patches or is there something I should
> > > change while adding the updates?
> > > 
> > > bye,
> > > Sumit
> > > 
> > > > 
> > > > > > 
> > > > > > > 0024: add primary and secondary RID base to the local range object
> > > > > > >       during ipa-adtrust-install
> > > > > > 
> > > > > > Not sure if setting the range belongs in the previous patch or this one.
> > > > > 
> > > > > I think it is right here, because a plain IPA server does not need the
> > > > > RID related attributes.
> > > > > 
> > > > > > We might decide to ask questions during ipa-adtrust-install if the range
> > > > > > is not available, maybe presenting a set of pre-canned choices if we can
> > > > > > detect them.
> > > > > 
> > > > > I agree here, too. But as above I would like to handle update issues
> > > > > in a second round.
> > > > > 
> > > > > > 
> > > > > > Finally I think we need to do a search with uid/gidNmber < base and
> > > > > > uid/gidNumber > max and prompt/warn the user if we detect any ID the
> > > > > > falls outside the configured range (either because we failed to detect
> > > > > > ranges on upgrade and the user botched the question or because the admin
> > > > > > added arbitrary IDs.
> > > > > > If a warning we should warn that missing a range that suitably covers
> > > > > > these IDs, those users/groups will not be available for the trust.
> > > > > > 
> > > > > > Maybe we should also have a simple ipa command that can list all
> > > > > > users/groups that fall outside the ranges as well.
> > > > > 
> > > > > I'm working on the ranges cli plugin to allow 'ipa range-add', 'ipa
> > > > > range-find' etc. I can add it there.
> > > > > 
> > 
> > Hi,
> > 
> > this new series of patches add the cli plugin to create the ID ranges
> > manually. I'm still working on a detection of the locally used id range
> > of an upgrade domain in ipa-adtrust-install and an plugin which rejects
> > new ranges which overlaps with existing ones.
> > 
> > bye,
> > Sumit
> 
> the attached patch adds a preop plugin which checks for overlaps with
> existing ranges.
> 
> bye,
> Sumit

Finally I added a method to guess and create the initial ID range, if no
one is preset, e.g. when updating from an older version of freeIPA. A
full series of patches is attached.

bye,
Sumit
-------------- next part --------------
From e38871dc5ca23ac7f43f65c7805509ab3fb3af91 Mon Sep 17 00:00:00 2001
From: Sumit Bose <sbose at redhat.com>
Date: Mon, 11 Jun 2012 18:31:36 +0200
Subject: [PATCH 1/5] Extend LDAP schema

The objectclass ipaIDobject can be used to reserve local UIDs, GIDs or
SIDs for objects that are no neither users nor groups.

The ipa*IDRange objectclasses will be used to store the used Posix ID
ranges of the local domains (ipaDomainIDRange) or the ranges reserved
for AD domains (ipaTrustedADDomainRange). To be able to map the Posix
IDs to a RID and back the corresponding ranges can be saved here as
well.
---
 install/share/60basev3.ldif |    8 ++++++++
 1 Datei ge?ndert, 8 Zeilen hinzugef?gt(+)

diff --git a/install/share/60basev3.ldif b/install/share/60basev3.ldif
index 2c24137b0dc39f215ed0e4b97079ffce0ec630d3..03561d13f45768006eb22e3dc00f41f35944dc56 100644
--- a/install/share/60basev3.ldif
+++ b/install/share/60basev3.ldif
@@ -29,6 +29,10 @@ attributeTypes: ( 2.16.840.1.113730.3.8.11.21 NAME 'ipaAllowToImpersonate' DESC
 attributeTypes: ( 2.16.840.1.113730.3.8.11.22 NAME 'ipaAllowedTarget' DESC 'Target principals alowed to get a ticket for' SUP distinguishedName X-ORIGIN 'IPA-v3')
 attributeTypes: (2.16.840.1.113730.3.8.11.30 NAME 'ipaSELinuxUser' DESC 'An SELinux user' EQUALITY caseIgnoreMatch ORDERING caseIgnoreOrderingMatch SUBSTR caseIgnoreSubstringsMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.15 SINGLE-VALUE X-ORIGIN 'IPA v3')
 attributeTypes: (2.16.840.1.113730.3.8.11.31 NAME 'ipaSshPubKey' DESC 'SSH public key' EQUALITY octetStringMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.40 X-ORIGIN 'IPA v3' )
+attributeTypes: (2.16.840.1.113730.3.8.11.33 NAME 'ipaBaseID' DESC 'First value of a Posix ID range' EQUALITY integerMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE X-ORIGIN 'IPA v3' )
+attributeTypes: (2.16.840.1.113730.3.8.11.34 NAME 'ipaIDRangeSize' DESC 'Size of a Posix ID range' EQUALITY integerMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE X-ORIGIN 'IPA v3' )
+attributeTypes: (2.16.840.1.113730.3.8.11.35 NAME 'ipaBaseRID' DESC 'First value of a RID range' EQUALITY integerMatch ORDERING integerOrderingMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE X-ORIGIN 'IPA v3' )
+attributeTypes: (2.16.840.1.113730.3.8.11.36 NAME 'ipaSecondaryBaseRID' DESC 'First value of a secondary RID range' EQUALITY integerMatch ORDERING integerOrderingMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE X-ORIGIN 'IPA v3' )
 objectClasses: (2.16.840.1.113730.3.8.12.1 NAME 'ipaExternalGroup' SUP top STRUCTURAL MUST ( cn ) MAY ( ipaExternalMember $ memberOf $ description $ owner) X-ORIGIN 'IPA v3' )
 objectClasses: (2.16.840.1.113730.3.8.12.2 NAME 'ipaNTUserAttrs' SUP top AUXILIARY MUST ( ipaNTSecurityIdentifier ) MAY ( ipaNTHash $ ipaNTLogonScript $ ipaNTProfilePath $ ipaNTHomeDirectory $ ipaNTHomeDirectoryDrive ) X-ORIGIN 'IPA v3' )
 objectClasses: (2.16.840.1.113730.3.8.12.3 NAME 'ipaNTGroupAttrs' SUP top AUXILIARY MUST ( ipaNTSecurityIdentifier ) X-ORIGIN 'IPA v3' )
@@ -40,3 +44,7 @@ objectClasses: (2.16.840.1.113730.3.8.12.10 NAME 'ipaSELinuxUserMap' SUP ipaAsso
 objectClasses: (2.16.840.1.113730.3.8.12.11 NAME 'ipaSshGroupOfPubKeys' ABSTRACT MAY ipaSshPubKey X-ORIGIN 'IPA v3' )
 objectClasses: (2.16.840.1.113730.3.8.12.12 NAME 'ipaSshUser' SUP ipaSshGroupOfPubKeys AUXILIARY X-ORIGIN 'IPA v3' )
 objectClasses: (2.16.840.1.113730.3.8.12.13 NAME 'ipaSshHost' SUP ipaSshGroupOfPubKeys AUXILIARY X-ORIGIN 'IPA v3' )
+objectClasses: (2.16.840.1.113730.3.8.12.14 NAME 'ipaIDobject' SUP top AUXILIARY MAY ( uidNumber $ gidNumber $ ipaNTSecurityIdentifier ) X-ORIGIN 'IPA v3' )
+objectClasses: (2.16.840.1.113730.3.8.12.15 NAME 'ipaIDrange' ABSTRACT MUST ( cn $ ipaBaseID $ ipaIDRangeSize ) X-ORIGIN 'IPA v3' )
+objectClasses: (2.16.840.1.113730.3.8.12.16 NAME 'ipaDomainIDRange' SUP ipaIDrange STRUCTURAL MAY ( ipaBaseRID $ ipaSecondaryBaseRID ) X-ORIGIN 'IPA v3' )
+objectClasses: (2.16.840.1.113730.3.8.12.17 NAME 'ipaTrustedADDomainRange' SUP ipaIDrange STRUCTURAL MUST ( ipaBaseRID $ ipaNTTrustedDomainSID ) X-ORIGIN 'IPA v3' )
-- 
1.7.10.2

-------------- next part --------------
From 3aa34e2be073732bb43cafb1d76801f3572c3c1e Mon Sep 17 00:00:00 2001
From: Sumit Bose <sbose at redhat.com>
Date: Tue, 12 Jun 2012 11:58:41 +0200
Subject: [PATCH 2/5] Add objects for initial ID range

---
 install/share/bootstrap-template.ldif |   14 ++++++++++++++
 install/updates/62-ranges.update      |   13 +++++++++++++
 install/updates/Makefile.am           |    1 +
 ipaserver/install/dsinstance.py       |    1 +
 4 Dateien ge?ndert, 29 Zeilen hinzugef?gt(+)
 create mode 100644 install/updates/62-ranges.update

diff --git a/install/share/bootstrap-template.ldif b/install/share/bootstrap-template.ldif
index 149b6c9b29c6fd363eb7baccd9648d49c260ff85..ca74bf6fade5f4e0591ee511f96cc3d58542887d 100644
--- a/install/share/bootstrap-template.ldif
+++ b/install/share/bootstrap-template.ldif
@@ -409,3 +409,17 @@ objectClass: top
 objectClass: nsContainer
 cn: usermap
 
+dn: cn=ranges,cn=etc,$SUFFIX
+changetype: add
+objectClass: top
+objectClass: nsContainer
+cn: ranges
+
+dn: cn=local_id_range,cn=ranges,cn=etc,$SUFFIX
+changetype: add
+objectClass: top
+objectClass: ipaIDrange
+objectClass: ipaDomainIDRange
+cn: local_id_range
+ipaBaseID: $IDSTART
+ipaIDRangeSize: $IDRANGE_SIZE
diff --git a/install/updates/62-ranges.update b/install/updates/62-ranges.update
new file mode 100644
index 0000000000000000000000000000000000000000..42c1e2a98b8b64164ae9ae0292aa7b91beac2b26
--- /dev/null
+++ b/install/updates/62-ranges.update
@@ -0,0 +1,13 @@
+dn: cn=schema
+add:attributeTypes: (2.16.840.1.113730.3.8.11.33 NAME 'ipaBaseID' DESC 'First value of a Posix ID range' EQUALITY integerMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE X-ORIGIN 'IPA v3' )
+add:attributeTypes: (2.16.840.1.113730.3.8.11.34 NAME 'ipaIDRangeSize' DESC 'Size of a Posix ID range' EQUALITY integerMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE X-ORIGIN 'IPA v3' )
+add:attributeTypes: (2.16.840.1.113730.3.8.11.35 NAME 'ipaBaseRID' DESC 'First value of a RID range' EQUALITY integerMatch ORDERING integerOrderingMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE X-ORIGIN 'IPA v3' )
+add:attributeTypes: (2.16.840.1.113730.3.8.11.36 NAME 'ipaSecondaryBaseRID' DESC 'First value of a secondary RID range' EQUALITY integerMatch ORDERING integerOrderingMatch SYNTAX 1.3.6.1.4.1.1466.115.121.1.27 SINGLE-VALUE X-ORIGIN 'IPA v3' )
+add:objectClasses: (2.16.840.1.113730.3.8.12.15 NAME 'ipaIDrange' ABSTRACT MUST ( cn $$ ipaBaseID $$ ipaIDRangeSize ) X-ORIGIN 'IPA v3' )
+add:objectClasses: (2.16.840.1.113730.3.8.12.16 NAME 'ipaDomainIDRange' SUP ipaIDrange STRUCTURAL MAY ( ipaBaseRID $$ ipaSecondaryBaseRID ) X-ORIGIN 'IPA v3' )
+add:objectClasses: (2.16.840.1.113730.3.8.12.17 NAME 'ipaTrustedADDomainRange' SUP ipaIDrange STRUCTURAL MUST ( ipaBaseRID $$ ipaNTTrustedDomainSID ) X-ORIGIN 'IPA v3' )
+
+dn: cn=ranges,cn=etc,$SUFFIX
+default: obectClass: top
+default: objectClass: nsContainer
+default: cn: ranges
diff --git a/install/updates/Makefile.am b/install/updates/Makefile.am
index 412630e4e6d13167e2c0ae17c54f8ad84a4797fa..e45690f14c41dbd9eb10b5969ee14a257b8c7883 100644
--- a/install/updates/Makefile.am
+++ b/install/updates/Makefile.am
@@ -35,6 +35,7 @@ app_DATA =				\
 	55-pbacmemberof.update		\
 	60-trusts.update		\
 	61-trusts-s4u2proxy.update	\
+	62-ranges.update		\
 	$(NULL)
 
 EXTRA_DIST =				\
diff --git a/ipaserver/install/dsinstance.py b/ipaserver/install/dsinstance.py
index fb620a82e0d432d50e6c40ab8a7053ced153965a..93c6b50c274506949d1cdb81ad952141de10dab1 100644
--- a/ipaserver/install/dsinstance.py
+++ b/ipaserver/install/dsinstance.py
@@ -313,6 +313,7 @@ class DsInstance(service.Service):
                              IDMAX=self.idmax, HOST=self.fqdn,
                              ESCAPED_SUFFIX= escape_dn_chars(self.suffix.lower()),
                              GROUP=DS_GROUP,
+                             IDRANGE_SIZE=self.idmax-self.idstart+1
                          )
 
     def __create_ds_user(self):
-- 
1.7.10.2

-------------- next part --------------
From 2a71ae1a34cf211bf5f0d8f526f4d740e38ed888 Mon Sep 17 00:00:00 2001
From: Sumit Bose <sbose at redhat.com>
Date: Tue, 12 Jun 2012 17:53:36 +0200
Subject: [PATCH 3/5] Set RID bases for local domain during
 ipa-adtrust-install

---
 install/tools/ipa-adtrust-install    |   10 +++-
 ipaserver/install/adtrustinstance.py |   87 +++++++++++++++++++++++++++++++++-
 2 Dateien ge?ndert, 95 Zeilen hinzugef?gt(+), 2 Zeilen entfernt(-)

diff --git a/install/tools/ipa-adtrust-install b/install/tools/ipa-adtrust-install
index 49bcf54e7c26c92f246c7612940d441e60a86e1f..6678018e6346d75d5042894cfb833d38079d3f21 100755
--- a/install/tools/ipa-adtrust-install
+++ b/install/tools/ipa-adtrust-install
@@ -48,6 +48,12 @@ def parse_options():
     parser.add_option("--no-msdcs", dest="no_msdcs", action="store_true",
                       default=False, help="Do not create DNS service records " \
                                           "for Windows in managed DNS server")
+    parser.add_option("--rid-base", dest="rid_base", type=int, default=1000,
+                      help="Start value for mapping UIDs and GIDs to RIDs")
+    parser.add_option("--secondary-rid-base", dest="secondary_rid_base",
+                      type=int, default=100000000,
+                      help="Start value of the secondary range for mapping " \
+                           "UIDs and GIDs to RIDs")
     parser.add_option("-U", "--unattended", dest="unattended", action="store_true",
                       default=False, help="unattended installation never prompts the user")
 
@@ -207,7 +213,9 @@ def main():
         api.Backend.ldap2.connect(ccache)
 
     smb.setup(api.env.host, ip_address, api.env.realm, api.env.domain,
-              netbios_name, options.no_msdcs)
+              netbios_name, options.rid_base, options.secondary_rid_base,
+              options.no_msdcs)
+    smb.find_local_id_range()
     smb.create_instance()
 
     print """
diff --git a/ipaserver/install/adtrustinstance.py b/ipaserver/install/adtrustinstance.py
index 97a204bbbb9bd4c1990b9498993cb4bc453db6c7..5b7a3165d919882a6a4ddf84721f016339a7b794 100644
--- a/ipaserver/install/adtrustinstance.py
+++ b/ipaserver/install/adtrustinstance.py
@@ -114,6 +114,8 @@ class ADTRUSTInstance(service.Service):
         self.cifs_principal = None
         self.cifs_agent = None
         self.selinux_booleans = None
+        self.rid_base = None
+        self.secondary_rid_base = None
 
         service.Service.__init__(self, "smb", dm_password=dm_password)
 
@@ -174,6 +176,47 @@ class ADTRUSTInstance(service.Service):
         except:
             print "Failed to modify IPA admin group object"
 
+    def __add_rid_bases(self):
+        """
+        Add RID bases to the range object for the local ID range.
+
+        TODO: handle missing or multiple ranges more gracefully.
+        """
+
+        try:
+            res = self.admin_conn.search_s("cn=ranges,cn=etc,"+self.suffix,
+                                           ldap.SCOPE_ONELEVEL,
+                                           "(objectclass=ipaDomainIDRange)")
+            if len(res) != 1:
+                root_logger.critical("Found more than one ID range for the " \
+                                     "local domain.")
+                raise RuntimeError("Too many ID ranges\n")
+
+            if res[0].getValue('ipaBaseRID') or \
+               res[0].getValue('ipaSecondaryBaseRID'):
+                print "RID bases already set, nothing to do"
+                return
+
+            size = res[0].getValue('ipaIDRangeSize')
+            if abs(self.rid_base - self.secondary_rid_base) > size:
+                print "Primary and secondary RID base are too close. " \
+                      "They have to differ at least by %d." % size
+                raise RuntimeError("RID bases too close.\n")
+
+            try:
+                self.admin_conn.modify_s(res[0].dn,
+                                         [(ldap.MOD_ADD, "ipaBaseRID", \
+                                                 str(self.rid_base)), \
+                                         (ldap.MOD_ADD, "ipaSecondaryBaseRID", \
+                                                 str(self.secondary_rid_base))])
+            except:
+                print "Failed to add RID bases to the local range object"
+
+        except errors.NotFound as e:
+            root_logger.critical("ID range of the local domain not found, " \
+                                 "define it and run again.")
+            raise e
+
     def __create_samba_domain_object(self):
 
         try:
@@ -410,12 +453,14 @@ class ADTRUSTInstance(service.Service):
                              FQDN = self.fqdn)
 
     def setup(self, fqdn, ip_address, realm_name, domain_name, netbios_name,
-              no_msdcs=False, smbd_user="samba"):
+              rid_base, secondary_rid_base, no_msdcs=False, smbd_user="samba"):
         self.fqdn = fqdn
         self.ip_address = ip_address
         self.realm_name = realm_name
         self.domain_name = domain_name
         self.netbios_name = netbios_name
+        self.rid_base = rid_base
+        self.secondary_rid_base = secondary_rid_base
         self.no_msdcs = no_msdcs
         self.smbd_user = smbd_user
         self.suffix = ipautil.realm_to_suffix(self.realm_name)
@@ -436,6 +481,45 @@ class ADTRUSTInstance(service.Service):
 
         self.__setup_sub_dict()
 
+    def find_local_id_range(self):
+        self.ldap_connect()
+
+        if self.admin_conn.search_s("cn=ranges,cn=etc," + self.suffix,
+                                    ldap.SCOPE_ONELEVEL,
+                                    "objectclass=ipaDomainIDRange"):
+            return
+
+        try:
+            entry = self.admin_conn.getEntry("cn=admins,cn=groups,cn=accounts," \
+                                                                  + self.suffix,
+                                             ldap.SCOPE_BASE)
+        except errors.NotFound:
+            raise ValueError("No local ID range and no admins group found.\n" \
+                             "Add local ID range manually and try again!")
+
+        base_id = int(entry.getValue('gidNumber'))
+        id_range_size = 200000
+
+        id_filter = "(&" \
+                      "(|(objectclass=posixAccount)" \
+                        "(objectclass=posixGroup)" \
+                        "(objectclass=ipaIDObject))" \
+                      "(|(uidNumber<=%d)(uidNumber>=%d)" \
+                        "(gidNumber<=%d)(gidNumner>=%d)))" % \
+                     ((base_id - 1), (base_id + id_range_size),
+                      (base_id - 1), (base_id + id_range_size))
+        if self.admin_conn.search_s("cn=accounts," + self.suffix,
+                                   ldap.SCOPE_SUBTREE, id_filter):
+            raise ValueError("There are objects with IDs out of the expected" \
+                             "range.\nAdd local ID range manually and try " \
+                             "again!")
+
+        entry = ipaldap.Entry("cn=local_id_range,cn=ranges,cn=etc," + self.suffix)
+        entry.setValue('objectclass', 'ipaDomainIDRange')
+        entry.setValue('cn', 'local_id_range')
+        entry.setValue('ipaBaseID', str(base_id))
+        entry.setValue('ipaIDRangeSize', str(id_range_size))
+        self.admin_conn.addEntry(entry)
 
     def create_instance(self):
 
@@ -448,6 +532,7 @@ class ADTRUSTInstance(service.Service):
         self.step("writing samba config file", self.__write_smb_conf)
         self.step("adding cifs Kerberos principal", self.__setup_principal)
         self.step("adding admin(group) SIDs", self.__add_admin_sids)
+        self.step("adding RID bases", self.__add_rid_bases)
         self.step("activating CLDAP plugin", self.__add_cldap_module)
         self.step("activating extdom plugin", self.__add_extdom_module)
         self.step("activating sidgen plugin and task", self.__add_sidgen_module)
-- 
1.7.10.2

-------------- next part --------------
From 6c7f6c022aa779a8d648a759d01bf9a55120af55 Mon Sep 17 00:00:00 2001
From: Sumit Bose <sbose at redhat.com>
Date: Wed, 13 Jun 2012 20:58:54 +0200
Subject: [PATCH 4/5] Add CLI for ID ranges

---
 API.txt                 |   52 +++++++++++++++++++
 ipalib/constants.py     |    1 +
 ipalib/plugins/range.py |  126 +++++++++++++++++++++++++++++++++++++++++++++++
 3 Dateien ge?ndert, 179 Zeilen hinzugef?gt(+)
 create mode 100644 ipalib/plugins/range.py

diff --git a/API.txt b/API.txt
index a0c22143dc04b47400003a5fc84a40d1c71a5e82..9d91832d995d605b161c13a5ec915f5d84f1d515 100644
--- a/API.txt
+++ b/API.txt
@@ -2340,6 +2340,58 @@ option: Str('version?', exclude='webui')
 output: Output('summary', (<type 'unicode'>, <type 'NoneType'>), None)
 output: Entry('result', <type 'dict'>, Gettext('A dictionary representing an LDAP entry', domain='ipa', localedir=None))
 output: Output('value', <type 'unicode'>, None)
+command: range_add
+args: 1,10,3
+arg: Str('cn', attribute=True, cli_name='name', multivalue=False, primary_key=True, required=True)
+option: Int('ipabaseid', attribute=True, cli_name='base_id', multivalue=False, required=True)
+option: Int('ipaidrangesize', attribute=True, cli_name='range_size', multivalue=False, required=True)
+option: Int('ipabaserid', attribute=True, cli_name='rid_base', multivalue=False, required=True)
+option: Int('ipasecondarybaserid', attribute=True, cli_name='secondary_rid_base', multivalue=False, required=False)
+option: Str('ipanttrusteddomainsid', attribute=True, cli_name='dom_sid', multivalue=False, required=False)
+option: Str('setattr*', cli_name='setattr', exclude='webui')
+option: Str('addattr*', cli_name='addattr', exclude='webui')
+option: Flag('all', autofill=True, cli_name='all', default=False, exclude='webui')
+option: Flag('raw', autofill=True, cli_name='raw', default=False, exclude='webui')
+option: Str('version?', exclude='webui')
+output: Output('summary', (<type 'unicode'>, <type 'NoneType'>), None)
+output: Entry('result', <type 'dict'>, Gettext('A dictionary representing an LDAP entry', domain='ipa', localedir=None))
+output: Output('value', <type 'unicode'>, None)
+command: range_del
+args: 1,1,3
+arg: Str('cn', attribute=True, cli_name='name', multivalue=True, primary_key=True, query=True, required=True)
+option: Flag('continue', autofill=True, cli_name='continue', default=False)
+output: Output('summary', (<type 'unicode'>, <type 'NoneType'>), None)
+output: Output('result', <type 'dict'>, None)
+output: Output('value', <type 'unicode'>, None)
+command: range_find
+args: 1,12,4
+arg: Str('criteria?', noextrawhitespace=False)
+option: Str('cn', attribute=True, autofill=False, cli_name='name', multivalue=False, primary_key=True, query=True, required=False)
+option: Int('ipabaseid', attribute=True, autofill=False, cli_name='base_id', multivalue=False, query=True, required=False)
+option: Int('ipaidrangesize', attribute=True, autofill=False, cli_name='range_size', multivalue=False, query=True, required=False)
+option: Int('ipabaserid', attribute=True, autofill=False, cli_name='rid_base', multivalue=False, query=True, required=False)
+option: Int('ipasecondarybaserid', attribute=True, autofill=False, cli_name='secondary_rid_base', multivalue=False, query=True, required=False)
+option: Str('ipanttrusteddomainsid', attribute=True, autofill=False, cli_name='dom_sid', multivalue=False, query=True, required=False)
+option: Int('timelimit?', autofill=False, minvalue=0)
+option: Int('sizelimit?', autofill=False, minvalue=0)
+option: Flag('all', autofill=True, cli_name='all', default=False, exclude='webui')
+option: Flag('raw', autofill=True, cli_name='raw', default=False, exclude='webui')
+option: Str('version?', exclude='webui')
+option: Flag('pkey_only?', autofill=True, default=False)
+output: Output('summary', (<type 'unicode'>, <type 'NoneType'>), None)
+output: ListOfEntries('result', (<type 'list'>, <type 'tuple'>), Gettext('A list of LDAP entries', domain='ipa', localedir=None))
+output: Output('count', <type 'int'>, None)
+output: Output('truncated', <type 'bool'>, None)
+command: range_show
+args: 1,4,3
+arg: Str('cn', attribute=True, cli_name='name', multivalue=False, primary_key=True, query=True, required=True)
+option: Flag('rights', autofill=True, default=False)
+option: Flag('all', autofill=True, cli_name='all', default=False, exclude='webui')
+option: Flag('raw', autofill=True, cli_name='raw', default=False, exclude='webui')
+option: Str('version?', exclude='webui')
+output: Output('summary', (<type 'unicode'>, <type 'NoneType'>), None)
+output: Entry('result', <type 'dict'>, Gettext('A dictionary representing an LDAP entry', domain='ipa', localedir=None))
+output: Output('value', <type 'unicode'>, None)
 command: role_add
 args: 1,6,3
 arg: Str('cn', attribute=True, cli_name='name', multivalue=False, primary_key=True, required=True)
diff --git a/ipalib/constants.py b/ipalib/constants.py
index 8f87a18eef80e9824203aedf50e29e9819ae7c18..c4ba32007f74f0b8ccc6c8c518587dbf76530217 100644
--- a/ipalib/constants.py
+++ b/ipalib/constants.py
@@ -104,6 +104,7 @@ DEFAULT_CONFIG = (
     ('container_cifsdomains', 'cn=ad,cn=etc'),
     ('container_trusts', 'cn=trusts'),
     ('container_adtrusts', 'cn=ad,cn=trusts'),
+    ('container_ranges', 'cn=ranges,cn=etc'),
  
     # Ports, hosts, and URIs:
     # FIXME: let's renamed xmlrpc_uri to rpc_xml_uri
diff --git a/ipalib/plugins/range.py b/ipalib/plugins/range.py
new file mode 100644
index 0000000000000000000000000000000000000000..37482b09a6517e3b129249c9937621da17232c8c
--- /dev/null
+++ b/ipalib/plugins/range.py
@@ -0,0 +1,126 @@
+# Authors:
+#     Sumit Bose <sbose at redhat.com>
+#
+# Copyright (C) 2012  Red Hat
+# see file 'COPYING' for use and warranty information
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program.  If not, see <http://www.gnu.org/licenses/>.
+
+from ipalib.plugins.baseldap import *
+from ipalib import api, Str, Password, DefaultFrom, _, ngettext, Object
+from ipalib.parameters import Enum
+from ipalib import Command
+from ipalib import errors
+from ipapython import ipautil
+from ipalib import util
+
+
+__doc__ = _("""
+Manage ID ranges
+""")
+
+class range(LDAPObject):
+    """
+    Range object.
+    """
+
+    range_time = ('domain', 'ad', 'ipa')
+    container_dn = api.env.container_ranges
+    object_name = ('range')
+    object_name_plural = ('ranges')
+    object_class = ['ipaIDrange']
+    possible_objectclasses = ['ipadomainidrange', 'ipatrustedaddomainrange']
+    default_attributes = ['cn', 'ipabaseid', 'ipaidrangesize', 'ipabaserid',
+                          'ipasecondarybaserid', 'ipanttrusteddomainsid']
+
+    label = _('Ranges')
+    label_singular = _('Range')
+
+    takes_params = (
+        Str('cn',
+            cli_name='name',
+            label=_('Range name'),
+            primary_key=True,
+        ),
+        Int('ipabaseid',
+            cli_name='base_id',
+            label=_("First Posix ID of the range"),
+        ),
+        Int('ipaidrangesize',
+            cli_name='range_size',
+            label=_("Number of IDs in the range"),
+        ),
+        Int('ipabaserid',
+            cli_name='rid_base',
+            label=_('First RID of the corresponding RID range'),
+        ),
+        Int('ipasecondarybaserid?',
+            cli_name='secondary_rid_base',
+            label=_('First RID of the secondary RID range'),
+        ),
+        Str('ipanttrusteddomainsid?',
+            cli_name='dom_sid',
+            label=_('Domain SID of the trusted domain'),
+        ),
+    )
+
+class range_add(LDAPCreate):
+    __doc__ = _('Add new ID range.')
+
+    msg_summary = _('Added ID range "%(value)s"')
+
+    def pre_callback(self, ldap, dn, entry_attrs, attrs_list, *keys, **options):
+        if 'ipanttrusteddomainsid' not in options and \
+           'ipasecondarybaserid' not in options:
+            raise errors.ValidationError(name=_('Range setup'),
+                reason=_('Ranges for local domain ' \
+                         'must have a secondary RID base'))
+
+        if 'ipanttrusteddomainsid' in options:
+            entry_attrs['objectclass'].append('ipatrustedaddomainrange')
+        else:
+            entry_attrs['objectclass'].append('ipadomainidrange')
+
+        return dn
+
+class range_del(LDAPDelete):
+    __doc__ = _('Delete an ID range.')
+
+    msg_summary = _('Deleted ID range "%(value)s"')
+
+class range_find(LDAPSearch):
+    __doc__ = _('Search for ranges.')
+
+    msg_summary = ngettext(
+        '%(count)d range matched', '%(count)d rangess matched', 0
+    )
+
+    # Since all range types are stored within separate containers under
+    # 'cn=ranges,cn=etc' search can be done on a one-level scope
+    def pre_callback(self, ldap, filters, attrs_list, base_dn, scope, *args, **options):
+        return (filters, base_dn, ldap.SCOPE_ONELEVEL)
+
+class range_show(LDAPRetrieve):
+    __doc__ = _('Display information about a range.')
+
+    def pre_callback(self, ldap, dn, entry_attrs, *keys, **options):
+        return dn
+
+api.register(range)
+api.register(range_add)
+#api.register(range_mod)
+api.register(range_del)
+api.register(range_find)
+api.register(range_show)
+
-- 
1.7.10.2

-------------- next part --------------
From 93dcc6b15130d0250d35458b511e44695ad9e05d Mon Sep 17 00:00:00 2001
From: Sumit Bose <sbose at redhat.com>
Date: Mon, 18 Jun 2012 21:25:31 +0200
Subject: [PATCH 5/5] Add range check preop plugin

To make sure that ID ranges do not overlap this plugin checks new
additions and changes for conflicts with existing ranges.
---
 daemons/configure.ac                               |    1 +
 daemons/ipa-slapi-plugins/Makefile.am              |    1 +
 .../ipa-slapi-plugins/ipa-range-check/Makefile.am  |   46 ++
 .../ipa-range-check/ipa_range_check.c              |  440 ++++++++++++++++++++
 .../ipa-range-check/range-check-conf.ldif          |   16 +
 freeipa.spec.in                                    |    2 +
 ipaserver/install/dsinstance.py                    |    4 +
 7 Dateien ge?ndert, 510 Zeilen hinzugef?gt(+)
 create mode 100644 daemons/ipa-slapi-plugins/ipa-range-check/Makefile.am
 create mode 100644 daemons/ipa-slapi-plugins/ipa-range-check/ipa_range_check.c
 create mode 100644 daemons/ipa-slapi-plugins/ipa-range-check/range-check-conf.ldif

diff --git a/daemons/configure.ac b/daemons/configure.ac
index 76ebaa67f802e20009ef764bdbdcd09cc7937a22..b94673026a2c6b71670a67b1f629d9960d8fad31 100644
--- a/daemons/configure.ac
+++ b/daemons/configure.ac
@@ -342,6 +342,7 @@ AC_CONFIG_FILES([
     ipa-slapi-plugins/ipa-uuid/Makefile
     ipa-slapi-plugins/ipa-modrdn/Makefile
     ipa-slapi-plugins/ipa-sidgen/Makefile
+    ipa-slapi-plugins/ipa-range-check/Makefile
 ])
 
 AC_OUTPUT
diff --git a/daemons/ipa-slapi-plugins/Makefile.am b/daemons/ipa-slapi-plugins/Makefile.am
index 5a3c9e7034020b4f80815e865c2ed0de419264c9..c79e68db112c9d21bcbffba3d00442d2fd20ab3a 100644
--- a/daemons/ipa-slapi-plugins/Makefile.am
+++ b/daemons/ipa-slapi-plugins/Makefile.am
@@ -11,6 +11,7 @@ SUBDIRS =			\
 	ipa-version		\
 	ipa-winsync		\
 	ipa-sidgen		\
+	ipa-range-check		\
 	$(NULL)
 
 EXTRA_DIST =			\
diff --git a/daemons/ipa-slapi-plugins/ipa-range-check/Makefile.am b/daemons/ipa-slapi-plugins/ipa-range-check/Makefile.am
new file mode 100644
index 0000000000000000000000000000000000000000..f284b42ff339bc97463260749d948f9aff9f54a4
--- /dev/null
+++ b/daemons/ipa-slapi-plugins/ipa-range-check/Makefile.am
@@ -0,0 +1,46 @@
+NULL =
+
+PLUGIN_COMMON_DIR=../common
+
+INCLUDES =							\
+	-I.							\
+	-I$(srcdir)						\
+	-I$(PLUGIN_COMMON_DIR)					\
+	-I/usr/include/dirsrv					\
+	-DPREFIX=\""$(prefix)"\" 				\
+	-DBINDIR=\""$(bindir)"\"				\
+	-DLIBDIR=\""$(libdir)"\" 				\
+	-DLIBEXECDIR=\""$(libexecdir)"\"			\
+	-DDATADIR=\""$(datadir)"\"				\
+	$(AM_CFLAGS)						\
+	$(LDAP_CFLAGS)					\
+	$(WARN_CFLAGS)						\
+	$(NULL)
+
+plugindir = $(libdir)/dirsrv/plugins
+plugin_LTLIBRARIES = 		\
+	libipa_range_check.la	\
+	$(NULL)
+
+libipa_range_check_la_SOURCES = 	\
+	ipa_range_check.c		\
+	$(NULL)
+
+libipa_range_check_la_LDFLAGS = -avoid-version
+
+libipa_range_check_la_LIBADD = 	\
+	$(LDAP_LIBS)		\
+	$(NULL)
+
+appdir = $(IPA_DATA_DIR)
+app_DATA =			\
+	range-check-conf.ldif	\
+	$(NULL)
+
+EXTRA_DIST =			\
+	$(app_DATA)		\
+	$(NULL)
+
+MAINTAINERCLEANFILES =		\
+	*~			\
+	Makefile.in
diff --git a/daemons/ipa-slapi-plugins/ipa-range-check/ipa_range_check.c b/daemons/ipa-slapi-plugins/ipa-range-check/ipa_range_check.c
new file mode 100644
index 0000000000000000000000000000000000000000..499e54a9c4a4c9134a231c0cd09e700390565a14
--- /dev/null
+++ b/daemons/ipa-slapi-plugins/ipa-range-check/ipa_range_check.c
@@ -0,0 +1,440 @@
+/** BEGIN COPYRIGHT BLOCK
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Additional permission under GPLv3 section 7:
+ *
+ * In the following paragraph, "GPL" means the GNU General Public
+ * License, version 3 or any later version, and "Non-GPL Code" means
+ * code that is governed neither by the GPL nor a license
+ * compatible with the GPL.
+ *
+ * You may link the code of this Program with Non-GPL Code and convey
+ * linked combinations including the two, provided that such Non-GPL
+ * Code only links to the code of this Program through those well
+ * defined interfaces identified in the file named EXCEPTION found in
+ * the source code files (the "Approved Interfaces"). The files of
+ * Non-GPL Code may instantiate templates or use macros or inline
+ * functions from the Approved Interfaces without causing the resulting
+ * work to be covered by the GPL. Only the copyright holders of this
+ * Program may make changes or additions to the list of Approved
+ * Interfaces.
+ *
+ * Authors:
+ * Sumit Bose <sbose at redhat.com>
+ *
+ * Copyright (C) 2011 Red Hat, Inc.
+ * All rights reserved.
+ * END COPYRIGHT BLOCK **/
+
+#include <stdlib.h>
+#include <errno.h>
+#include <stdbool.h>
+#include <dirsrv/slapi-plugin.h>
+
+#include "util.h"
+
+#define IPA_CN "cn"
+#define IPA_BASE_ID "ipaBaseID"
+#define IPA_ID_RANGE_SIZE "ipaIDRangeSize"
+#define IPA_BASE_RID "ipaBaseRID"
+#define IPA_SECONDARY_BASE_RID "ipaSecondaryBaseRID"
+#define RANGES_FILTER "objectclass=ipaIDRange"
+
+#define IPA_PLUGIN_NAME "ipa-range-check"
+#define IPA_RANGE_CHECK_FEATURE_DESC "IPA ID range check plugin"
+#define IPA_RANGE_CHECK_PLUGIN_DESC "Check if newly added or modified " \
+                                    "ID ranges do not overlap with existing ones"
+
+Slapi_PluginDesc ipa_range_check_plugin_desc = {
+    IPA_RANGE_CHECK_FEATURE_DESC,
+    "FreeIPA project",
+    "FreeIPA/1.0",
+    IPA_RANGE_CHECK_PLUGIN_DESC
+};
+
+struct ipa_range_check_ctx {
+    Slapi_ComponentId *plugin_id;
+    const char *base_dn;
+};
+
+struct range_info {
+    char *name;
+    uint32_t base_id;
+    uint32_t id_range_size;
+    uint32_t base_rid;
+    uint32_t secondary_base_rid;
+};
+
+static int slapi_entry_to_range_info(struct slapi_entry *entry,
+                                     struct range_info **_range)
+{
+    int ret;
+    unsigned long ul_val;
+    struct range_info *range = NULL;
+
+    range = calloc(1, sizeof(struct range_info));
+    if (range == NULL) {
+        return ENOMEM;
+    }
+
+    range->name = slapi_entry_attr_get_charptr(entry, IPA_CN);
+    if (range->name == NULL) {
+        return EINVAL;
+    }
+
+    ul_val = slapi_entry_attr_get_ulong(entry, IPA_BASE_ID);
+    if (ul_val == 0 || ul_val >= UINT32_MAX) {
+        ret = ERANGE;
+        goto done;
+    }
+    range->base_id = ul_val;
+
+    ul_val = slapi_entry_attr_get_ulong(entry, IPA_ID_RANGE_SIZE);
+    if (ul_val == 0 || ul_val >= UINT32_MAX) {
+        ret = ERANGE;
+        goto done;
+    }
+    range->id_range_size = ul_val;
+
+    ul_val = slapi_entry_attr_get_ulong(entry, IPA_BASE_RID);
+    if (ul_val >= UINT32_MAX) {
+        ret = ERANGE;
+        goto done;
+    }
+    range->base_rid = ul_val;
+
+    ul_val = slapi_entry_attr_get_ulong(entry, IPA_SECONDARY_BASE_RID);
+    if (ul_val >= UINT32_MAX) {
+        ret = ERANGE;
+        goto done;
+    }
+    range->secondary_base_rid = ul_val;
+
+    *_range = range;
+    ret = 0;
+
+done:
+    if (ret != 0) {
+        free(range);
+    }
+
+    return ret;
+}
+
+#define IN_RANGE(x,base,size) ( (x) >= (base) && ((x) - (base)) < (size) )
+static bool ranges_overlap(struct range_info *r1, struct range_info *r2)
+{
+    if (r1->name != NULL && r2->name != NULL &&
+        strcasecmp(r1->name, r2->name) == 0) {
+        return false;
+    }
+
+    if (IN_RANGE(r1->base_id, r2->base_id, r2->id_range_size) ||
+        IN_RANGE((r1->base_id + r1->id_range_size - 1), r2->base_id, r2->id_range_size) ||
+        IN_RANGE(r2->base_id, r1->base_id, r1->id_range_size) ||
+        IN_RANGE((r2->base_id + r2->id_range_size - 1), r1->base_id, r1->id_range_size)) {
+        return true;
+    }
+
+    return false;
+}
+
+static int ipa_range_check_start(Slapi_PBlock *pb)
+{
+    return 0;
+}
+
+static int ipa_range_check_close(Slapi_PBlock *pb)
+{
+    return 0;
+}
+
+static int ipa_range_check_pre_op(Slapi_PBlock *pb, int modtype)
+{
+    int ret;
+    int is_repl_op;
+    struct slapi_entry *entry = NULL;
+    bool free_entry = false;
+    struct range_info *new_range = NULL;
+    struct range_info *old_range = NULL;
+    const char *dn_str;
+    Slapi_DN *dn = NULL;
+    struct ipa_range_check_ctx *ctx;
+    LDAPMod **mods = NULL;
+    Slapi_PBlock *search_pb = NULL;
+    int search_result;
+    Slapi_Entry **search_entries = NULL;
+    size_t c;
+    bool overlap = true;
+    const char *check_attr;
+    char *errmsg = NULL;
+
+    ret = slapi_pblock_get(pb, SLAPI_IS_REPLICATED_OPERATION, &is_repl_op);
+    if (ret != 0) {
+        LOG_FATAL("slapi_pblock_get failed!?\n");
+        return LDAP_OPERATIONS_ERROR;
+    }
+
+    if (is_repl_op) {
+        LOG("Is replicated operation, nothing to do.\n");
+        return LDAP_SUCCESS;
+    }
+
+    ret = slapi_pblock_get(pb, SLAPI_PLUGIN_PRIVATE, &ctx);
+    if (ret != 0) {
+        LOG_FATAL("Missing private plugin context.\n");
+        goto done;
+    }
+
+    ret = slapi_pblock_get(pb, SLAPI_TARGET_DN, &dn_str);
+    if (ret != 0) {
+        LOG_FATAL("Missing target DN.\n");
+        goto done;
+    }
+
+    dn = slapi_sdn_new_dn_byref(dn_str);
+    if (dn == NULL) {
+        LOG_FATAL("Failed to convert target DN.\n");
+        ret = LDAP_OPERATIONS_ERROR;
+        goto done;
+    }
+
+
+    switch (modtype) {
+        case LDAP_CHANGETYPE_ADD:
+            ret = slapi_pblock_get(pb, SLAPI_ADD_ENTRY, &entry);
+            if (ret != 0) {
+                LOG_FATAL("Missing entry to add.\n");
+                goto done;
+            }
+
+            /* Check if this is a range object */
+            check_attr = slapi_entry_attr_get_charptr(entry, IPA_BASE_ID);
+            if (check_attr == NULL) {
+                LOG("Not an ID range object, nothing to do.\n");
+                ret = 0;
+                goto done;
+            }
+
+            break;
+        case  LDAP_CHANGETYPE_MODIFY:
+            ret = slapi_search_internal_get_entry(dn, NULL, &entry,
+                                                  ctx->plugin_id);
+            if (ret != 0 || entry == NULL) {
+                LOG_FATAL("Missing entry to modify.\n");
+                ret = LDAP_NO_SUCH_OBJECT;
+                goto done;
+            }
+            free_entry = true;
+
+            /* Check if this is a range object */
+            check_attr = slapi_entry_attr_get_charptr(entry, IPA_BASE_ID);
+            if (check_attr == NULL) {
+                LOG("Not an ID range object, nothing to do.\n");
+                ret = 0;
+                goto done;
+            }
+
+            ret = slapi_pblock_get(pb, SLAPI_MODIFY_MODS, &mods);
+            if (ret != 0) {
+                LOG_FATAL("Missing modify values.\n");
+                goto done;
+            }
+
+            ret = slapi_entry_apply_mods(entry, mods);
+            if (ret != 0) {
+                LOG_FATAL("Failed to apply modifications.\n");
+                goto done;
+            }
+
+            break;
+        default:
+            ret = LDAP_OPERATIONS_ERROR;
+            LOG_FATAL("Unsupported LDAP operation.\n");
+            goto done;
+    }
+
+    ret = slapi_entry_to_range_info(entry, &new_range);
+    if (ret != 0) {
+        LOG_FATAL("Failed to convert LDAP entry to range struct.\n");
+        goto done;
+    }
+
+    search_pb = slapi_pblock_new();
+    if (search_pb == NULL) {
+        LOG_FATAL("Failed to create new pblock.\n");
+        ret = LDAP_OPERATIONS_ERROR;
+        goto done;
+    }
+
+    slapi_search_internal_set_pb(search_pb, ctx->base_dn,
+                                 LDAP_SCOPE_SUBTREE, RANGES_FILTER,
+                                 NULL, 0, NULL, NULL, ctx->plugin_id, 0);
+
+    ret = slapi_search_internal_pb(search_pb);
+    if (ret != 0) {
+        LOG_FATAL("Starting internal search failed.\n");
+        goto done;
+    }
+
+    ret = slapi_pblock_get(search_pb, SLAPI_PLUGIN_INTOP_RESULT, &search_result);
+    if (ret != 0 || search_result != LDAP_SUCCESS) {
+        LOG_FATAL("Internal search failed.\n");
+        ret = LDAP_OPERATIONS_ERROR;
+        goto done;
+    }
+
+    ret = slapi_pblock_get(search_pb, SLAPI_PLUGIN_INTOP_SEARCH_ENTRIES,
+                           &search_entries);
+    if (ret != 0) {
+        LOG_FATAL("Failed to read searched entries.\n");
+        goto done;
+    }
+
+    if (search_entries == NULL || search_entries[0] == NULL) {
+        LOG("No existing entries.\n");
+        ret = 0;
+        goto done;
+    }
+
+    for (c = 0; search_entries[c] != NULL; c++) {
+        ret = slapi_entry_to_range_info(search_entries[c], &old_range);
+        if (ret != 0) {
+            LOG_FATAL("Failed to convert LDAP entry to range struct.\n");
+            goto done;
+        }
+
+        overlap = ranges_overlap(old_range, new_range);
+        free(old_range);
+        old_range = NULL;
+        if (overlap) {
+            LOG_FATAL("New range overlaps with existing one.\n");
+            ret = LDAP_CONSTRAINT_VIOLATION;
+            errmsg = "New range overlaps with existing one.";
+            goto done;
+        }
+    }
+    LOG("No overlaps found.\n");
+
+    ret = 0;
+
+done:
+    slapi_free_search_results_internal(search_pb);
+    slapi_pblock_destroy(search_pb);
+    slapi_sdn_free(&dn);
+    free(old_range);
+    free(new_range);
+    if (free_entry) {
+        slapi_entry_free(entry);
+    }
+
+    if (ret != 0) {
+        if (errmsg == NULL) {
+            errmsg = "Range Check error";
+        }
+        slapi_send_ldap_result(pb, ret, NULL, errmsg, 0, NULL);
+    }
+
+    return ret;
+}
+
+static int ipa_range_check_mod_pre_op(Slapi_PBlock * pb)
+{
+    return ipa_range_check_pre_op(pb, LDAP_CHANGETYPE_MODIFY);
+}
+
+static int ipa_range_check_add_pre_op(Slapi_PBlock *pb)
+{
+    return ipa_range_check_pre_op(pb, LDAP_CHANGETYPE_ADD);
+}
+
+static int ipa_range_check_init_ctx(Slapi_PBlock *pb,
+                                    struct ipa_range_check_ctx **_ctx)
+{
+    struct ipa_range_check_ctx *ctx;
+    Slapi_Entry *entry;
+    int ret;
+
+    ctx = calloc(1, sizeof(struct ipa_range_check_ctx));
+    if (ctx == NULL) {
+        return LDAP_OPERATIONS_ERROR;
+    }
+
+    ret = slapi_pblock_get(pb, SLAPI_PLUGIN_IDENTITY, &ctx->plugin_id);
+    if ((ret != 0) || (ctx->plugin_id == NULL)) {
+        LOG_FATAL("Could not get identity or identity was NULL\n");
+        if (ret == 0) {
+            ret = -1;
+        }
+        goto done;
+    }
+
+    slapi_pblock_get(pb, SLAPI_PLUGIN_CONFIG_ENTRY, &entry);
+    if (entry == NULL) {
+        LOG_FATAL("Plugin configuration not found!\n");
+        ret = EINVAL;
+        goto done;
+    }
+
+    ctx->base_dn = slapi_entry_attr_get_charptr(entry, "nsslapd-basedn");
+    if (ctx->base_dn == NULL) {
+        LOG_FATAL("Base DN not found in plugin configuration!\n");
+        ret = EINVAL;
+        goto done;
+    }
+
+    ret = 0;
+
+done:
+    if (ret != 0) {
+        free(ctx);
+    } else {
+        *_ctx = ctx;
+    }
+
+    return ret;
+}
+
+int ipa_range_check_init(Slapi_PBlock *pb)
+{
+    int ret;
+    struct ipa_range_check_ctx *rc_ctx;
+
+    ret = ipa_range_check_init_ctx(pb, &rc_ctx);
+    if (ret != 0) {
+        LOG_FATAL("Failed ot initialize range check plugin.\n");
+        /* do not cause DS to stop, simply do nothing */
+        return 0;
+    }
+
+    ret = 0;
+    if (slapi_pblock_set(pb, SLAPI_PLUGIN_VERSION,
+                         SLAPI_PLUGIN_VERSION_01) != 0 ||
+        slapi_pblock_set(pb, SLAPI_PLUGIN_START_FN,
+                         (void *) ipa_range_check_start) != 0 ||
+        slapi_pblock_set(pb, SLAPI_PLUGIN_CLOSE_FN,
+                         (void *) ipa_range_check_close) != 0 ||
+        slapi_pblock_set(pb, SLAPI_PLUGIN_DESCRIPTION,
+                         (void *) &ipa_range_check_plugin_desc) != 0 ||
+        slapi_pblock_set(pb, SLAPI_PLUGIN_PRE_MODIFY_FN,
+                         (void *) ipa_range_check_mod_pre_op) != 0 ||
+        slapi_pblock_set(pb, SLAPI_PLUGIN_PRE_ADD_FN,
+                         (void *) ipa_range_check_add_pre_op) != 0 ||
+        slapi_pblock_set(pb, SLAPI_PLUGIN_PRIVATE, rc_ctx) != 0) {
+        LOG_FATAL("failed to register plugin\n");
+        ret = EFAIL;
+    }
+
+    return ret;
+}
diff --git a/daemons/ipa-slapi-plugins/ipa-range-check/range-check-conf.ldif b/daemons/ipa-slapi-plugins/ipa-range-check/range-check-conf.ldif
new file mode 100644
index 0000000000000000000000000000000000000000..6452304f4324a37c8b2b72c95ac623743c7d00b3
--- /dev/null
+++ b/daemons/ipa-slapi-plugins/ipa-range-check/range-check-conf.ldif
@@ -0,0 +1,16 @@
+dn: cn=IPA Range-Check,cn=plugins,cn=config
+changetype: add
+objectclass: top
+objectclass: nsSlapdPlugin
+objectclass: extensibleObject
+cn: IPA Range-Check
+nsslapd-pluginpath: libipa_range_check
+nsslapd-plugininitfunc: ipa_range_check_init
+nsslapd-plugintype: preoperation
+nsslapd-pluginenabled: on
+nsslapd-pluginid: ipa_range_check_version
+nsslapd-pluginversion: 1.0
+nsslapd-pluginvendor: Red Hat, Inc.
+nsslapd-plugindescription: IPA Range-Check plugin
+nsslapd-plugin-depends-on-type: database
+nsslapd-basedn: $SUFFIX
diff --git a/freeipa.spec.in b/freeipa.spec.in
index a868cc0407941445b4fa16addfcc6fd1e55dc650..ef8af3f27b429e369eb0e96fd6391f89231d5801 100644
--- a/freeipa.spec.in
+++ b/freeipa.spec.in
@@ -364,6 +364,7 @@ rm %{buildroot}/%{plugin_dir}/libipa_cldap.la
 rm %{buildroot}/%{plugin_dir}/libipa_extdom_extop.la
 rm %{buildroot}/%{plugin_dir}/libipa_sidgen.la
 rm %{buildroot}/%{plugin_dir}/libipa_sidgen_task.la
+rm %{buildroot}/%{plugin_dir}/libipa_range_check.la
 rm %{buildroot}/%{_libdir}/krb5/plugins/kdb/ipadb.la
 rm %{buildroot}/%{_libdir}/samba/pdb/ipasam.la
 
@@ -640,6 +641,7 @@ fi
 %attr(755,root,root) %{plugin_dir}/libipa_modrdn.so
 %attr(755,root,root) %{plugin_dir}/libipa_lockout.so
 %attr(755,root,root) %{plugin_dir}/libipa_cldap.so
+%attr(755,root,root) %{plugin_dir}/libipa_range_check.so
 %dir %{_localstatedir}/lib/ipa
 %attr(700,root,root) %dir %{_localstatedir}/lib/ipa/sysrestore
 %attr(700,root,root) %dir %{_localstatedir}/lib/ipa/sysupgrade
diff --git a/ipaserver/install/dsinstance.py b/ipaserver/install/dsinstance.py
index 93c6b50c274506949d1cdb81ad952141de10dab1..d74ee8987a3387b78ec7203c9b8c70059b35a812 100644
--- a/ipaserver/install/dsinstance.py
+++ b/ipaserver/install/dsinstance.py
@@ -240,6 +240,7 @@ class DsInstance(service.Service):
         self.step("configuring netgroups from hostgroups", self.__host_nis_groups)
         self.step("creating default Sudo bind user", self.__add_sudo_binduser)
         self.step("creating default Auto Member layout", self.__add_automember_config)
+        self.step("adding range check plugin", self.__add_range_check_plugin)
         if hbac_allow:
             self.step("creating default HBAC rule allow_all", self.add_hbac)
 
@@ -788,6 +789,9 @@ class DsInstance(service.Service):
     def __add_replica_automember_config(self):
         self._ldap_mod("replica-automember.ldif", self.sub_dict)
 
+    def __add_range_check_plugin(self):
+        self._ldap_mod("range-check-conf.ldif", self.sub_dict)
+
     def replica_populate(self):
         self.ldap_connect()
 
-- 
1.7.10.2



More information about the Freeipa-devel mailing list