[Freeipa-devel] [PATCH] 0060 Ensure ipa-adtrust-install is run as admin user

Alexander Bokovoy abokovoy at redhat.com
Wed Jul 18 12:55:34 UTC 2012


On Wed, 18 Jul 2012, Alexander Bokovoy wrote:
>On Tue, 17 Jul 2012, Rob Crittenden wrote:
>>Alexander Bokovoy wrote:
>>>On Fri, 13 Jul 2012, Alexander Bokovoy wrote:
>>>>Hi,
>>>>
>>>>when adding AD trusts support, we need to ensure we have valid kerberos
>>>>ticket of the user from 'admins' group or otherwise appropriate ACIs
>>>>will not be granted.
>>>>
>>>>This patch introduces a check for that. We already check if
>>>>ipa-adtrust-install is run by root so this complements existing checks.
>>>>
>>>>https://fedorahosted.org/freeipa/ticket/2815
>>>After discussing on IRC with Simo and Rob, we came to conclusion that it
>>>is possible to switch to LDAPI and autobind feature of dirsrv for
>>>authentication and remove requirement for Directory Manager credentials
>>>altogether.
>>>
>>>Updated patch makes use of LDAPI + autobind under root privileges to map
>>>automatically to Directory Manager privileges in dirsrv. Additionally it
>>>ensures we have Kerberos credentials to fetch keytab with CIFS service
>>>key.
>>>
>>>Service._ldap_mod() is extended to switch to autobind when self.ldapi is
>>>set to True and we are running as root.
>>>
>>>For those interested in why ACIError is mapped to 'outdated Kerberos
>>>credentials' error message, this is because we'll get ACIError for 'ipa
>>>user-show <uid>' command when authenticated by the Kerberos credentials
>>>for <uid> in a default ccache only when Kerberos credentials are stale --
>>>either belong to a user that was removed or to a previous IPA install
>>>that was wiped before reinstalling. The latter is how I discovered
>>>this case. :)
>>
>>I think that this should raise an exception if one tries to use 
>>ldapi, doesn't provide the DM password and is not root. Otherwise 
>>it won't authenticate at all.
>This is not exactly true.
>
>$ id
>uid=757000001(abokovoy) gid=757000001(abokovoy) groups=757000001(abokovoy)
>context=unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023
>
>$ ldapsearch -vvv -H ldapi://%2fvar%2frun%2fslapd-IPA-LOCAL.socket '*' >/dev/null
>ldap_initialize( ldapi://%2Fvar%2Frun%2Fslapd-IPA-LOCAL.socket/??base )
>SASL/EXTERNAL authentication started
>ldap_sasl_interactive_bind_s: Inappropriate authentication (48)
>	additional info: SASL EXTERNAL bind requires an SSL connection
>
>$ ldapsearch -vvv -Y GSSAPI -H ldapi://%2fvar%2frun%2fslapd-IPA-LOCAL.socket '*' >/dev/null
>ldap_initialize( ldapi://%2Fvar%2Frun%2Fslapd-IPA-LOCAL.socket/??base )
>SASL/GSSAPI authentication started
>SASL username: abokovoy at IPA.LOCAL
>SASL SSF: 56
>SASL data security layer installed.
>filter: (objectclass=*)
>requesting: *
>
>So GSSAPI auth works with LDAPI access. I can simply enforce -Y GSSAPI
>when non-root and no dm_password regardless of self.ldapi, this would
>extend previously available logic to following:
>
>- ldapi: use -H ldapi://url instead of -h hostname
>- dm_password: add Directory Manager auth
>- root without dm_password: use autobind
>- non-root without dm_password: use GSSAPI
>
>>In reality, I think all this service code always runs as root, so 
>>it may be a moot point, but this code is kinda convoluted.
>Yep.
Here is updated patch.

-- 
/ Alexander Bokovoy
-------------- next part --------------
>From 81b1e0305ac8ad3516bb7bcaeb13999480e0f14f Mon Sep 17 00:00:00 2001
From: Alexander Bokovoy <abokovoy at redhat.com>
Date: Fri, 13 Jul 2012 18:12:48 +0300
Subject: [PATCH 2/5] Ensure ipa-adtrust-install is run with Kerberos ticket
 for admin user

When setting up AD trusts support, ipa-adtrust-install utility
needs to be run as:
   - root, for performing Samba configuration and using LDAPI/autobind
   - kinit-ed IPA admin user, to ensure proper ACIs are granted to
     fetch keytab

As result, we can get rid of Directory Manager credentials in ipa-adtrust-install

https://fedorahosted.org/freeipa/ticket/2815
---
 install/tools/ipa-adtrust-install       |   42 +++++++++++++++++++++++--------
 install/tools/man/ipa-adtrust-install.1 |    3 ---
 ipaserver/install/adtrustinstance.py    |   21 ++++++++--------
 ipaserver/install/service.py            |   15 +++++++++--
 4 files changed, 55 insertions(+), 26 deletions(-)

diff --git a/install/tools/ipa-adtrust-install b/install/tools/ipa-adtrust-install
index 6678018e6346d75d5042894cfb833d38079d3f21..f367d5b2b516bd411bce9275ff299eb3ffdf6bf9 100755
--- a/install/tools/ipa-adtrust-install
+++ b/install/tools/ipa-adtrust-install
@@ -37,8 +37,6 @@ log_file_name = "/var/log/ipaserver-install.log"
 
 def parse_options():
     parser = IPAOptionParser(version=version.VERSION)
-    parser.add_option("-p", "--ds-password", dest="dm_password",
-                      sensitive=True, help="directory manager password")
     parser.add_option("-d", "--debug", dest="debug", action="store_true",
                       default=False, help="print debugging information")
     parser.add_option("--ip-address", dest="ip_address",
@@ -86,6 +84,30 @@ def read_netbios_name(netbios_default):
 
     return netbios_name
 
+def ensure_kerberos_admin_rights(api):
+    try:
+        ctx = krbV.default_context()
+        ccache = ctx.default_ccache()
+        principal = ccache.principal()
+        api.Backend.ldap2.connect(ccache.name)
+        user = api.Command.user_show(unicode(principal[0]))['result']
+        group = api.Command.group_show(u'admins')['result']
+        api.Backend.ldap2.disconnect()
+        if not (user['uid'][0] in group['member_user'] and
+                group['cn'][0] in user['memberof_group']):
+            raise errors.RequirementError(name='admins group membership')
+    except Exception, e:
+        error_messages = dict(
+           ACIError = "Outdated Kerberos credentials. Use kdestroy and kinit to update your ticket",
+           Krb5Error = "Must have Kerberos credentials to setup AD trusts on server",
+           RequirementError = "Must have administrative privileges to setup AD trusts on server"
+        )
+        name = type(e).__name__
+        if name in error_messages:
+            sys.exit(error_messages[name])
+        else:
+            sys.exit("Unrecognized error during check of admin rights: %s\n%s" % (name, str(e)))
+
 def main():
     safe_options, options = parse_options()
 
@@ -128,6 +150,8 @@ def main():
     api.bootstrap(**cfg)
     api.finalize()
 
+    ensure_kerberos_admin_rights(api)
+
     if adtrustinstance.ipa_smb_conf_exists():
         if not options.unattended:
             while True:
@@ -194,9 +218,8 @@ def main():
     if not options.unattended and ( not netbios_name or not options.netbios_name):
         netbios_name = read_netbios_name(netbios_name)
 
-    dm_password = options.dm_password or read_password("Directory Manager",
-                                             confirm=False, validate=False)
-    smb = adtrustinstance.ADTRUSTInstance(fstore, dm_password)
+    smb = adtrustinstance.ADTRUSTInstance(fstore)
+    smb.realm = api.env.realm
 
     # try the connection
     try:
@@ -205,12 +228,9 @@ def main():
     except ldap.INVALID_CREDENTIALS, e:
         sys.exit("Password is not valid!")
 
-    if smb.dm_password:
-        api.Backend.ldap2.connect(bind_dn="cn=Directory Manager", bind_pw=smb.dm_password)
-    else:
-        # See if our LDAP server is up and we can talk to it over GSSAPI
-        ccache = krbV.default_context().default_ccache().name
-        api.Backend.ldap2.connect(ccache)
+    # See if our LDAP server is up and we can talk to it over GSSAPI
+    ccache = krbV.default_context().default_ccache().name
+    api.Backend.ldap2.connect(ccache)
 
     smb.setup(api.env.host, ip_address, api.env.realm, api.env.domain,
               netbios_name, options.rid_base, options.secondary_rid_base,
diff --git a/install/tools/man/ipa-adtrust-install.1 b/install/tools/man/ipa-adtrust-install.1
index b61da19088b40d6a9e53784f9a061913ecda4321..22337c3df8827670657bf405b6c49ba2f8624d6d 100644
--- a/install/tools/man/ipa-adtrust-install.1
+++ b/install/tools/man/ipa-adtrust-install.1
@@ -27,9 +27,6 @@ trust to an Active Directory domain. This requires that the IPA server is
 already installed and configured.
 .SH "OPTIONS"
 .TP
-\fB\-p\fR \fIDM_PASSWORD\fR, \fB\-\-ds\-password\fR=\fIDM_PASSWORD\fR
-The password to be used by the Directory Server for the Directory Manager user
-.TP
 \fB\-d\fR, \fB\-\-debug\fR
 Enable debug logging when more verbose output is needed
 .TP
diff --git a/ipaserver/install/adtrustinstance.py b/ipaserver/install/adtrustinstance.py
index 20feec4df309b5793aa1c29fdf18bc5bfe180943..9dcbec2d61d935f90e74cc65b30a0f1d0c0f9d2a 100644
--- a/ipaserver/install/adtrustinstance.py
+++ b/ipaserver/install/adtrustinstance.py
@@ -96,10 +96,9 @@ class ADTRUSTInstance(service.Service):
     OBJC_GROUP = "ipaNTGroupAttrs"
     OBJC_DOMAIN = "ipaNTDomainAttrs"
 
-    def __init__(self, fstore=None, dm_password=None):
+    def __init__(self, fstore=None):
         self.fqdn = None
         self.ip_address = None
-        self.realm_name = None
         self.domain_name = None
         self.netbios_name = None
         self.no_msdcs = None
@@ -118,7 +117,7 @@ class ADTRUSTInstance(service.Service):
         self.rid_base = None
         self.secondary_rid_base = None
 
-        service.Service.__init__(self, "smb", dm_password=dm_password)
+        service.Service.__init__(self, "smb", dm_password=None, ldapi=True)
 
         if fstore:
             self.fstore = fstore
@@ -436,6 +435,8 @@ class ADTRUSTInstance(service.Service):
         # We do not let the system start IPA components on its own,
         # Instead we reply on the IPA init script to start only enabled
         # components as found in our LDAP configuration tree
+        # Note that self.dm_password is None for ADTrustInstance because
+        # we ensure to be called as root and using ldapi to use autobind
         try:
             self.ldap_enable('ADTRUST', self.fqdn, self.dm_password, \
                              self.suffix)
@@ -449,7 +450,7 @@ class ADTRUSTInstance(service.Service):
             root_logger.info("EXTID Service startup entry already exists.")
 
     def __setup_sub_dict(self):
-        self.sub_dict = dict(REALM = self.realm_name,
+        self.sub_dict = dict(REALM = self.realm,
                              SUFFIX = self.suffix,
                              NETBIOS_NAME = self.netbios_name,
                              SMB_DN = self.smb_dn,
@@ -460,16 +461,16 @@ class ADTRUSTInstance(service.Service):
               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.realm = 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)
+        self.suffix = ipautil.realm_to_suffix(self.realm)
         self.ldapi_socket = "%%2fvar%%2frun%%2fslapd-%s.socket" % \
-                            realm_to_serverid(self.realm_name)
+                            realm_to_serverid(self.realm)
 
         self.smb_conf = "/etc/samba/smb.conf"
 
@@ -479,7 +480,7 @@ class ADTRUSTInstance(service.Service):
         self.trust_dn = str(DN(api.env.container_trusts, self.suffix))
         self.smb_dom_dn = str(DN(('cn', self.domain_name),
                                  api.env.container_cifsdomains, self.suffix))
-        self.cifs_principal = "cifs/" + self.fqdn + "@" + self.realm_name
+        self.cifs_principal = "cifs/" + self.fqdn + "@" + self.realm
         self.cifs_agent = str(DN(('krbprincipalname', self.cifs_principal.lower()),
                                  api.env.container_service,
                                  self.suffix))
@@ -522,11 +523,11 @@ class ADTRUSTInstance(service.Service):
                              "range.\nAdd local ID range manually and try " \
                              "again!")
 
-        entry = ipaldap.Entry(str(DN(('cn', ('%s_id_range' % self.realm_name)),
+        entry = ipaldap.Entry(str(DN(('cn', ('%s_id_range' % self.realm)),
                                      api.env.container_ranges,
                                      self.suffix)))
         entry.setValue('objectclass', 'ipaDomainIDRange')
-        entry.setValue('cn', ('%s_id_range' % self.realm_name))
+        entry.setValue('cn', ('%s_id_range' % self.realm))
         entry.setValue('ipaBaseID', str(base_id))
         entry.setValue('ipaIDRangeSize', str(id_range_size))
         self.admin_conn.addEntry(entry)
diff --git a/ipaserver/install/service.py b/ipaserver/install/service.py
index 5c2699e3fa4c115c972528d4c2cc6aa170711837..65c8e1b87412fc6aba1187615e39c2ec523e00e5 100644
--- a/ipaserver/install/service.py
+++ b/ipaserver/install/service.py
@@ -107,15 +107,26 @@ class Service(object):
             if sub_dict.has_key('RANDOM_PASSWORD'):
                 nologlist.append(sub_dict['RANDOM_PASSWORD'])
 
+        args = ["/usr/bin/ldapmodify", "-v", "-f", path]
+
+        # Support autobind when running as root and using ldapi
+        if self.ldapi:
+            if not self.admin_conn:
+                self.ldap_connect()
+            args += ["-H", self.admin_conn._uri]
+        else:
+            args += ["-h", hostname]
+
         if self.dm_password:
             [pw_fd, pw_name] = tempfile.mkstemp()
             os.write(pw_fd, self.dm_password)
             os.close(pw_fd)
             auth_parms = ["-x", "-D", "cn=Directory Manager", "-y", pw_name]
         else:
-            auth_parms = ["-Y", "GSSAPI"]
+            auth_params = []
+            if os.getegid() != 0:
+                auth_parms = ["-Y", "GSSAPI"]
 
-        args = ["/usr/bin/ldapmodify", "-h", hostname, "-v", "-f", path]
         args += auth_parms
 
         try:
-- 
1.7.10.4



More information about the Freeipa-devel mailing list