Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement support for GSSAPI extension RFC 5587 #121

Merged
merged 1 commit into from
Jul 21, 2017
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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()
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can we test a specific attr to make sure that's right, too?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done -- see here.

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