mirror of
https://salsa.debian.org/freeipa-team/freeipa.git
synced 2025-02-25 18:55:28 -06:00
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:
parent
aebb72e1fb
commit
aad73fad60
@ -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 \
|
||||||
|
@ -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)
|
||||||
|
|
||||||
|
@ -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)
|
||||||
|
|
||||||
|
@ -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()
|
||||||
|
|
||||||
|
@ -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
|
||||||
|
@ -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
|
||||||
|
@ -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']):
|
||||||
|
@ -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
|
||||||
|
@ -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
|
||||||
|
@ -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()")}
|
||||||
|
|
||||||
|
@ -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")
|
||||||
|
@ -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:
|
||||||
|
@ -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
|
|
||||||
|
@ -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)
|
|
||||||
|
|
||||||
|
|
@ -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',
|
||||||
|
@ -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']
|
||||||
|
@ -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
|
||||||
|
@ -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.
|
||||||
|
@ -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()
|
||||||
|
@ -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))
|
||||||
|
@ -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
|
||||||
|
@ -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)
|
||||||
|
|
||||||
|
@ -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:
|
||||||
|
@ -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:
|
||||||
|
@ -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)
|
||||||
|
@ -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
|
||||||
|
@ -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)
|
||||||
|
@ -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?',
|
||||||
|
@ -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
|
||||||
|
|
||||||
|
@ -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
|
||||||
|
@ -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:
|
||||||
|
@ -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)
|
||||||
|
|
||||||
|
@ -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
|
||||||
|
|
||||||
|
@ -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)
|
||||||
|
@ -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',
|
||||||
|
@ -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
|
||||||
|
@ -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.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user