On 04/09/2015 03:38 PM, Jan Cholasta wrote:


Some comments:

Patch 15:

1) The functions should be as similar as possible:

     a) kinit_password() should have a 'ccache_path' argument instead of
passing the path in KRB5CCNAME in the 'env' argument.

     b) I don't think kinit_password() should have the 'env' argument at
all. You can always call kinit with LC_ALL=C and set other variables in
os.environ if you want.

     c) The arguments should have the same ordering.

     d) Either set KRB5CCNAME in both kinit_keytab() and
kinit_password() or in none of them.

     e) Either rename armor_ccache to armor_ccache_path or ccache_path
to ccache.

I have done some reordering of parameters in both functions so they are very similar now and the parameter ordering should make more sense (at least to me).

Neither of them sets KRB5CCNAME env. variable since I think that it is not a very good practice and the developer should be responsible for pointing to correct CCache path. Jan agrees with this and the other patches are updated accordingly.

2) Space before comma in docstring:

+    Given a ccache_path , keytab file and a principal kinit as that user.


3) I would prefer if the default value of 'armor_ccache' in
kinit_password() was None.

Fixed.

Patch 16:

1) The callback should not be named 'validate_kinit_attempts_option',
but rather 'kinit_attempts_callback', as it doesn't just validate the
value.

Fixed.

2) Why is there the sys.maxint upper bound on --kinit-attempts again? A
comment with explanation would be nice.

It actually doesn't make much sense to have such upper bound, so I have removed it from the check and updated the error message accordingly.

Patch 17:

1) Is there a reason for the ccache filename changes in DNSSEC code?

That was Petr Spacek's request since a sane naming of persistent Ccaches makes debugging of Kerberos-related errors a bit easier for him.

Attaching updated patches.

--
Martin^3 Babinsky
From 6c3e15f121e78e7c0c85ed4f1e167f88eeecc5ee Mon Sep 17 00:00:00 2001
From: Martin Babinsky <mbabi...@redhat.com>
Date: Mon, 16 Mar 2015 16:28:54 +0100
Subject: [PATCH 1/3] ipautil: new functions kinit_keytab and kinit_password

kinit_keytab replaces kinit_hostprincipal and performs Kerberos auth using
keytab file. Function is also able to repeat authentication multiple times
before giving up and raising Krb5Error.

kinit_password wraps kinit auth using password and also supports FAST
authentication using httpd armor ccache.

---
 ipapython/ipautil.py | 72 +++++++++++++++++++++++++++++++++++++++-------------
 1 file changed, 55 insertions(+), 17 deletions(-)

diff --git a/ipapython/ipautil.py b/ipapython/ipautil.py
index 6a06a8e956552597dfd48128b60a1dd6a4cc92f6..963ede59469e41d678e7b12f46ff94c1b53acfdc 100644
--- a/ipapython/ipautil.py
+++ b/ipapython/ipautil.py
@@ -1185,27 +1185,65 @@ def wait_for_open_socket(socket_name, timeout=0):
             else:
                 raise e
 
-def kinit_hostprincipal(keytab, ccachedir, principal):
+
+def kinit_keytab(principal, keytab, ccache_path, attempts=1):
+    """
+    Given a ccache_path, keytab file and a principal kinit as that user.
+
+    The optional parameter 'attempts' specifies how many times the credential
+    initialization should be attempted in case of non-responsive KDC.
     """
-    Given a ccache directory and a principal kinit as that user.
+    errors_to_retry = {krbV.KRB5KDC_ERR_SVC_UNAVAILABLE,
+                       krbV.KRB5_KDC_UNREACH}
+    root_logger.debug("Initializing principal %s using keytab %s"
+                      % (principal, keytab))
+    root_logger.debug("using ccache path %s" % ccache_path)
+    for attempt in range(1, attempts + 1):
+        try:
+            krbcontext = krbV.default_context()
+            ktab = krbV.Keytab(name=keytab, context=krbcontext)
+            princ = krbV.Principal(name=principal, context=krbcontext)
+            ccache = krbV.CCache(name=ccache_path, context=krbcontext,
+                                 primary_principal=princ)
+            ccache.init(princ)
+            ccache.init_creds_keytab(keytab=ktab, principal=princ)
+            root_logger.debug("Attempt %d/%d: success"
+                              % (attempt, attempts))
+            return
+        except krbV.Krb5Error as e:
+            if e.args[0] not in errors_to_retry:
+                raise
+            root_logger.debug("Attempt %d/%d: failed: %s"
+                              % (attempt, attempts, e))
+            if attempt == attempts:
+                root_logger.debug("Maximum number of attempts (%d) reached"
+                                  % attempts)
+                raise
+            root_logger.debug("Waiting 5 seconds before next retry")
+            time.sleep(5)
 
-    This blindly overwrites the current CCNAME so if you need to save
-    it do so before calling this function.
 
-    Thus far this is used to kinit as the local host.
+def kinit_password(principal, password, ccache_path, armor_ccache_path=None):
     """
-    try:
-        ccache_file = 'FILE:%s/ccache' % ccachedir
-        krbcontext = krbV.default_context()
-        ktab = krbV.Keytab(name=keytab, context=krbcontext)
-        princ = krbV.Principal(name=principal, context=krbcontext)
-        os.environ['KRB5CCNAME'] = ccache_file
-        ccache = krbV.CCache(name=ccache_file, context=krbcontext, primary_principal=princ)
-        ccache.init(princ)
-        ccache.init_creds_keytab(keytab=ktab, principal=princ)
-        return ccache_file
-    except krbV.Krb5Error, e:
-        raise StandardError('Error initializing principal %s in %s: %s' % (principal, keytab, str(e)))
+    perform interactive kinit as principal using password. If using FAST for
+    web-based authentication, use armor_ccache_path to specify http service
+    ccache.
+    """
+    root_logger.debug("Initializing principal %s using password" % principal)
+    args = [paths.KINIT, principal, '-c', ccache_path]
+    if armor_ccache_path:
+        root_logger.debug("Using armor ccache %s for FAST webauth"
+                          % armor_ccache_path)
+        args.extend(['-T', armor_ccache_path])
+
+    # this workaround is needed to populate raised CalledProcessError.output
+    # with the stderr from kinit and get some meaningful error message when
+    # needed (e.g. in rpcserver)
+    (stdout, stderr, retcode) = run(args, stdin=password, env={'LC_ALL': 'C'},
+                                    raiseonerr=False)
+    if retcode:
+        raise CalledProcessError(retcode, cmd=' '.join(args), output=stderr)
+
 
 def dn_attribute_property(private_name):
     '''
-- 
2.1.0

From d1c69270c89f164ba607ecf617d8e86cf8089272 Mon Sep 17 00:00:00 2001
From: Martin Babinsky <mbabi...@redhat.com>
Date: Mon, 16 Mar 2015 16:30:55 +0100
Subject: [PATCH 2/3] ipa-client-install: try to get host TGT several times
 before giving up

New option '--kinit-attempts' enables the host to make multiple attempts to
obtain host TGT from master before giving up and aborting client installation.

In addition, all kinit attempts were replaced by calls to
'ipautil.kinit_keytab' and 'ipautil.kinit_password'.

https://fedorahosted.org/freeipa/ticket/4808
---
 ipa-client/ipa-install/ipa-client-install | 64 ++++++++++++++++++-------------
 ipa-client/man/ipa-client-install.1       |  8 ++++
 2 files changed, 46 insertions(+), 26 deletions(-)

diff --git a/ipa-client/ipa-install/ipa-client-install b/ipa-client/ipa-install/ipa-client-install
index e31d83dc98d411d281c9913af6cd45b41e2b51a1..e0c8cd337ed2e650f749f74e53d168b418bc8d04 100755
--- a/ipa-client/ipa-install/ipa-client-install
+++ b/ipa-client/ipa-install/ipa-client-install
@@ -31,6 +31,7 @@ try:
     from ConfigParser import RawConfigParser
     from optparse import SUPPRESS_HELP, OptionGroup, OptionValueError
     import shutil
+    from krbV import Krb5Error
 
     import nss.nss as nss
     import SSSDConfig
@@ -91,6 +92,14 @@ def parse_options():
 
         parser.values.ca_cert_file = value
 
+    def kinit_attempts_callback(option, opt, value, parser):
+        if value < 1:
+            raise OptionValueError(
+                "Option %s expects an integer greater than 0."
+                % opt)
+
+        parser.values.kinit_attempts = value
+
     parser = IPAOptionParser(version=version.VERSION)
 
     basic_group = OptionGroup(parser, "basic options")
@@ -144,6 +153,11 @@ def parse_options():
                       help="do not modify the nsswitch.conf and PAM configuration")
     basic_group.add_option("-f", "--force", dest="force", action="store_true",
                       default=False, help="force setting of LDAP/Kerberos conf")
+    basic_group.add_option('--kinit-attempts', dest='kinit_attempts',
+                           action='callback', type='int', default=5,
+                           callback=kinit_attempts_callback,
+                           help=("number of attempts to obtain host TGT"
+                                 " (defaults to %default)."))
     basic_group.add_option("-d", "--debug", dest="debug", action="store_true",
                       default=False, help="print debugging information")
     basic_group.add_option("-U", "--unattended", dest="unattended",
@@ -2352,6 +2366,7 @@ def install(options, env, fstore, statestore):
             root_logger.debug(
                 "will use principal provided as option: %s", options.principal)
 
+    host_principal = 'host/%s@%s' % (hostname, cli_realm)
     if not options.on_master:
         nolog = tuple()
         # First test out the kerberos configuration
@@ -2410,29 +2425,24 @@ def install(options, env, fstore, statestore):
                         else:
                             stdin = sys.stdin.readline()
 
-                (stderr, stdout, returncode) = run(["kinit", principal],
-                                                    raiseonerr=False,
-                                                    stdin=stdin,
-                                                    env=env)
-                if returncode != 0:
+                try:
+                    ipautil.kinit_password(principal, stdin, ccache_name)
+                except CalledProcessError as e:
                     print_port_conf_info()
-                    root_logger.error("Kerberos authentication failed")
-                    root_logger.info("%s", stdout)
+                    root_logger.error("Kerberos authentication failed: %s"
+                                      % e.output)
                     return CLIENT_INSTALL_ERROR
             elif options.keytab:
                 join_args.append("-f")
                 if os.path.exists(options.keytab):
-                    (stderr, stdout, returncode) = run(
-                        [paths.KINIT,'-k', '-t', options.keytab,
-                            'host/%s@%s' % (hostname, cli_realm)],
-                        env=env,
-                        raiseonerr=False)
-
-                    if returncode != 0:
+                    try:
+                        ipautil.kinit_keytab(host_principal, options.keytab,
+                                             ccache_name,
+                                             attempts=options.kinit_attempts)
+                    except Krb5Error as e:
                         print_port_conf_info()
-                        root_logger.error("Kerberos authentication failed "
-                                          "using keytab: %s", options.keytab)
-                        root_logger.info("%s", stdout)
+                        root_logger.error("Kerberos authentication failed: %s"
+                                          % e)
                         return CLIENT_INSTALL_ERROR
                 else:
                     root_logger.error("Keytab file could not be found: %s"
@@ -2504,10 +2514,12 @@ def install(options, env, fstore, statestore):
             # Once we have the TGT, it's usable on any server.
             env['KRB5CCNAME'] = os.environ['KRB5CCNAME'] = CCACHE_FILE
             try:
-                run([paths.KINIT, '-k', '-t', paths.KRB5_KEYTAB,
-                        'host/%s@%s' % (hostname, cli_realm)], env=env)
-            except CalledProcessError, e:
-                root_logger.error("Failed to obtain host TGT.")
+                ipautil.kinit_keytab(host_principal, paths.KRB5_KEYTAB,
+                                     CCACHE_FILE,
+                                     attempts=options.kinit_attempts)
+            except Krb5Error as e:
+                print_port_conf_info()
+                root_logger.error("Failed to obtain host TGT: %s" % e)
                 # failure to get ticket makes it impossible to login and bind
                 # from sssd to LDAP, abort installation and rollback changes
                 return CLIENT_INSTALL_ERROR
@@ -2544,16 +2556,16 @@ def install(options, env, fstore, statestore):
             return CLIENT_INSTALL_ERROR
         root_logger.info("Configured /etc/sssd/sssd.conf")
 
-    host_principal = 'host/%s@%s' % (hostname, cli_realm)
     if options.on_master:
         # If on master assume kerberos is already configured properly.
         # Get the host TGT.
         os.environ['KRB5CCNAME'] = CCACHE_FILE
         try:
-            run([paths.KINIT, '-k', '-t', paths.KRB5_KEYTAB,
-                    host_principal])
-        except CalledProcessError, e:
-            root_logger.error("Failed to obtain host TGT.")
+            ipautil.kinit_keytab(host_principal, paths.KRB5_KEYTAB,
+                                 CCACHE_FILE,
+                                 attempts=options.kinit_attempts)
+        except Krb5Error as e:
+            root_logger.error("Failed to obtain host TGT: %s" % e)
             return CLIENT_INSTALL_ERROR
     else:
         # Configure krb5.conf
diff --git a/ipa-client/man/ipa-client-install.1 b/ipa-client/man/ipa-client-install.1
index 726a6c133132dd2e3ba2fde43d8a2ec0549bfcef..985cfb064917bba171c68956fc54a62202308892 100644
--- a/ipa-client/man/ipa-client-install.1
+++ b/ipa-client/man/ipa-client-install.1
@@ -152,6 +152,14 @@ Do not use Authconfig to modify the nsswitch.conf and PAM configuration.
 \fB\-f\fR, \fB\-\-force\fR
 Force the settings even if errors occur
 .TP
+\fB\-\-kinit\-attempts\fR=\fIKINIT_ATTEMPTS\fR
+In case of unresponsive KDC (e.g. when enrolling multiple hosts at once in a
+heavy load environment) repeat the request for host Kerberos ticket up to a
+total number of \fIKINIT_ATTEMPTS\fR times before giving up and aborting client
+installation. Default number of attempts is 5. The request is not repeated when
+there is a problem with host credentials themselves (e.g. wrong keytab format
+or invalid principal) so using this option will not lead to account lockouts.
+.TP
 \fB\-d\fR, \fB\-\-debug\fR
 Print debugging information to stdout
 .TP
-- 
2.1.0

From 071c3800fb9d7f355580af19fb603340cc956da0 Mon Sep 17 00:00:00 2001
From: Martin Babinsky <mbabi...@redhat.com>
Date: Mon, 16 Mar 2015 16:43:10 +0100
Subject: [PATCH 3/3] Adopted kinit_keytab and kinit_password for kerberos auth

Calls to ipautil.run using kinit were replaced with calls
kinit_keytab/kinit_password functions implemented in the PATCH 0015.
---
 daemons/dnssec/ipa-dnskeysync-replica              |  5 ++-
 daemons/dnssec/ipa-dnskeysyncd                     |  4 +-
 daemons/dnssec/ipa-ods-exporter                    |  5 ++-
 .../certmonger/dogtag-ipa-ca-renew-agent-submit    |  4 +-
 install/restart_scripts/renew_ca_cert              |  7 ++--
 install/restart_scripts/renew_ra_cert              |  6 ++-
 ipa-client/ipa-install/ipa-client-automount        |  8 ++--
 ipa-client/ipaclient/ipa_certupdate.py             |  4 +-
 ipaserver/rpcserver.py                             | 47 ++++++++++------------
 9 files changed, 50 insertions(+), 40 deletions(-)

diff --git a/daemons/dnssec/ipa-dnskeysync-replica b/daemons/dnssec/ipa-dnskeysync-replica
index 8a0ae8a9aedf128e85e1a666cdb25b7dfe23c475..924281a472707437d69aa855cfd4766d3091ceb5 100755
--- a/daemons/dnssec/ipa-dnskeysync-replica
+++ b/daemons/dnssec/ipa-dnskeysync-replica
@@ -139,14 +139,15 @@ log.setLevel(level=logging.DEBUG)
 # Kerberos initialization
 PRINCIPAL = str('%s/%s' % (DAEMONNAME, ipalib.api.env.host))
 log.debug('Kerberos principal: %s', PRINCIPAL)
-ipautil.kinit_hostprincipal(paths.IPA_DNSKEYSYNCD_KEYTAB, WORKDIR, PRINCIPAL)
+ccache_filename = os.path.join(WORKDIR, 'ipa-dnskeysync-replica.ccache')
+ipautil.kinit_keytab(PRINCIPAL, paths.IPA_DNSKEYSYNCD_KEYTAB, ccache_filename)
 log.debug('Got TGT')
 
 # LDAP initialization
 ldap = ipalib.api.Backend[ldap2]
 # fixme
 log.debug('Connecting to LDAP')
-ldap.connect(ccache="%s/ccache" % WORKDIR)
+ldap.connect(ccache=ccache_filename)
 log.debug('Connected')
 
 
diff --git a/daemons/dnssec/ipa-dnskeysyncd b/daemons/dnssec/ipa-dnskeysyncd
index 9191303438dd6419902a94b43043e46304c9c397..8d81bb04924310ac75a8e026f9b0055a287477e3 100755
--- a/daemons/dnssec/ipa-dnskeysyncd
+++ b/daemons/dnssec/ipa-dnskeysyncd
@@ -65,7 +65,9 @@ log = root_logger
 # Kerberos initialization
 PRINCIPAL = str('%s/%s' % (DAEMONNAME, api.env.host))
 log.debug('Kerberos principal: %s', PRINCIPAL)
-ipautil.kinit_hostprincipal(KEYTAB_FB, WORKDIR, PRINCIPAL)
+ccache_filename = os.path.join(WORKDIR, 'ipa-dnskeysyncd.ccache')
+os.environ['KRB5CCNAME'] = ccache_filename
+ipautil.kinit_keytab(PRINCIPAL, KEYTAB_FB, ccache_filename)
 
 # LDAP initialization
 basedn = DN(api.env.container_dns, api.env.basedn)
diff --git a/daemons/dnssec/ipa-ods-exporter b/daemons/dnssec/ipa-ods-exporter
index 401f350878a980588cf6a36067335daae66ffc9c..0862c32948a328641d902e2fba711f0f56178ce8 100755
--- a/daemons/dnssec/ipa-ods-exporter
+++ b/daemons/dnssec/ipa-ods-exporter
@@ -399,7 +399,8 @@ ipalib.api.finalize()
 # Kerberos initialization
 PRINCIPAL = str('%s/%s' % (DAEMONNAME, ipalib.api.env.host))
 log.debug('Kerberos principal: %s', PRINCIPAL)
-ipautil.kinit_hostprincipal(paths.IPA_ODS_EXPORTER_KEYTAB, WORKDIR, PRINCIPAL)
+ccache_name = os.path.join(WORKDIR, 'ipa-ods-exporter.ccache')
+ipautil.kinit_keytab(PRINCIPAL, paths.IPA_ODS_EXPORTER_KEYTAB, ccache_name)
 log.debug('Got TGT')
 
 # LDAP initialization
@@ -407,7 +408,7 @@ dns_dn = DN(ipalib.api.env.container_dns, ipalib.api.env.basedn)
 ldap = ipalib.api.Backend[ldap2]
 # fixme
 log.debug('Connecting to LDAP')
-ldap.connect(ccache="%s/ccache" % WORKDIR)
+ldap.connect(ccache=ccache_name)
 log.debug('Connected')
 
 
diff --git a/install/certmonger/dogtag-ipa-ca-renew-agent-submit b/install/certmonger/dogtag-ipa-ca-renew-agent-submit
index 7b91fc61148912c77d0ae962b3847d73e8d0720e..66f3bf742198a4793f8004b37e97b17bebe8f1b5 100755
--- a/install/certmonger/dogtag-ipa-ca-renew-agent-submit
+++ b/install/certmonger/dogtag-ipa-ca-renew-agent-submit
@@ -440,7 +440,9 @@ def main():
     certs.renewal_lock.acquire()
     try:
         principal = str('host/%s@%s' % (api.env.host, api.env.realm))
-        ipautil.kinit_hostprincipal(paths.KRB5_KEYTAB, tmpdir, principal)
+        ccache_filename = os.path.join(tmpdir, 'ccache')
+        os.environ['KRB5CCNAME'] = ccache_filename
+        ipautil.kinit_keytab(principal, paths.KRB5_KEYTAB, ccache_filename)
 
         profile = os.environ.get('CERTMONGER_CA_PROFILE')
         if profile:
diff --git a/install/restart_scripts/renew_ca_cert b/install/restart_scripts/renew_ca_cert
index c7bd5d74c5b4659b3ad66d630653ff6419868d99..632ee708bf2aff6438257ce2a5efc4b31df81176 100644
--- a/install/restart_scripts/renew_ca_cert
+++ b/install/restart_scripts/renew_ca_cert
@@ -73,8 +73,9 @@ def _main():
     tmpdir = tempfile.mkdtemp(prefix="tmp-")
     try:
         principal = str('host/%s@%s' % (api.env.host, api.env.realm))
-        ccache = ipautil.kinit_hostprincipal(paths.KRB5_KEYTAB, tmpdir,
-                                             principal)
+        ccache_filename = '%s/ccache' % tmpdir
+        os.environ['KRB5CCNAME'] = ccache_filename
+        ipautil.kinit_keytab(principal, paths.KRB5_KEYTAB, ccache_filename)
 
         ca = cainstance.CAInstance(host_name=api.env.host, ldapi=False)
         ca.update_cert_config(nickname, cert, configured_constants)
@@ -139,7 +140,7 @@ def _main():
             conn = None
             try:
                 conn = ldap2(shared_instance=False, ldap_uri=api.env.ldap_uri)
-                conn.connect(ccache=ccache)
+                conn.connect(ccache=ccache_filename)
             except Exception, e:
                 syslog.syslog(
                     syslog.LOG_ERR, "Failed to connect to LDAP: %s" % e)
diff --git a/install/restart_scripts/renew_ra_cert b/install/restart_scripts/renew_ra_cert
index 7dae3562380e919b2cc5f53825820291fc93fdc5..57c1fc294d9dcdb35b5c149a0599f02a5ab6bde7 100644
--- a/install/restart_scripts/renew_ra_cert
+++ b/install/restart_scripts/renew_ra_cert
@@ -42,8 +42,10 @@ def _main():
     tmpdir = tempfile.mkdtemp(prefix="tmp-")
     try:
         principal = str('host/%s@%s' % (api.env.host, api.env.realm))
-        ccache = ipautil.kinit_hostprincipal(paths.KRB5_KEYTAB, tmpdir,
-                                             principal)
+        ccache_filename = '%s/ccache' % tmpdir
+        os.environ['KRB5CCNAME'] = ccache_filename
+        ipautil.kinit_keytab(principal, paths.KRB5_KEYTAB,
+                             ccache_filename)
 
         ca = cainstance.CAInstance(host_name=api.env.host, ldapi=False)
         if ca.is_renewal_master():
diff --git a/ipa-client/ipa-install/ipa-client-automount b/ipa-client/ipa-install/ipa-client-automount
index ca56f97832159c55cb7010db1b406b52c206462f..57035af707770c7c982a8cda5ec01f86d19dd67d 100755
--- a/ipa-client/ipa-install/ipa-client-automount
+++ b/ipa-client/ipa-install/ipa-client-automount
@@ -26,6 +26,7 @@ import os
 import urlparse
 import time
 import tempfile
+from krbV import Krb5Error
 
 import SSSDConfig
 
@@ -432,9 +433,10 @@ def main():
     try:
         try:
             os.environ['KRB5CCNAME'] = ccache_name
-            ipautil.run([paths.KINIT, '-k', '-t', paths.KRB5_KEYTAB, 'host/%s@%s' % (api.env.host, api.env.realm)])
-        except ipautil.CalledProcessError, e:
-            sys.exit("Failed to obtain host TGT.")
+            host_princ = str('host/%s@%s' % (api.env.host, api.env.realm))
+            ipautil.kinit_keytab(host_princ, paths.KRB5_KEYTAB, ccache_name)
+        except Krb5Error as e:
+            sys.exit("Failed to obtain host TGT: %s" % e)
         # Now we have a TGT, connect to IPA
         try:
             api.Backend.rpcclient.connect()
diff --git a/ipa-client/ipaclient/ipa_certupdate.py b/ipa-client/ipaclient/ipa_certupdate.py
index 5ec5026f5ff7bf93227ce0a551fbf5cf60b9175d..e0a60fcec97effa2869ddb16961084af13a5fd49 100644
--- a/ipa-client/ipaclient/ipa_certupdate.py
+++ b/ipa-client/ipaclient/ipa_certupdate.py
@@ -55,9 +55,11 @@ class CertUpdate(admintool.AdminTool):
         ldap = ipaldap.IPAdmin(server)
 
         tmpdir = tempfile.mkdtemp(prefix="tmp-")
+        ccache_name = os.path.join(tmpdir, 'ccache')
+        os.environ['KRB5CCNAME'] = ccache_name
         try:
             principal = str('host/%s@%s' % (api.env.host, api.env.realm))
-            ipautil.kinit_hostprincipal(paths.KRB5_KEYTAB, tmpdir, principal)
+            ipautil.kinit_keytab(principal, paths.KRB5_KEYTAB, ccache_name)
 
             api.Backend.rpcclient.connect()
             try:
diff --git a/ipaserver/rpcserver.py b/ipaserver/rpcserver.py
index 4173ed918d2ce992aa79d18b2ac3338b35388918..feba628e8ec57095e81c8e715a4738484f825373 100644
--- a/ipaserver/rpcserver.py
+++ b/ipaserver/rpcserver.py
@@ -30,6 +30,7 @@ import datetime
 import urlparse
 import json
 import traceback
+from krbV import Krb5Error
 
 import ldap.controls
 from pyasn1.type import univ, namedtype
@@ -958,8 +959,8 @@ class login_password(Backend, KerberosSession, HTTP_Status):
 
     def kinit(self, user, realm, password, ccache_name):
         # get http service ccache as an armor for FAST to enable OTP authentication
-        armor_principal = krb5_format_service_principal_name(
-            'HTTP', self.api.env.host, realm)
+        armor_principal = str(krb5_format_service_principal_name(
+            'HTTP', self.api.env.host, realm))
         keytab = paths.IPA_KEYTAB
         armor_name = "%sA_%s" % (krbccache_prefix, user)
         armor_path = os.path.join(krbccache_dir, armor_name)
@@ -967,34 +968,30 @@ class login_password(Backend, KerberosSession, HTTP_Status):
         self.debug('Obtaining armor ccache: principal=%s keytab=%s ccache=%s',
                    armor_principal, keytab, armor_path)
 
-        (stdout, stderr, returncode) = ipautil.run(
-            [paths.KINIT, '-kt', keytab, armor_principal],
-            env={'KRB5CCNAME': armor_path}, raiseonerr=False)
-
-        if returncode != 0:
-            raise CCacheError()
+        try:
+            ipautil.kinit_keytab(armor_principal, paths.IPA_KEYTAB, armor_path)
+        except Krb5Error as e:
+            raise CCacheError(str(e))
 
         # Format the user as a kerberos principal
         principal = krb5_format_principal_name(user, realm)
 
-        (stdout, stderr, returncode) = ipautil.run(
-            [paths.KINIT, principal, '-T', armor_path],
-            env={'KRB5CCNAME': ccache_name, 'LC_ALL': 'C'},
-            stdin=password, raiseonerr=False)
+        try:
+            ipautil.kinit_password(principal, password, ccache_name,
+                                   armor_ccache_path=armor_path)
 
-        self.debug('kinit: principal=%s returncode=%s, stderr="%s"',
-                   principal, returncode, stderr)
-
-        self.debug('Cleanup the armor ccache')
-        ipautil.run(
-            [paths.KDESTROY, '-A', '-c', armor_path],
-            env={'KRB5CCNAME': armor_path},
-            raiseonerr=False)
-
-        if returncode != 0:
-            if stderr.strip() == 'kinit: Cannot read password while getting initial credentials':
-                raise PasswordExpired(principal=principal, message=unicode(stderr))
-            raise InvalidSessionPassword(principal=principal, message=unicode(stderr))
+            self.debug('Cleanup the armor ccache')
+            ipautil.run(
+                [paths.KDESTROY, '-A', '-c', armor_path],
+                env={'KRB5CCNAME': armor_path},
+                raiseonerr=False)
+        except ipautil.CalledProcessError as e:
+            if ('kinit: Cannot read password while '
+                    'getting initial credentials') in e.output:
+                raise PasswordExpired(principal=principal,
+                                      message=unicode(e.output))
+            raise InvalidSessionPassword(principal=principal,
+                                         message=unicode(e.output))
 
 class change_password(Backend, HTTP_Status):
 
-- 
2.1.0

-- 
Manage your subscription for the Freeipa-devel mailing list:
https://www.redhat.com/mailman/listinfo/freeipa-devel
Contribute to FreeIPA: http://www.freeipa.org/page/Contribute/Code

Reply via email to