590d18
From 2c577bc650613f5adfcc1efed0bfa40187eb362e Mon Sep 17 00:00:00 2001
590d18
From: Alexander Bokovoy <abokovoy@redhat.com>
590d18
Date: Thu, 13 Aug 2015 17:18:57 +0300
590d18
Subject: [PATCH] trusts: harden trust-fetch-domains oddjobd-based script
590d18
590d18
When ipa-getkeytab is used to fetch trusted domain object credentials,
590d18
the fetched entry has always kvno 1. ipa-getkeytab always adds a key to
590d18
keytab which means older key versions will be in the SSSD keytab and
590d18
will confuse libkrb5 ccache initialization code as all kvno values are
590d18
equal to 1. Wrong key is picked up then and kinit fails.
590d18
590d18
To solve this problem, always remove existing
590d18
/var/lib/sss/keytabs/forest.keytab before retrieving a new one.
590d18
590d18
To make sure script's input cannot be used to define what should be
590d18
removed (by passing a relative path), make sure we retrieve trusted
590d18
forest name from LDAP. If it is not possible to retrieve, the script
590d18
will issue an exception and quit. If abrtd is running, this will be
590d18
recorded as a 'crash' and an attempt to use script by malicious user
590d18
would be recorded as well in the abrtd journal.
590d18
590d18
Additionally, as com.redhat.idm.trust-fetch-domains will create
590d18
ID ranges for the domains of the trusted forest if they don't exist,
590d18
it needs permissions to do so. The permission should be granted only
590d18
to cifs/ipa.master@IPA.REALM services which means they must have
590d18
krbprincipalname=cifs/*@IPA.REALM,cn=services,... DN and be members of
590d18
cn=adtrust agents,cn=sysaccounts,... group.
590d18
590d18
Solves https://bugzilla.redhat.com/show_bug.cgi?id=1250190
590d18
590d18
Ticket https://fedorahosted.org/freeipa/ticket/5182
590d18
590d18
Reviewed-By: Tomas Babej <tbabej@redhat.com>
590d18
---
590d18
 install/oddjob/com.redhat.idm.trust-fetch-domains | 29 +++++++++++++++++++----
590d18
 install/updates/20-aci.update                     |  4 ++++
590d18
 2 files changed, 28 insertions(+), 5 deletions(-)
590d18
590d18
diff --git a/install/oddjob/com.redhat.idm.trust-fetch-domains b/install/oddjob/com.redhat.idm.trust-fetch-domains
590d18
index e50c81e50e73b258bf08737c2d9a13a8832eb69f..6a2171d5f1936fabd00081bbbcea562a0e7041b8 100755
590d18
--- a/install/oddjob/com.redhat.idm.trust-fetch-domains
590d18
+++ b/install/oddjob/com.redhat.idm.trust-fetch-domains
590d18
@@ -41,6 +41,9 @@ def retrieve_keytab(api, ccache_name, oneway_keytab_name, oneway_principal):
590d18
                       "-p", oneway_principal,
590d18
                       "-k", oneway_keytab_name,
590d18
                       "-r"]
590d18
+    if os.path.isfile(oneway_keytab_name):
590d18
+        os.unlink(oneway_keytab_name)
590d18
+
590d18
     (stdout, stderr, retcode) = ipautil.run(getkeytab_args,
590d18
                                             env={'KRB5CCNAME': ccache_name, 'LANG': 'C'},
590d18
                                             raiseonerr=False)
590d18
@@ -111,7 +114,6 @@ from ipalib.plugins import trust
590d18
 # retrieve the keys to oneway_keytab_name.
590d18
 
590d18
 keytab_name = '/etc/samba/samba.keytab'
590d18
-oneway_keytab_name = '/var/lib/sss/keytabs/' + trusted_domain + '.keytab'
590d18
 
590d18
 principal = str('cifs/' + api.env.host)
590d18
 
590d18
@@ -137,10 +139,20 @@ else:
590d18
 old_ccache = os.environ.get('KRB5CCNAME')
590d18
 api.Backend.ldap2.connect(ccache)
590d18
 
590d18
+# Retrieve own NetBIOS name and trusted forest's name.
590d18
+# We use script's input to retrieve the trusted forest's name to sanitize input
590d18
+# for file-level access as we might need to wipe out keytab in /var/lib/sss/keytabs
590d18
 own_trust_dn = DN(('cn', api.env.domain),('cn','ad'), ('cn', 'etc'), api.env.basedn)
590d18
 own_trust_entry = api.Backend.ldap2.get_entry(own_trust_dn, ['ipantflatname'])
590d18
-own_trust_flatname = own_trust_entry['ipantflatname'][0].upper()
590d18
+own_trust_flatname = own_trust_entry.single_value.get('ipantflatname').upper()
590d18
+trusted_domain_dn = DN(('cn', trusted_domain.lower()), api.env.container_adtrusts, api.env.basedn)
590d18
+trusted_domain_entry = api.Backend.ldap2.get_entry(trusted_domain_dn, ['cn'])
590d18
+trusted_domain = trusted_domain_entry.single_value.get('cn').lower()
590d18
 
590d18
+# At this point if we didn't find trusted forest name, an exception will be raised
590d18
+# and script will quit. This is actually intended.
590d18
+
590d18
+oneway_keytab_name = '/var/lib/sss/keytabs/' + trusted_domain + '.keytab'
590d18
 oneway_principal = str('%s$@%s' % (own_trust_flatname, trusted_domain.upper()))
590d18
 
590d18
 # If keytab does not exist, retrieve it
590d18
@@ -152,11 +164,18 @@ try:
590d18
     # The keytab may have stale key material (from older trust-add run)
590d18
     if not os.path.isfile(oneway_ccache_name):
590d18
         oneway_ccache = kinit_keytab(oneway_principal, oneway_keytab_name, oneway_ccache_name)
590d18
+    else:
590d18
+        oneway_ccache_check = KRB5_CCache(oneway_ccache_name)
590d18
+        if not oneway_ccache_check.credential_is_valid(oneway_principal):
590d18
+            # If credentials were invalid, obtain them again
590d18
+            oneway_ccache = kinit_keytab(oneway_principal, oneway_keytab_name, oneway_ccache_name)
590d18
+        else:
590d18
+            oneway_ccache = oneway_ccache_check.ccache
590d18
 except krbV.Krb5Error as e:
590d18
     # If there was failure on using keytab, assume it is stale and retrieve again
590d18
     retrieve_keytab(api, ccache_name, oneway_keytab_name, oneway_principal)
590d18
 
590d18
-if oneway_ccache:
590d18
+try:
590d18
     # There wasn existing ccache, validate its content
590d18
     oneway_ccache_check = KRB5_CCache(oneway_ccache_name)
590d18
     if not oneway_ccache_check.credential_is_valid(oneway_principal):
590d18
@@ -164,7 +183,7 @@ if oneway_ccache:
590d18
         oneway_ccache = kinit_keytab(oneway_principal, oneway_keytab_name, oneway_ccache_name)
590d18
     else:
590d18
         oneway_ccache = oneway_ccache_check.ccache
590d18
-else:
590d18
+except krbV.Krb5Error as e:
590d18
     oneway_ccache = kinit_keytab(oneway_principal, oneway_keytab_name, oneway_ccache_name)
590d18
 
590d18
 # We are done: we have ccache with TDO credentials and can fetch domains
590d18
@@ -193,7 +212,7 @@ if domains:
590d18
                 dom['range_type'] = u'ipa-ad-trust'
590d18
                 # Do not pass ipaserver.dcerpc.TrustInstance to trust.add_range
590d18
                 # to force it using existing credentials cache
590d18
-                trust.add_range(None, range_name, dom['ipanttrusteddomainsid'],
590d18
+                trust.add_range(api, None, range_name, dom['ipanttrusteddomainsid'],
590d18
                                 trusted_domain, name, **dom)
590d18
         except errors.DuplicateEntry:
590d18
             # Ignore updating duplicate entries
590d18
diff --git a/install/updates/20-aci.update b/install/updates/20-aci.update
590d18
index 0bdeeb6acefad4f79385a1ddaec95df2e40377fb..cba1897e1fd1136fbcbd7c6ccaf03bfa4b696a44 100644
590d18
--- a/install/updates/20-aci.update
590d18
+++ b/install/updates/20-aci.update
590d18
@@ -87,3 +87,7 @@ add:aci:(targetattr = "usercertificate")(version 3.0;acl "selfservice:Users can
590d18
 # Hosts can add their own services
590d18
 dn: cn=services,cn=accounts,$SUFFIX
590d18
 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";)
590d18
+
590d18
+# CIFS service on the master can manage ID ranges
590d18
+dn: cn=ranges,cn=etc,$SUFFIX
590d18
+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";)
590d18
-- 
590d18
2.4.3
590d18