summaryrefslogtreecommitdiffstats
path: root/ipalib
diff options
context:
space:
mode:
Diffstat (limited to 'ipalib')
-rw-r--r--ipalib/krb_utils.py333
-rw-r--r--ipalib/plugins/kerberos.py125
-rw-r--r--ipalib/plugins/passwd.py6
-rw-r--r--ipalib/plugins/vault.py7
-rw-r--r--ipalib/rpc.py9
-rw-r--r--ipalib/util.py12
6 files changed, 77 insertions, 415 deletions
diff --git a/ipalib/krb_utils.py b/ipalib/krb_utils.py
index 19cd0ad79..db1cffc1e 100644
--- a/ipalib/krb_utils.py
+++ b/ipalib/krb_utils.py
@@ -16,25 +16,22 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
-import krbV
import time
import re
-from ipapython.ipa_log_manager import *
+import gssapi
-#-------------------------------------------------------------------------------
-
-# Kerberos constants, should be defined in krbV, but aren't
-KRB5_GC_CACHED = 0x2
+from ipalib import errors
-# Kerberos error codes, should be defined in krbV, but aren't
-KRB5_CC_NOTFOUND = -1765328243 # Matching credential not found
-KRB5_FCC_NOFILE = -1765328189 # No credentials cache found
-KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN = -1765328377 # Server not found in Kerberos database
-KRB5KRB_AP_ERR_TKT_EXPIRED = -1765328352 # Ticket expired
-KRB5_FCC_PERM = -1765328190 # Credentials cache permissions incorrect
-KRB5_CC_FORMAT = -1765328185 # Bad format in credentials cache
-KRB5_REALM_CANT_RESOLVE = -1765328164 # Cannot resolve network address for KDC in requested realm
+#-------------------------------------------------------------------------------
+# Kerberos error codes
+KRB5_CC_NOTFOUND = 2529639053 # Matching credential not found
+KRB5_FCC_NOFILE = 2529639107 # No credentials cache found
+KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN = 2529638919 # Server not found in Kerberos database
+KRB5KRB_AP_ERR_TKT_EXPIRED = 2529638944 # Ticket expired
+KRB5_FCC_PERM = 2529639106 # Credentials cache permissions incorrect
+KRB5_CC_FORMAT = 2529639111 # Bad format in credentials cache
+KRB5_REALM_CANT_RESOLVE = 2529639132 # Cannot resolve network address for KDC in requested realm
krb_ticket_expiration_threshold = 60*5 # number of seconds to accmodate clock skew
krb5_time_fmt = '%m/%d/%y %H:%M:%S'
@@ -136,260 +133,66 @@ def krb5_format_time(timestamp):
'''
return time.strftime(krb5_time_fmt, time.localtime(timestamp))
-class KRB5_CCache(object):
- '''
- Kerberos stores a TGT (Ticket Granting Ticket) and the service
- tickets bound to it in a ccache (credentials cache). ccaches are
- bound to a Kerberos user principal. This class opens a Kerberos
- ccache and allows one to manipulate it. Most useful is the
- extraction of ticket entries (cred's) in the ccache and the
- ability to examine their attributes.
+def get_credentials(name=None, ccache_name=None):
'''
+ Obtains GSSAPI credentials with given principal name from ccache. When no
+ principal name specified, it retrieves the default one for given
+ credentials cache.
- def __init__(self, ccache):
- '''
- :parameters:
- ccache
- The name of a Kerberos ccache used to hold Kerberos tickets.
- :returns:
- `KRB5_CCache` object encapsulting the ccache.
- '''
- log_mgr.get_logger(self, True)
- self.context = None
- self.scheme = None
- self.name = None
- self.ccache = None
- self.principal = None
-
- try:
- self.context = krbV.default_context()
- self.scheme, self.name = krb5_parse_ccache(ccache)
- self.ccache = krbV.CCache(name=str(ccache), context=self.context)
- self.principal = self.ccache.principal()
- except krbV.Krb5Error as e:
- error_code = e.args[0]
- message = e.args[1]
- if error_code == KRB5_FCC_NOFILE:
- raise ValueError('"%s", ccache="%s"' % (message, ccache))
- else:
- raise e
-
- def ccache_str(self):
- '''
- A Kerberos ccache is identified by a name comprised of a
- scheme and location component. This function returns that
- canonical name. See `krb5_parse_ccache()`
-
- :returns:
- The name of ccache with it's scheme and location components.
- '''
-
- return '%s:%s' % (self.scheme, self.name)
-
- def __str__(self):
- return 'cache="%s" principal="%s"' % (self.ccache_str(), self.principal.name)
-
- def get_credentials(self, principal):
- '''
- Given a Kerberos principal return the krbV credentials
- tuple describing the credential. If the principal does
- not exist in the ccache a KeyError is raised.
-
- :parameters:
- principal
- The Kerberos principal whose ticket is being retrieved.
- The principal may be either a string formatted as a
- Kerberos V5 principal or it may be a `krbV.Principal`
- object.
- :returns:
- A krbV credentials tuple. If the principal is not in the
- ccache a KeyError is raised.
-
- '''
-
- if isinstance(principal, krbV.Principal):
- krbV_principal = principal
- else:
- try:
- krbV_principal = krbV.Principal(str(principal), self.context)
- except Exception as e:
- self.error('could not create krbV principal from "%s", %s', principal, e)
- raise e
-
- creds_tuple = (self.principal,
- krbV_principal,
- (0, None), # keyblock: (enctype, contents)
- (0, 0, 0, 0), # times: (authtime, starttime, endtime, renew_till)
- 0,0, # is_skey, ticket_flags
- None, # addrlist
- None, # ticket_data
- None, # second_ticket_data
- None) # adlist
- try:
- cred = self.ccache.get_credentials(creds_tuple, KRB5_GC_CACHED)
- except krbV.Krb5Error as e:
- error_code = e.args[0]
- if error_code == KRB5_CC_NOTFOUND:
- raise KeyError('"%s" credential not found in "%s" ccache' % \
- (krbV_principal.name, self.ccache_str()))
- raise e
- except Exception as e:
- raise e
-
- return cred
-
- def get_credential_times(self, principal):
- '''
- Given a Kerberos principal return the ticket timestamps if the
- principal's ticket in the ccache is valid. If the principal
- does not exist in the ccache a KeyError is raised.
-
- The return credential time values are Unix timestamps in
- localtime.
-
- The returned timestamps are:
-
- authtime
- The time when the ticket was issued.
- starttime
- The time when the ticket becomes valid.
- endtime
- The time when the ticket expires.
- renew_till
- The time when the ticket becomes no longer renewable (if renewable).
-
- :parameters:
- principal
- The Kerberos principal whose ticket is being validated.
- The principal may be either a string formatted as a
- Kerberos V5 principal or it may be a `krbV.Principal`
- object.
- :returns:
- return authtime, starttime, endtime, renew_till
- '''
-
- if isinstance(principal, krbV.Principal):
- krbV_principal = principal
- else:
- try:
- krbV_principal = krbV.Principal(str(principal), self.context)
- except Exception as e:
- self.error('could not create krbV principal from "%s", %s', principal, e)
- raise e
-
- try:
- cred = self.get_credentials(krbV_principal)
- authtime, starttime, endtime, renew_till = cred[3]
-
- self.debug('get_credential_times: principal=%s, authtime=%s, starttime=%s, endtime=%s, renew_till=%s',
- krbV_principal.name,
- krb5_format_time(authtime), krb5_format_time(starttime),
- krb5_format_time(endtime), krb5_format_time(renew_till))
-
- return authtime, starttime, endtime, renew_till
-
- except KeyError as e:
- raise e
- except Exception as e:
- self.error('get_credential_times failed, principal="%s" error="%s"', krbV_principal.name, e)
- raise e
-
- def credential_is_valid(self, principal):
- '''
- Given a Kerberos principal return a boolean indicating if the
- principal's ticket in the ccache is valid. If the ticket is
- not in the ccache False is returned. If the ticket
- exists in the ccache it's validity is checked and returned.
-
- :parameters:
- principal
- The Kerberos principal whose ticket is being validated.
- The principal may be either a string formatted as a
- Kerberos V5 principal or it may be a `krbV.Principal`
- object.
- :returns:
- True if the principal's ticket exists and is valid. False if
- the ticket does not exist or if the ticket is not valid.
- '''
-
- try:
- authtime, starttime, endtime, renew_till = self.get_credential_times(principal)
- except KeyError as e:
- return False
- except Exception as e:
- self.error('credential_is_valid failed, principal="%s" error="%s"', principal, e)
- raise e
-
-
- now = time.time()
- if starttime > now:
- return False
- if endtime < now:
- return False
- return True
-
- def valid(self, host, realm):
- '''
- Test to see if ldap service ticket or the TGT is valid.
-
- :parameters:
- host
- ldap server
- realm
- kerberos realm
- :returns:
- True if either the ldap service ticket or the TGT is valid,
- False otherwise.
- '''
-
- try:
- principal = krb5_format_service_principal_name('HTTP', host, realm)
- valid = self.credential_is_valid(principal)
- if valid:
- return True
- except KeyError:
- pass
-
- try:
- principal = krb5_format_tgt_principal_name(realm)
- valid = self.credential_is_valid(principal)
- return valid
- except KeyError:
- return False
-
- def endtime(self, host, realm):
- '''
- Returns the minimum endtime for tickets of interest (ldap service or TGT).
+ :parameters:
+ name
+ gssapi.Name object specifying principal or None for the default
+ ccache_name
+ string specifying Kerberos credentials cache name or None for the
+ default
+ :returns:
+ gssapi.Credentials object
+ '''
+ store = None
+ if ccache_name:
+ store = {'ccache': ccache_name}
+ try:
+ return gssapi.Credentials(usage='initiate', name=name, store=store)
+ except gssapi.exceptions.GSSError as e:
+ if e.min_code == KRB5_FCC_NOFILE:
+ raise ValueError('"%s", ccache="%s"' % (e.message, ccache_name))
+ raise
+
+def get_principal(ccache_name=None):
+ '''
+ Gets default principal name from given credentials cache.
- :parameters:
- host
- ldap server
- realm
- kerberos realm
- :returns:
- UNIX timestamp value.
- '''
+ :parameters:
+ ccache_name
+ string specifying Kerberos credentials cache name or None for the
+ default
+ :returns:
+ Default principal name as string
+ '''
+ creds = get_credentials(ccache_name=ccache_name)
+ return unicode(creds.name)
- result = 0
- try:
- principal = krb5_format_service_principal_name('HTTP', host, realm)
- authtime, starttime, endtime, renew_till = self.get_credential_times(principal)
- if result:
- result = min(result, endtime)
- else:
- result = endtime
- except KeyError:
- pass
+def get_credentials_if_valid(name=None, ccache_name=None):
+ '''
+ Obtains GSSAPI credentials with principal name from ccache. When no
+ principal name specified, it retrieves the default one for given
+ credentials cache. When the credentials cannot be retrieved or aren't valid
+ it returns None.
- try:
- principal = krb5_format_tgt_principal_name(realm)
- authtime, starttime, endtime, renew_till = self.get_credential_times(principal)
- if result:
- result = min(result, endtime)
- else:
- result = endtime
- except KeyError:
- pass
+ :parameters:
+ name
+ gssapi.Name object specifying principal or None for the default
+ ccache_name
+ string specifying Kerberos credentials cache name or None for the
+ default
+ :returns:
+ gssapi.Credentials object or None if valid credentials weren't found
+ '''
- self.debug('KRB5_CCache %s endtime=%s (%s)', self.ccache_str(), result, krb5_format_time(result))
- return result
+ try:
+ creds = get_credentials(name=name, ccache_name=ccache_name)
+ if creds.lifetime > 0:
+ return creds
+ return None
+ except gssapi.exceptions.ExpiredCredentialsError:
+ return None
diff --git a/ipalib/plugins/kerberos.py b/ipalib/plugins/kerberos.py
deleted file mode 100644
index 3ed6d7671..000000000
--- a/ipalib/plugins/kerberos.py
+++ /dev/null
@@ -1,125 +0,0 @@
-# Authors:
-# Jason Gerard DeRose <jderose@redhat.com>
-#
-# Copyright (C) 2008 Red Hat
-# see file 'COPYING' for use and warranty information
-#
-# This program is free software; you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program. If not, see <http://www.gnu.org/licenses/>.
-
-"""
-Backend plugin for Kerberos.
-
-This wraps the python-kerberos and python-krbV bindings.
-"""
-
-import sys
-from ipalib import api
-from ipalib.backend import Backend
-from ipalib.plugable import Registry
-import krbV
-
-register = Registry()
-
-ENCODING = 'UTF-8'
-
-
-@register()
-class krb(Backend):
- """
- Kerberos backend plugin.
-
- This wraps the `krbV` bindings (and will eventually wrap the `kerberos`
- bindings also). Importantly, this plugin does correct Unicode
- encoding/decoding of values going-to/coming-from the bindings.
- """
-
- def __default_ccache(self):
- """
- Return the ``krbV.CCache`` for the default credential cache.
- """
- return krbV.default_context().default_ccache()
-
- def __default_principal(self):
- """
- Return the ``krb5.Principal`` for the default credential cache.
- """
- return self.__default_ccache().principal()
-
- def __get_ccache(self, ccname):
- """
- Return the ``krbV.CCache`` for the ``ccname`` credential ccache.
- """
- return krbV.CCache(ccname)
-
- def __get_principal(self, ccname):
- """
- Return the ``krb5.Principal`` for the ``ccname`` credential ccache.
- """
- return self.__get_ccache(ccname).principal()
-
- def default_ccname(self):
- """
- Return the default ccache file name (schema+name).
-
- This will return something like 'FILE:/tmp/krb5cc_500'.
-
- This cannot return anything meaningful if used in the server as a
- request is processed.
- """
- default_ccache = self.__default_ccache()
- ccname = "%(type)s:%(name)s" % dict(type=default_ccache.type,
- name=default_ccache.name)
- return ccname
-
- def default_principal(self):
- """
- Return the principal name in default credential cache.
-
- This will return something like 'admin@EXAMPLE.COM'. If no credential
- cache exists for the invoking user, None is returned.
-
- This cannot return anything meaningful if used in the server as a
- request is processed.
- """
- return self.__default_principal().name.decode(ENCODING)
-
- def default_realm(self):
- """
- Return the realm from the default credential cache.
-
- This will return something like 'EXAMPLE.COM'. If no credential cache
- exists for the invoking user, None is returned.
-
- This cannot return anything meaningful if used in the server as a
- request is processed.
- """
- return krbV.default_context().default_realm.decode(ENCODING)
-
- def get_principal(self, ccname):
- """
- Return the principal from credential cache file at ``ccname``.
-
- This will return something like 'admin@EXAMPLE.COM'.
- """
- return self.__get_principal(ccname).name.decode(ENCODING)
-
- def get_realm(self, ccname):
- """
- Return the realm from credential cache file at ``ccname``.
-
- This will return something like 'EXAMPLE.COM'.
- """
- return self.__get_principal(ccname).realm.decode(ENCODING)
-
-
diff --git a/ipalib/plugins/passwd.py b/ipalib/plugins/passwd.py
index f5fc14d51..a4f791c1b 100644
--- a/ipalib/plugins/passwd.py
+++ b/ipalib/plugins/passwd.py
@@ -17,7 +17,7 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
-from ipalib import api, errors, util
+from ipalib import api, errors, krb_utils
from ipalib import Command
from ipalib import Str, Password
from ipalib import _
@@ -58,7 +58,7 @@ def get_current_password(principal):
current password is prompted for, otherwise return a fixed value to
be ignored later.
"""
- current_principal = util.get_current_principal()
+ current_principal = krb_utils.get_principal()
if current_principal == normalize_principal(principal):
return None
else:
@@ -74,7 +74,7 @@ class passwd(Command):
label=_('User name'),
primary_key=True,
autofill=True,
- default_from=lambda: util.get_current_principal(),
+ default_from=lambda: krb_utils.get_principal(),
normalizer=lambda value: normalize_principal(value),
),
Password('password',
diff --git a/ipalib/plugins/vault.py b/ipalib/plugins/vault.py
index 6a07a76b5..18436e399 100644
--- a/ipalib/plugins/vault.py
+++ b/ipalib/plugins/vault.py
@@ -34,7 +34,6 @@ from cryptography.hazmat.primitives.serialization import load_pem_public_key,\
load_pem_private_key
import nss.nss as nss
-import krbV
from ipalib.frontend import Command, Object, Local
from ipalib import api, errors
@@ -640,7 +639,7 @@ class vault_add(PKQuery, Local):
else:
backend = self.api.Backend.rpcclient
if not backend.isconnected():
- backend.connect(ccache=krbV.default_context().default_ccache())
+ backend.connect()
if vault_type == u'standard':
@@ -1239,7 +1238,7 @@ class vault_archive(PKQuery, Local):
else:
backend = self.api.Backend.rpcclient
if not backend.isconnected():
- backend.connect(ccache=krbV.default_context().default_ccache())
+ backend.connect()
# retrieve vault info
vault = self.api.Command.vault_show(*args, **options)['result']
@@ -1508,7 +1507,7 @@ class vault_retrieve(PKQuery, Local):
else:
backend = self.api.Backend.rpcclient
if not backend.isconnected():
- backend.connect(ccache=krbV.default_context().default_ccache())
+ backend.connect()
# retrieve vault info
vault = self.api.Command.vault_show(*args, **options)['result']
diff --git a/ipalib/rpc.py b/ipalib/rpc.py
index 04b8d01d9..9d0fc8f7b 100644
--- a/ipalib/rpc.py
+++ b/ipalib/rpc.py
@@ -55,7 +55,6 @@ from ipalib.errors import (public_errors, UnknownError, NetworkError,
KerberosError, XMLRPCMarshallError, JSONError, ConversionError)
from ipalib import errors, capabilities
from ipalib.request import context, Connection
-from ipalib.util import get_current_principal
from ipapython.ipa_log_manager import root_logger
from ipapython import ipautil
from ipapython import kernel_keyring
@@ -66,7 +65,8 @@ from ipalib.text import _
import ipapython.nsslib
from ipapython.nsslib import NSSHTTPS, NSSConnection
from ipalib.krb_utils import KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN, KRB5KRB_AP_ERR_TKT_EXPIRED, \
- KRB5_FCC_PERM, KRB5_FCC_NOFILE, KRB5_CC_FORMAT, KRB5_REALM_CANT_RESOLVE
+ KRB5_FCC_PERM, KRB5_FCC_NOFILE, KRB5_CC_FORMAT, \
+ KRB5_REALM_CANT_RESOLVE, get_principal
from ipapython.dn import DN
from ipalib.capabilities import VERSION_WITHOUT_CAPABILITIES
from ipalib import api
@@ -518,10 +518,7 @@ class KerbTransport(SSLTransport):
self._sec_context = None
def _handle_exception(self, e, service=None):
- # kerberos library coerced error codes to signed, gssapi uses unsigned
minor = e.min_code
- if minor & (1 << 31):
- minor -= 1 << 32
if minor == KRB5KDC_ERR_S_PRINCIPAL_UNKNOWN:
raise errors.ServiceError(service=service)
elif minor == KRB5_FCC_NOFILE:
@@ -835,7 +832,7 @@ class RPCClient(Connectible):
delegate=False, nss_dir=None):
try:
rpc_uri = self.env[self.env_rpc_uri_key]
- principal = get_current_principal()
+ principal = get_principal()
setattr(context, 'principal', principal)
# We have a session cookie, try using the session URI to see if it
# is still valid
diff --git a/ipalib/util.py b/ipalib/util.py
index 0d5f85040..4a75b820a 100644
--- a/ipalib/util.py
+++ b/ipalib/util.py
@@ -61,18 +61,6 @@ def json_serialize(obj):
return ''
return json_serialize(obj.__json__())
-def get_current_principal():
- try:
- import gssapi
- cred = gssapi.Credentials(usage='initiate')
- return unicode(cred.name)
- except ImportError:
- raise RuntimeError('python-gssapi is not available.')
- except gssapi.exceptions.GSSError:
- #TODO: do a kinit?
- raise errors.CCacheError()
-
-
def validate_host_dns(log, fqdn):
"""
See if the hostname has a DNS A/AAAA record.