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 ldap plugin connection defaults to match ipaldap
- Unified getting default realm
  Using api.env.realm instead of krbV call

Reviewed-By: Jan Cholasta <jcholast@redhat.com>
Reviewed-By: Robbie Harwood <rharwood@redhat.com>
Reviewed-By: Simo Sorce <ssorce@redhat.com>
This commit is contained in:
Michael Simacek 2015-07-20 16:04:07 +02:00 committed by Jan Cholasta
parent aebb72e1fb
commit aad73fad60
37 changed files with 249 additions and 649 deletions

View File

@ -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 \ libtevent-devel nspr-devel nss-devel openssl-devel openldap-devel krb5-devel \
krb5-workstation libuuid-devel libcurl-devel xmlrpc-c-devel popt-devel \ krb5-workstation libuuid-devel libcurl-devel xmlrpc-c-devel popt-devel \
autoconf automake m4 libtool gettext python-devel python-ldap \ 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 \ python-rhsm pyOpenSSL pylint python-polib libipa_hbac-python python-memcached \
sssd python-lxml python-pyasn1 python-qrcode-core python-dns m2crypto \ sssd python-lxml python-pyasn1 python-qrcode-core python-dns m2crypto \
check libsss_idmap-devel libsss_nss_idmap-devel java-headless rhino \ check libsss_idmap-devel libsss_nss_idmap-devel java-headless rhino \

View File

@ -12,7 +12,7 @@ from binascii import hexlify
from datetime import datetime from datetime import datetime
import dns.dnssec import dns.dnssec
import fcntl import fcntl
from krbV import Krb5Error from gssapi.exceptions import GSSError
import logging import logging
import os import os
from pprint import pprint from pprint import pprint
@ -146,7 +146,7 @@ ccache_filename = os.path.join(WORKDIR, 'ipa-dnskeysync-replica.ccache')
try: try:
ipautil.kinit_keytab(PRINCIPAL, paths.IPA_DNSKEYSYNCD_KEYTAB, ipautil.kinit_keytab(PRINCIPAL, paths.IPA_DNSKEYSYNCD_KEYTAB,
ccache_filename, attempts=5) ccache_filename, attempts=5)
except Krb5Error as e: except GSSError as e:
log.critical('Kerberos authentication failed: %s', e) log.critical('Kerberos authentication failed: %s', e)
sys.exit(1) sys.exit(1)

View File

@ -20,7 +20,7 @@ from datetime import datetime
import dateutil.tz import dateutil.tz
import dns.dnssec import dns.dnssec
import fcntl import fcntl
from krbV import Krb5Error from gssapi.exceptions import GSSError
import logging import logging
import os import os
import subprocess import subprocess
@ -487,7 +487,7 @@ ccache_name = os.path.join(WORKDIR, 'ipa-ods-exporter.ccache')
try: try:
ipautil.kinit_keytab(PRINCIPAL, paths.IPA_ODS_EXPORTER_KEYTAB, ccache_name, ipautil.kinit_keytab(PRINCIPAL, paths.IPA_ODS_EXPORTER_KEYTAB, ccache_name,
attempts=5) attempts=5)
except Krb5Error as e: except GSSError as e:
log.critical('Kerberos authentication failed: %s', e) log.critical('Kerberos authentication failed: %s', e)
sys.exit(1) sys.exit(1)

View File

@ -37,9 +37,7 @@ api.finalize()
# Backend.ldap.connect(), otherwise Backend.rpcclient.connect(). # Backend.ldap.connect(), otherwise Backend.rpcclient.connect().
if api.env.in_server: if api.env.in_server:
api.Backend.ldap2.connect( api.Backend.ldap2.connect()
ccache=api.Backend.krb.default_ccname()
)
else: else:
api.Backend.rpcclient.connect() api.Backend.rpcclient.connect()

View File

@ -68,7 +68,6 @@ BuildRequires: gettext
BuildRequires: python-devel BuildRequires: python-devel
BuildRequires: python-ldap BuildRequires: python-ldap
BuildRequires: python-setuptools BuildRequires: python-setuptools
BuildRequires: python-krbV
BuildRequires: python-nss BuildRequires: python-nss
BuildRequires: python-cryptography BuildRequires: python-cryptography
BuildRequires: python-netaddr BuildRequires: python-netaddr
@ -128,7 +127,7 @@ Requires: mod_wsgi
Requires: mod_auth_gssapi >= 1.1.0-2 Requires: mod_auth_gssapi >= 1.1.0-2
Requires: mod_nss >= 1.0.8-26 Requires: mod_nss >= 1.0.8-26
Requires: python-ldap >= 2.4.15 Requires: python-ldap >= 2.4.15
Requires: python-krbV Requires: python-gssapi >= 1.1.2
Requires: python-sssdconfig Requires: python-sssdconfig
Requires: acl Requires: acl
Requires: python-pyasn1 Requires: python-pyasn1
@ -263,7 +262,7 @@ Requires: certmonger >= 0.78
Requires: nss-tools Requires: nss-tools
Requires: bind-utils Requires: bind-utils
Requires: oddjob-mkhomedir Requires: oddjob-mkhomedir
Requires: python-krbV Requires: python-gssapi >= 1.1.2
Requires: python-dns >= 1.11.1 Requires: python-dns >= 1.11.1
Requires: libsss_autofs Requires: libsss_autofs
Requires: autofs Requires: autofs
@ -287,7 +286,6 @@ Summary: IPA administrative tools
Group: System Environment/Base Group: System Environment/Base
Requires: %{name}-python = %{version}-%{release} Requires: %{name}-python = %{version}-%{release}
Requires: %{name}-client = %{version}-%{release} Requires: %{name}-client = %{version}-%{release}
Requires: python-krbV
Requires: python-ldap Requires: python-ldap
Conflicts: %{alt_name}-admintools Conflicts: %{alt_name}-admintools

View File

@ -7,33 +7,10 @@ from ipalib import api, errors
from ipapython.dn import DN from ipapython.dn import DN
from ipalib.config import Env from ipalib.config import Env
from ipalib.constants import DEFAULT_CONFIG from ipalib.constants import DEFAULT_CONFIG
from ipalib.krb_utils import KRB5_CCache from ipapython.ipautil import kinit_keytab
import sys import sys
import os, pwd import os, pwd
import krbV import gssapi
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)
def retrieve_keytab(api, ccache_name, oneway_keytab_name, oneway_principal): def retrieve_keytab(api, ccache_name, oneway_keytab_name, oneway_principal):
getkeytab_args = ["/usr/sbin/ipa-getkeytab", 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 # - if not, initialize it from Samba's keytab
# - refer the correct ccache object for further use # - refer the correct ccache object for further use
# #
if not os.path.isfile(ccache_name): have_ccache = False
ccache = kinit_keytab(principal, keytab_name, ccache_name) try:
cred = kinit_keytab(principal, keytab_name, ccache_name)
ccache_check = KRB5_CCache(ccache_name) if cred.lifetime > 0:
if not ccache_check.credential_is_valid(principal): have_ccache = True
ccache = kinit_keytab(principal, keytab_name, ccache_name) except gssapi.exceptions.ExpiredCredentialsError:
else: pass
ccache = ccache_check.ccache 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') 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. # Retrieve own NetBIOS name and trusted forest's name.
# We use script's input to retrieve the trusted forest's name to sanitize input # 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): if not os.path.isfile(oneway_keytab_name):
retrieve_keytab(api, ccache_name, oneway_keytab_name, oneway_principal) retrieve_keytab(api, ccache_name, oneway_keytab_name, oneway_principal)
oneway_ccache = None
try: try:
# The keytab may have stale key material (from older trust-add run) have_ccache = False
if not os.path.isfile(oneway_ccache_name): try:
oneway_ccache = kinit_keytab(oneway_principal, oneway_keytab_name, oneway_ccache_name) # The keytab may have stale key material (from older trust-add run)
else: cred = kinit_keytab(oneway_principal, oneway_keytab_name, oneway_ccache_name)
oneway_ccache_check = KRB5_CCache(oneway_ccache_name) if cred.lifetime > 0:
if not oneway_ccache_check.credential_is_valid(oneway_principal): have_ccache = True
# If credentials were invalid, obtain them again except gssapi.exceptions.ExpiredCredentialsError:
oneway_ccache = kinit_keytab(oneway_principal, oneway_keytab_name, oneway_ccache_name) pass
else: if not have_ccache:
oneway_ccache = oneway_ccache_check.ccache if os.path.exists(oneway_ccache_name):
except krbV.Krb5Error as e: 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 # If there was failure on using keytab, assume it is stale and retrieve again
retrieve_keytab(api, ccache_name, oneway_keytab_name, oneway_principal) retrieve_keytab(api, ccache_name, oneway_keytab_name, oneway_principal)
if os.path.exists(oneway_ccache_name):
try: os.unlink(oneway_ccache_name)
# There wasn existing ccache, validate its content kinit_keytab(oneway_principal, oneway_keytab_name, oneway_ccache_name)
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)
# We are done: we have ccache with TDO credentials and can fetch domains # We are done: we have ccache with TDO credentials and can fetch domains
ipa_domain = api.env.domain ipa_domain = api.env.domain

View File

@ -21,14 +21,14 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>. # along with this program. If not, see <http://www.gnu.org/licenses/>.
# #
import gssapi
from ipaserver.install import adtrustinstance from ipaserver.install import adtrustinstance
from ipaserver.install.installutils import * from ipaserver.install.installutils import *
from ipaserver.install import service from ipaserver.install import service
from ipapython import version from ipapython import version
from ipapython import ipautil, sysrestore, ipaldap from ipapython import ipautil, sysrestore, ipaldap
from ipalib import api, errors, util from ipalib import api, errors, krb_utils
from ipapython.config import IPAOptionParser from ipapython.config import IPAOptionParser
import krbV
from ipaplatform.paths import paths from ipaplatform.paths import paths
from ipapython.ipa_log_manager import * from ipapython.ipa_log_manager import *
from ipapython.dn import DN from ipapython.dn import DN
@ -302,21 +302,19 @@ def main():
print "Proceeding with credentials that existed before" print "Proceeding with credentials that existed before"
try: try:
ctx = krbV.default_context() principal = krb_utils.get_principal()
ccache = ctx.default_ccache() except gssapi.exceptions.GSSError as e:
principal = ccache.principal() sys.exit("Must have Kerberos credentials to setup AD trusts on server: %s" % e.message)
except krbV.Krb5Error as e:
sys.exit("Must have Kerberos credentials to setup AD trusts on server")
try: try:
api.Backend.ldap2.connect(ccache) api.Backend.ldap2.connect()
except errors.ACIError as e: except errors.ACIError as e:
sys.exit("Outdated Kerberos credentials. Use kdestroy and kinit to update your ticket") sys.exit("Outdated Kerberos credentials. Use kdestroy and kinit to update your ticket")
except errors.DatabaseError as e: except errors.DatabaseError as e:
sys.exit("Cannot connect to the LDAP database. Please check if IPA is running") sys.exit("Cannot connect to the LDAP database. Please check if IPA is running")
try: 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'] group = api.Command.group_show(u'admins')['result']
if not (user['uid'][0] in group['member_user'] and if not (user['uid'][0] in group['member_user'] and
group['cn'][0] in user['memberof_group']): group['cn'][0] in user['memberof_group']):

View File

@ -22,12 +22,11 @@
import sys import sys
import os import os
import krbV
from ipapython.ipa_log_manager import * from ipapython.ipa_log_manager import *
from ipaserver.install import (replication, installutils, bindinstance, from ipaserver.install import (replication, installutils, bindinstance,
cainstance, certs) cainstance, certs)
from ipalib import api, errors, util from ipalib import api, errors
from ipalib.constants import CACERT from ipalib.constants import CACERT
from ipapython import ipautil, ipaldap, version, dogtag from ipapython import ipautil, ipaldap, version, dogtag
from ipapython.dn import DN from ipapython.dn import DN
@ -407,7 +406,7 @@ def main():
api.finalize() api.finalize()
dirman_passwd = None dirman_passwd = None
realm = krbV.default_context().default_realm realm = api.env.realm
if options.host: if options.host:
host = options.host host = options.host

View File

@ -20,7 +20,7 @@
import sys import sys
import os import os
import re, krbV import re
import traceback import traceback
from urllib2 import urlparse from urllib2 import urlparse
import ldap import ldap
@ -1379,7 +1379,7 @@ def main():
api.finalize() api.finalize()
dirman_passwd = None dirman_passwd = None
realm = krbV.default_context().default_realm realm = api.env.realm
if options.host: if options.host:
host = options.host host = options.host
@ -1404,8 +1404,7 @@ def main():
api.Backend.ldap2.connect(bind_dn=DN(('cn', 'Directory Manager')), api.Backend.ldap2.connect(bind_dn=DN(('cn', 'Directory Manager')),
bind_pw=options.dirman_passwd) bind_pw=options.dirman_passwd)
else: else:
ccache = krbV.default_context().default_ccache() api.Backend.ldap2.connect()
api.Backend.ldap2.connect(ccache=ccache)
if args[0] == "list": if args[0] == "list":
replica = None replica = None

View File

@ -9,7 +9,7 @@ URL: http://www.freeipa.org
Source0: %{name}-%{version}.tgz Source0: %{name}-%{version}.tgz
BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-root-%(%{__id_u} -n) 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()")} %{!?python_sitelib: %define python_sitelib %(%{__python} -c "from distutils.sysconfig import get_python_lib; print get_python_lib()")}

View File

@ -26,7 +26,7 @@ import os
import urlparse import urlparse
import time import time
import tempfile import tempfile
from krbV import Krb5Error import gssapi
import SSSDConfig import SSSDConfig
@ -427,15 +427,14 @@ def main():
print "Location: %s" % options.location print "Location: %s" % options.location
root_logger.debug('Using automount location %s' % options.location) root_logger.debug('Using automount location %s' % options.location)
# Verify that the location is valid ccache_dir = tempfile.mkdtemp()
(ccache_fd, ccache_name) = tempfile.mkstemp() ccache_name = os.path.join(ccache_dir, 'ccache')
os.close(ccache_fd)
try: try:
try: try:
host_princ = str('host/%s@%s' % (api.env.host, api.env.realm)) host_princ = str('host/%s@%s' % (api.env.host, api.env.realm))
ipautil.kinit_keytab(host_princ, paths.KRB5_KEYTAB, ccache_name) ipautil.kinit_keytab(host_princ, paths.KRB5_KEYTAB, ccache_name)
os.environ['KRB5CCNAME'] = 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) sys.exit("Failed to obtain host TGT: %s" % e)
# Now we have a TGT, connect to IPA # Now we have a TGT, connect to IPA
try: try:
@ -457,6 +456,7 @@ def main():
sys.exit("Cannot connect to the server due to generic error: %s" % str(e)) sys.exit("Cannot connect to the server due to generic error: %s" % str(e))
finally: finally:
os.remove(ccache_name) 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): if not options.unattended and not ipautil.user_input("Continue to configure the system with these values?", False):
sys.exit("Installation aborted") sys.exit("Installation aborted")

View File

@ -31,8 +31,8 @@ try:
from ConfigParser import RawConfigParser from ConfigParser import RawConfigParser
from optparse import SUPPRESS_HELP, OptionGroup, OptionValueError from optparse import SUPPRESS_HELP, OptionGroup, OptionValueError
import shutil import shutil
from krbV import Krb5Error
import dns import dns
import gssapi
import nss.nss as nss import nss.nss as nss
import SSSDConfig import SSSDConfig
@ -2618,7 +2618,7 @@ def install(options, env, fstore, statestore):
ccache_name, ccache_name,
config=krb_name, config=krb_name,
attempts=options.kinit_attempts) attempts=options.kinit_attempts)
except Krb5Error as e: except gssapi.exceptions.GSSError as e:
print_port_conf_info() print_port_conf_info()
root_logger.error("Kerberos authentication failed: %s" root_logger.error("Kerberos authentication failed: %s"
% e) % e)
@ -2698,7 +2698,7 @@ def install(options, env, fstore, statestore):
config=krb_name, config=krb_name,
attempts=options.kinit_attempts) attempts=options.kinit_attempts)
env['KRB5CCNAME'] = os.environ['KRB5CCNAME'] = CCACHE_FILE env['KRB5CCNAME'] = os.environ['KRB5CCNAME'] = CCACHE_FILE
except Krb5Error as e: except gssapi.exceptions.GSSError as e:
print_port_conf_info() print_port_conf_info()
root_logger.error("Failed to obtain host TGT: %s" % e) root_logger.error("Failed to obtain host TGT: %s" % e)
# failure to get ticket makes it impossible to login and bind # failure to get ticket makes it impossible to login and bind
@ -2745,7 +2745,7 @@ def install(options, env, fstore, statestore):
CCACHE_FILE, CCACHE_FILE,
attempts=options.kinit_attempts) attempts=options.kinit_attempts)
os.environ['KRB5CCNAME'] = CCACHE_FILE 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) root_logger.error("Failed to obtain host TGT: %s" % e)
return CLIENT_INSTALL_ERROR return CLIENT_INSTALL_ERROR
else: else:

View File

@ -16,25 +16,22 @@
# You should have received a copy of the GNU General Public License # You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>. # along with this program. If not, see <http://www.gnu.org/licenses/>.
import krbV
import time import time
import re import re
from ipapython.ipa_log_manager import * import gssapi
from ipalib import errors
#------------------------------------------------------------------------------- #-------------------------------------------------------------------------------
# Kerberos constants, should be defined in krbV, but aren't # Kerberos error codes
KRB5_GC_CACHED = 0x2 KRB5_CC_NOTFOUND = 2529639053 # Matching credential not found
KRB5_FCC_NOFILE = 2529639107 # No credentials cache found
# Kerberos error codes, should be defined in krbV, but aren't KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN = 2529638919 # Server not found in Kerberos database
KRB5_CC_NOTFOUND = -1765328243 # Matching credential not found KRB5KRB_AP_ERR_TKT_EXPIRED = 2529638944 # Ticket expired
KRB5_FCC_NOFILE = -1765328189 # No credentials cache found KRB5_FCC_PERM = 2529639106 # Credentials cache permissions incorrect
KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN = -1765328377 # Server not found in Kerberos database KRB5_CC_FORMAT = 2529639111 # Bad format in credentials cache
KRB5KRB_AP_ERR_TKT_EXPIRED = -1765328352 # Ticket expired KRB5_REALM_CANT_RESOLVE = 2529639132 # Cannot resolve network address for KDC in requested realm
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
krb_ticket_expiration_threshold = 60*5 # number of seconds to accmodate clock skew krb_ticket_expiration_threshold = 60*5 # number of seconds to accmodate clock skew
krb5_time_fmt = '%m/%d/%y %H:%M:%S' 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)) 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 Obtains GSSAPI credentials with given principal name from ccache. When no
tickets bound to it in a ccache (credentials cache). ccaches are principal name specified, it retrieves the default one for given
bound to a Kerberos user principal. This class opens a Kerberos credentials cache.
ccache and allows one to manipulate it. Most useful is the
extraction of ticket entries (cred's) in the ccache and the :parameters:
ability to examine their attributes. 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:
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)
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
''' '''
def __init__(self, ccache): try:
''' creds = get_credentials(name=name, ccache_name=ccache_name)
:parameters: if creds.lifetime > 0:
ccache return creds
The name of a Kerberos ccache used to hold Kerberos tickets. return None
:returns: except gssapi.exceptions.ExpiredCredentialsError:
`KRB5_CCache` object encapsulting the ccache. return None
'''
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:
host
ldap server
realm
kerberos realm
:returns:
UNIX timestamp value.
'''
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
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
self.debug('KRB5_CCache %s endtime=%s (%s)', self.ccache_str(), result, krb5_format_time(result))
return result

View File

@ -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)

View File

@ -17,7 +17,7 @@
# You should have received a copy of the GNU General Public License # You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>. # 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 Command
from ipalib import Str, Password from ipalib import Str, Password
from ipalib import _ from ipalib import _
@ -58,7 +58,7 @@ def get_current_password(principal):
current password is prompted for, otherwise return a fixed value to current password is prompted for, otherwise return a fixed value to
be ignored later. be ignored later.
""" """
current_principal = util.get_current_principal() current_principal = krb_utils.get_principal()
if current_principal == normalize_principal(principal): if current_principal == normalize_principal(principal):
return None return None
else: else:
@ -74,7 +74,7 @@ class passwd(Command):
label=_('User name'), label=_('User name'),
primary_key=True, primary_key=True,
autofill=True, autofill=True,
default_from=lambda: util.get_current_principal(), default_from=lambda: krb_utils.get_principal(),
normalizer=lambda value: normalize_principal(value), normalizer=lambda value: normalize_principal(value),
), ),
Password('password', Password('password',

View File

@ -34,7 +34,6 @@ from cryptography.hazmat.primitives.serialization import load_pem_public_key,\
load_pem_private_key load_pem_private_key
import nss.nss as nss import nss.nss as nss
import krbV
from ipalib.frontend import Command, Object, Local from ipalib.frontend import Command, Object, Local
from ipalib import api, errors from ipalib import api, errors
@ -640,7 +639,7 @@ class vault_add(PKQuery, Local):
else: else:
backend = self.api.Backend.rpcclient backend = self.api.Backend.rpcclient
if not backend.isconnected(): if not backend.isconnected():
backend.connect(ccache=krbV.default_context().default_ccache()) backend.connect()
if vault_type == u'standard': if vault_type == u'standard':
@ -1239,7 +1238,7 @@ class vault_archive(PKQuery, Local):
else: else:
backend = self.api.Backend.rpcclient backend = self.api.Backend.rpcclient
if not backend.isconnected(): if not backend.isconnected():
backend.connect(ccache=krbV.default_context().default_ccache()) backend.connect()
# retrieve vault info # retrieve vault info
vault = self.api.Command.vault_show(*args, **options)['result'] vault = self.api.Command.vault_show(*args, **options)['result']
@ -1508,7 +1507,7 @@ class vault_retrieve(PKQuery, Local):
else: else:
backend = self.api.Backend.rpcclient backend = self.api.Backend.rpcclient
if not backend.isconnected(): if not backend.isconnected():
backend.connect(ccache=krbV.default_context().default_ccache()) backend.connect()
# retrieve vault info # retrieve vault info
vault = self.api.Command.vault_show(*args, **options)['result'] vault = self.api.Command.vault_show(*args, **options)['result']

View File

@ -55,7 +55,6 @@ from ipalib.errors import (public_errors, UnknownError, NetworkError,
KerberosError, XMLRPCMarshallError, JSONError, ConversionError) KerberosError, XMLRPCMarshallError, JSONError, ConversionError)
from ipalib import errors, capabilities from ipalib import errors, capabilities
from ipalib.request import context, Connection from ipalib.request import context, Connection
from ipalib.util import get_current_principal
from ipapython.ipa_log_manager import root_logger from ipapython.ipa_log_manager import root_logger
from ipapython import ipautil from ipapython import ipautil
from ipapython import kernel_keyring from ipapython import kernel_keyring
@ -66,7 +65,8 @@ from ipalib.text import _
import ipapython.nsslib import ipapython.nsslib
from ipapython.nsslib import NSSHTTPS, NSSConnection from ipapython.nsslib import NSSHTTPS, NSSConnection
from ipalib.krb_utils import KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN, KRB5KRB_AP_ERR_TKT_EXPIRED, \ 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 ipapython.dn import DN
from ipalib.capabilities import VERSION_WITHOUT_CAPABILITIES from ipalib.capabilities import VERSION_WITHOUT_CAPABILITIES
from ipalib import api from ipalib import api
@ -518,10 +518,7 @@ class KerbTransport(SSLTransport):
self._sec_context = None self._sec_context = None
def _handle_exception(self, e, service=None): def _handle_exception(self, e, service=None):
# kerberos library coerced error codes to signed, gssapi uses unsigned
minor = e.min_code minor = e.min_code
if minor & (1 << 31):
minor -= 1 << 32
if minor == KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN: if minor == KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN:
raise errors.ServiceError(service=service) raise errors.ServiceError(service=service)
elif minor == KRB5_FCC_NOFILE: elif minor == KRB5_FCC_NOFILE:
@ -835,7 +832,7 @@ class RPCClient(Connectible):
delegate=False, nss_dir=None): delegate=False, nss_dir=None):
try: try:
rpc_uri = self.env[self.env_rpc_uri_key] rpc_uri = self.env[self.env_rpc_uri_key]
principal = get_current_principal() principal = get_principal()
setattr(context, 'principal', principal) setattr(context, 'principal', principal)
# We have a session cookie, try using the session URI to see if it # We have a session cookie, try using the session URI to see if it
# is still valid # is still valid

View File

@ -61,18 +61,6 @@ def json_serialize(obj):
return '' return ''
return json_serialize(obj.__json__()) 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): def validate_host_dns(log, fqdn):
""" """
See if the hostname has a DNS A/AAAA record. See if the hostname has a DNS A/AAAA record.

View File

@ -176,17 +176,6 @@ def __parse_config(discover_server = True):
def __discover_config(discover_server = True): def __discover_config(discover_server = True):
servers = [] servers = []
try: 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: if not config.default_domain:
# try once with REALM -> domain # try once with REALM -> domain
domain = str(config.default_realm).lower() domain = str(config.default_realm).lower()

View File

@ -34,7 +34,7 @@ import xmlrpclib
import datetime import datetime
import netaddr import netaddr
import time import time
import krbV import gssapi
import pwd import pwd
import grp import grp
from dns import resolver, rdatatype from dns import resolver, rdatatype
@ -54,6 +54,11 @@ GEN_PWD_LEN = 12
IPA_BASEDN_INFO = 'ipa v2.0' 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: try:
from subprocess import CalledProcessError from subprocess import CalledProcessError
except ImportError: 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 The optional parameter 'attempts' specifies how many times the credential
initialization should be attempted in case of non-responsive KDC. initialization should be attempted in case of non-responsive KDC.
""" """
errors_to_retry = {krbV.KRB5KDC_ERR_SVC_UNAVAILABLE, errors_to_retry = {KRB5KDC_ERR_SVC_UNAVAILABLE,
krbV.KRB5_KDC_UNREACH} KRB5_KDC_UNREACH}
root_logger.debug("Initializing principal %s using keytab %s" root_logger.debug("Initializing principal %s using keytab %s"
% (principal, keytab)) % (principal, keytab))
root_logger.debug("using ccache %s" % ccache_name) root_logger.debug("using ccache %s" % ccache_name)
@ -1218,18 +1223,15 @@ def kinit_keytab(principal, keytab, ccache_name, config=None, attempts=1):
else: else:
os.environ.pop('KRB5_CONFIG', None) os.environ.pop('KRB5_CONFIG', None)
try: try:
krbcontext = krbV.default_context() name = gssapi.Name(principal, gssapi.NameType.kerberos_principal)
ktab = krbV.Keytab(name=keytab, context=krbcontext) store = {'ccache': ccache_name,
princ = krbV.Principal(name=principal, context=krbcontext) 'client_keytab': keytab}
ccache = krbV.CCache(name=ccache_name, context=krbcontext, cred = gssapi.Credentials(name=name, store=store, usage='initiate')
primary_principal=princ)
ccache.init(princ)
ccache.init_creds_keytab(keytab=ktab, principal=princ)
root_logger.debug("Attempt %d/%d: success" root_logger.debug("Attempt %d/%d: success"
% (attempt, attempts)) % (attempt, attempts))
return return cred
except krbV.Krb5Error as e: except gssapi.exceptions.GSSError as e:
if e.args[0] not in errors_to_retry: if e.min_code not in errors_to_retry:
raise raise
root_logger.debug("Attempt %d/%d: failed: %s" root_logger.debug("Attempt %d/%d: failed: %s"
% (attempt, attempts, e)) % (attempt, attempts, e))

View File

@ -23,7 +23,7 @@ from optparse import OptionGroup
import base64 import base64
from nss import nss from nss import nss
from nss.error import NSPRError from nss.error import NSPRError
import krbV import gssapi
from ipapython import admintool, certmonger, ipautil from ipapython import admintool, certmonger, ipautil
from ipapython.dn import DN from ipapython.dn import DN
@ -126,9 +126,8 @@ class CACertManage(admintool.AdminTool):
password = self.options.password password = self.options.password
if not password: if not password:
try: try:
ccache = krbV.default_context().default_ccache() conn.connect()
conn.connect(ccache=ccache) except (gssapi.exceptions.GSSError, errors.ACIError):
except (krbV.Krb5Error, errors.ACIError):
pass pass
else: else:
return conn return conn

View File

@ -26,8 +26,6 @@
import os import os
import sys import sys
import krbV
from ipalib import api from ipalib import api
from ipapython import ipautil, admintool from ipapython import ipautil, admintool
from ipaplatform.paths import paths from ipaplatform.paths import paths
@ -100,7 +98,7 @@ class LDAPUpdater_Upgrade(LDAPUpdater):
super(LDAPUpdater_Upgrade, self).run() super(LDAPUpdater_Upgrade, self).run()
options = self.options options = self.options
realm = krbV.default_context().default_realm realm = api.env.realm
upgrade = IPAUpgrade(realm, self.files, upgrade = IPAUpgrade(realm, self.files,
schema_files=options.schema_files) schema_files=options.schema_files)

View File

@ -30,7 +30,7 @@ from lxml import etree
import dateutil.parser import dateutil.parser
import dateutil.tz import dateutil.tz
import nss.nss as nss import nss.nss as nss
import krbV import gssapi
from ipapython import admintool from ipapython import admintool
from ipalib import api, errors from ipalib import api, errors
@ -509,9 +509,8 @@ class OTPTokenImport(admintool.AdminTool):
conn = ldap2(api) conn = ldap2(api)
try: try:
ccache = krbV.default_context().default_ccache() conn.connect()
conn.connect(ccache=ccache) except (gssapi.exceptions.GSSError, errors.ACIError):
except (krbV.Krb5Error, errors.ACIError):
raise admintool.ScriptError("Unable to connect to LDAP! Did you kinit?") raise admintool.ScriptError("Unable to connect to LDAP! Did you kinit?")
try: try:

View File

@ -17,7 +17,7 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>. # along with this program. If not, see <http://www.gnu.org/licenses/>.
# #
import krbV import gssapi
import sys import sys
from ipalib import api from ipalib import api
@ -321,12 +321,10 @@ class WinsyncMigrate(admintool.AdminTool):
# Setup LDAP connection # Setup LDAP connection
try: try:
ctx = krbV.default_context() api.Backend.ldap2.connect()
ccache = ctx.default_ccache()
api.Backend.ldap2.connect(ccache)
cls.ldap = api.Backend.ldap2 cls.ldap = api.Backend.ldap2
except krbV.Krb5Error as e: except gssapi.exceptions.GSSError as e:
sys.exit("Must have Kerberos credentials to migrate Winsync users.") sys.exit("Must have Kerberos credentials to migrate Winsync users. Error: %s" % e)
except errors.ACIError as e: except errors.ACIError as e:
sys.exit("Outdated Kerberos credentials. Use kdestroy and kinit to update your ticket.") sys.exit("Outdated Kerberos credentials. Use kdestroy and kinit to update your ticket.")
except errors.DatabaseError as e: except errors.DatabaseError as e:

View File

@ -32,7 +32,6 @@ import pwd
import fnmatch import fnmatch
import re import re
import krbV
import ldap import ldap
from ipaserver.install import installutils from ipaserver.install import installutils
@ -272,13 +271,8 @@ class LDAPUpdate:
if sub_dict.get("REALM"): if sub_dict.get("REALM"):
self.realm = sub_dict["REALM"] self.realm = sub_dict["REALM"]
else: else:
krbctx = krbV.default_context() self.realm = api.env.realm
try: suffix = ipautil.realm_to_suffix(self.realm) if self.realm else None
self.realm = krbctx.default_realm
suffix = ipautil.realm_to_suffix(self.realm)
except krbV.Krb5Error:
self.realm = None
suffix = None
if suffix is not None: if suffix is not None:
assert isinstance(suffix, DN) assert isinstance(suffix, DN)

View File

@ -20,9 +20,9 @@
import pprint import pprint
import ldap.schema import ldap.schema
import krbV
import ipapython.version import ipapython.version
from ipalib import api
from ipapython.ipa_log_manager import log_mgr from ipapython.ipa_log_manager import log_mgr
from ipapython.dn import DN from ipapython.dn import DN
from ipaserver.install.ldapupdate import connect 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] SCHEMA_ELEMENT_CLASSES_KEYS = [x[0] for x in SCHEMA_ELEMENT_CLASSES]
conn = connect(ldapi=ldapi, dm_password=dm_password, conn = connect(ldapi=ldapi, dm_password=dm_password,
realm=krbV.default_context().default_realm, realm=api.env.realm,
fqdn=installutils.get_fqdn()) fqdn=installutils.get_fqdn())
old_schema = conn.schema old_schema = conn.schema

View File

@ -9,7 +9,6 @@ import pwd
import fileinput import fileinput
import ConfigParser import ConfigParser
import sys import sys
import krbV
from ipalib import api from ipalib import api
import SSSDConfig import SSSDConfig
@ -1567,7 +1566,7 @@ def upgrade_check(options):
def upgrade(): def upgrade():
realm = krbV.default_context().default_realm realm = api.env.realm
schema_files = [os.path.join(ipautil.SHARE_DIR, f) for f schema_files = [os.path.join(ipautil.SHARE_DIR, f) for f
in dsinstance.ALL_SCHEMA_FILES] in dsinstance.ALL_SCHEMA_FILES]
data_upgrade = IPAUpgrade(realm, schema_files=schema_files) data_upgrade = IPAUpgrade(realm, schema_files=schema_files)

View File

@ -21,8 +21,6 @@
Joining an IPA domain Joining an IPA domain
""" """
import krbV
from ipalib import api from ipalib import api
from ipalib import Command, Str from ipalib import Command, Str
from ipalib import errors from ipalib import errors
@ -30,15 +28,6 @@ from ipalib import _
from ipaserver.install import installutils 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): def validate_host(ugettext, cn):
""" """
Require at least one dot in the hostname (to support localhost.localdomain) Require at least one dot in the hostname (to support localhost.localdomain)
@ -66,7 +55,7 @@ class join(Command):
takes_options = ( takes_options = (
Str('realm', Str('realm',
doc=_("The IPA realm"), doc=_("The IPA realm"),
default_from=lambda: get_realm(), default_from=lambda: api.env.realm,
autofill=True, autofill=True,
), ),
Str('nshardwareplatform?', Str('nshardwareplatform?',

View File

@ -30,11 +30,12 @@ Backend plugin for LDAP.
import os import os
import pwd import pwd
import krbV
import ldap as _ldap import ldap as _ldap
from ipalib import krb_utils
from ipapython.dn import DN from ipapython.dn import DN
from ipapython.ipaldap import SASL_GSSAPI, LDAPClient from ipapython.ipaldap import (LDAPClient, AUTOBIND_AUTO, AUTOBIND_ENABLED,
AUTOBIND_DISABLED)
try: try:
@ -88,13 +89,14 @@ class ldap2(CrudBackend, LDAPClient):
def create_connection(self, ccache=None, bind_dn=None, bind_pw='', def create_connection(self, ccache=None, bind_dn=None, bind_pw='',
tls_cacertfile=None, tls_certfile=None, tls_keyfile=None, 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. Connect to LDAP server.
Keyword arguments: Keyword arguments:
ldapuri -- the LDAP server to connect to 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_dn -- dn used to bind to the server
bind_pw -- password used to bind to the server bind_pw -- password used to bind to the server
debug_level -- LDAP debug level option debug_level -- LDAP debug level option
@ -122,8 +124,6 @@ class ldap2(CrudBackend, LDAPClient):
conn = self._conn conn = self._conn
with self.error_handler(): 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) minssf = conn.get_option(_ldap.OPT_X_SASL_SSF_MIN)
maxssf = conn.get_option(_ldap.OPT_X_SASL_SSF_MAX) maxssf = conn.get_option(_ldap.OPT_X_SASL_SSF_MAX)
# Always connect with at least an SSF of 56, confidentiality # Always connect with at least an SSF of 56, confidentiality
@ -134,33 +134,37 @@ class ldap2(CrudBackend, LDAPClient):
if maxssf < minssf: if maxssf < minssf:
conn.set_option(_ldap.OPT_X_SASL_SSF_MAX, minssf) conn.set_option(_ldap.OPT_X_SASL_SSF_MAX, minssf)
if ccache is not None: ldapi = self.ldap_uri.startswith('ldapi://')
if isinstance(ccache, krbV.CCache):
principal = ccache.principal().name if bind_pw:
# Get a fully qualified CCACHE name (schema+name) self.simple_bind(bind_dn, bind_pw,
# As we do not use the krbV.CCache object later, server_controls=serverctrls,
# we can safely overwrite it client_controls=clientctrls)
ccache = "%(type)s:%(name)s" % dict(type=ccache.type, elif autobind != AUTOBIND_DISABLED and os.getegid() == 0 and ldapi:
name=ccache.name) try:
else: pw_name = pwd.getpwuid(os.geteuid()).pw_name
principal = krbV.CCache(name=ccache, self.external_bind(pw_name,
context=krbV.default_context()).principal().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:
os.environ['KRB5CCNAME'] = ccache
principal = krb_utils.get_principal(ccache_name=ccache)
os.environ['KRB5CCNAME'] = ccache
self.gssapi_bind(server_controls=serverctrls, self.gssapi_bind(server_controls=serverctrls,
client_controls=clientctrls) client_controls=clientctrls)
setattr(context, 'principal', principal) 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,
server_controls=serverctrls,
client_controls=clientctrls)
else:
self.simple_bind(bind_dn, bind_pw,
server_controls=serverctrls,
client_controls=clientctrls)
return conn return conn

View File

@ -30,7 +30,8 @@ import datetime
import urlparse import urlparse
import json import json
import traceback import traceback
from krbV import Krb5Error import gssapi
import time
import ldap.controls import ldap.controls
from pyasn1.type import univ, namedtype from pyasn1.type import univ, namedtype
@ -54,8 +55,8 @@ from ipalib.session import (
default_max_session_duration, krbccache_dir, krbccache_prefix) default_max_session_duration, krbccache_dir, krbccache_prefix)
from ipalib.backend import Backend from ipalib.backend import Backend
from ipalib.krb_utils import ( from ipalib.krb_utils import (
KRB5_CCache, krb_ticket_expiration_threshold, krb5_format_principal_name, krb_ticket_expiration_threshold, krb5_format_principal_name,
krb5_format_service_principal_name) krb5_format_service_principal_name, get_credentials, get_credentials_if_valid)
from ipapython import ipautil from ipapython import ipautil
from ipaplatform.paths import paths from ipaplatform.paths import paths
from ipapython.version import VERSION from ipapython.version import VERSION
@ -593,8 +594,8 @@ class KerberosSession(object):
session_data['ccache_data'] = load_ccache_data(ccache_name) session_data['ccache_data'] = load_ccache_data(ccache_name)
# Set when the session will expire # Set when the session will expire
cc = KRB5_CCache(ccache_name) creds = get_credentials(ccache_name=ccache_name)
endtime = cc.endtime(self.api.env.host, self.api.env.realm) endtime = creds.lifetime + time.time()
self.update_session_expiration(session_data, endtime) self.update_session_expiration(session_data, endtime)
# Store the session data now that it's been updated with the ccache # 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) ipa_ccache_name = bind_ipa_ccache(ccache_data)
# Redirect to login if Kerberos credentials are expired # Redirect to login if Kerberos credentials are expired
cc = KRB5_CCache(ipa_ccache_name) creds = get_credentials_if_valid(ccache_name=ipa_ccache_name)
if not cc.valid(self.api.env.host, self.api.env.realm): if not creds:
self.debug('ccache expired, deleting session, need login') self.debug('ccache expired, deleting session, need login')
# The request is finished with the ccache, destroy it. # The request is finished with the ccache, destroy it.
release_ipa_ccache(ipa_ccache_name) release_ipa_ccache(ipa_ccache_name)
return self.need_login(start_response) return self.need_login(start_response)
# Update the session expiration based on the Kerberos expiration # 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) self.update_session_expiration(session_data, endtime)
# Store the session data in the per-thread context # Store the session data in the per-thread context
@ -962,7 +963,7 @@ class login_password(Backend, KerberosSession, HTTP_Status):
try: try:
ipautil.kinit_keytab(armor_principal, paths.IPA_KEYTAB, armor_path) ipautil.kinit_keytab(armor_principal, paths.IPA_KEYTAB, armor_path)
except Krb5Error as e: except gssapi.exceptions.GSSError as e:
raise CCacheError(str(e)) raise CCacheError(str(e))
# Format the user as a kerberos principal # Format the user as a kerberos principal
@ -1229,15 +1230,15 @@ class xmlserver_session(xmlserver, KerberosSession):
ipa_ccache_name = bind_ipa_ccache(ccache_data) ipa_ccache_name = bind_ipa_ccache(ccache_data)
# Redirect to /ipa/xml if Kerberos credentials are expired # Redirect to /ipa/xml if Kerberos credentials are expired
cc = KRB5_CCache(ipa_ccache_name) creds = get_credentials_if_valid(ccache_name=ipa_ccache_name)
if not cc.valid(self.api.env.host, self.api.env.realm): if not creds:
self.debug('xmlserver_session.__call_: ccache expired, deleting session, need login') self.debug('xmlserver_session.__call_: ccache expired, deleting session, need login')
# The request is finished with the ccache, destroy it. # The request is finished with the ccache, destroy it.
release_ipa_ccache(ipa_ccache_name) release_ipa_ccache(ipa_ccache_name)
return self.need_login(start_response) return self.need_login(start_response)
# Update the session expiration based on the Kerberos expiration # 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) self.update_session_expiration(session_data, endtime)
# Store the session data in the per-thread context # Store the session data in the per-thread context

View File

@ -22,7 +22,6 @@ Base class for all cmdline tests
""" """
import nose import nose
import krbV
import distutils.spawn import distutils.spawn
import os import os
@ -33,11 +32,9 @@ from ipatests.test_xmlrpc.xmlrpc_test import XMLRPC_test
from ipaserver.plugins.ldap2 import ldap2 from ipaserver.plugins.ldap2 import ldap2
# See if our LDAP server is up and we can talk to it over GSSAPI # See if our LDAP server is up and we can talk to it over GSSAPI
ccache = krbV.default_context().default_ccache()
try: try:
conn = ldap2(api) conn = ldap2(api)
conn.connect(ccache=ccache) conn.connect()
conn.disconnect() conn.disconnect()
server_available = True server_available = True
except errors.DatabaseError: except errors.DatabaseError:

View File

@ -26,10 +26,10 @@ from cmdline import cmdline_test
from ipalib import api from ipalib import api
from ipalib import errors from ipalib import errors
import tempfile import tempfile
from ipapython import ipautil from ipapython import ipautil, ipaldap
import nose import nose
import tempfile import tempfile
import krbV import gssapi
from ipaserver.plugins.ldap2 import ldap2 from ipaserver.plugins.ldap2 import ldap2
from ipapython.dn import DN from ipapython.dn import DN
@ -37,21 +37,18 @@ def use_keytab(principal, keytab):
try: try:
tmpdir = tempfile.mkdtemp(prefix = "tmp-") tmpdir = tempfile.mkdtemp(prefix = "tmp-")
ccache_file = 'FILE:%s/ccache' % tmpdir ccache_file = 'FILE:%s/ccache' % tmpdir
krbcontext = krbV.default_context() name = gssapi.Name(principal, gssapi.NameType.kerberos_principal)
principal = str(principal) store = {'ccache': ccache_file,
keytab = krbV.Keytab(name=keytab, context=krbcontext) 'client_keytab': keytab}
principal = krbV.Principal(name=principal, context=krbcontext)
os.environ['KRB5CCNAME'] = ccache_file os.environ['KRB5CCNAME'] = ccache_file
ccache = krbV.CCache(name=ccache_file, context=krbcontext, primary_principal=principal) gssapi.Credentials(name=name, usage='initiate', store=store)
ccache.init(principal)
ccache.init_creds_keytab(keytab=keytab, principal=principal)
conn = ldap2(api) conn = ldap2(api)
conn.connect(ccache=ccache) conn.connect(autobind=ipaldap.AUTOBIND_DISABLED)
conn.disconnect() conn.disconnect()
except krbV.Krb5Error as e: except gssapi.exceptions.GSSError as e:
raise StandardError('Unable to bind to LDAP. Error initializing principal %s in %s: %s' % (principal.name, keytab, str(e))) raise StandardError('Unable to bind to LDAP. Error initializing principal %s in %s: %s' % (principal, keytab, str(e)))
finally: finally:
del os.environ['KRB5CCNAME'] os.environ.pop('KRB5CCNAME', None)
if tmpdir: if tmpdir:
shutil.rmtree(tmpdir) shutil.rmtree(tmpdir)

View File

@ -34,7 +34,6 @@ try:
except ImportError: except ImportError:
have_ldap2 = False have_ldap2 = False
else: else:
import krbV
have_ldap2 = True have_ldap2 = True
_dns_zone_record = DNSName(u'@') _dns_zone_record = DNSName(u'@')
@ -402,7 +401,7 @@ def _get_nameservers_ldap(conn):
def get_nameservers(): def get_nameservers():
ldap = ldap2(api) ldap = ldap2(api)
ldap.connect(ccache=krbV.default_context().default_ccache()) ldap.connect()
nameservers = [normalize_zone(x) for x in _get_nameservers_ldap(ldap)] nameservers = [normalize_zone(x) for x in _get_nameservers_ldap(ldap)]
return nameservers return nameservers

View File

@ -22,7 +22,6 @@ Test the `ipalib/plugins/netgroup.py` module.
""" """
import nose import nose
import krbV
from ipalib import api from ipalib import api
from ipalib import errors 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 # Global so we can save the value between tests
netgroup_dn = None 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' netgroup1 = u'netgroup1'
netgroup2 = u'netgroup2' netgroup2 = u'netgroup2'
netgroup_single = u'a' 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 # # Do an LDAP query to the compat area and verify that the entry
# # is correct # # is correct
# conn = ldap2(api) # conn = ldap2(api)
# conn.connect(ccache=ccache) # conn.connect()
# try: # try:
# entries = conn.find_entries('cn=%s' % self.ng_cn, # entries = conn.find_entries('cn=%s' % self.ng_cn,
# base_dn='cn=ng,cn=compat,%s' % api.env.basedn) # base_dn='cn=ng,cn=compat,%s' % api.env.basedn)

View File

@ -37,7 +37,6 @@ try:
except ImportError: except ImportError:
have_ldap2 = False have_ldap2 = False
else: else:
import krbV
have_ldap2 = True have_ldap2 = True
permission1 = u'testperm' permission1 = u'testperm'
@ -3175,7 +3174,7 @@ class test_managed_permissions(Declarative):
def add_managed_permission(self): def add_managed_permission(self):
"""Add a managed permission and the corresponding ACI""" """Add a managed permission and the corresponding ACI"""
ldap = ldap2(api) ldap = ldap2(api)
ldap.connect(ccache=krbV.default_context().default_ccache()) ldap.connect()
result = api.Command.permission_add(permission1, type=u'user', result = api.Command.permission_add(permission1, type=u'user',
ipapermright=u'write', ipapermright=u'write',

View File

@ -37,13 +37,27 @@ from paste import httpserver
import paste.gzipper import paste.gzipper
from paste.urlmap import URLMap from paste.urlmap import URLMap
from ipalib import api 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): class KRBCheater(object):
def __init__(self, app): def __init__(self, app):
self.app = app self.app = app
self.url = app.url self.url = app.url
self.ccname = api.Backend.krb.default_ccname() self.ccname = get_default_ccache_name()
def __call__(self, environ, start_response): def __call__(self, environ, start_response):
environ['KRB5CCNAME'] = self.ccname environ['KRB5CCNAME'] = self.ccname

View File

@ -50,7 +50,6 @@ class IPATypeChecker(TypeChecker):
# 'class or module': ['generated', 'properties'] # 'class or module': ['generated', 'properties']
ignore = { ignore = {
# Python standard library & 3rd party classes # Python standard library & 3rd party classes
'krbV.Principal': ['name'],
'socket._socketobject': ['sendall'], 'socket._socketobject': ['sendall'],
# should be 'subprocess.Popen' # should be 'subprocess.Popen'
'.Popen': ['stdin', 'stdout', 'stderr', 'pid', 'returncode', 'poll', '.Popen': ['stdin', 'stdout', 'stderr', 'pid', 'returncode', 'poll',
@ -68,7 +67,6 @@ class IPATypeChecker(TypeChecker):
'ipalib.base.NameSpace': ['add', 'mod', 'del', 'show', 'find'], 'ipalib.base.NameSpace': ['add', 'mod', 'del', 'show', 'find'],
'ipalib.cli.Collector': ['__options'], 'ipalib.cli.Collector': ['__options'],
'ipalib.config.Env': ['*'], 'ipalib.config.Env': ['*'],
'ipalib.krb_utils.KRB5_CCache': LOGGING_ATTRS,
'ipalib.parameters.Param': ['cli_name', 'cli_short_name', 'label', 'ipalib.parameters.Param': ['cli_name', 'cli_short_name', 'label',
'default', 'doc', 'required', 'multivalue', 'primary_key', 'default', 'doc', 'required', 'multivalue', 'primary_key',
'normalizer', 'default_from', 'autofill', 'query', 'attribute', 'normalizer', 'default_from', 'autofill', 'query', 'attribute',
@ -261,7 +259,7 @@ Errors were found during the static code check.
for mod in sorted(linter.missing): for mod in sorted(linter.missing):
print >> sys.stderr, " " + mod print >> sys.stderr, " " + mod
print >> sys.stderr, """ 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. python packages are installed.
""" """