diff options
37 files changed, 245 insertions, 645 deletions
@@ -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/daemons/dnssec/ipa-dnskeysync-replica b/daemons/dnssec/ipa-dnskeysync-replica index b80b38962..d21626808 100755 --- a/daemons/dnssec/ipa-dnskeysync-replica +++ b/daemons/dnssec/ipa-dnskeysync-replica @@ -12,7 +12,7 @@ from binascii import hexlify from datetime import datetime import dns.dnssec import fcntl -from krbV import Krb5Error +from gssapi.exceptions import GSSError import logging import os from pprint import pprint @@ -146,7 +146,7 @@ ccache_filename = os.path.join(WORKDIR, 'ipa-dnskeysync-replica.ccache') try: ipautil.kinit_keytab(PRINCIPAL, paths.IPA_DNSKEYSYNCD_KEYTAB, ccache_filename, attempts=5) -except Krb5Error as e: +except GSSError as e: log.critical('Kerberos authentication failed: %s', e) sys.exit(1) diff --git a/daemons/dnssec/ipa-ods-exporter b/daemons/dnssec/ipa-ods-exporter index 4d5423797..e7d30014e 100755 --- a/daemons/dnssec/ipa-ods-exporter +++ b/daemons/dnssec/ipa-ods-exporter @@ -20,7 +20,7 @@ from datetime import datetime import dateutil.tz import dns.dnssec import fcntl -from krbV import Krb5Error +from gssapi.exceptions import GSSError import logging import os import subprocess @@ -487,7 +487,7 @@ ccache_name = os.path.join(WORKDIR, 'ipa-ods-exporter.ccache') try: ipautil.kinit_keytab(PRINCIPAL, paths.IPA_ODS_EXPORTER_KEYTAB, ccache_name, attempts=5) -except Krb5Error as e: +except GSSError as e: log.critical('Kerberos authentication failed: %s', e) sys.exit(1) diff --git a/doc/examples/python-api.py b/doc/examples/python-api.py index 805925f26..215bb9153 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() else: api.Backend.rpcclient.connect() diff --git a/freeipa.spec.in b/freeipa.spec.in index 46f348e2a..4f902bbea 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 @@ -128,7 +127,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.2 Requires: python-sssdconfig Requires: acl Requires: python-pyasn1 @@ -263,7 +262,7 @@ Requires: certmonger >= 0.78 Requires: nss-tools Requires: bind-utils Requires: oddjob-mkhomedir -Requires: python-krbV +Requires: python-gssapi >= 1.1.2 Requires: python-dns >= 1.11.1 Requires: libsss_autofs Requires: autofs @@ -287,7 +286,6 @@ Summary: IPA administrative tools Group: System Environment/Base Requires: %{name}-python = %{version}-%{release} Requires: %{name}-client = %{version}-%{release} -Requires: python-krbV 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 6a2171d5f..d19e06f12 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", @@ -127,17 +104,21 @@ 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 +have_ccache = False +try: + cred = kinit_keytab(principal, keytab_name, ccache_name) + if cred.lifetime > 0: + have_ccache = True +except gssapi.exceptions.ExpiredCredentialsError: + pass +if not have_ccache: + # delete stale ccache and try again + if os.path.exists(oneway_ccache_name): + 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) # Retrieve own NetBIOS name and trusted forest's name. # We use script's input to retrieve the trusted forest's name to sanitize input @@ -159,32 +140,25 @@ 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) - else: - oneway_ccache_check = KRB5_CCache(oneway_ccache_name) - if not oneway_ccache_check.credential_is_valid(oneway_principal): - # If credentials were invalid, obtain them again - oneway_ccache = kinit_keytab(oneway_principal, oneway_keytab_name, oneway_ccache_name) - else: - oneway_ccache = oneway_ccache_check.ccache -except krbV.Krb5Error as e: + have_ccache = False + try: + # The keytab may have stale key material (from older trust-add run) + cred = kinit_keytab(oneway_principal, oneway_keytab_name, oneway_ccache_name) + if cred.lifetime > 0: + have_ccache = True + except gssapi.exceptions.ExpiredCredentialsError: + pass + if not have_ccache: + if os.path.exists(oneway_ccache_name): + os.unlink(oneway_ccache_name) + 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) - -try: - # There wasn existing ccache, validate its content - oneway_ccache_check = KRB5_CCache(oneway_ccache_name) - if not oneway_ccache_check.credential_is_valid(oneway_principal): - # If credentials were invalid, obtain them again - oneway_ccache = kinit_keytab(oneway_principal, oneway_keytab_name, oneway_ccache_name) - else: - oneway_ccache = oneway_ccache_check.ccache -except krbV.Krb5Error as e: - oneway_ccache = kinit_keytab(oneway_principal, oneway_keytab_name, oneway_ccache_name) + if os.path.exists(oneway_ccache_name): + os.unlink(oneway_ccache_name) + 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 diff --git a/install/tools/ipa-adtrust-install b/install/tools/ipa-adtrust-install index a3037440c..4d0e5707e 100755 --- a/install/tools/ipa-adtrust-install +++ b/install/tools/ipa-adtrust-install @@ -21,14 +21,14 @@ # along with this program. If not, see <http://www.gnu.org/licenses/>. # +import gssapi from ipaserver.install import adtrustinstance 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 +302,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 as e: - sys.exit("Must have Kerberos credentials to setup AD trusts on server") + principal = krb_utils.get_principal() + except gssapi.exceptions.GSSError as e: + sys.exit("Must have Kerberos credentials to setup AD trusts on server: %s" % e.message) try: - api.Backend.ldap2.connect(ccache) + api.Backend.ldap2.connect() except errors.ACIError as e: sys.exit("Outdated Kerberos credentials. Use kdestroy and kinit to update your ticket") except errors.DatabaseError as 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.partition('@')[0].partition('/')[0])['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 3a5c78aa4..eec8bb2c8 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 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 = api.env.realm if options.host: host = options.host diff --git a/install/tools/ipa-replica-manage b/install/tools/ipa-replica-manage index 1c9c7d32c..f26c6ab60 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 @@ -1379,7 +1379,7 @@ def main(): api.finalize() dirman_passwd = None - realm = krbV.default_context().default_realm + realm = api.env.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() if args[0] == "list": replica = None diff --git a/ipa-client/ipa-client.spec.in b/ipa-client/ipa-client.spec.in index 686259ad2..4413937bb 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 0739a2e6b..4abc853dc 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 @@ -427,15 +427,14 @@ def main(): print "Location: %s" % options.location root_logger.debug('Using automount location %s' % options.location) - # Verify that the location is valid - (ccache_fd, ccache_name) = tempfile.mkstemp() - os.close(ccache_fd) + ccache_dir = tempfile.mkdtemp() + ccache_name = os.path.join(ccache_dir, 'ccache') try: try: 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: @@ -457,6 +456,7 @@ def main(): sys.exit("Cannot connect to the server due to generic error: %s" % str(e)) finally: os.remove(ccache_name) + os.rmdir(ccache_dir) if not options.unattended and not ipautil.user_input("Continue to configure the system with these values?", False): sys.exit("Installation aborted") diff --git a/ipa-client/ipa-install/ipa-client-install b/ipa-client/ipa-install/ipa-client-install index c2131ffb6..9352b0049 100755 --- a/ipa-client/ipa-install/ipa-client-install +++ b/ipa-client/ipa-install/ipa-client-install @@ -31,8 +31,8 @@ try: from ConfigParser import RawConfigParser from optparse import SUPPRESS_HELP, OptionGroup, OptionValueError import shutil - from krbV import Krb5Error import dns + import gssapi import nss.nss as nss import SSSDConfig @@ -2618,7 +2618,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) @@ -2698,7 +2698,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 @@ -2745,7 +2745,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 19cd0ad79..db1cffc1e 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 -#------------------------------------------------------------------------------- - -# Kerberos constants, should be defined in krbV, but aren't -KRB5_GC_CACHED = 0x2 +from ipalib import errors -# 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,66 @@ def krb5_format_time(timestamp): ''' return time.strftime(krb5_time_fmt, time.localtime(timestamp)) -class KRB5_CCache(object): - ''' - 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. +def get_credentials(name=None, ccache_name=None): ''' + Obtains GSSAPI credentials with given principal name from ccache. When no + principal name specified, it retrieves the default one for given + credentials cache. - 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 as 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 as 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 as 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 as 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 as 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 as e: - raise e - except Exception as 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 as e: - return False - except Exception as 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). + :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 + +def get_principal(ccache_name=None): + ''' + Gets default principal name from given credentials cache. - :parameters: - host - ldap server - realm - kerberos realm - :returns: - UNIX timestamp value. - ''' + :parameters: + ccache_name + string specifying Kerberos credentials cache name or None for the + default + :returns: + Default principal name as string + ''' + creds = get_credentials(ccache_name=ccache_name) + return unicode(creds.name) - 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_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. - 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 + :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 + ''' - self.debug('KRB5_CCache %s endtime=%s (%s)', self.ccache_str(), result, krb5_format_time(result)) - return result + try: + creds = get_credentials(name=name, ccache_name=ccache_name) + if creds.lifetime > 0: + return creds + return None + except gssapi.exceptions.ExpiredCredentialsError: + return None diff --git a/ipalib/plugins/kerberos.py b/ipalib/plugins/kerberos.py deleted file mode 100644 index 3ed6d7671..000000000 --- a/ipalib/plugins/kerberos.py +++ /dev/null @@ -1,125 +0,0 @@ -# Authors: -# Jason Gerard DeRose <jderose@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 'admin@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 'admin@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 f5fc14d51..a4f791c1b 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 6a07a76b5..18436e399 100644 --- a/ipalib/plugins/vault.py +++ b/ipalib/plugins/vault.py @@ -34,7 +34,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 @@ -640,7 +639,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() if vault_type == u'standard': @@ -1239,7 +1238,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() # retrieve vault info vault = self.api.Command.vault_show(*args, **options)['result'] @@ -1508,7 +1507,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() # retrieve vault info vault = self.api.Command.vault_show(*args, **options)['result'] diff --git a/ipalib/rpc.py b/ipalib/rpc.py index 04b8d01d9..9d0fc8f7b 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 @@ -518,10 +518,7 @@ class KerbTransport(SSLTransport): self._sec_context = None 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: @@ -835,7 +832,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 0d5f85040..4a75b820a 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 8b6b5ac93..d586d66d6 100644 --- a/ipapython/config.py +++ b/ipapython/config.py @@ -176,17 +176,6 @@ def __parse_config(discover_server = True): def __discover_config(discover_server = True): servers = [] 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 - except ImportError: - pass - if not config.default_realm: - return False - if not config.default_domain: # try once with REALM -> domain domain = str(config.default_realm).lower() diff --git a/ipapython/ipautil.py b/ipapython/ipautil.py index c3ffb1d5c..d959bb369 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: @@ -1206,8 +1211,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) @@ -1218,18 +1223,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 34a931d1a..c991cafa1 100644 --- a/ipaserver/install/ipa_cacert_manage.py +++ b/ipaserver/install/ipa_cacert_manage.py @@ -23,7 +23,7 @@ from optparse import OptionGroup import base64 from nss import nss from nss.error import NSPRError -import krbV +import gssapi from ipapython import admintool, certmonger, ipautil from ipapython.dn import DN @@ -126,9 +126,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() + except (gssapi.exceptions.GSSError, errors.ACIError): pass else: return conn diff --git a/ipaserver/install/ipa_ldap_updater.py b/ipaserver/install/ipa_ldap_updater.py index 2c4f28af4..8321c20a3 100644 --- a/ipaserver/install/ipa_ldap_updater.py +++ b/ipaserver/install/ipa_ldap_updater.py @@ -26,8 +26,6 @@ import os import sys -import krbV - from ipalib import api from ipapython import ipautil, admintool from ipaplatform.paths import paths @@ -100,7 +98,7 @@ class LDAPUpdater_Upgrade(LDAPUpdater): super(LDAPUpdater_Upgrade, self).run() options = self.options - realm = krbV.default_context().default_realm + realm = api.env.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 386ca4273..ae89f7e07 100644 --- a/ipaserver/install/ipa_otptoken_import.py +++ b/ipaserver/install/ipa_otptoken_import.py @@ -30,7 +30,7 @@ from lxml import etree import dateutil.parser import dateutil.tz import nss.nss as nss -import krbV +import gssapi from ipapython import admintool from ipalib import api, errors @@ -509,9 +509,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() + except (gssapi.exceptions.GSSError, 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 097b8c806..75d1dbe31 100644 --- a/ipaserver/install/ipa_winsync_migrate.py +++ b/ipaserver/install/ipa_winsync_migrate.py @@ -17,7 +17,7 @@ # along with this program. If not, see <http://www.gnu.org/licenses/>. # -import krbV +import gssapi import sys from ipalib import api @@ -321,12 +321,10 @@ 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() cls.ldap = api.Backend.ldap2 - except krbV.Krb5Error as e: - sys.exit("Must have Kerberos credentials to migrate Winsync users.") + except gssapi.exceptions.GSSError as e: + sys.exit("Must have Kerberos credentials to migrate Winsync users. Error: %s" % e) except errors.ACIError as e: sys.exit("Outdated Kerberos credentials. Use kdestroy and kinit to update your ticket.") except errors.DatabaseError as e: diff --git a/ipaserver/install/ldapupdate.py b/ipaserver/install/ldapupdate.py index 1f3aca542..0444327aa 100644 --- a/ipaserver/install/ldapupdate.py +++ b/ipaserver/install/ldapupdate.py @@ -32,7 +32,6 @@ import pwd import fnmatch import re -import krbV import ldap from ipaserver.install import installutils @@ -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 = api.env.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 03edb6307..f98d0e949 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 api 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=api.env.realm, fqdn=installutils.get_fqdn()) old_schema = conn.schema diff --git a/ipaserver/install/server/upgrade.py b/ipaserver/install/server/upgrade.py index f8a4ff282..cb92250e4 100644 --- a/ipaserver/install/server/upgrade.py +++ b/ipaserver/install/server/upgrade.py @@ -9,7 +9,6 @@ import pwd import fileinput import ConfigParser import sys -import krbV from ipalib import api import SSSDConfig @@ -1567,7 +1566,7 @@ def upgrade_check(options): def upgrade(): - realm = krbV.default_context().default_realm + realm = api.env.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 3b668053c..7342117e7 100644 --- a/ipaserver/plugins/join.py +++ b/ipaserver/plugins/join.py @@ -21,8 +21,6 @@ Joining an IPA domain """ -import krbV - from ipalib import api from ipalib import Command, Str from ipalib import errors @@ -30,15 +28,6 @@ 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: api.env.realm, autofill=True, ), Str('nshardwareplatform?', diff --git a/ipaserver/plugins/ldap2.py b/ipaserver/plugins/ldap2.py index 68feee4f0..acaf45fdd 100644 --- a/ipaserver/plugins/ldap2.py +++ b/ipaserver/plugins/ldap2.py @@ -30,11 +30,12 @@ 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 +from ipapython.ipaldap import (LDAPClient, AUTOBIND_AUTO, AUTOBIND_ENABLED, + AUTOBIND_DISABLED) try: @@ -88,13 +89,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=AUTOBIND_AUTO, serverctrls=None, + clientctrls=None): """ 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 @@ -122,8 +124,6 @@ class ldap2(CrudBackend, LDAPClient): conn = self._conn with self.error_handler(): - if self.ldap_uri.startswith('ldapi://') and ccache: - conn.set_option(_ldap.OPT_HOST_NAME, self.api.env.host) minssf = conn.get_option(_ldap.OPT_X_SASL_SSF_MIN) maxssf = conn.get_option(_ldap.OPT_X_SASL_SSF_MAX) # Always connect with at least an SSF of 56, confidentiality @@ -134,33 +134,37 @@ 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) - else: - principal = krbV.CCache(name=ccache, - context=krbV.default_context()).principal().name + ldapi = self.ldap_uri.startswith('ldapi://') - os.environ['KRB5CCNAME'] = ccache - self.gssapi_bind(server_controls=serverctrls, + if bind_pw: + self.simple_bind(bind_dn, bind_pw, + server_controls=serverctrls, client_controls=clientctrls) - setattr(context, 'principal', principal) - else: - # no kerberos ccache, use simple bind or external sasl - if autobind: - pent = pwd.getpwuid(os.geteuid()) - self.external_bind(pent.pw_name, + elif autobind != AUTOBIND_DISABLED and os.getegid() == 0 and ldapi: + try: + pw_name = pwd.getpwuid(os.geteuid()).pw_name + self.external_bind(pw_name, server_controls=serverctrls, client_controls=clientctrls) + except errors.NotFound: + if autobind == AUTOBIND_ENABLED: + # autobind was required and failed, raise + # exception that it failed + raise + else: + if ldapi: + with self.error_handler(): + conn.set_option(_ldap.OPT_HOST_NAME, self.api.env.host) + if ccache is None: + os.environ.pop('KRB5CCNAME', None) else: - self.simple_bind(bind_dn, bind_pw, - server_controls=serverctrls, - client_controls=clientctrls) + os.environ['KRB5CCNAME'] = ccache + + principal = krb_utils.get_principal(ccache_name=ccache) + + self.gssapi_bind(server_controls=serverctrls, + client_controls=clientctrls) + setattr(context, 'principal', principal) return conn diff --git a/ipaserver/rpcserver.py b/ipaserver/rpcserver.py index 8fa097344..df75d98bb 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 0ae8cc079..e41b027a3 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() 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 2b99c268e..d9ab0daaa 100644 --- a/ipatests/test_cmdline/test_ipagetkeytab.py +++ b/ipatests/test_cmdline/test_ipagetkeytab.py @@ -26,10 +26,10 @@ from cmdline import cmdline_test from ipalib import api from ipalib import errors import tempfile -from ipapython import ipautil +from ipapython import ipautil, ipaldap 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(autobind=ipaldap.AUTOBIND_DISABLED) conn.disconnect() - except krbV.Krb5Error as 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 caad00de3..f0b8edaa1 100644 --- a/ipatests/test_xmlrpc/test_dns_plugin.py +++ b/ipatests/test_xmlrpc/test_dns_plugin.py @@ -34,7 +34,6 @@ try: except ImportError: have_ldap2 = False else: - import krbV have_ldap2 = True _dns_zone_record = DNSName(u'@') @@ -402,7 +401,7 @@ def _get_nameservers_ldap(conn): def get_nameservers(): ldap = ldap2(api) - ldap.connect(ccache=krbV.default_context().default_ccache()) + ldap.connect() 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 afe0c617d..10553c21f 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() # 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 2d1a7d5e7..971d0e6cb 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() result = api.Command.permission_add(permission1, type=u'user', ipapermright=u'write', diff --git a/lite-server.py b/lite-server.py index 99089b00f..0e867915f 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 @@ -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. """ |