mirror of
https://salsa.debian.org/freeipa-team/freeipa.git
synced 2025-01-26 16:16:31 -06:00
Port from python-kerberos to python-gssapi
kerberos library doesn't support Python 3 and probably never will. python-gssapi library is Python 3 compatible. https://fedorahosted.org/freeipa/ticket/5147 Reviewed-By: Christian Heimes <cheimes@redhat.com> 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
3257ac6b87
commit
f0b4c4487e
@ -20,7 +20,7 @@ systemd-units samba-devel samba-python libwbclient-devel libtalloc-devel \
|
||||
libtevent-devel nspr-devel nss-devel openssl-devel openldap-devel krb5-devel \
|
||||
krb5-workstation libuuid-devel libcurl-devel xmlrpc-c-devel popt-devel \
|
||||
autoconf automake m4 libtool gettext python-devel python-ldap \
|
||||
python-setuptools python-krbV python-nss python-netaddr python-kerberos \
|
||||
python-setuptools python-krbV python-nss python-netaddr python-gssapi \
|
||||
python-rhsm pyOpenSSL pylint python-polib libipa_hbac-python python-memcached \
|
||||
sssd python-lxml python-pyasn1 python-qrcode-core python-dns m2crypto \
|
||||
check libsss_idmap-devel libsss_nss_idmap-devel java-headless rhino \
|
||||
|
@ -72,7 +72,7 @@ BuildRequires: python-krbV
|
||||
BuildRequires: python-nss
|
||||
BuildRequires: python-cryptography
|
||||
BuildRequires: python-netaddr
|
||||
BuildRequires: python-kerberos >= 1.1-14
|
||||
BuildRequires: python-gssapi >= 1.1.1
|
||||
BuildRequires: python-rhsm
|
||||
BuildRequires: pyOpenSSL
|
||||
BuildRequires: pylint >= 1.0
|
||||
@ -303,7 +303,7 @@ IPA administrators.
|
||||
%package python
|
||||
Summary: Python libraries used by IPA
|
||||
Group: System Environment/Libraries
|
||||
Requires: python-kerberos >= 1.1-14
|
||||
Requires: python-gssapi >= 1.1.1
|
||||
Requires: gnupg
|
||||
Requires: iproute
|
||||
Requires: keyutils
|
||||
|
112
ipalib/rpc.py
112
ipalib/rpc.py
@ -44,7 +44,7 @@ from urllib2 import urlparse
|
||||
|
||||
from xmlrpclib import (Binary, Fault, DateTime, dumps, loads, ServerProxy,
|
||||
Transport, ProtocolError, MININT, MAXINT)
|
||||
import kerberos
|
||||
import gssapi
|
||||
from dns import resolver, rdatatype
|
||||
from dns.exception import DNSException
|
||||
from nss.error import NSPRError
|
||||
@ -510,24 +510,32 @@ class KerbTransport(SSLTransport):
|
||||
"""
|
||||
Handles Kerberos Negotiation authentication to an XML-RPC server.
|
||||
"""
|
||||
flags = kerberos.GSS_C_MUTUAL_FLAG | kerberos.GSS_C_SEQUENCE_FLAG
|
||||
flags = [gssapi.RequirementFlag.mutual_authentication,
|
||||
gssapi.RequirementFlag.out_of_sequence_detection]
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
SSLTransport.__init__(self, *args, **kwargs)
|
||||
self._sec_context = None
|
||||
|
||||
def _handle_exception(self, e, service=None):
|
||||
(major, minor) = ipautil.get_gsserror(e)
|
||||
if minor[1] == KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN:
|
||||
# kerberos library coerced error codes to signed, gssapi uses unsigned
|
||||
minor = e.min_code
|
||||
if minor & (1 << 31):
|
||||
minor -= 1 << 32
|
||||
if minor == KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN:
|
||||
raise errors.ServiceError(service=service)
|
||||
elif minor[1] == KRB5_FCC_NOFILE:
|
||||
elif minor == KRB5_FCC_NOFILE:
|
||||
raise errors.NoCCacheError()
|
||||
elif minor[1] == KRB5KRB_AP_ERR_TKT_EXPIRED:
|
||||
elif minor == KRB5KRB_AP_ERR_TKT_EXPIRED:
|
||||
raise errors.TicketExpired()
|
||||
elif minor[1] == KRB5_FCC_PERM:
|
||||
elif minor == KRB5_FCC_PERM:
|
||||
raise errors.BadCCachePerms()
|
||||
elif minor[1] == KRB5_CC_FORMAT:
|
||||
elif minor == KRB5_CC_FORMAT:
|
||||
raise errors.BadCCacheFormat()
|
||||
elif minor[1] == KRB5_REALM_CANT_RESOLVE:
|
||||
elif minor == KRB5_REALM_CANT_RESOLVE:
|
||||
raise errors.CannotResolveKDC()
|
||||
else:
|
||||
raise errors.KerberosError(major=major, minor=minor)
|
||||
raise errors.KerberosError(major=e.maj_code, minor=minor)
|
||||
|
||||
def get_host_info(self, host):
|
||||
"""
|
||||
@ -548,30 +556,83 @@ class KerbTransport(SSLTransport):
|
||||
service = "HTTP@" + host.split(':')[0]
|
||||
|
||||
try:
|
||||
(rc, vc) = kerberos.authGSSClientInit(service=service,
|
||||
gssflags=self.flags)
|
||||
except kerberos.GSSError, e:
|
||||
self._handle_exception(e)
|
||||
|
||||
try:
|
||||
kerberos.authGSSClientStep(vc, "")
|
||||
except kerberos.GSSError, e:
|
||||
name = gssapi.Name(service, gssapi.NameType.hostbased_service)
|
||||
self._sec_context = gssapi.SecurityContext(name=name, flags=self.flags)
|
||||
response = self._sec_context.step()
|
||||
except gssapi.exceptions.GSSError as e:
|
||||
self._handle_exception(e, service=service)
|
||||
|
||||
self._set_auth_header(extra_headers, response)
|
||||
|
||||
return (host, extra_headers, x509)
|
||||
|
||||
def _set_auth_header(self, extra_headers, token):
|
||||
for (h, v) in extra_headers:
|
||||
if h == 'Authorization':
|
||||
extra_headers.remove((h, v))
|
||||
break
|
||||
|
||||
extra_headers.append(
|
||||
('Authorization', 'negotiate %s' % kerberos.authGSSClientResponse(vc))
|
||||
)
|
||||
if token:
|
||||
extra_headers.append(
|
||||
('Authorization', 'negotiate %s' % base64.b64encode(token))
|
||||
)
|
||||
|
||||
return (host, extra_headers, x509)
|
||||
def _auth_complete(self, response):
|
||||
if self._sec_context:
|
||||
header = response.getheader('www-authenticate', '')
|
||||
token = None
|
||||
for field in header.split(','):
|
||||
k, _, v = field.strip().partition(' ')
|
||||
if k.lower() == 'negotiate':
|
||||
try:
|
||||
token = base64.b64decode(v)
|
||||
break
|
||||
# b64decode raises TypeError on invalid input
|
||||
except TypeError:
|
||||
pass
|
||||
if not token:
|
||||
raise KerberosError("No valid Negotiate header in server response")
|
||||
token = self._sec_context.step(token=token)
|
||||
if self._sec_context.complete:
|
||||
self._sec_context = None
|
||||
return True
|
||||
self._set_auth_header(self._extra_headers, token)
|
||||
return False
|
||||
return True
|
||||
|
||||
def single_request(self, host, handler, request_body, verbose=0):
|
||||
# Based on xmlrpclib.Transport.single_request
|
||||
try:
|
||||
return SSLTransport.single_request(self, host, handler, request_body, verbose)
|
||||
h = SSLTransport.make_connection(self, host)
|
||||
if verbose:
|
||||
h.set_debuglevel(1)
|
||||
|
||||
while True:
|
||||
self.send_request(h, handler, request_body)
|
||||
self.send_host(h, host)
|
||||
self.send_user_agent(h)
|
||||
self.send_content(h, request_body)
|
||||
|
||||
response = h.getresponse(buffering=True)
|
||||
if response.status != 200:
|
||||
if (response.getheader("content-length", 0)):
|
||||
response.read()
|
||||
|
||||
if response.status == 401:
|
||||
if not self._auth_complete(response):
|
||||
continue
|
||||
|
||||
raise ProtocolError(
|
||||
host + handler,
|
||||
response.status, response.reason,
|
||||
response.msg)
|
||||
|
||||
self.verbose = verbose
|
||||
if not self._auth_complete(response):
|
||||
continue
|
||||
return self.parse_response(response)
|
||||
except gssapi.exceptions.GSSError as e:
|
||||
self._handle_exception(e)
|
||||
finally:
|
||||
self.close()
|
||||
|
||||
@ -632,8 +693,9 @@ class DelegatedKerbTransport(KerbTransport):
|
||||
Handles Kerberos Negotiation authentication and TGT delegation to an
|
||||
XML-RPC server.
|
||||
"""
|
||||
flags = kerberos.GSS_C_DELEG_FLAG | kerberos.GSS_C_MUTUAL_FLAG | \
|
||||
kerberos.GSS_C_SEQUENCE_FLAG
|
||||
flags = [gssapi.RequirementFlag.delegate_to_peer,
|
||||
gssapi.RequirementFlag.mutual_authentication,
|
||||
gssapi.RequirementFlag.out_of_sequence_detection]
|
||||
|
||||
|
||||
class RPCClient(Connectible):
|
||||
|
@ -63,15 +63,12 @@ def json_serialize(obj):
|
||||
|
||||
def get_current_principal():
|
||||
try:
|
||||
import kerberos
|
||||
rc, vc = kerberos.authGSSClientInit("notempty")
|
||||
rc = kerberos.authGSSClientInquireCred(vc)
|
||||
username = kerberos.authGSSClientUserName(vc)
|
||||
kerberos.authGSSClientClean(vc)
|
||||
return unicode(username)
|
||||
import gssapi
|
||||
cred = gssapi.Credentials(usage='initiate')
|
||||
return unicode(cred.name)
|
||||
except ImportError:
|
||||
raise RuntimeError('python-kerberos is not available.')
|
||||
except kerberos.GSSError, e:
|
||||
raise RuntimeError('python-gssapi is not available.')
|
||||
except gssapi.exceptions.GSSError:
|
||||
#TODO: do a kinit?
|
||||
raise errors.CCacheError()
|
||||
|
||||
|
@ -783,23 +783,6 @@ def user_input(prompt, default = None, allow_empty = True):
|
||||
return ret
|
||||
|
||||
|
||||
def get_gsserror(e):
|
||||
"""
|
||||
A GSSError exception looks differently in python 2.4 than it does
|
||||
in python 2.5. Deal with it.
|
||||
"""
|
||||
|
||||
try:
|
||||
major = e[0]
|
||||
minor = e[1]
|
||||
except:
|
||||
major = e[0][0]
|
||||
minor = e[0][1]
|
||||
|
||||
return (major, minor)
|
||||
|
||||
|
||||
|
||||
def host_port_open(host, port, socket_type=socket.SOCK_STREAM, socket_timeout=None):
|
||||
for res in socket.getaddrinfo(host, port, socket.AF_UNSPEC, socket_type):
|
||||
af, socktype, proto, canonname, sa = res
|
||||
|
Loading…
Reference in New Issue
Block a user