summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorMichael Simacek <msimacek@redhat.com>2015-07-16 18:22:00 +0200
committerJan Cholasta <jcholast@redhat.com>2015-08-05 08:08:00 +0200
commitf0b4c4487ed77a3037cbbc46206d598c58f06bb1 (patch)
tree2c27ce8d917f3b23ff4e814a845c4bc9b6aa1e3c
parent3257ac6b876e9e62cae58060c96c525ff0df1ae3 (diff)
downloadfreeipa-f0b4c4487ed77a3037cbbc46206d598c58f06bb1.zip
freeipa-f0b4c4487ed77a3037cbbc46206d598c58f06bb1.tar.gz
freeipa-f0b4c4487ed77a3037cbbc46206d598c58f06bb1.tar.xz
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>
-rw-r--r--BUILD.txt2
-rw-r--r--freeipa.spec.in4
-rw-r--r--ipalib/rpc.py112
-rw-r--r--ipalib/util.py13
-rw-r--r--ipapython/ipautil.py17
5 files changed, 95 insertions, 53 deletions
diff --git a/BUILD.txt b/BUILD.txt
index 6a28beb..53012b1 100644
--- a/BUILD.txt
+++ b/BUILD.txt
@@ -20,7 +20,7 @@ systemd-units samba-devel samba-python libwbclient-devel libtalloc-devel \
libtevent-devel nspr-devel nss-devel openssl-devel openldap-devel krb5-devel \
krb5-workstation libuuid-devel libcurl-devel xmlrpc-c-devel popt-devel \
autoconf automake m4 libtool gettext python-devel python-ldap \
-python-setuptools python-krbV python-nss python-netaddr python-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 \
diff --git a/freeipa.spec.in b/freeipa.spec.in
index 0351952..57d3d26 100644
--- a/freeipa.spec.in
+++ b/freeipa.spec.in
@@ -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
diff --git a/ipalib/rpc.py b/ipalib/rpc.py
index 466b49a..4176bbd 100644
--- a/ipalib/rpc.py
+++ b/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):
diff --git a/ipalib/util.py b/ipalib/util.py
index 649a487..5a67014 100644
--- a/ipalib/util.py
+++ b/ipalib/util.py
@@ -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()
diff --git a/ipapython/ipautil.py b/ipapython/ipautil.py
index 88e8970..05a7eeb 100644
--- a/ipapython/ipautil.py
+++ b/ipapython/ipautil.py
@@ -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