86baa9
From 12a079a0b599cf192c4bee8bcc37f1824f82539e Mon Sep 17 00:00:00 2001
86baa9
From: Alexander Bokovoy <abokovoy@redhat.com>
86baa9
Date: Sun, 31 Mar 2019 10:39:05 +0300
86baa9
Subject: [PATCH] oddjob: allow to pass options to trust-fetch-domains
86baa9
86baa9
Refactor com.redhat.idm.trust-fetch.domains oddjob helper to allow
86baa9
passing administrative credentials and a domain controller to talk to.
86baa9
86baa9
This approach allows to avoid rediscovering a domain controller in case
86baa9
a user actually specified the domain controller when establishing trust.
86baa9
86baa9
It also allows to pass through admin credentials if user decides to do
86baa9
so. The latter will be used later to allow updating trust topology in a
86baa9
similar oddjob helper.
86baa9
86baa9
Resolves: https://pagure.io/freeipa/issue/7895
86baa9
Reviewed-By: Christian Heimes <cheimes@redhat.com>
86baa9
(cherry picked from commit de4a9875d410c68ae4f9602b70c753a11034b31b)
86baa9
---
86baa9
 API.txt                                       |  4 +-
86baa9
 VERSION.m4                                    |  4 +-
86baa9
 .../oddjob/com.redhat.idm.trust-fetch-domains | 91 +++++++++++--------
86baa9
 .../etc/oddjobd.conf.d/oddjobd-ipa-trust.conf |  2 +-
86baa9
 ipaserver/plugins/trust.py                    | 31 ++++++-
86baa9
 5 files changed, 83 insertions(+), 49 deletions(-)
86baa9
86baa9
diff --git a/API.txt b/API.txt
86baa9
index 222e30915ccc1fb4a6f3ce228669453f346fdde4..325df7a6ef48afbc438bcc1f7b8ca64efb229886 100644
86baa9
--- a/API.txt
86baa9
+++ b/API.txt
86baa9
@@ -5765,10 +5765,12 @@ output: Output('result', type=[<type 'dict'>])
86baa9
 output: Output('summary', type=[<type 'unicode'>, <type 'NoneType'>])
86baa9
 output: ListOfPrimaryKeys('value')
86baa9
 command: trust_fetch_domains/1
86baa9
-args: 1,5,4
86baa9
+args: 1,7,4
86baa9
 arg: Str('cn', cli_name='realm')
86baa9
 option: Flag('all', autofill=True, cli_name='all', default=False)
86baa9
 option: Flag('raw', autofill=True, cli_name='raw', default=False)
86baa9
+option: Str('realm_admin?', cli_name='admin')
86baa9
+option: Password('realm_passwd?', cli_name='password', confirm=False)
86baa9
 option: Str('realm_server?', cli_name='server')
86baa9
 option: Flag('rights', autofill=True, default=False)
86baa9
 option: Str('version?')
86baa9
diff --git a/VERSION.m4 b/VERSION.m4
86baa9
index b1425626ca00ffbcc902c66685cb27cbb2136539..da391d8bd4dad6ae8e7418e6af54a9d481133cc8 100644
86baa9
--- a/VERSION.m4
86baa9
+++ b/VERSION.m4
86baa9
@@ -82,8 +82,8 @@ define(IPA_DATA_VERSION, 20100614120000)
86baa9
 #                                                      #
86baa9
 ########################################################
86baa9
 define(IPA_API_VERSION_MAJOR, 2)
86baa9
-define(IPA_API_VERSION_MINOR, 230)
86baa9
-# Last change: Added `automember-find-orphans' command
86baa9
+define(IPA_API_VERSION_MINOR, 231)
86baa9
+# Last change: Added admin creds to trust-fetch-domains
86baa9
 
86baa9
 
86baa9
 ########################################################
86baa9
diff --git a/install/oddjob/com.redhat.idm.trust-fetch-domains b/install/oddjob/com.redhat.idm.trust-fetch-domains
86baa9
index 30150793d98011d153081477c0856618e1454ba5..1e90759e0e6e7be36745ec12f4478bfec0f0358e 100755
86baa9
--- a/install/oddjob/com.redhat.idm.trust-fetch-domains
86baa9
+++ b/install/oddjob/com.redhat.idm.trust-fetch-domains
86baa9
@@ -14,11 +14,31 @@ import pwd
86baa9
 import six
86baa9
 import gssapi
86baa9
 
86baa9
-from ipalib.install.kinit import kinit_keytab
86baa9
+from ipalib.install.kinit import kinit_keytab, kinit_password
86baa9
 
86baa9
 if six.PY3:
86baa9
     unicode = str
86baa9
 
86baa9
+
86baa9
+def parse_options():
86baa9
+    usage = "%prog <trusted domain name>\n"
86baa9
+    parser = config.IPAOptionParser(usage=usage,
86baa9
+                                    formatter=config.IPAFormatter())
86baa9
+
86baa9
+    parser.add_option("-d", "--debug", action="store_true", dest="debug",
86baa9
+                      help="Display debugging information")
86baa9
+    parser.add_option("-s", "--server", action="store", dest="server",
86baa9
+                      help="Domain controller for the Active Directory domain (optional)")
86baa9
+    parser.add_option("-a", "--admin", action="store", dest="admin",
86baa9
+                      help="Active Directory administrator (optional)")
86baa9
+    parser.add_option("-p", "--password", action="store", dest="password",
86baa9
+                      help="Display debugging information")
86baa9
+
86baa9
+    options, args = parser.parse_args()
86baa9
+    safe_options = parser.get_safe_opts(options)
86baa9
+
86baa9
+    return safe_options, options, args
86baa9
+
86baa9
 def retrieve_keytab(api, ccache_name, oneway_keytab_name, oneway_principal):
86baa9
     getkeytab_args = ["/usr/sbin/ipa-getkeytab",
86baa9
                       "-s", api.env.host,
86baa9
@@ -40,7 +60,7 @@ def retrieve_keytab(api, ccache_name, oneway_keytab_name, oneway_principal):
86baa9
         pass
86baa9
 
86baa9
 
86baa9
-def get_forest_root_domain(api_instance, trusted_domain):
86baa9
+def get_forest_root_domain(api_instance, trusted_domain, server=None):
86baa9
     """
86baa9
     retrieve trusted forest root domain for given domain name
86baa9
 
86baa9
@@ -53,25 +73,11 @@ def get_forest_root_domain(api_instance, trusted_domain):
86baa9
     flatname = trustconfig_show()['result']['ipantflatname'][0]
86baa9
 
86baa9
     remote_domain = dcerpc.retrieve_remote_domain(
86baa9
-        api_instance.env.host, flatname, trusted_domain)
86baa9
+        api_instance.env.host, flatname, trusted_domain,
86baa9
+        realm_server=server)
86baa9
 
86baa9
     return remote_domain.info['dns_forest']
86baa9
 
86baa9
-
86baa9
-def parse_options():
86baa9
-    usage = "%prog <trusted domain name>\n"
86baa9
-    parser = config.IPAOptionParser(usage=usage,
86baa9
-                                    formatter=config.IPAFormatter())
86baa9
-
86baa9
-    parser.add_option("-d", "--debug", action="store_true", dest="debug",
86baa9
-                      help="Display debugging information")
86baa9
-
86baa9
-    options, args = parser.parse_args()
86baa9
-    safe_options = parser.get_safe_opts(options)
86baa9
-
86baa9
-    return safe_options, options, args
86baa9
-
86baa9
-
86baa9
 if not is_ipa_configured():
86baa9
     # LSB status code 6: program is not configured
86baa9
     raise ScriptError("IPA is not configured " +
86baa9
@@ -153,32 +159,37 @@ trusted_domain = trusted_domain_entry.single_value.get('cn').lower()
86baa9
 # At this point if we didn't find trusted forest name, an exception will be raised
86baa9
 # and script will quit. This is actually intended.
86baa9
 
86baa9
-oneway_keytab_name = '/var/lib/sss/keytabs/' + trusted_domain + '.keytab'
86baa9
-oneway_principal = str('%s$@%s' % (own_trust_flatname, trusted_domain.upper()))
86baa9
+if not (options.admin and options.password):
86baa9
+    oneway_keytab_name = '/var/lib/sss/keytabs/' + trusted_domain + '.keytab'
86baa9
+    oneway_principal = str('%s$@%s' % (own_trust_flatname, trusted_domain.upper()))
86baa9
 
86baa9
-# If keytab does not exist, retrieve it
86baa9
-if not os.path.isfile(oneway_keytab_name):
86baa9
-    retrieve_keytab(api, ccache_name, oneway_keytab_name, oneway_principal)
86baa9
+    # If keytab does not exist, retrieve it
86baa9
+    if not os.path.isfile(oneway_keytab_name):
86baa9
+        retrieve_keytab(api, ccache_name, oneway_keytab_name, oneway_principal)
86baa9
 
86baa9
-try:
86baa9
-    have_ccache = False
86baa9
     try:
86baa9
-        # The keytab may have stale key material (from older trust-add run)
86baa9
-        cred = kinit_keytab(oneway_principal, oneway_keytab_name, oneway_ccache_name)
86baa9
-        if cred.lifetime > 0:
86baa9
-            have_ccache = True
86baa9
-    except gssapi.exceptions.ExpiredCredentialsError:
86baa9
-        pass
86baa9
-    if not have_ccache:
86baa9
+        have_ccache = False
86baa9
+        try:
86baa9
+            # The keytab may have stale key material (from older trust-add run)
86baa9
+            cred = kinit_keytab(oneway_principal, oneway_keytab_name, oneway_ccache_name)
86baa9
+            if cred.lifetime > 0:
86baa9
+                have_ccache = True
86baa9
+        except gssapi.exceptions.ExpiredCredentialsError:
86baa9
+            pass
86baa9
+        if not have_ccache:
86baa9
+            if os.path.exists(oneway_ccache_name):
86baa9
+                os.unlink(oneway_ccache_name)
86baa9
+            kinit_keytab(oneway_principal, oneway_keytab_name, oneway_ccache_name)
86baa9
+    except gssapi.exceptions.GSSError:
86baa9
+        # If there was failure on using keytab, assume it is stale and retrieve again
86baa9
+        retrieve_keytab(api, ccache_name, oneway_keytab_name, oneway_principal)
86baa9
         if os.path.exists(oneway_ccache_name):
86baa9
             os.unlink(oneway_ccache_name)
86baa9
         kinit_keytab(oneway_principal, oneway_keytab_name, oneway_ccache_name)
86baa9
-except gssapi.exceptions.GSSError:
86baa9
-    # If there was failure on using keytab, assume it is stale and retrieve again
86baa9
-    retrieve_keytab(api, ccache_name, oneway_keytab_name, oneway_principal)
86baa9
-    if os.path.exists(oneway_ccache_name):
86baa9
-        os.unlink(oneway_ccache_name)
86baa9
-    kinit_keytab(oneway_principal, oneway_keytab_name, oneway_ccache_name)
86baa9
+else:
86baa9
+    cred = kinit_password(options.admin, options.password,
86baa9
+                          oneway_ccache_name,
86baa9
+                          canonicalize=True, enterprise=True)
86baa9
 
86baa9
 # We are done: we have ccache with TDO credentials and can fetch domains
86baa9
 ipa_domain = api.env.domain
86baa9
@@ -186,9 +197,9 @@ os.environ['KRB5CCNAME'] = oneway_ccache_name
86baa9
 
86baa9
 # retrieve the forest root domain name and contact it to retrieve trust
86baa9
 # topology info
86baa9
-forest_root = get_forest_root_domain(api, trusted_domain)
86baa9
+forest_root = get_forest_root_domain(api, trusted_domain, server=options.server)
86baa9
 
86baa9
-domains = dcerpc.fetch_domains(api, ipa_domain, forest_root, creds=True)
86baa9
+domains = dcerpc.fetch_domains(api, ipa_domain, forest_root, creds=True, server=options.server)
86baa9
 trust_domain_object = api.Command.trust_show(trusted_domain, raw=True)['result']
86baa9
 trust.add_new_domains_from_trust(api, None, trust_domain_object, domains)
86baa9
 
86baa9
diff --git a/install/oddjob/etc/oddjobd.conf.d/oddjobd-ipa-trust.conf b/install/oddjob/etc/oddjobd.conf.d/oddjobd-ipa-trust.conf
86baa9
index 630a4e6cd4c0d3ed9ce48bf9b882b7d630f6bbf3..9f3f168a51abfdd1cb7cb8b76221bb4ec806859c 100644
86baa9
--- a/install/oddjob/etc/oddjobd.conf.d/oddjobd-ipa-trust.conf
86baa9
+++ b/install/oddjob/etc/oddjobd.conf.d/oddjobd-ipa-trust.conf
86baa9
@@ -11,7 +11,7 @@
86baa9
       <interface name="com.redhat.idm.trust">
86baa9
         <method name="fetch_domains">
86baa9
           
86baa9
-		  arguments="1"
86baa9
+		  arguments="30"
86baa9
                   argument_passing_method="cmdline"
86baa9
 		  prepend_user_name="no"/>
86baa9
         </method>
86baa9
diff --git a/ipaserver/plugins/trust.py b/ipaserver/plugins/trust.py
86baa9
index 91cd1387ce058f34c2319676767d9e50f9b46ed1..bb9e0fe3303989c49cf339df6e5aeeb4ce1435cf 100644
86baa9
--- a/ipaserver/plugins/trust.py
86baa9
+++ b/ipaserver/plugins/trust.py
86baa9
@@ -418,9 +418,19 @@ def add_range(myapi, trustinstance, range_name, dom_sid, *keys, **options):
86baa9
     return range_type, range_size, base_id
86baa9
 
86baa9
 
86baa9
-def fetch_trusted_domains_over_dbus(myapi, forest_name):
86baa9
+def fetch_trusted_domains_over_dbus(myapi, *keys, **options):
86baa9
     if not _bindings_installed:
86baa9
         return
86baa9
+
86baa9
+    forest_name = keys[0]
86baa9
+    method_options = []
86baa9
+    if 'realm_server' in options:
86baa9
+        method_options.extend(['--server', options['realm_server']])
86baa9
+    if 'realm_admin' in options:
86baa9
+        method_options.extend(['--admin', options['realm_admin']])
86baa9
+    if 'realm_passwd' in options:
86baa9
+        method_options.extend(['--password', options['realm_passwd']])
86baa9
+
86baa9
     # Calling oddjobd-activated service via DBus has some quirks:
86baa9
     # - Oddjobd registers multiple canonical names on the same address
86baa9
     # - python-dbus only follows name owner changes when mainloop is in use
86baa9
@@ -436,7 +446,8 @@ def fetch_trusted_domains_over_dbus(myapi, forest_name):
86baa9
         fetch_domains_method = intf.get_dbus_method(
86baa9
                 'fetch_domains',
86baa9
                 dbus_interface=DBUS_IFACE_TRUST)
86baa9
-        (_ret, _stdout, _stderr) = fetch_domains_method(forest_name)
86baa9
+        (_ret, _stdout, _stderr) = fetch_domains_method(
86baa9
+            [forest_name] + method_options)
86baa9
     except dbus.DBusException as e:
86baa9
         logger.error('Failed to call %s.fetch_domains helper.'
86baa9
                      'DBus exception is %s.', DBUS_IFACE_TRUST, str(e))
86baa9
@@ -1760,10 +1771,20 @@ class trust_fetch_domains(LDAPRetrieve):
86baa9
 
86baa9
     has_output = output.standard_list_of_entries
86baa9
     takes_options = LDAPRetrieve.takes_options + (
86baa9
+        Str('realm_admin?',
86baa9
+            cli_name='admin',
86baa9
+            label=_("Active Directory domain administrator"),
86baa9
+            ),
86baa9
+        Password('realm_passwd?',
86baa9
+                 cli_name='password',
86baa9
+                 label=_("Active Directory domain administrator's password"),
86baa9
+                 confirm=False,
86baa9
+                 ),
86baa9
         Str('realm_server?',
86baa9
             cli_name='server',
86baa9
-            label=_('Domain controller for the Active Directory domain (optional)'),
86baa9
-        ),
86baa9
+            label=_('Domain controller for the Active Directory domain '
86baa9
+                    '(optional)'),
86baa9
+            ),
86baa9
     )
86baa9
 
86baa9
     def execute(self, *keys, **options):
86baa9
@@ -1784,7 +1805,7 @@ class trust_fetch_domains(LDAPRetrieve):
86baa9
         # With privilege separation we also cannot authenticate as
86baa9
         # HTTP/ principal because we have no access to its key material.
86baa9
         # Thus, we'll use DBus call out to oddjobd helper in all cases
86baa9
-        fetch_trusted_domains_over_dbus(self.api, keys[0])
86baa9
+        fetch_trusted_domains_over_dbus(self.api, *keys, **options)
86baa9
         result['summary'] = unicode(_('List of trust domains successfully '
86baa9
                                       'refreshed. Use trustdomain-find '
86baa9
                                       'command to list them.'))
86baa9
-- 
86baa9
2.20.1
86baa9