[Freeipa-devel] [PATCH] 0195 harden trust-fetch-domains oddjobd script

Alexander Bokovoy abokovoy at redhat.com
Tue Aug 18 08:52:52 UTC 2015


On Mon, 17 Aug 2015, Tomas Babej wrote:
>
>
>On 08/17/2015 09:03 AM, Alexander Bokovoy wrote:
>> On Mon, 17 Aug 2015, Tomas Babej wrote:
>>>
>>>
>>> On 08/13/2015 04:29 PM, Alexander Bokovoy wrote:
>>>> Hi,
>>>>
>>>> see commit message for details.
>>>>
>>>>
>>>>
>>>
>>> Hi,
>>>
>>> code-wise this looks good to me. Unfortunately, I have not been able to
>>> verify in my setup that it fixes the issue in the linked BZ:
>>>
>>> $ echo Secret123456 | ipa trust-add --type=ad ad.test --range-type
>>> ipa-ad-trust --admin Administrator --password
>>> ------------------------------------------------
>>> Added Active Directory trust for realm "ad.test"
>>> ------------------------------------------------
>>>  Realm name: ad.test
>>>  Domain NetBIOS name: AD
>>>  Domain Security Identifier: S-1-5-21-1469936554-2294197481-461507924
>>>  SID blacklist incoming: S-1-5-20, S-1-5-3, S-1-5-2, S-1-5-1, S-1-5-7,
>>> S-1-5-6, S-1-5-5, S-1-5-4, S-1-5-9, S-1-5-8,
>>>                          S-1-5-17, S-1-5-16, S-1-5-15, S-1-5-14,
>>> S-1-5-13, S-1-5-12, S-1-5-11, S-1-5-10, S-1-3, S-1-2,
>>>                          S-1-1, S-1-0, S-1-5-19, S-1-5-18
>>>  SID blacklist outgoing: S-1-5-20, S-1-5-3, S-1-5-2, S-1-5-1, S-1-5-7,
>>> S-1-5-6, S-1-5-5, S-1-5-4, S-1-5-9, S-1-5-8,
>>>                          S-1-5-17, S-1-5-16, S-1-5-15, S-1-5-14,
>>> S-1-5-13, S-1-5-12, S-1-5-11, S-1-5-10, S-1-3, S-1-2,
>>>                          S-1-1, S-1-0, S-1-5-19, S-1-5-18
>>>  Trust direction: Trusting forest
>>>  Trust type: Active Directory domain
>>>  Trust status: Established and verified
>>>
>>> $ idrange-find
>>>
>>> ----------------
>>> 2 ranges matched
>>> ----------------
>>>  Range name: AD.TEST_id_range
>>>  First Posix ID of the range: 191200000
>>>  Number of IDs in the range: 200000
>>>  First RID of the corresponding RID range: 0
>>>  Domain SID of the trusted domain:
>>> S-1-5-21-1469936554-2294197481-461507924
>>>  Range type: Active Directory domain range
>>>
>>>  Range name: IPA.TEST_id_range
>>>  First Posix ID of the range: 695200000
>>>  Number of IDs in the range: 200000
>>>  First RID of the corresponding RID range: 1000
>>>  First RID of the secondary RID range: 100000000
>>>  Range type: local domain range
>>> ----------------------------
>>> Number of entries returned 2
>>> ----------------------------
>>>
>>> However, I have one child subdomain in the setup:
>>>
>>> $ ipa trustdomain-find
>>> Realm name: ad.test
>>>  Domain name: ad.test
>>>  Domain NetBIOS name: AD
>>>  Domain Security Identifier: S-1-5-21-1469936554-2294197481-461507924
>>>  Domain enabled: True
>>>
>>>  Domain name: sub.ad.test
>>>  Domain NetBIOS name: SUB
>>>  Domain Security Identifier: S-1-5-21-10134726-2575992721-4229914074
>>>  Domain enabled: True
>>> ----------------------------
>>> Number of entries returned 2
>>> ----------------------------
>> Look for AVCs, if there are any.
>>
>> Also start abrtd and it should pick up any python exceptions in the
>> helper as 'crashes'.
>>
>
>Right. Insufficient LDAP permissions caused the following backtrace in
>the oddjob helper:
>
>ipaldap.py:948:error_handler:ACIError: Insufficient access: Insufficient
>'add' privilege to add the entry
>'cn=SUB.AD.TEST_id_range,cn=ranges,cn=etc,dc=ipa,dc=test'.
>
>Traceback (most recent call last):
>  File "/usr/libexec/ipa/com.redhat.idm.trust-fetch-domains", line 216,
>in <module>
>    trusted_domain, name, **dom)
>  File "/usr/lib/python2.7/site-packages/ipalib/plugins/trust.py", line
>347, in add_range
>    ipanttrusteddomainsid=dom_sid)
>  File "/usr/lib/python2.7/site-packages/ipalib/frontend.py", line 443,
>in __call__
>    ret = self.run(*args, **options)
>  File "/usr/lib/python2.7/site-packages/ipalib/frontend.py", line 760,
>in run
>    return self.execute(*args, **options)
>  File "/usr/lib/python2.7/site-packages/ipalib/plugins/baseldap.py",
>line 1234, in execute
>    self._exc_wrapper(keys, options, ldap.add_entry)(entry_attrs)
>  File "/usr/lib/python2.7/site-packages/ipalib/plugins/baseldap.py",
>line 1145, in wrapped
>    return func(*call_args, **call_kwargs)
>  File "/usr/lib/python2.7/site-packages/ipapython/ipaldap.py", line
>1442, in add_entry
>    self.conn.add_s(str(entry.dn), attrs.items())
>  File "/usr/lib64/python2.7/contextlib.py", line 35, in __exit__
>    self.gen.throw(type, value, traceback)
>  File "/usr/lib/python2.7/site-packages/ipapython/ipaldap.py", line
>948, in error_handler
>    raise errors.ACIError(info=info)
>ACIError: Insufficient access: Insufficient 'add' privilege to add the
>entry 'cn=SUB.AD.TEST_id_range,cn=ranges,cn=etc,dc=ipa,dc=test'.
>
>Local variables in innermost frame:
>info: "Insufficient 'add' privilege to add the entry
>'cn=SUB.AD.TEST_id_range,cn=ranges,cn=etc,dc=ipa,dc=test'."
>arg_desc: None
>self: ipaserver.plugins.ldap2.ldap2()
>e: INSUFFICIENT_ACCESS({'info': "Insufficient 'add' privilege to add the
>entry 'cn=SUB.AD.TEST_id_range,cn=ranges,cn=etc,dc=ipa,dc=test'.\n",
>'desc': 'Insufficient access'},)
>desc: 'Insufficient access'
Updated patch attached.

You can install freeipa from my COPR abbra/freeipa-oneway (you need
mkosek/freeipa-master COPR for dependencies) to test.
-- 
/ Alexander Bokovoy
-------------- next part --------------
From eb667407662906b609282b13f87129c80c058a9f Mon Sep 17 00:00:00 2001
From: Alexander Bokovoy <abokovoy at redhat.com>
Date: Thu, 13 Aug 2015 17:18:57 +0300
Subject: [PATCH] trusts: harden trust-fetch-domains oddjobd-based script

When ipa-getkeytab is used to fetch trusted domain object credentials,
the fetched entry has always kvno 1. ipa-getkeytab always adds a key to
keytab which means older key versions will be in the SSSD keytab and
will confuse libkrb5 ccache initialization code as all kvno values are
equal to 1. Wrong key is picked up then and kinit fails.

To solve this problem, always remove existing
/var/lib/sss/keytabs/forest.keytab before retrieving a new one.

To make sure script's input cannot be used to define what should be
removed (by passing a relative path), make sure we retrieve trusted
forest name from LDAP. If it is not possible to retrieve, the script
will issue an exception and quit. If abrtd is running, this will be
recorded as a 'crash' and an attempt to use script by malicious user
would be recorded as well in the abrtd journal.

Additionally, as com.redhat.idm.trust-fetch-domains will create
ID ranges for the domains of the trusted forest if they don't exist,
it needs permissions to do so. The permission should be granted only
to cifs/ipa.master at IPA.REALM services which means they must have
krbprincipalname=cifs/*@IPA.REALM,cn=services,... DN and be members of
cn=adtrust agents,cn=sysaccounts,... group.

Solves https://bugzilla.redhat.com/show_bug.cgi?id=1250190

Ticket https://fedorahosted.org/freeipa/ticket/5182
---
 install/oddjob/com.redhat.idm.trust-fetch-domains | 29 +++++++++++++++++++----
 install/updates/20-aci.update                     |  4 ++++
 2 files changed, 28 insertions(+), 5 deletions(-)

diff --git a/install/oddjob/com.redhat.idm.trust-fetch-domains b/install/oddjob/com.redhat.idm.trust-fetch-domains
index e50c81e..6a2171d 100755
--- a/install/oddjob/com.redhat.idm.trust-fetch-domains
+++ b/install/oddjob/com.redhat.idm.trust-fetch-domains
@@ -41,6 +41,9 @@ def retrieve_keytab(api, ccache_name, oneway_keytab_name, oneway_principal):
                       "-p", oneway_principal,
                       "-k", oneway_keytab_name,
                       "-r"]
+    if os.path.isfile(oneway_keytab_name):
+        os.unlink(oneway_keytab_name)
+
     (stdout, stderr, retcode) = ipautil.run(getkeytab_args,
                                             env={'KRB5CCNAME': ccache_name, 'LANG': 'C'},
                                             raiseonerr=False)
@@ -111,7 +114,6 @@ from ipalib.plugins import trust
 # retrieve the keys to oneway_keytab_name.
 
 keytab_name = '/etc/samba/samba.keytab'
-oneway_keytab_name = '/var/lib/sss/keytabs/' + trusted_domain + '.keytab'
 
 principal = str('cifs/' + api.env.host)
 
@@ -137,10 +139,20 @@ else:
 old_ccache = os.environ.get('KRB5CCNAME')
 api.Backend.ldap2.connect(ccache)
 
+# Retrieve own NetBIOS name and trusted forest's name.
+# We use script's input to retrieve the trusted forest's name to sanitize input
+# for file-level access as we might need to wipe out keytab in /var/lib/sss/keytabs
 own_trust_dn = DN(('cn', api.env.domain),('cn','ad'), ('cn', 'etc'), api.env.basedn)
 own_trust_entry = api.Backend.ldap2.get_entry(own_trust_dn, ['ipantflatname'])
-own_trust_flatname = own_trust_entry['ipantflatname'][0].upper()
+own_trust_flatname = own_trust_entry.single_value.get('ipantflatname').upper()
+trusted_domain_dn = DN(('cn', trusted_domain.lower()), api.env.container_adtrusts, api.env.basedn)
+trusted_domain_entry = api.Backend.ldap2.get_entry(trusted_domain_dn, ['cn'])
+trusted_domain = trusted_domain_entry.single_value.get('cn').lower()
 
+# At this point if we didn't find trusted forest name, an exception will be raised
+# and script will quit. This is actually intended.
+
+oneway_keytab_name = '/var/lib/sss/keytabs/' + trusted_domain + '.keytab'
 oneway_principal = str('%s$@%s' % (own_trust_flatname, trusted_domain.upper()))
 
 # If keytab does not exist, retrieve it
@@ -152,11 +164,18 @@ try:
     # The keytab may have stale key material (from older trust-add run)
     if not os.path.isfile(oneway_ccache_name):
         oneway_ccache = kinit_keytab(oneway_principal, oneway_keytab_name, oneway_ccache_name)
+    else:
+        oneway_ccache_check = KRB5_CCache(oneway_ccache_name)
+        if not oneway_ccache_check.credential_is_valid(oneway_principal):
+            # If credentials were invalid, obtain them again
+            oneway_ccache = kinit_keytab(oneway_principal, oneway_keytab_name, oneway_ccache_name)
+        else:
+            oneway_ccache = oneway_ccache_check.ccache
 except krbV.Krb5Error as e:
     # If there was failure on using keytab, assume it is stale and retrieve again
     retrieve_keytab(api, ccache_name, oneway_keytab_name, oneway_principal)
 
-if oneway_ccache:
+try:
     # There wasn existing ccache, validate its content
     oneway_ccache_check = KRB5_CCache(oneway_ccache_name)
     if not oneway_ccache_check.credential_is_valid(oneway_principal):
@@ -164,7 +183,7 @@ if oneway_ccache:
         oneway_ccache = kinit_keytab(oneway_principal, oneway_keytab_name, oneway_ccache_name)
     else:
         oneway_ccache = oneway_ccache_check.ccache
-else:
+except krbV.Krb5Error as e:
     oneway_ccache = kinit_keytab(oneway_principal, oneway_keytab_name, oneway_ccache_name)
 
 # We are done: we have ccache with TDO credentials and can fetch domains
@@ -193,7 +212,7 @@ if domains:
                 dom['range_type'] = u'ipa-ad-trust'
                 # Do not pass ipaserver.dcerpc.TrustInstance to trust.add_range
                 # to force it using existing credentials cache
-                trust.add_range(None, range_name, dom['ipanttrusteddomainsid'],
+                trust.add_range(api, None, range_name, dom['ipanttrusteddomainsid'],
                                 trusted_domain, name, **dom)
         except errors.DuplicateEntry:
             # Ignore updating duplicate entries
diff --git a/install/updates/20-aci.update b/install/updates/20-aci.update
index 0bdeeb6..cba1897 100644
--- a/install/updates/20-aci.update
+++ b/install/updates/20-aci.update
@@ -87,3 +87,7 @@ add:aci:(targetattr = "usercertificate")(version 3.0;acl "selfservice:Users can
 # Hosts can add their own services
 dn: cn=services,cn=accounts,$SUFFIX
 add:aci: (target = "ldap:///krbprincipalname=*/($$dn)@$REALM,cn=services,cn=accounts,$SUFFIX")(targetfilter = "(objectClass=ipaKrbPrincipal)")(version 3.0;acl "Hosts can add own services"; allow(add) userdn="ldap:///fqdn=($$dn),cn=computers,cn=accounts,$SUFFIX";)
+
+# CIFS service on the master can manage ID ranges
+dn: cn=ranges,cn=etc,$SUFFIX
+add:aci: (target = "ldap:///cn=*,cn=ranges,cn=etc,$SUFFIX")(targetfilter = "(objectClass=ipaIDrange)")(version 3.0;acl "CIFS service can manage ID ranges for trust"; allow(all) userdn="ldap:///krbprincipalname=cifs/*@$REALM,cn=services,cn=accounts,$SUFFIX" and groupdn="ldap:///cn=adtrust agents,cn=sysaccounts,cn=etc,$SUFFIX";)
-- 
2.4.3



More information about the Freeipa-devel mailing list