Hi,

this is the first attempt to port FreeIPA from deprecated python3-incompatible python-krbV library to python-gssapi. The patch depends on python-kerberos->python-gssapi patch [1] to apply cleanly, but the overlap is small, so I think it can be at least partially reviewed without it.

Comments:
I removed Backend.krb and KRB5_CCache classes as they were wrappers around krbV classes. I added few utility functions to krb_utils module that perform part of its functionality (no need for classes, because gssapi acquire calls don't pass any context objects, they wouldn't have any state).

I merged the two different kinit_keytab functions.

GSSAPI doesn't provide any method (that I'm aware of) to get default ccache name. In most cases this is not needed as we can simply not pass any name and it will use the default. The ldap plugin had to be adjusted for this - the connect method now takes new use_gssapi argument, which can turn on gssapi support without the need to supply explicit ccache name. The only place where the ccache name is really needed is the test server, where I use system klist command to obtain it.

It's also not possible to directly get default realm name, what I do is importing nonexistent name, cannonicalizing it and extracting the realm from it. Which should work but is ugly. It would be better if we could modify the places that use it to not need it at all, but it's mostly used in ldap code and I don't understand that part of FreeIPA. Alternative would be parsing /etc/krb.conf.

Sorry for long patch, but I'm afraid it cannot be reasonably split.


Ticket:
https://fedorahosted.org/freeipa/ticket/5164

[1] https://fedorahosted.org/freeipa/ticket/5147

--
Michael Simacek
From 4257e828cf733bc3c336efde6faadc5b91666909 Mon Sep 17 00:00:00 2001
From: Michael Simacek <msima...@redhat.com>
Date: Mon, 20 Jul 2015 16:04:07 +0200
Subject: [PATCH] Port from python-krbV to python-gssapi

python-krbV library is deprecated and doesn't work with python 3. Replacing all
it's usages with python-gssapi.

- Removed Backend.krb and KRB5_CCache classes
  They were wrappers around krbV classes that cannot really work without them
- Added few utility functions for querying GSSAPI credentials
  In krb_utils module. They provide replacements for KRB5_CCache.
- Merged two kinit_keytab functions
- Changed how ldap connection get default ccache
  Since default ccache name cannot be retireved through GSSAPI, connect method
  now takes use_gssapi boolean argument that tells it to use the default if
  none provided.
- Unified getting default realm
  But the implementation of getting it is done by a workaround (importing
  nonexistent name, canonicalizing it and extracting the realm from it),
  because GSSAPI doesn't provide any direct way to do this.
---
 BUILD.txt                                         |   2 +-
 doc/examples/python-api.py                        |   4 +-
 freeipa.spec.in                                   |   7 +-
 install/oddjob/com.redhat.idm.trust-fetch-domains |  69 ++---
 install/tools/ipa-adtrust-install                 |  13 +-
 install/tools/ipa-csreplica-manage                |   5 +-
 install/tools/ipa-replica-manage                  |   9 +-
 ipa-client/ipa-client.spec.in                     |   2 +-
 ipa-client/ipa-install/ipa-client-automount       |   4 +-
 ipa-client/ipa-install/ipa-client-install         |   8 +-
 ipalib/krb_utils.py                               | 344 +++++-----------------
 ipalib/plugins/kerberos.py                        | 125 --------
 ipalib/plugins/passwd.py                          |   6 +-
 ipalib/plugins/vault.py                           |   7 +-
 ipalib/rpc.py                                     |   9 +-
 ipalib/util.py                                    |  12 -
 ipapython/config.py                               |   6 +-
 ipapython/ipautil.py                              |  28 +-
 ipaserver/install/ipa_cacert_manage.py            |   6 +-
 ipaserver/install/ipa_ldap_updater.py             |   6 +-
 ipaserver/install/ipa_otptoken_import.py          |   6 +-
 ipaserver/install/ipa_winsync_migrate.py          |   7 +-
 ipaserver/install/ldapupdate.py                   |  12 +-
 ipaserver/install/schemaupdate.py                 |   4 +-
 ipaserver/install/server/upgrade.py               |   5 +-
 ipaserver/plugins/join.py                         |  15 +-
 ipaserver/plugins/ldap2.py                        |  25 +-
 ipaserver/rpcserver.py                            |  25 +-
 ipatests/test_cmdline/cmdline.py                  |   5 +-
 ipatests/test_cmdline/test_ipagetkeytab.py        |  21 +-
 ipatests/test_xmlrpc/test_dns_plugin.py           |   3 +-
 ipatests/test_xmlrpc/test_netgroup_plugin.py      |   6 +-
 ipatests/test_xmlrpc/test_permission_plugin.py    |   3 +-
 lite-server.py                                    |  16 +-
 make-lint                                         |   4 +-
 35 files changed, 218 insertions(+), 611 deletions(-)
 delete mode 100644 ipalib/plugins/kerberos.py

diff --git a/BUILD.txt b/BUILD.txt
index 53012b14d05673d4fbc4d0567e877348d5e78444..4507fa357375edc09377368ab3d1eb1c48994a61 100644
--- a/BUILD.txt
+++ b/BUILD.txt
@@ -20,7 +20,7 @@ systemd-units samba-devel samba-python libwbclient-devel libtalloc-devel \
 libtevent-devel nspr-devel nss-devel openssl-devel openldap-devel krb5-devel \
 krb5-workstation libuuid-devel libcurl-devel xmlrpc-c-devel popt-devel \
 autoconf automake m4 libtool gettext python-devel python-ldap \
-python-setuptools python-krbV python-nss python-netaddr python-gssapi \
+python-setuptools python-nss python-netaddr python-gssapi \
 python-rhsm pyOpenSSL pylint python-polib libipa_hbac-python python-memcached \
 sssd python-lxml python-pyasn1 python-qrcode-core python-dns m2crypto \
 check libsss_idmap-devel libsss_nss_idmap-devel java-headless rhino \
diff --git a/doc/examples/python-api.py b/doc/examples/python-api.py
index 805925f262774edca20fd0f31eda214b83a24fc9..52f37bd3e0911f6e9c24bb8cb1bb5e9e46461410 100755
--- a/doc/examples/python-api.py
+++ b/doc/examples/python-api.py
@@ -37,9 +37,7 @@ api.finalize()
 # Backend.ldap.connect(), otherwise Backend.rpcclient.connect().
 
 if api.env.in_server:
-    api.Backend.ldap2.connect(
-        ccache=api.Backend.krb.default_ccname()
-     )
+    api.Backend.ldap2.connect(use_gssapi=True)
 else:
     api.Backend.rpcclient.connect()
 
diff --git a/freeipa.spec.in b/freeipa.spec.in
index 91fc62003cc30a8dd43a77703f8b827ab90d42aa..7ad82310946b50afe8e55206577251088b258798 100644
--- a/freeipa.spec.in
+++ b/freeipa.spec.in
@@ -68,7 +68,6 @@ BuildRequires:  gettext
 BuildRequires:  python-devel
 BuildRequires:  python-ldap
 BuildRequires:  python-setuptools
-BuildRequires:  python-krbV
 BuildRequires:  python-nss
 BuildRequires:  python-cryptography
 BuildRequires:  python-netaddr
@@ -129,7 +128,7 @@ Requires: mod_wsgi
 Requires: mod_auth_gssapi >= 1.1.0-2
 Requires: mod_nss >= 1.0.8-26
 Requires: python-ldap >= 2.4.15
-Requires: python-krbV
+Requires: python-gssapi >= 1.1.1
 Requires: python-sssdconfig
 Requires: acl
 Requires: python-pyasn1
@@ -265,7 +264,7 @@ Requires: certmonger >= 0.78
 Requires: nss-tools
 Requires: bind-utils
 Requires: oddjob-mkhomedir
-Requires: python-krbV
+Requires: python-gssapi >= 1.1.1
 Requires: python-dns >= 1.11.1
 Requires: libsss_autofs
 Requires: autofs
@@ -289,7 +288,7 @@ Summary: IPA administrative tools
 Group: System Environment/Base
 Requires: %{name}-python = %{version}-%{release}
 Requires: %{name}-client = %{version}-%{release}
-Requires: python-krbV
+Requires: python-gssapi >= 1.1.1
 Requires: python-ldap
 
 Conflicts: %{alt_name}-admintools
diff --git a/install/oddjob/com.redhat.idm.trust-fetch-domains b/install/oddjob/com.redhat.idm.trust-fetch-domains
index e50c81e50e73b258bf08737c2d9a13a8832eb69f..3c038a28820be152d229887c47aa799dedf90aea 100755
--- a/install/oddjob/com.redhat.idm.trust-fetch-domains
+++ b/install/oddjob/com.redhat.idm.trust-fetch-domains
@@ -7,33 +7,10 @@ from ipalib import api, errors
 from ipapython.dn import DN
 from ipalib.config import Env
 from ipalib.constants import DEFAULT_CONFIG
-from ipalib.krb_utils import KRB5_CCache
+from ipapython.ipautil import kinit_keytab
 import sys
 import os, pwd
-import krbV
-import time
-
-# This version is different from the original in ipapyton.ipautil
-# in the fact that it returns a krbV.CCache object.
-def kinit_keytab(principal, keytab, ccache_name, attempts=1):
-    errors_to_retry = {krbV.KRB5KDC_ERR_SVC_UNAVAILABLE,
-                       krbV.KRB5_KDC_UNREACH}
-    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_name, context=krbcontext,
-                                 primary_principal=princ)
-            ccache.init(princ)
-            ccache.init_creds_keytab(keytab=ktab, principal=princ)
-            return ccache
-        except krbV.Krb5Error as e:
-            if e.args[0] not in errors_to_retry:
-                raise
-            if attempt == attempts:
-                raise
-            time.sleep(5)
+import gssapi
 
 def retrieve_keytab(api, ccache_name, oneway_keytab_name, oneway_principal):
     getkeytab_args = ["/usr/sbin/ipa-getkeytab",
@@ -125,17 +102,17 @@ ccache_name = '/var/run/ipa/krb5cc_oddjob_trusts'
 #   - if not, initialize it from Samba's keytab
 # - refer the correct ccache object for further use
 #
-if not os.path.isfile(ccache_name):
-    ccache = kinit_keytab(principal, keytab_name, ccache_name)
-
-ccache_check = KRB5_CCache(ccache_name)
-if not ccache_check.credential_is_valid(principal):
-    ccache = kinit_keytab(principal, keytab_name, ccache_name)
-else:
-    ccache = ccache_check.ccache
+try:
+    cred = kinit_keytab(principal, keytab_name, ccache_name)
+    # would raise exception if expired
+    cred.lifetime
+except gssapi.exceptions.ExpiredCredentialsError:
+    # delete stale ccache and try again
+    os.unlink(ccache_name)
+    cred = kinit_keytab(principal, keytab_name, ccache_name)
 
 old_ccache = os.environ.get('KRB5CCNAME')
-api.Backend.ldap2.connect(ccache)
+api.Backend.ldap2.connect(ccache_name)
 
 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'])
@@ -147,26 +124,18 @@ oneway_principal = str('%s$@%s' % (own_trust_flatname, trusted_domain.upper()))
 if not os.path.isfile(oneway_keytab_name):
     retrieve_keytab(api, ccache_name, oneway_keytab_name, oneway_principal)
 
-oneway_ccache = None
 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)
-except krbV.Krb5Error as e:
+    try:
+        # The keytab may have stale key material (from older trust-add run)
+        cred = kinit_keytab(oneway_principal, oneway_keytab_name, oneway_ccache_name)
+        # would raise exception if expired
+        cred.lifetime
+    except gssapi.exceptions.ExpiredCredentialsError:
+        cred = kinit_keytab(oneway_principal, oneway_keytab_name, oneway_ccache_name)
+except gssapi.exceptions.GSSError:
     # 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:
-    # 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):
-        # 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
-else:
-    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
 ipa_domain = api.env.domain
 os.environ['KRB5CCNAME'] = oneway_ccache_name
diff --git a/install/tools/ipa-adtrust-install b/install/tools/ipa-adtrust-install
index 5340c31d16ed78da0cb39725d9ae93c76470b698..6bdca440c31584325413cc7fcdde3eda45701d9c 100755
--- a/install/tools/ipa-adtrust-install
+++ b/install/tools/ipa-adtrust-install
@@ -26,9 +26,8 @@ from ipaserver.install.installutils import *
 from ipaserver.install import service
 from ipapython import version
 from ipapython import ipautil, sysrestore, ipaldap
-from ipalib import api, errors, util
+from ipalib import api, errors, krb_utils
 from ipapython.config import IPAOptionParser
-import krbV
 from ipaplatform.paths import paths
 from ipapython.ipa_log_manager import *
 from ipapython.dn import DN
@@ -302,21 +301,19 @@ def main():
             print "Proceeding with credentials that existed before"
 
     try:
-        ctx = krbV.default_context()
-        ccache = ctx.default_ccache()
-        principal = ccache.principal()
-    except krbV.Krb5Error, e:
+        principal = krb_utils.get_principal()
+    except errors.CCacheError:
         sys.exit("Must have Kerberos credentials to setup AD trusts on server")
 
     try:
-        api.Backend.ldap2.connect(ccache)
+        api.Backend.ldap2.connect(use_gssapi=True)
     except errors.ACIError, e:
         sys.exit("Outdated Kerberos credentials. Use kdestroy and kinit to update your ticket")
     except errors.DatabaseError, e:
         sys.exit("Cannot connect to the LDAP database. Please check if IPA is running")
 
     try:
-        user = api.Command.user_show(unicode(principal[0]))['result']
+        user = api.Command.user_show(principal)['result']
         group = api.Command.group_show(u'admins')['result']
         if not (user['uid'][0] in group['member_user'] and
                 group['cn'][0] in user['memberof_group']):
diff --git a/install/tools/ipa-csreplica-manage b/install/tools/ipa-csreplica-manage
index 6f6c6c75a122274eeb221f6e0eb15959dec56786..02a8419ae179f1f21a2a3de736cadbf4680ad415 100755
--- a/install/tools/ipa-csreplica-manage
+++ b/install/tools/ipa-csreplica-manage
@@ -22,12 +22,11 @@
 import sys
 import os
 
-import krbV
 from ipapython.ipa_log_manager import *
 
 from ipaserver.install import (replication, installutils, bindinstance,
     cainstance, certs)
-from ipalib import api, errors, util
+from ipalib import api, errors, krb_utils
 from ipalib.constants import CACERT
 from ipapython import ipautil, ipaldap, version, dogtag
 from ipapython.dn import DN
@@ -407,7 +406,7 @@ def main():
     api.finalize()
 
     dirman_passwd = None
-    realm = krbV.default_context().default_realm
+    realm = krb_utils.get_default_realm()
 
     if options.host:
         host = options.host
diff --git a/install/tools/ipa-replica-manage b/install/tools/ipa-replica-manage
index 50a57f70ec452c0df5bf2ea55d2a136e8149aa41..4653821e08ed3cb288ae95e673105b86320dd4eb 100755
--- a/install/tools/ipa-replica-manage
+++ b/install/tools/ipa-replica-manage
@@ -20,7 +20,7 @@
 import sys
 import os
 
-import re, krbV
+import re
 import traceback
 from urllib2 import urlparse
 import ldap
@@ -32,7 +32,7 @@ from ipaserver.install import replication, dsinstance, installutils
 from ipaserver.install import bindinstance, cainstance, certs
 from ipaserver.install import opendnssecinstance, dnskeysyncinstance
 from ipapython import version, ipaldap
-from ipalib import api, errors, util
+from ipalib import api, errors, krb_utils
 from ipalib.constants import CACERT
 from ipalib.util import create_topology_graph, get_topology_connection_errors
 from ipapython.ipa_log_manager import *
@@ -1379,7 +1379,7 @@ def main():
     api.finalize()
 
     dirman_passwd = None
-    realm = krbV.default_context().default_realm
+    realm = krb_utils.get_default_realm()
 
     if options.host:
         host = options.host
@@ -1404,8 +1404,7 @@ def main():
         api.Backend.ldap2.connect(bind_dn=DN(('cn', 'Directory Manager')),
                                   bind_pw=options.dirman_passwd)
     else:
-        ccache = krbV.default_context().default_ccache()
-        api.Backend.ldap2.connect(ccache=ccache)
+        api.Backend.ldap2.connect(use_gssapi=True)
 
     if args[0] == "list":
         replica = None
diff --git a/ipa-client/ipa-client.spec.in b/ipa-client/ipa-client.spec.in
index 686259ad24b241c232dce83b695a05f6fd6c3849..4413937bb6c98ef4ad8e87f1b94246b98d82755c 100644
--- a/ipa-client/ipa-client.spec.in
+++ b/ipa-client/ipa-client.spec.in
@@ -9,7 +9,7 @@ URL:            http://www.freeipa.org
 Source0:        %{name}-%{version}.tgz
 BuildRoot:      %{_tmppath}/%{name}-%{version}-%{release}-root-%(%{__id_u} -n)
 
-Requires: python python-ldap python-krbV ipa-python cyrus-sasl-gssapi
+Requires: python python-ldap python-gssapi ipa-python cyrus-sasl-gssapi
 
 %{!?python_sitelib: %define python_sitelib %(%{__python} -c "from distutils.sysconfig import get_python_lib; print get_python_lib()")}
 
diff --git a/ipa-client/ipa-install/ipa-client-automount b/ipa-client/ipa-install/ipa-client-automount
index eee141812cace38ed795c3f3b4120bded2257d1b..0128e7c3d899be5610cb49f23f18760c6c8786e5 100755
--- a/ipa-client/ipa-install/ipa-client-automount
+++ b/ipa-client/ipa-install/ipa-client-automount
@@ -26,7 +26,7 @@ import os
 import urlparse
 import time
 import tempfile
-from krbV import Krb5Error
+import gssapi
 
 import SSSDConfig
 
@@ -435,7 +435,7 @@ def main():
             host_princ = str('host/%s@%s' % (api.env.host, api.env.realm))
             ipautil.kinit_keytab(host_princ, paths.KRB5_KEYTAB, ccache_name)
             os.environ['KRB5CCNAME'] = ccache_name
-        except Krb5Error as e:
+        except gssapi.exceptions.GSSError as e:
             sys.exit("Failed to obtain host TGT: %s" % e)
         # Now we have a TGT, connect to IPA
         try:
diff --git a/ipa-client/ipa-install/ipa-client-install b/ipa-client/ipa-install/ipa-client-install
index 91323ae115a27d221bcbc43fee887c56d99c8635..746d32b4dd269a47c786e7b7900b828513d5f93b 100755
--- a/ipa-client/ipa-install/ipa-client-install
+++ b/ipa-client/ipa-install/ipa-client-install
@@ -31,7 +31,7 @@ try:
     from ConfigParser import RawConfigParser
     from optparse import SUPPRESS_HELP, OptionGroup, OptionValueError
     import shutil
-    from krbV import Krb5Error
+    import gssapi
 
     import nss.nss as nss
     import SSSDConfig
@@ -2442,7 +2442,7 @@ def install(options, env, fstore, statestore):
                                              ccache_name,
                                              config=krb_name,
                                              attempts=options.kinit_attempts)
-                    except Krb5Error as e:
+                    except gssapi.exceptions.GSSError as e:
                         print_port_conf_info()
                         root_logger.error("Kerberos authentication failed: %s"
                                           % e)
@@ -2522,7 +2522,7 @@ def install(options, env, fstore, statestore):
                                      config=krb_name,
                                      attempts=options.kinit_attempts)
                 env['KRB5CCNAME'] = os.environ['KRB5CCNAME'] = CCACHE_FILE
-            except Krb5Error as e:
+            except gssapi.exceptions.GSSError 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
@@ -2569,7 +2569,7 @@ def install(options, env, fstore, statestore):
                                  CCACHE_FILE,
                                  attempts=options.kinit_attempts)
             os.environ['KRB5CCNAME'] = CCACHE_FILE
-        except Krb5Error as e:
+        except gssapi.exceptions.GSSError as e:
             root_logger.error("Failed to obtain host TGT: %s" % e)
             return CLIENT_INSTALL_ERROR
     else:
diff --git a/ipalib/krb_utils.py b/ipalib/krb_utils.py
index 9a557ce5cf1b4c3ef7587d3fb545827c1ade1f1d..5019789ff5fe47cbd0c5c2d8bb55dc0374a6b548 100644
--- a/ipalib/krb_utils.py
+++ b/ipalib/krb_utils.py
@@ -16,25 +16,22 @@
 # You should have received a copy of the GNU General Public License
 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
 
-import krbV
 import time
 import re
-from ipapython.ipa_log_manager import *
+import gssapi
+
+from ipalib import errors
 
 #-------------------------------------------------------------------------------
 
-# Kerberos constants, should be defined in krbV, but aren't
-KRB5_GC_CACHED = 0x2
-
-# Kerberos error codes, should be defined in krbV, but aren't
-KRB5_CC_NOTFOUND                = -1765328243 # Matching credential not found
-KRB5_FCC_NOFILE                 = -1765328189 # No credentials cache found
-KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN = -1765328377 # Server not found in Kerberos database
-KRB5KRB_AP_ERR_TKT_EXPIRED      = -1765328352 # Ticket expired
-KRB5_FCC_PERM                   = -1765328190 # Credentials cache permissions incorrect
-KRB5_CC_FORMAT                  = -1765328185 # Bad format in credentials cache
-KRB5_REALM_CANT_RESOLVE         = -1765328164 # Cannot resolve network address for KDC in requested realm
-
+# Kerberos error codes
+KRB5_CC_NOTFOUND                = 2529639053 # Matching credential not found
+KRB5_FCC_NOFILE                 = 2529639107 # No credentials cache found
+KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN = 2529638919 # Server not found in Kerberos database
+KRB5KRB_AP_ERR_TKT_EXPIRED      = 2529638944 # Ticket expired
+KRB5_FCC_PERM                   = 2529639106 # Credentials cache permissions incorrect
+KRB5_CC_FORMAT                  = 2529639111 # Bad format in credentials cache
+KRB5_REALM_CANT_RESOLVE         = 2529639132 # Cannot resolve network address for KDC in requested realm
 
 krb_ticket_expiration_threshold = 60*5 # number of seconds to accmodate clock skew
 krb5_time_fmt = '%m/%d/%y %H:%M:%S'
@@ -136,260 +133,79 @@ def krb5_format_time(timestamp):
     '''
     return time.strftime(krb5_time_fmt, time.localtime(timestamp))
 
-class KRB5_CCache(object):
+def get_credentials(name=None, ccache_name=None):
     '''
-    Kerberos stores a TGT (Ticket Granting Ticket) and the service
-    tickets bound to it in a ccache (credentials cache). ccaches are
-    bound to a Kerberos user principal. This class opens a Kerberos
-    ccache and allows one to manipulate it. Most useful is the
-    extraction of ticket entries (cred's) in the ccache and the
-    ability to examine their attributes.
+    Obtains GSSAPI credentials with given principal name from ccache. When no
+    principal name specified, it retrieves the default one for given
+    credentials cache.
+
+    :parameters:
+      name
+        gssapi.Name object specifying principal or None for the default
+      ccache_name
+        string specifying Kerberos credentials cache name or None for the
+        default
+    :returns:
+      gssapi.Credentials object
+    ''''
+    store = None
+    if ccache_name:
+        store = {'ccache': ccache_name}
+    try:
+        return gssapi.Credentials(usage='initiate', name=name, store=store)
+    except gssapi.exceptions.GSSError as e:
+        if e.min_code == KRB5_FCC_NOFILE:
+            raise ValueError('"%s", ccache="%s"' % (e.message, ccache_name))
+        raise errors.CCacheError()
+
+def get_principal(ccache_name=None):
     '''
+    Gets default principal name from given credentials cache.
+
+    :parameters:
+      ccache_name
+        string specifying Kerberos credentials cache name or None for the
+        default
+    :returns:
+      Default principal name as string
+    '''
+    creds = get_credentials(name=name, ccache_name=ccache_name)
+    return unicode(creds.name)
 
-    def __init__(self, ccache):
-        '''
-        :parameters:
-          ccache
-            The name of a Kerberos ccache used to hold Kerberos tickets.
-        :returns:
-          `KRB5_CCache` object encapsulting the ccache.
-        '''
-        log_mgr.get_logger(self, True)
-        self.context = None
-        self.scheme = None
-        self.name = None
-        self.ccache = None
-        self.principal = None
-
-        try:
-            self.context = krbV.default_context()
-            self.scheme, self.name = krb5_parse_ccache(ccache)
-            self.ccache = krbV.CCache(name=str(ccache), context=self.context)
-            self.principal = self.ccache.principal()
-        except krbV.Krb5Error, e:
-            error_code = e.args[0]
-            message = e.args[1]
-            if error_code == KRB5_FCC_NOFILE:
-                raise ValueError('"%s", ccache="%s"' % (message, ccache))
-            else:
-                raise e
-
-    def ccache_str(self):
-        '''
-        A Kerberos ccache is identified by a name comprised of a
-        scheme and location component. This function returns that
-        canonical name. See `krb5_parse_ccache()`
-
-        :returns:
-          The name of ccache with it's scheme and location components.
-        '''
-
-        return '%s:%s' % (self.scheme, self.name)
-
-    def __str__(self):
-        return 'cache="%s" principal="%s"' % (self.ccache_str(), self.principal.name)
-
-    def get_credentials(self, principal):
-        '''
-        Given a Kerberos principal return the krbV credentials
-        tuple describing the credential. If the principal does
-        not exist in the ccache a KeyError is raised.
-
-        :parameters:
-          principal
-            The Kerberos principal whose ticket is being retrieved.
-            The principal may be either a string formatted as a
-            Kerberos V5 principal or it may be a `krbV.Principal`
-            object.
-        :returns:
-          A krbV credentials tuple. If the principal is not in the
-          ccache a KeyError is raised.
-
-        '''
-
-        if isinstance(principal, krbV.Principal):
-            krbV_principal = principal
-        else:
-            try:
-                krbV_principal = krbV.Principal(str(principal), self.context)
-            except Exception, e:
-                self.error('could not create krbV principal from "%s", %s', principal, e)
-                raise e
-
-        creds_tuple = (self.principal,
-                       krbV_principal,
-                       (0, None),    # keyblock: (enctype, contents)
-                       (0, 0, 0, 0), # times: (authtime, starttime, endtime, renew_till)
-                       0,0,          # is_skey, ticket_flags
-                       None,         # addrlist
-                       None,         # ticket_data
-                       None,         # second_ticket_data
-                       None)         # adlist
-        try:
-            cred = self.ccache.get_credentials(creds_tuple, KRB5_GC_CACHED)
-        except krbV.Krb5Error, e:
-            error_code = e.args[0]
-            if error_code == KRB5_CC_NOTFOUND:
-                raise KeyError('"%s" credential not found in "%s" ccache' % \
-                               (krbV_principal.name, self.ccache_str()))
-            raise e
-        except Exception, e:
-            raise e
-
-        return cred
-
-    def get_credential_times(self, principal):
-        '''
-        Given a Kerberos principal return the ticket timestamps if the
-        principal's ticket in the ccache is valid.  If the principal
-        does not exist in the ccache a KeyError is raised.
-
-        The return credential time values are Unix timestamps in
-        localtime.
-
-        The returned timestamps are:
-
-        authtime
-          The time when the ticket was issued.
-        starttime
-          The time when the ticket becomes valid.
-        endtime
-          The time when the ticket expires.
-        renew_till
-          The time when the ticket becomes no longer renewable (if renewable).
-
-        :parameters:
-          principal
-            The Kerberos principal whose ticket is being validated.
-            The principal may be either a string formatted as a
-            Kerberos V5 principal or it may be a `krbV.Principal`
-            object.
-        :returns:
-            return authtime, starttime, endtime, renew_till
-        '''
-
-        if isinstance(principal, krbV.Principal):
-            krbV_principal = principal
-        else:
-            try:
-                krbV_principal = krbV.Principal(str(principal), self.context)
-            except Exception, e:
-                self.error('could not create krbV principal from "%s", %s', principal, e)
-                raise e
-
-        try:
-            cred = self.get_credentials(krbV_principal)
-            authtime, starttime, endtime, renew_till = cred[3]
-
-            self.debug('get_credential_times: principal=%s, authtime=%s, starttime=%s, endtime=%s, renew_till=%s',
-                       krbV_principal.name,
-                       krb5_format_time(authtime), krb5_format_time(starttime),
-                       krb5_format_time(endtime), krb5_format_time(renew_till))
-
-            return authtime, starttime, endtime, renew_till
-
-        except KeyError, e:
-            raise e
-        except Exception, e:
-            self.error('get_credential_times failed, principal="%s" error="%s"', krbV_principal.name, e)
-            raise e
-
-    def credential_is_valid(self, principal):
-        '''
-        Given a Kerberos principal return a boolean indicating if the
-        principal's ticket in the ccache is valid. If the ticket is
-        not in the ccache False is returned. If the ticket
-        exists in the ccache it's validity is checked and returned.
-
-        :parameters:
-          principal
-            The Kerberos principal whose ticket is being validated.
-            The principal may be either a string formatted as a
-            Kerberos V5 principal or it may be a `krbV.Principal`
-            object.
-        :returns:
-          True if the principal's ticket exists and is valid. False if
-          the ticket does not exist or if the ticket is not valid.
-        '''
-
-        try:
-            authtime, starttime, endtime, renew_till = self.get_credential_times(principal)
-        except KeyError, e:
-            return False
-        except Exception, e:
-            self.error('credential_is_valid failed, principal="%s" error="%s"', principal, e)
-            raise e
-
-
-        now = time.time()
-        if starttime > now:
-            return False
-        if endtime < now:
-            return False
-        return True
-
-    def valid(self, host, realm):
-        '''
-        Test to see if ldap service ticket or the TGT is valid.
-
-        :parameters:
-          host
-            ldap server
-          realm
-            kerberos realm
-        :returns:
-          True if either the ldap service ticket or the TGT is valid,
-          False otherwise.
-        '''
-
-        try:
-            principal = krb5_format_service_principal_name('HTTP', host, realm)
-            valid = self.credential_is_valid(principal)
-            if valid:
-                return True
-        except KeyError:
-            pass
-
-        try:
-            principal = krb5_format_tgt_principal_name(realm)
-            valid = self.credential_is_valid(principal)
-            return valid
-        except KeyError:
-            return False
-
-    def endtime(self, host, realm):
-        '''
-        Returns the minimum endtime for tickets of interest (ldap service or TGT).
+def get_credentials_if_valid(name=None, ccache_name=None):
+    '''
+    Obtains GSSAPI credentials with principal name from ccache. When no
+    principal name specified, it retrieves the default one for given
+    credentials cache. When the credentials cannot be retrieved or aren't valid
+    it returns None.
+
+    :parameters:
+      name
+        gssapi.Name object specifying principal or None for the default
+      ccache_name
+        string specifying Kerberos credentials cache name or None for the
+        default
+    :returns:
+      gssapi.Credentials object or None if valid credentials weren't found
+    '''
 
-        :parameters:
-          host
-            ldap server
-          realm
-            kerberos realm
-        :returns:
-          UNIX timestamp value.
-        '''
+    try:
+        creds = get_credentials(name=name, ccache_name=ccache_name)
+        # property access would raise exception if expired
+        if creds.lifetime > 0:
+            return creds
+    except (errors.CCacheError, gssapi.exceptions.ExpiredCredentialsError):
+        return None
 
-        result = 0
-        try:
-            principal = krb5_format_service_principal_name('HTTP', host, realm)
-            authtime, starttime, endtime, renew_till = self.get_credential_times(principal)
-            if result:
-                result = min(result, endtime)
-            else:
-                result = endtime
-        except KeyError:
-            pass
+def get_default_realm():
+    '''
+    Returns default Kerberos realm name.
 
-        try:
-            principal = krb5_format_tgt_principal_name(realm)
-            authtime, starttime, endtime, renew_till = self.get_credential_times(principal)
-            if result:
-                result = min(result, endtime)
-            else:
-                result = endtime
-        except KeyError:
-            pass
+    :returns:
+      Default realm name as string or None if it cannot be determined.
+    '''
 
-        self.debug('KRB5_CCache %s endtime=%s (%s)', self.ccache_str(), result, krb5_format_time(result))
-        return result
+    # FIXME this is a temporary workaround. We should find some nicer solution
+    name = gssapi.Name('notempty', gssapi.NameType.user)
+    can_name = unicode(name.canonicalize(gssapi.MechType.kerberos))
+    return can_name.partition('@')[2] or None
diff --git a/ipalib/plugins/kerberos.py b/ipalib/plugins/kerberos.py
deleted file mode 100644
index 3ed6d76719af46689cc3a0f6890ec243f9c685d3..0000000000000000000000000000000000000000
--- a/ipalib/plugins/kerberos.py
+++ /dev/null
@@ -1,125 +0,0 @@
-# Authors:
-#   Jason Gerard DeRose <jder...@redhat.com>
-#
-# Copyright (C) 2008  Red Hat
-# see file 'COPYING' for use and warranty information
-#
-# This program is free software; you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program.  If not, see <http://www.gnu.org/licenses/>.
-
-"""
-Backend plugin for Kerberos.
-
-This wraps the python-kerberos and python-krbV bindings.
-"""
-
-import sys
-from ipalib import api
-from ipalib.backend import Backend
-from ipalib.plugable import Registry
-import krbV
-
-register = Registry()
-
-ENCODING = 'UTF-8'
-
-
-@register()
-class krb(Backend):
-    """
-    Kerberos backend plugin.
-
-    This wraps the `krbV` bindings (and will eventually wrap the `kerberos`
-    bindings also).  Importantly, this plugin does correct Unicode
-    encoding/decoding of values going-to/coming-from the bindings.
-    """
-
-    def __default_ccache(self):
-        """
-        Return the ``krbV.CCache`` for the default credential cache.
-        """
-        return krbV.default_context().default_ccache()
-
-    def __default_principal(self):
-        """
-        Return the ``krb5.Principal`` for the default credential cache.
-        """
-        return self.__default_ccache().principal()
-
-    def __get_ccache(self, ccname):
-        """
-        Return the ``krbV.CCache`` for the ``ccname`` credential ccache.
-        """
-        return krbV.CCache(ccname)
-
-    def __get_principal(self, ccname):
-        """
-        Return the ``krb5.Principal`` for the ``ccname`` credential ccache.
-        """
-        return self.__get_ccache(ccname).principal()
-
-    def default_ccname(self):
-        """
-        Return the default ccache file name (schema+name).
-
-        This will return something like 'FILE:/tmp/krb5cc_500'.
-
-        This cannot return anything meaningful if used in the server as a
-        request is processed.
-        """
-        default_ccache = self.__default_ccache()
-        ccname = "%(type)s:%(name)s" % dict(type=default_ccache.type,
-                                            name=default_ccache.name)
-        return ccname
-
-    def default_principal(self):
-        """
-        Return the principal name in default credential cache.
-
-        This will return something like 'ad...@example.com'.  If no credential
-        cache exists for the invoking user, None is returned.
-
-        This cannot return anything meaningful if used in the server as a
-        request is processed.
-        """
-        return self.__default_principal().name.decode(ENCODING)
-
-    def default_realm(self):
-        """
-        Return the realm from the default credential cache.
-
-        This will return something like 'EXAMPLE.COM'.  If no credential cache
-        exists for the invoking user, None is returned.
-
-        This cannot return anything meaningful if used in the server as a
-        request is processed.
-        """
-        return krbV.default_context().default_realm.decode(ENCODING)
-
-    def get_principal(self, ccname):
-        """
-        Return the principal from credential cache file at ``ccname``.
-
-        This will return something like 'ad...@example.com'.
-        """
-        return self.__get_principal(ccname).name.decode(ENCODING)
-
-    def get_realm(self, ccname):
-        """
-        Return the realm from credential cache file at ``ccname``.
-
-        This will return something like 'EXAMPLE.COM'.
-        """
-        return self.__get_principal(ccname).realm.decode(ENCODING)
-
-
diff --git a/ipalib/plugins/passwd.py b/ipalib/plugins/passwd.py
index f5fc14d510ea7eea1e16435fe11d982d4ca49d79..a4f791c1bdf4b6f695b3a79f00a54c084f423229 100644
--- a/ipalib/plugins/passwd.py
+++ b/ipalib/plugins/passwd.py
@@ -17,7 +17,7 @@
 # You should have received a copy of the GNU General Public License
 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
 
-from ipalib import api, errors, util
+from ipalib import api, errors, krb_utils
 from ipalib import Command
 from ipalib import Str, Password
 from ipalib import _
@@ -58,7 +58,7 @@ def get_current_password(principal):
     current password is prompted for, otherwise return a fixed value to
     be ignored later.
     """
-    current_principal = util.get_current_principal()
+    current_principal = krb_utils.get_principal()
     if current_principal == normalize_principal(principal):
         return None
     else:
@@ -74,7 +74,7 @@ class passwd(Command):
             label=_('User name'),
             primary_key=True,
             autofill=True,
-            default_from=lambda: util.get_current_principal(),
+            default_from=lambda: krb_utils.get_principal(),
             normalizer=lambda value: normalize_principal(value),
         ),
         Password('password',
diff --git a/ipalib/plugins/vault.py b/ipalib/plugins/vault.py
index 81197f9328c7ed890fa336f464bfcda475ac6189..a8c04ef2be432a2fc7bfe9ab0606d17c69313fd7 100644
--- a/ipalib/plugins/vault.py
+++ b/ipalib/plugins/vault.py
@@ -33,7 +33,6 @@ from cryptography.hazmat.primitives.serialization import load_pem_public_key,\
     load_pem_private_key
 
 import nss.nss as nss
-import krbV
 
 from ipalib.frontend import Command, Object, Local
 from ipalib import api, errors
@@ -574,7 +573,7 @@ class vault_add(PKQuery, Local):
         else:
             backend = self.api.Backend.rpcclient
         if not backend.isconnected():
-            backend.connect(ccache=krbV.default_context().default_ccache())
+            backend.connect(use_gssapi=True)
 
         if vault_type == u'standard':
 
@@ -915,7 +914,7 @@ class vault_archive(PKQuery, Local):
         else:
             backend = self.api.Backend.rpcclient
         if not backend.isconnected():
-            backend.connect(ccache=krbV.default_context().default_ccache())
+            backend.connect(use_gssapi=True)
 
         # retrieve vault info
         vault = self.api.Command.vault_show(*args, **options)['result']
@@ -1178,7 +1177,7 @@ class vault_retrieve(PKQuery, Local):
         else:
             backend = self.api.Backend.rpcclient
         if not backend.isconnected():
-            backend.connect(ccache=krbV.default_context().default_ccache())
+            backend.connect(use_gssapi=True)
 
         # retrieve vault info
         vault = self.api.Command.vault_show(*args, **options)['result']
diff --git a/ipalib/rpc.py b/ipalib/rpc.py
index 43c7760616c502f07321ad0a3c69e89c6db6f0f9..47b995a5052293957a7711d09706e19f99e496d5 100644
--- a/ipalib/rpc.py
+++ b/ipalib/rpc.py
@@ -55,7 +55,6 @@ from ipalib.errors import (public_errors, UnknownError, NetworkError,
     KerberosError, XMLRPCMarshallError, JSONError, ConversionError)
 from ipalib import errors, capabilities
 from ipalib.request import context, Connection
-from ipalib.util import get_current_principal
 from ipapython.ipa_log_manager import root_logger
 from ipapython import ipautil
 from ipapython import kernel_keyring
@@ -66,7 +65,8 @@ from ipalib.text import _
 import ipapython.nsslib
 from ipapython.nsslib import NSSHTTPS, NSSConnection
 from ipalib.krb_utils import KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN, KRB5KRB_AP_ERR_TKT_EXPIRED, \
-                             KRB5_FCC_PERM, KRB5_FCC_NOFILE, KRB5_CC_FORMAT, KRB5_REALM_CANT_RESOLVE
+                             KRB5_FCC_PERM, KRB5_FCC_NOFILE, KRB5_CC_FORMAT, \
+                             KRB5_REALM_CANT_RESOLVE, get_principal
 from ipapython.dn import DN
 from ipalib.capabilities import VERSION_WITHOUT_CAPABILITIES
 from ipalib import api
@@ -514,10 +514,7 @@ class KerbTransport(SSLTransport):
              gssapi.RequirementFlag.out_of_sequence_detection]
 
     def _handle_exception(self, e, service=None):
-        # kerberos library coerced error codes to signed, gssapi uses unsigned
         minor = e.min_code
-        if minor & (1 << 31):
-            minor -= 1 << 32
         if minor == KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN:
             raise errors.ServiceError(service=service)
         elif minor == KRB5_FCC_NOFILE:
@@ -774,7 +771,7 @@ class RPCClient(Connectible):
                           delegate=False, nss_dir=None):
         try:
             rpc_uri = self.env[self.env_rpc_uri_key]
-            principal = get_current_principal()
+            principal = get_principal()
             setattr(context, 'principal', principal)
             # We have a session cookie, try using the session URI to see if it
             # is still valid
diff --git a/ipalib/util.py b/ipalib/util.py
index 5a670146e0ea569458bf0086b075df3d4b7b4b5b..c90b9c9ca68b2e22f7356354866b0dded26b62d6 100644
--- a/ipalib/util.py
+++ b/ipalib/util.py
@@ -61,18 +61,6 @@ def json_serialize(obj):
         return ''
     return json_serialize(obj.__json__())
 
-def get_current_principal():
-    try:
-        import gssapi
-        cred = gssapi.Credentials(usage='initiate')
-        return unicode(cred.name)
-    except ImportError:
-        raise RuntimeError('python-gssapi is not available.')
-    except gssapi.exceptions.GSSError:
-        #TODO: do a kinit?
-        raise errors.CCacheError()
-
-
 def validate_host_dns(log, fqdn):
     """
     See if the hostname has a DNS A/AAAA record.
diff --git a/ipapython/config.py b/ipapython/config.py
index 60f556dc946651903652aa284ce75bd1e8f13d6c..211bacfe6242996014ef849bcfdd07a8bb23f9dd 100644
--- a/ipapython/config.py
+++ b/ipapython/config.py
@@ -178,10 +178,8 @@ def __discover_config(discover_server = True):
     try:
         if not config.default_realm:
             try:
-                # only import krbV when we need it
-                import krbV
-                krbctx = krbV.default_context()
-                config.default_realm = krbctx.default_realm
+                from ipalib import krb_utils
+                config.default_realm = krb_utils.get_default_realm()
             except ImportError:
                 pass
             if not config.default_realm:
diff --git a/ipapython/ipautil.py b/ipapython/ipautil.py
index 05a7eebf012b27e3e34cb74778c2724e9deaf616..f02a7e007f68c5f8eecd788df28656af7065a0c3 100644
--- a/ipapython/ipautil.py
+++ b/ipapython/ipautil.py
@@ -34,7 +34,7 @@ import xmlrpclib
 import datetime
 import netaddr
 import time
-import krbV
+import gssapi
 import pwd
 import grp
 from dns import resolver, rdatatype
@@ -54,6 +54,11 @@ GEN_PWD_LEN = 12
 
 IPA_BASEDN_INFO = 'ipa v2.0'
 
+# Having this in krb_utils would cause circular import
+KRB5_KDC_UNREACH = 2529639068 # Cannot contact any KDC for requested realm
+KRB5KDC_ERR_SVC_UNAVAILABLE = 2529638941 # A service is not available that is
+                                         # required to process the request
+
 try:
     from subprocess import CalledProcessError
 except ImportError:
@@ -1193,8 +1198,8 @@ def kinit_keytab(principal, keytab, ccache_name, config=None, attempts=1):
     The optional parameter 'attempts' specifies how many times the credential
     initialization should be attempted in case of non-responsive KDC.
     """
-    errors_to_retry = {krbV.KRB5KDC_ERR_SVC_UNAVAILABLE,
-                       krbV.KRB5_KDC_UNREACH}
+    errors_to_retry = {KRB5KDC_ERR_SVC_UNAVAILABLE,
+                       KRB5_KDC_UNREACH}
     root_logger.debug("Initializing principal %s using keytab %s"
                       % (principal, keytab))
     root_logger.debug("using ccache %s" % ccache_name)
@@ -1205,18 +1210,15 @@ def kinit_keytab(principal, keytab, ccache_name, config=None, attempts=1):
         else:
             os.environ.pop('KRB5_CONFIG', None)
         try:
-            krbcontext = krbV.default_context()
-            ktab = krbV.Keytab(name=keytab, context=krbcontext)
-            princ = krbV.Principal(name=principal, context=krbcontext)
-            ccache = krbV.CCache(name=ccache_name, context=krbcontext,
-                                 primary_principal=princ)
-            ccache.init(princ)
-            ccache.init_creds_keytab(keytab=ktab, principal=princ)
+            name = gssapi.Name(principal, gssapi.NameType.kerberos_principal)
+            store = {'ccache': ccache_name,
+                     'client_keytab': keytab}
+            cred = gssapi.Credentials(name=name, store=store, usage='initiate')
             root_logger.debug("Attempt %d/%d: success"
                               % (attempt, attempts))
-            return
-        except krbV.Krb5Error as e:
-            if e.args[0] not in errors_to_retry:
+            return cred
+        except gssapi.exceptions.GSSError as e:
+            if e.min_code not in errors_to_retry:
                 raise
             root_logger.debug("Attempt %d/%d: failed: %s"
                               % (attempt, attempts, e))
diff --git a/ipaserver/install/ipa_cacert_manage.py b/ipaserver/install/ipa_cacert_manage.py
index 01ec805fc2094326d119827b4358c143f45f3ec4..0406a9bdebde17d6ad616ff278b250ac2331715b 100644
--- a/ipaserver/install/ipa_cacert_manage.py
+++ b/ipaserver/install/ipa_cacert_manage.py
@@ -23,7 +23,6 @@ from optparse import OptionGroup
 import base64
 from nss import nss
 from nss.error import NSPRError
-import krbV
 
 from ipapython import admintool, certmonger, ipautil
 from ipapython.dn import DN
@@ -126,9 +125,8 @@ class CACertManage(admintool.AdminTool):
         password = self.options.password
         if not password:
             try:
-                ccache = krbV.default_context().default_ccache()
-                conn.connect(ccache=ccache)
-            except (krbV.Krb5Error, errors.ACIError):
+                conn.connect(use_gssapi=True)
+            except (errors.CCacheError, errors.ACIError):
                 pass
             else:
                 return conn
diff --git a/ipaserver/install/ipa_ldap_updater.py b/ipaserver/install/ipa_ldap_updater.py
index f07eecf232916e979f7f92f0fe15e4976ad48ec6..39fc315ab3becca0c26e1de1496e4b9d76356174 100644
--- a/ipaserver/install/ipa_ldap_updater.py
+++ b/ipaserver/install/ipa_ldap_updater.py
@@ -26,9 +26,7 @@
 import os
 import sys
 
-import krbV
-
-from ipalib import api
+from ipalib import api, krb_utils
 from ipapython import ipautil, admintool
 from ipaplatform.paths import paths
 from ipaserver.install import installutils, dsinstance, schemaupdate
@@ -100,7 +98,7 @@ class LDAPUpdater_Upgrade(LDAPUpdater):
         super(LDAPUpdater_Upgrade, self).run()
         options = self.options
 
-        realm = krbV.default_context().default_realm
+        realm = krb_utils.get_default_realm()
         upgrade = IPAUpgrade(realm, self.files,
                              schema_files=options.schema_files)
 
diff --git a/ipaserver/install/ipa_otptoken_import.py b/ipaserver/install/ipa_otptoken_import.py
index 386ca4273c413d9f6a121956d0db3f0c44fe5c24..7116be5781f91ece89b42f2019d1da6fbd39e1b8 100644
--- a/ipaserver/install/ipa_otptoken_import.py
+++ b/ipaserver/install/ipa_otptoken_import.py
@@ -30,7 +30,6 @@ from lxml import etree
 import dateutil.parser
 import dateutil.tz
 import nss.nss as nss
-import krbV
 
 from ipapython import admintool
 from ipalib import api, errors
@@ -509,9 +508,8 @@ class OTPTokenImport(admintool.AdminTool):
 
         conn = ldap2(api)
         try:
-            ccache = krbV.default_context().default_ccache()
-            conn.connect(ccache=ccache)
-        except (krbV.Krb5Error, errors.ACIError):
+            conn.connect(use_gssapi=True)
+        except (errors.CCacheError, errors.ACIError):
             raise admintool.ScriptError("Unable to connect to LDAP! Did you kinit?")
 
         try:
diff --git a/ipaserver/install/ipa_winsync_migrate.py b/ipaserver/install/ipa_winsync_migrate.py
index cbe068458cc0b6853573c9d23d4cb6386a816bb3..7f6c855c6637746abd700d5dc7c562ee7a345207 100644
--- a/ipaserver/install/ipa_winsync_migrate.py
+++ b/ipaserver/install/ipa_winsync_migrate.py
@@ -17,7 +17,6 @@
 # along with this program.  If not, see <http://www.gnu.org/licenses/>.
 #
 
-import krbV
 import sys
 
 from ipalib import api
@@ -315,11 +314,9 @@ class WinsyncMigrate(admintool.AdminTool):
 
         # Setup LDAP connection
         try:
-            ctx = krbV.default_context()
-            ccache = ctx.default_ccache()
-            api.Backend.ldap2.connect(ccache)
+            api.Backend.ldap2.connect(use_gssapi=True)
             cls.ldap = api.Backend.ldap2
-        except krbV.Krb5Error, e:
+        except errors.CCacheError:
             sys.exit("Must have Kerberos credentials to migrate Winsync users.")
         except errors.ACIError, e:
             sys.exit("Outdated Kerberos credentials. Use kdestroy and kinit to update your ticket.")
diff --git a/ipaserver/install/ldapupdate.py b/ipaserver/install/ldapupdate.py
index 6f796dfdc8bbac1bb99a8b5a1bd5a6aaa778db16..b1133f8e5b8319e98e32fa61afb3abbc03cf09b5 100644
--- a/ipaserver/install/ldapupdate.py
+++ b/ipaserver/install/ldapupdate.py
@@ -32,13 +32,12 @@ import pwd
 import fnmatch
 import re
 
-import krbV
 import ldap
 
 from ipaserver.install import installutils
 from ipapython import ipautil, ipaldap
 from ipalib import errors
-from ipalib import api, create_api
+from ipalib import api, create_api, krb_utils
 from ipalib import constants
 from ipaplatform.paths import paths
 from ipaplatform import services
@@ -272,13 +271,8 @@ class LDAPUpdate:
         if sub_dict.get("REALM"):
             self.realm = sub_dict["REALM"]
         else:
-            krbctx = krbV.default_context()
-            try:
-                self.realm = krbctx.default_realm
-                suffix = ipautil.realm_to_suffix(self.realm)
-            except krbV.Krb5Error:
-                self.realm = None
-                suffix = None
+            self.realm = krb_utils.get_default_realm()
+            suffix = ipautil.realm_to_suffix(self.realm) if self.realm else None
 
         if suffix is not None:
             assert isinstance(suffix, DN)
diff --git a/ipaserver/install/schemaupdate.py b/ipaserver/install/schemaupdate.py
index 03edb63075e893ca9887559de6470b9f94c40cf9..4055abf922c4b9e41e7f1c11fd33c67014bc8063 100644
--- a/ipaserver/install/schemaupdate.py
+++ b/ipaserver/install/schemaupdate.py
@@ -20,9 +20,9 @@
 import pprint
 
 import ldap.schema
-import krbV
 
 import ipapython.version
+from ipalib import krb_utils
 from ipapython.ipa_log_manager import log_mgr
 from ipapython.dn import DN
 from ipaserver.install.ldapupdate import connect
@@ -106,7 +106,7 @@ def update_schema(schema_files, ldapi=False, dm_password=None,):
     SCHEMA_ELEMENT_CLASSES_KEYS = [x[0] for x in SCHEMA_ELEMENT_CLASSES]
 
     conn = connect(ldapi=ldapi, dm_password=dm_password,
-                   realm=krbV.default_context().default_realm,
+                   realm=krb_utils.get_default_realm(),
                    fqdn=installutils.get_fqdn())
 
     old_schema = conn.schema
diff --git a/ipaserver/install/server/upgrade.py b/ipaserver/install/server/upgrade.py
index f295655dc2aa592e0215f15017c9b65af49eef80..53603488af5bfc4e3904b88c53c8a719f55b82e3 100644
--- a/ipaserver/install/server/upgrade.py
+++ b/ipaserver/install/server/upgrade.py
@@ -9,12 +9,11 @@ import pwd
 import fileinput
 import ConfigParser
 import sys
-import krbV
 
 from ipalib import api
 import SSSDConfig
-import ipalib.util
 import ipalib.errors
+from ipalib import krb_utils
 from ipaplatform import services
 from ipaplatform.tasks import tasks
 from ipapython import ipautil, sysrestore, version, certdb
@@ -1554,7 +1553,7 @@ def upgrade_check(options):
 
 
 def upgrade():
-    realm = krbV.default_context().default_realm
+    realm = krb_utils.get_default_realm()
     schema_files = [os.path.join(ipautil.SHARE_DIR, f) for f
                     in dsinstance.ALL_SCHEMA_FILES]
     data_upgrade = IPAUpgrade(realm, schema_files=schema_files)
diff --git a/ipaserver/plugins/join.py b/ipaserver/plugins/join.py
index 3b668053c5d275c53da0c897fb4f82442a725564..a2ca417a9464eca687dcd3a2ab398f07d0785dfe 100644
--- a/ipaserver/plugins/join.py
+++ b/ipaserver/plugins/join.py
@@ -21,24 +21,13 @@
 Joining an IPA domain
 """
 
-import krbV
-
-from ipalib import api
+from ipalib import api, krb_utils
 from ipalib import Command, Str
 from ipalib import errors
 from ipalib import _
 from ipaserver.install import installutils
 
 
-def get_realm():
-    """
-    Returns the default kerberos realm configured for this server.
-    """
-    krbctx = krbV.default_context()
-
-    return unicode(krbctx.default_realm)
-
-
 def validate_host(ugettext, cn):
     """
     Require at least one dot in the hostname (to support localhost.localdomain)
@@ -66,7 +55,7 @@ class join(Command):
     takes_options = (
         Str('realm',
             doc=_("The IPA realm"),
-            default_from=lambda: get_realm(),
+            default_from=lambda: krb_utils.get_default_realm(),
             autofill=True,
         ),
         Str('nshardwareplatform?',
diff --git a/ipaserver/plugins/ldap2.py b/ipaserver/plugins/ldap2.py
index 68feee4f09eb12e50867dfbe3c482a359838aa82..10ceee699c519495c075a4bc3d42cd74aa85dc13 100644
--- a/ipaserver/plugins/ldap2.py
+++ b/ipaserver/plugins/ldap2.py
@@ -30,9 +30,9 @@ Backend plugin for LDAP.
 import os
 import pwd
 
-import krbV
 import ldap as _ldap
 
+from ipalib import krb_utils
 from ipapython.dn import DN
 from ipapython.ipaldap import SASL_GSSAPI, LDAPClient
 
@@ -88,13 +88,14 @@ class ldap2(CrudBackend, LDAPClient):
 
     def create_connection(self, ccache=None, bind_dn=None, bind_pw='',
             tls_cacertfile=None, tls_certfile=None, tls_keyfile=None,
-            debug_level=0, autobind=False, serverctrls=None, clientctrls=None):
+            debug_level=0, autobind=False, serverctrls=None, clientctrls=None,
+            use_gssapi=False):
         """
         Connect to LDAP server.
 
         Keyword arguments:
         ldapuri -- the LDAP server to connect to
-        ccache -- Kerberos V5 ccache object or name
+        ccache -- Kerberos ccache name
         bind_dn -- dn used to bind to the server
         bind_pw -- password used to bind to the server
         debug_level -- LDAP debug level option
@@ -102,6 +103,7 @@ class ldap2(CrudBackend, LDAPClient):
         tls_certfile -- TLS certificate filename
         tls_keyfile - TLS bind key filename
         autobind - autobind as the current user
+        use_gssapi -- bind using GSSAPI, implied if ccache specified
 
         Extends backend.Connectible.create_connection.
         """
@@ -134,19 +136,14 @@ class ldap2(CrudBackend, LDAPClient):
                 if maxssf < minssf:
                     conn.set_option(_ldap.OPT_X_SASL_SSF_MAX, minssf)
 
-        if ccache is not None:
-            if isinstance(ccache, krbV.CCache):
-                principal = ccache.principal().name
-                # Get a fully qualified CCACHE name (schema+name)
-                # As we do not use the krbV.CCache object later,
-                # we can safely overwrite it
-                ccache = "%(type)s:%(name)s" % dict(type=ccache.type,
-                                                    name=ccache.name)
+        if use_gssapi or ccache is not None:
+            if ccache is None:
+                os.environ.pop('KRB5CCNAME', None)
             else:
-                principal = krbV.CCache(name=ccache,
-                    context=krbV.default_context()).principal().name
+                os.environ['KRB5CCNAME'] = ccache
+
+            principal = krb_utils.get_principal(ccache_name=ccache)
 
-            os.environ['KRB5CCNAME'] = ccache
             self.gssapi_bind(server_controls=serverctrls,
                              client_controls=clientctrls)
             setattr(context, 'principal', principal)
diff --git a/ipaserver/rpcserver.py b/ipaserver/rpcserver.py
index ead830def127492cecda09cb1dc7964c314f2912..a232b4d11f1f55f10d38a6af4521d3c5fdeb5ab8 100644
--- a/ipaserver/rpcserver.py
+++ b/ipaserver/rpcserver.py
@@ -30,7 +30,8 @@ import datetime
 import urlparse
 import json
 import traceback
-from krbV import Krb5Error
+import gssapi
+import time
 
 import ldap.controls
 from pyasn1.type import univ, namedtype
@@ -54,8 +55,8 @@ from ipalib.session import (
     default_max_session_duration, krbccache_dir, krbccache_prefix)
 from ipalib.backend import Backend
 from ipalib.krb_utils import (
-    KRB5_CCache, krb_ticket_expiration_threshold, krb5_format_principal_name,
-    krb5_format_service_principal_name)
+    krb_ticket_expiration_threshold, krb5_format_principal_name,
+    krb5_format_service_principal_name, get_credentials, get_credentials_if_valid)
 from ipapython import ipautil
 from ipaplatform.paths import paths
 from ipapython.version import VERSION
@@ -593,8 +594,8 @@ class KerberosSession(object):
         session_data['ccache_data'] = load_ccache_data(ccache_name)
 
         # Set when the session will expire
-        cc = KRB5_CCache(ccache_name)
-        endtime = cc.endtime(self.api.env.host, self.api.env.realm)
+        creds = get_credentials(ccache_name=ccache_name)
+        endtime = creds.lifetime + time.time()
         self.update_session_expiration(session_data, endtime)
 
         # Store the session data now that it's been updated with the ccache
@@ -789,15 +790,15 @@ class jsonserver_session(jsonserver, KerberosSession):
         ipa_ccache_name = bind_ipa_ccache(ccache_data)
 
         # Redirect to login if Kerberos credentials are expired
-        cc = KRB5_CCache(ipa_ccache_name)
-        if not cc.valid(self.api.env.host, self.api.env.realm):
+        creds = get_credentials_if_valid(ccache_name=ipa_ccache_name)
+        if not creds:
             self.debug('ccache expired, deleting session, need login')
             # The request is finished with the ccache, destroy it.
             release_ipa_ccache(ipa_ccache_name)
             return self.need_login(start_response)
 
         # Update the session expiration based on the Kerberos expiration
-        endtime = cc.endtime(self.api.env.host, self.api.env.realm)
+        endtime = creds.lifetime + time.time()
         self.update_session_expiration(session_data, endtime)
 
         # Store the session data in the per-thread context
@@ -962,7 +963,7 @@ class login_password(Backend, KerberosSession, HTTP_Status):
 
         try:
             ipautil.kinit_keytab(armor_principal, paths.IPA_KEYTAB, armor_path)
-        except Krb5Error as e:
+        except gssapi.exceptions.GSSError as e:
             raise CCacheError(str(e))
 
         # Format the user as a kerberos principal
@@ -1229,15 +1230,15 @@ class xmlserver_session(xmlserver, KerberosSession):
         ipa_ccache_name = bind_ipa_ccache(ccache_data)
 
         # Redirect to /ipa/xml if Kerberos credentials are expired
-        cc = KRB5_CCache(ipa_ccache_name)
-        if not cc.valid(self.api.env.host, self.api.env.realm):
+        creds = get_credentials_if_valid(ccache_name=ipa_ccache_name)
+        if not creds:
             self.debug('xmlserver_session.__call_: ccache expired, deleting session, need login')
             # The request is finished with the ccache, destroy it.
             release_ipa_ccache(ipa_ccache_name)
             return self.need_login(start_response)
 
         # Update the session expiration based on the Kerberos expiration
-        endtime = cc.endtime(self.api.env.host, self.api.env.realm)
+        endtime = creds.lifetime + time.time()
         self.update_session_expiration(session_data, endtime)
 
         # Store the session data in the per-thread context
diff --git a/ipatests/test_cmdline/cmdline.py b/ipatests/test_cmdline/cmdline.py
index a7ed677937cbd3b14c365d045306b2fdb2f30e8b..ec69998d9bd4d6f8fc8eab07da07172e51e15b5b 100644
--- a/ipatests/test_cmdline/cmdline.py
+++ b/ipatests/test_cmdline/cmdline.py
@@ -22,7 +22,6 @@ Base class for all cmdline tests
 """
 
 import nose
-import krbV
 import distutils.spawn
 import os
 
@@ -33,11 +32,9 @@ from ipatests.test_xmlrpc.xmlrpc_test import XMLRPC_test
 from ipaserver.plugins.ldap2 import ldap2
 
 # See if our LDAP server is up and we can talk to it over GSSAPI
-ccache = krbV.default_context().default_ccache()
-
 try:
     conn = ldap2(api)
-    conn.connect(ccache=ccache)
+    conn.connect(use_gssapi=True)
     conn.disconnect()
     server_available = True
 except errors.DatabaseError:
diff --git a/ipatests/test_cmdline/test_ipagetkeytab.py b/ipatests/test_cmdline/test_ipagetkeytab.py
index bc948bf09a68b9017a4101fba6603e68c096571c..533487f1d0b163a6c4c2c0fffefd4a711b114e4b 100644
--- a/ipatests/test_cmdline/test_ipagetkeytab.py
+++ b/ipatests/test_cmdline/test_ipagetkeytab.py
@@ -29,7 +29,7 @@ import tempfile
 from ipapython import ipautil
 import nose
 import tempfile
-import krbV
+import gssapi
 from ipaserver.plugins.ldap2 import ldap2
 from ipapython.dn import DN
 
@@ -37,21 +37,18 @@ def use_keytab(principal, keytab):
     try:
         tmpdir = tempfile.mkdtemp(prefix = "tmp-")
         ccache_file = 'FILE:%s/ccache' % tmpdir
-        krbcontext = krbV.default_context()
-        principal = str(principal)
-        keytab = krbV.Keytab(name=keytab, context=krbcontext)
-        principal = krbV.Principal(name=principal, context=krbcontext)
+        name = gssapi.Name(principal, gssapi.NameType.kerberos_principal)
+        store = {'ccache': ccache_file,
+                 'client_keytab': keytab}
         os.environ['KRB5CCNAME'] = ccache_file
-        ccache = krbV.CCache(name=ccache_file, context=krbcontext, primary_principal=principal)
-        ccache.init(principal)
-        ccache.init_creds_keytab(keytab=keytab, principal=principal)
+        gssapi.Credentials(name=name, usage='initiate', store=store)
         conn = ldap2(api)
-        conn.connect(ccache=ccache)
+        conn.connect(use_gssapi=True)
         conn.disconnect()
-    except krbV.Krb5Error, e:
-        raise StandardError('Unable to bind to LDAP. Error initializing principal %s in %s: %s' % (principal.name, keytab, str(e)))
+    except gssapi.exceptions.GSSError as e:
+        raise StandardError('Unable to bind to LDAP. Error initializing principal %s in %s: %s' % (principal, keytab, str(e)))
     finally:
-        del os.environ['KRB5CCNAME']
+        os.environ.pop('KRB5CCNAME', None)
         if tmpdir:
             shutil.rmtree(tmpdir)
 
diff --git a/ipatests/test_xmlrpc/test_dns_plugin.py b/ipatests/test_xmlrpc/test_dns_plugin.py
index 83b5b2aec386bf73019fab83e3fe041819a00858..82f5d57e1aa17b101bfc950d915bc236f89c21eb 100644
--- a/ipatests/test_xmlrpc/test_dns_plugin.py
+++ b/ipatests/test_xmlrpc/test_dns_plugin.py
@@ -33,7 +33,6 @@ try:
 except ImportError:
     have_ldap2 = False
 else:
-    import krbV
     have_ldap2 = True
 
 _dns_zone_record = DNSName(u'@')
@@ -401,7 +400,7 @@ def _get_nameservers_ldap(conn):
 
 def get_nameservers():
         ldap = ldap2(api)
-        ldap.connect(ccache=krbV.default_context().default_ccache())
+        ldap.connect(use_gssapi=True)
         nameservers = [normalize_zone(x) for x in _get_nameservers_ldap(ldap)]
         return nameservers
 
diff --git a/ipatests/test_xmlrpc/test_netgroup_plugin.py b/ipatests/test_xmlrpc/test_netgroup_plugin.py
index 64cc422102d581728567d4fc35fc0cee96487718..3914115ac712c63e6411380239eeececec4e0ad9 100644
--- a/ipatests/test_xmlrpc/test_netgroup_plugin.py
+++ b/ipatests/test_xmlrpc/test_netgroup_plugin.py
@@ -22,7 +22,6 @@ Test the `ipalib/plugins/netgroup.py` module.
 """
 
 import nose
-import krbV
 
 from ipalib import api
 from ipalib import errors
@@ -36,9 +35,6 @@ from ipatests.test_xmlrpc.test_user_plugin import get_user_result
 # Global so we can save the value between tests
 netgroup_dn = None
 
-# See if our LDAP server is up and we can talk to it over GSSAPI
-ccache = krbV.default_context().default_ccache().name
-
 netgroup1 = u'netgroup1'
 netgroup2 = u'netgroup2'
 netgroup_single = u'a'
@@ -1298,7 +1294,7 @@ class test_netgroup(Declarative):
 #        # Do an LDAP query to the compat area and verify that the entry
 #        # is correct
 #        conn = ldap2(api)
-#        conn.connect(ccache=ccache)
+#        conn.connect(use_gssapi=True)
 #        try:
 #            entries = conn.find_entries('cn=%s' % self.ng_cn,
 #                      base_dn='cn=ng,cn=compat,%s' % api.env.basedn)
diff --git a/ipatests/test_xmlrpc/test_permission_plugin.py b/ipatests/test_xmlrpc/test_permission_plugin.py
index c899c428edfcd19c2e7f538cdc38b693e11c8715..ed3c9e5e6d40144985d371533054b2fd7e898ec6 100644
--- a/ipatests/test_xmlrpc/test_permission_plugin.py
+++ b/ipatests/test_xmlrpc/test_permission_plugin.py
@@ -37,7 +37,6 @@ try:
 except ImportError:
     have_ldap2 = False
 else:
-    import krbV
     have_ldap2 = True
 
 permission1 = u'testperm'
@@ -3175,7 +3174,7 @@ class test_managed_permissions(Declarative):
     def add_managed_permission(self):
         """Add a managed permission and the corresponding ACI"""
         ldap = ldap2(api)
-        ldap.connect(ccache=krbV.default_context().default_ccache())
+        ldap.connect(use_gssapi=True)
 
         result = api.Command.permission_add(permission1, type=u'user',
                                             ipapermright=u'write',
diff --git a/lite-server.py b/lite-server.py
index 99089b00f9a9cc4c54a227c3aff55c7357ba2428..0e867915f19e16801556b4362e53f330da365f4e 100755
--- a/lite-server.py
+++ b/lite-server.py
@@ -37,13 +37,27 @@ from paste import httpserver
 import paste.gzipper
 from paste.urlmap import URLMap
 from ipalib import api
+from subprocess import check_output, CalledProcessError
+import re
+
+# Ugly hack for test purposes only. GSSAPI has no way to get default ccache
+# name, but we don't need it outside test server
+def get_default_ccache_name():
+    try:
+        out = check_output(['klist'])
+    except CalledProcessError:
+        raise RuntimeError("Default ccache not found. Did you kinit?")
+    match = re.match(r'^Ticket cache:\s*(\S+)', out)
+    if not match:
+        raise RuntimeError("Cannot obtain ccache name")
+    return match.group(1)
 
 
 class KRBCheater(object):
     def __init__(self, app):
         self.app = app
         self.url = app.url
-        self.ccname = api.Backend.krb.default_ccname()
+        self.ccname = get_default_ccache_name()
 
     def __call__(self, environ, start_response):
         environ['KRB5CCNAME'] = self.ccname
diff --git a/make-lint b/make-lint
index 0447985303f485a014fecf7d17d0b1c7eb6137bd..380e8c86d039be0351e2fefa3758194aa2397e12 100755
--- a/make-lint
+++ b/make-lint
@@ -50,7 +50,6 @@ class IPATypeChecker(TypeChecker):
     # 'class or module': ['generated', 'properties']
     ignore = {
         # Python standard library & 3rd party classes
-        'krbV.Principal': ['name'],
         'socket._socketobject': ['sendall'],
         # should be 'subprocess.Popen'
         '.Popen': ['stdin', 'stdout', 'stderr', 'pid', 'returncode', 'poll',
@@ -68,7 +67,6 @@ class IPATypeChecker(TypeChecker):
         'ipalib.base.NameSpace': ['add', 'mod', 'del', 'show', 'find'],
         'ipalib.cli.Collector': ['__options'],
         'ipalib.config.Env': ['*'],
-        'ipalib.krb_utils.KRB5_CCache': LOGGING_ATTRS,
         'ipalib.parameters.Param': ['cli_name', 'cli_short_name', 'label',
             'default', 'doc', 'required', 'multivalue', 'primary_key',
             'normalizer', 'default_from', 'autofill', 'query', 'attribute',
@@ -261,7 +259,7 @@ Errors were found during the static code check.
             for mod in sorted(linter.missing):
                 print >> sys.stderr, "    " + mod
             print >> sys.stderr, """
-Please make sure all of the required and optional (python-krbV, python-rhsm)
+Please make sure all of the required and optional (python-gssapi, python-rhsm)
 python packages are installed.
 """
 
-- 
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