Skip to content

Commit

Permalink
Implement support for GSSAPI extension RFC 5587
Browse files Browse the repository at this point in the history
RFC 5587 provides extended mech inquiry calls to GSSAPI.
This adds the ability to indicate mechs by their
mech attrs, along with determining the attrs supported
by a mech. These calls are provided as a part of
the raw interface and are not exposed in the high-level
interface due to not having objects for mechs or attrs.

Signed-off-by: Alexander Scheel <ascheel@redhat.com>
  • Loading branch information
cipherboy committed Jul 19, 2017
1 parent 0be0230 commit 66a7107
Show file tree
Hide file tree
Showing 6 changed files with 258 additions and 0 deletions.
2 changes: 2 additions & 0 deletions README.txt
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,8 @@ Extensions

In addition to RFC 2743/2744, Python-GSSAPI also has support for:

* RFC 5587 (Extended GSS Mechanism Inquiry APIs)

* RFC 5588 (GSS-API Extension for Storing Delegated Credentials)

* (Additional) Credential Store Extension
Expand Down
6 changes: 6 additions & 0 deletions gssapi/raw/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,12 @@
except ImportError:
pass

# optional RFC 5587 support
try:
from gssapi.raw.ext_rfc5587 import * # noqa
except ImportError:
pass

# optional RFC 5588 support
try:
from gssapi.raw.ext_rfc5588 import * # noqa
Expand Down
153 changes: 153 additions & 0 deletions gssapi/raw/ext_rfc5587.pyx
Original file line number Diff line number Diff line change
@@ -0,0 +1,153 @@
from gssapi.raw.cython_types cimport *
from gssapi.raw.oids cimport OID
from gssapi.raw.cython_converters cimport c_create_oid_set
GSSAPI="BASE" # This ensures that a full module is generated by Cython

from gssapi.raw.cython_converters cimport c_get_mech_oid_set

from gssapi.raw.named_tuples import InquireAttrsResult, DisplayAttrResult
from gssapi.raw.misc import GSSError

cdef extern from "python_gssapi_ext.h":
OM_uint32 gss_indicate_mechs_by_attrs(
OM_uint32 *minor_status,
const gss_OID_set desired_mech_attrs,
const gss_OID_set except_mech_attrs,
const gss_OID_set critical_mech_attrs,
gss_OID_set *mechs) nogil

OM_uint32 gss_inquire_attrs_for_mech(
OM_uint32 *minor_status,
const gss_OID mech,
gss_OID_set *mech_attrs,
gss_OID_set *known_mech_attrs) nogil

OM_uint32 gss_display_mech_attr(
OM_uint32 *minor_status,
const gss_OID mech_attr,
gss_buffer_t name,
gss_buffer_t short_desc,
gss_buffer_t long_desc) nogil


def indicate_mechs_by_attrs(desired_mech_attrs=None, except_mech_attrs=None,
critical_mech_attrs=None):
"""
indicate_mechs_by_attrs(desired_mech_attrs=None, except_mech_attrs=None,
critical_mech_attrs=None)
Get a set of mechanisms that have the specified attributes.
Args:
desired_mech_attrs ([OID]): Attributes that the output mechs MUST
offer
except_mech_attrs ([OID]): Attributes that the output mechs MUST NOT
offer
critical_mech_attrs ([OID]): Attributes that the output mechs MUST
understand and offer
Returns:
[MechType]: a set of mechs which satisfy the given criteria
Raises:
GSSError
"""
cdef OM_uint32 maj_stat, min_stat
cdef gss_OID_set desired_attrs = GSS_C_NO_OID_SET
cdef gss_OID_set except_attrs = GSS_C_NO_OID_SET
cdef gss_OID_set critical_attrs = GSS_C_NO_OID_SET
cdef gss_OID_set mechs

if desired_mech_attrs is not None:
desired_attrs = c_get_mech_oid_set(desired_mech_attrs)

if except_mech_attrs is not None:
except_attrs = c_get_mech_oid_set(except_mech_attrs)

if critical_mech_attrs is not None:
critical_attrs = c_get_mech_oid_set(critical_mech_attrs)

with nogil:
maj_stat = gss_indicate_mechs_by_attrs(&min_stat, desired_attrs,
except_attrs, critical_attrs,
&mechs)

if maj_stat == GSS_S_COMPLETE:
return c_create_oid_set(mechs)
else:
raise GSSError(maj_stat, min_stat)


def inquire_attrs_for_mech(OID mech):
"""
inquire_attrs_for_mech(mech)
Gets the set of attrs supported and known by a mechanism.
Args:
mech (MechType): Mechanism to inquire about
Returns:
InquireAttrsResult: the results of inquiry; a mech's attributes and
known attributes
Raises:
GSSError
"""
cdef OM_uint32 maj_stat, min_stat
cdef gss_OID m = GSS_C_NO_OID
cdef gss_OID_set mech_attrs = GSS_C_NO_OID_SET
cdef gss_OID_set known_mech_attrs = GSS_C_NO_OID_SET

if mech is not None:
m = &mech.raw_oid

with nogil:
maj_stat = gss_inquire_attrs_for_mech(&min_stat, m, &mech_attrs,
&known_mech_attrs)

if maj_stat == GSS_S_COMPLETE:
return InquireAttrsResult(c_create_oid_set(mech_attrs),
c_create_oid_set(known_mech_attrs))
else:
raise GSSError(maj_stat, min_stat)


def display_mech_attr(OID attr):
"""
display_mech_attrs(attr)
Returns information about attributes in human readable form.
Args:
attr (OID): Mechanism attribute to retrive names and descriptions of
Returns:
DisplayAttrResult: the results of displaying the attribute; mech name,
short description, and long description.
Raises:
GSSError
"""
cdef OM_uint32 maj_stat, min_stat
cdef gss_OID a = GSS_C_NO_OID
cdef gss_buffer_desc name
cdef gss_buffer_desc short_desc
cdef gss_buffer_desc long_desc

if attr is not None:
a = &attr.raw_oid

with nogil:
maj_stat = gss_display_mech_attr(&min_stat, a, &name, &short_desc,
&long_desc)

if maj_stat == GSS_S_COMPLETE:
out_name = name.value[:name.length]
out_short = short_desc.value[:short_desc.length]
out_long = long_desc.value[:long_desc.length]

gss_release_buffer(&min_stat, &name)
gss_release_buffer(&min_stat, &short_desc)
gss_release_buffer(&min_stat, &long_desc)

return DisplayAttrResult(out_name, out_short, out_long)
else:
raise GSSError(maj_stat, min_stat)
6 changes: 6 additions & 0 deletions gssapi/raw/named_tuples.py
Original file line number Diff line number Diff line change
Expand Up @@ -64,3 +64,9 @@
GetNameAttributeResult = namedtuple('GetNamedAttributeResult',
['values', 'display_values',
'authenticated', 'complete'])

InquireAttrsResult = namedtuple('InquireAttrsResult',
['mech_attrs', 'known_mech_attrs'])

DisplayAttrResult = namedtuple('DisplayAttrResult', ['name', 'short_desc',
'long_desc'])
90 changes: 90 additions & 0 deletions gssapi/tests/test_raw.py
Original file line number Diff line number Diff line change
Expand Up @@ -644,6 +644,96 @@ def test_add_cred_with_password(self):

new_creds.should_be_a(gb.Creds)

@ktu.gssapi_extension_test('rfc5587', 'RFC 5587')
def test_rfc5587(self):
mechs = gb.indicate_mechs_by_attrs(None, None, None)

mechs.should_be_a(set)
mechs.shouldnt_be_empty()

# We need last_attr to be an attribute on last_mech.
# Since mechs is of type set and thus not indexable, these
# are used to track the last visited mech for testing
# purposes, and saves a call to inquire_attrs_for_mech().
last_attr = None
last_mech = None

for mech in mechs:
mech.shouldnt_be_none()
mech.should_be_a(gb.OID)
last_mech = mech

inquire_out = gb.inquire_attrs_for_mech(mech)
mech_attrs = inquire_out.mech_attrs
known_mech_attrs = inquire_out.known_mech_attrs

mech_attrs.should_be_a(set)
mech_attrs.shouldnt_be_empty()

known_mech_attrs.should_be_a(set)
known_mech_attrs.shouldnt_be_empty()

# Verify that we get data for every available
# attribute. Testing the contents of a few known
# attributes is done in test_display_mech_attr().
for mech_attr in mech_attrs:
mech_attr.shouldnt_be_none()
mech_attr.should_be_a(gb.OID)

display_out = gb.display_mech_attr(mech_attr)
display_out.name.shouldnt_be_none()
display_out.short_desc.shouldnt_be_none()
display_out.long_desc.shouldnt_be_none()
display_out.name.should_be_a(bytes)
display_out.short_desc.should_be_a(bytes)
display_out.long_desc.should_be_a(bytes)

last_attr = mech_attr

for mech_attr in known_mech_attrs:
mech_attr.shouldnt_be_none()
mech_attr.should_be_a(gb.OID)

display_out = gb.display_mech_attr(mech_attr)
display_out.name.shouldnt_be_none()
display_out.short_desc.shouldnt_be_none()
display_out.long_desc.shouldnt_be_none()
display_out.name.should_be_a(bytes)
display_out.short_desc.should_be_a(bytes)
display_out.long_desc.should_be_a(bytes)

attrs = set([last_attr])

mechs = gb.indicate_mechs_by_attrs(attrs, None, None)
mechs.shouldnt_be_empty()
mechs.should_include(last_mech)

mechs = gb.indicate_mechs_by_attrs(None, attrs, None)
mechs.shouldnt_include(last_mech)

mechs = gb.indicate_mechs_by_attrs(None, None, attrs)
mechs.shouldnt_be_empty()
mechs.should_include(last_mech)

@ktu.gssapi_extension_test('rfc5587', 'RFC 5587')
def test_display_mech_attr(self):
test_attrs = [
# oid, name, short_desc, long_desc
# Taken from krb5/src/tests/gssapi/t_saslname
[gb.OID.from_int_seq("1.3.6.1.5.5.13.24"), b"GSS_C_MA_CBINDINGS",
b"channel-bindings", b"Mechanism supports channel bindings."],
[gb.OID.from_int_seq("1.3.6.1.5.5.13.1"),
b"GSS_C_MA_MECH_CONCRETE", b"concrete-mech",
b"Mechanism is neither a pseudo-mechanism nor a composite "
b"mechanism."]
]

for attr in test_attrs:
display_out = gb.display_mech_attr(attr[0])
display_out.name.should_be(attr[1])
display_out.short_desc.should_be(attr[2])
display_out.long_desc.should_be(attr[3])


class TestIntEnumFlagSet(unittest.TestCase):
def test_create_from_int(self):
Expand Down
1 change: 1 addition & 0 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -258,6 +258,7 @@ def gssapi_modules(lst):
main_file('chan_bindings'),
extension_file('s4u', 'gss_acquire_cred_impersonate_name'),
extension_file('cred_store', 'gss_store_cred_into'),
extension_file('rfc5587', 'gss_indicate_mechs_by_attrs'),
extension_file('rfc5588', 'gss_store_cred'),
extension_file('cred_imp_exp', 'gss_import_cred'),
extension_file('dce', 'gss_wrap_iov'),
Expand Down

0 comments on commit 66a7107

Please sign in to comment.