-
Notifications
You must be signed in to change notification settings - Fork 46
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
This commit introduces optional support for RFC 6680 to the low-level API. The `get_name_attribute` and `set_name_attribute` methods adhere to the pseudo-API (which uses a single call to get/set multiple values on a single attribute) instead of the C bindings (which require the use of a state variable to get multiple values, and require multiple calls to add multiple values to a single attribute). Note that gss_display_name_ext is not tested, since it is not implemented by MIT krb5. Additionally, in order to test the get/set/delete attribute methods, you will have to install the demo greet plugin that comes with krb5. Otherwise, the tests will be skipped. Part of #4 Also-Authored-By: Simo Sorce <simo@redhat.com>
- Loading branch information
1 parent
af11adf
commit 2b77959
Showing
8 changed files
with
505 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
from gssapi.raw.cython_types cimport * | ||
|
||
cdef extern from "python_gssapi.h": | ||
ctypedef struct gss_buffer_set_desc: | ||
size_t count | ||
gss_buffer_desc *elements | ||
ctypedef gss_buffer_set_desc* gss_buffer_set_t | ||
|
||
gss_buffer_set_t GSS_C_NO_BUFFER_SET | ||
|
||
OM_uint32 gss_release_buffer_set(OM_uint32 *min_stat, | ||
gss_buffer_set_t *buffer_set) nogil |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,305 @@ | ||
GSSAPI="BASE" # This ensures that a full module is generated by Cython | ||
|
||
from gssapi.raw.cython_types cimport * | ||
from gssapi.raw.ext_buffer_sets cimport * | ||
from gssapi.raw.names cimport Name | ||
from gssapi.raw.oids cimport OID | ||
|
||
from gssapi.raw.misc import GSSError | ||
from gssapi.raw.named_tuples import InquireNameResult, GetNameAttributeResult | ||
|
||
cdef extern from "gssapi/gssapi_ext.h": | ||
OM_uint32 gss_display_name_ext(OM_uint32 *min_stat, gss_name_t name, | ||
gss_OID name_type, | ||
gss_buffer_t output_name) nogil | ||
|
||
OM_uint32 gss_inquire_name(OM_uint32 *min_stat, gss_name_t name, | ||
int *name_is_mn, gss_OID *mech_type, | ||
gss_buffer_set_t *attrs) nogil | ||
|
||
OM_uint32 gss_get_name_attribute(OM_uint32 *min_stat, gss_name_t name, | ||
gss_buffer_t attr, int *authenticated, | ||
int *complete, gss_buffer_t value, | ||
gss_buffer_t display_value, | ||
int *more) nogil | ||
|
||
OM_uint32 gss_set_name_attribute(OM_uint32 *min_stat, gss_name_t name, | ||
int complete, gss_buffer_t attr, | ||
gss_buffer_t value) nogil | ||
|
||
OM_uint32 gss_delete_name_attribute(OM_uint32 *min_stat, gss_name_t name, | ||
gss_buffer_t attr) nogil | ||
|
||
OM_uint32 gss_export_name_composite(OM_uint32 *min_stat, gss_name_t name, | ||
gss_buffer_t exported_name) nogil | ||
|
||
# GSS_C_NT_COMPOSITE_EXPORT lives in ext_rfc6680_comp_oid.pyx | ||
|
||
|
||
def display_name_ext(Name name not None, OID name_type not None): | ||
""" | ||
Display the given Name using the given name type OID | ||
This method attempts to display the given Name using the syntax of | ||
the given name type. If this is not possible, an appropriate error | ||
will be raised. | ||
Args: | ||
name (Name): the name to display | ||
name_type (OID): the name type (see NameType) to use to | ||
display the given name | ||
Returns: | ||
bytes: the displayed name | ||
Raises: | ||
OperationUnavailableError: the given name could not be displayed | ||
using the given name type | ||
""" | ||
|
||
# GSS_C_EMPTY_BUFFER | ||
cdef gss_buffer_desc output_name = gss_buffer_desc(0, NULL) | ||
|
||
cdef OM_uint32 maj_stat, min_stat | ||
|
||
maj_stat = gss_display_name_ext(&min_stat, name.raw_name, | ||
&name_type.raw_oid, &output_name) | ||
|
||
if maj_stat == GSS_S_COMPLETE: | ||
name_text = output_name.value[:output_name.length] | ||
gss_release_buffer(&min_stat, &output_name) | ||
return name_text | ||
else: | ||
raise GSSError(maj_stat, min_stat) | ||
|
||
|
||
def inquire_name(Name name not None, mech_name=True, attrs=True): | ||
""" | ||
Get information about a Name | ||
This method retrives information about the given name, including | ||
the set of attribute names for the given name, as well as whether or | ||
not the name is a Mechanism Name. Additionally, if the given name is | ||
a Mechanism Name, the associated mechansim is returned as well. | ||
Args: | ||
name (Name): the name about which to inquire | ||
mech_name (bool): whether or not to retrieve if this name | ||
is a mech_name (and the associate mechanism) | ||
attrs (bool): whether or not to retrieve the attribute name list | ||
Returns: | ||
InquireNameResult: the set of attribute names for the given name, | ||
whether or not the name is a Mechanism Name, and potentially | ||
the associated mechanism if it is a Mechanism Name | ||
Raises: | ||
GSSError | ||
""" | ||
|
||
cdef int *name_is_mn_ptr = NULL | ||
cdef gss_OID *mn_mech_ptr = NULL | ||
cdef gss_buffer_set_t *attr_names_ptr = NULL | ||
|
||
cdef gss_buffer_set_t attr_names = GSS_C_NO_BUFFER_SET | ||
if attrs: | ||
attr_names_ptr = &attr_names | ||
|
||
cdef int name_is_mn = 0 | ||
cdef gss_OID mn_mech | ||
if mech_name: | ||
name_is_mn_ptr = &name_is_mn | ||
mn_mech_ptr = &mn_mech | ||
|
||
cdef OM_uint32 maj_stat, min_stat | ||
|
||
maj_stat = gss_inquire_name(&min_stat, name.raw_name, name_is_mn_ptr, | ||
mn_mech_ptr, attr_names_ptr) | ||
|
||
cdef int i | ||
cdef OID py_mech = None | ||
if maj_stat == GSS_S_COMPLETE: | ||
py_attr_names = [] | ||
|
||
if attr_names != GSS_C_NO_BUFFER_SET: | ||
for i in range(attr_names.count): | ||
attr_name = attr_names.elements[i] | ||
py_attr_names.append(attr_name.value[:attr_name.length]) | ||
|
||
gss_release_buffer_set(&min_stat, &attr_names) | ||
|
||
if name_is_mn: | ||
py_mech = OID() | ||
py_mech.raw_oid = mn_mech[0] | ||
|
||
return InquireNameResult(py_attr_names, <bint>name_is_mn, py_mech) | ||
else: | ||
raise GSSError(maj_stat, min_stat) | ||
|
||
|
||
def set_name_attribute(Name name not None, attr not None, value not None, | ||
bint complete=False): | ||
""" | ||
Set the value(s) of a Name attribute | ||
This method sets the value(s) of the given attribute on the given name. | ||
Note that this functionality more closely matches the pseudo-API | ||
presented in RFC 6680, not the C API (which uses multiple calls to | ||
add multiple values). However, multiple calls to this method will | ||
continue adding values, so :func:`delete_name_attribute` must be | ||
used in between calls to "clear" the values. | ||
Args: | ||
name (Name): the Name on which to set the attribute | ||
attr (bytes): the name of the attribute | ||
value (list): a list of bytes objects to use as the value(s) | ||
complete (bool): whether or not to mark this attribute's value | ||
set as being "complete" | ||
Raises: | ||
OperationUnavailableError: the given attribute name is unknown | ||
or could not be set | ||
""" | ||
|
||
cdef gss_buffer_desc attr_buff = gss_buffer_desc(len(attr), attr) | ||
cdef gss_buffer_desc val_buff | ||
|
||
cdef OM_uint32 maj_stat, min_stat | ||
|
||
cdef size_t value_len = len(value) | ||
cdef size_t i | ||
for val in value: | ||
val_buff = gss_buffer_desc(len(val), val) | ||
i += 1 | ||
if i == value_len: | ||
maj_stat = gss_set_name_attribute(&min_stat, name.raw_name, | ||
complete, &attr_buff, &val_buff) | ||
else: | ||
maj_stat = gss_set_name_attribute(&min_stat, name.raw_name, 0, | ||
&attr_buff, &val_buff) | ||
|
||
if maj_stat != GSS_S_COMPLETE: | ||
raise GSSError(maj_stat, min_stat) | ||
|
||
|
||
def get_name_attribute(Name name not None, attr not None, more=None): | ||
""" | ||
Get the value(s) of a Name attribute | ||
This method retrieves the value(s) of the given attribute | ||
for the given Name. | ||
Note that this functionality matches pseudo-API presented | ||
in RFC 6680, not the C API (which uses a state variable and | ||
multiple calls to retrieve multiple values). | ||
Args: | ||
name (Name): the Name from which to get the attribute | ||
attr (bytes): the name of the attribute | ||
Returns: | ||
GetNameAttributeResult: the raw version of the value(s), | ||
the human-readable version of the value(s), whether | ||
or not the attribute was authenticated, and whether or | ||
not the attribute's value set was marked as complete | ||
Raises: | ||
OperationUnavailableError: the given attribute is unknown | ||
or unset | ||
""" | ||
cdef gss_buffer_desc attr_buff = gss_buffer_desc(len(attr), attr) | ||
|
||
cdef gss_buffer_desc val_buff = gss_buffer_desc(0, NULL) | ||
cdef gss_buffer_desc displ_val_buff = gss_buffer_desc(0, NULL) | ||
cdef int complete | ||
cdef int authenticated | ||
|
||
cdef int more_val = -1 | ||
py_vals = [] | ||
py_displ_vals = [] | ||
|
||
cdef OM_uint32 maj_stat, min_stat | ||
|
||
while more_val != 0: | ||
maj_stat = gss_get_name_attribute(&min_stat, name.raw_name, | ||
&attr_buff, | ||
&authenticated, &complete, | ||
&val_buff, &displ_val_buff, | ||
&more_val) | ||
|
||
if maj_stat == GSS_S_COMPLETE: | ||
py_vals.append(val_buff.value[:val_buff.length]) | ||
py_displ_vals.append( | ||
displ_val_buff.value[:displ_val_buff.length]) | ||
|
||
gss_release_buffer(&min_stat, &val_buff) | ||
gss_release_buffer(&min_stat, &displ_val_buff) | ||
else: | ||
raise GSSError(maj_stat, min_stat) | ||
|
||
return GetNameAttributeResult(py_vals, py_displ_vals, <bint>authenticated, | ||
<bint>complete) | ||
|
||
|
||
def delete_name_attribute(Name name not None, attr not None): | ||
""" | ||
Remove an attribute from a Name | ||
This method removes an attribute from a Name. This method may be | ||
used before :func:`set_name_attribute` clear the values of an attribute | ||
before setting a new value (making the latter method work like a 'set' | ||
operation instead of an 'add' operation). | ||
Note that the removal of certain attributes may not be allowed. | ||
Args: | ||
name (Name): the name to remove the attribute from | ||
attr (bytes): the name of the attribute | ||
Raises: | ||
OperationUnavailableError | ||
UnauthorizedError | ||
""" | ||
|
||
cdef gss_buffer_desc attr_buff = gss_buffer_desc(len(attr), attr) | ||
|
||
cdef OM_uint32 maj_stat, min_stat | ||
|
||
maj_stat = gss_delete_name_attribute(&min_stat, name.raw_name, | ||
&attr_buff) | ||
|
||
if maj_stat != GSS_S_COMPLETE: | ||
raise GSSError(maj_stat, min_stat) | ||
|
||
|
||
def export_name_composite(Name name not None): | ||
""" | ||
Export a name, preserving attribute information | ||
This method functions similarly to :func:`export_name`, except that | ||
it preserves attribute information. The resulting bytes may be imported | ||
using :func:`import_name` with the `NameType.composite_export` name type. | ||
Args: | ||
name (Name): the name to export | ||
Returns: | ||
bytes: the exported composite name | ||
Raises: | ||
GSSError | ||
""" | ||
|
||
cdef gss_buffer_desc res = gss_buffer_desc(0, NULL) | ||
|
||
cdef OM_uint32 maj_stat, min_stat | ||
|
||
maj_stat = gss_export_name_composite(&min_stat, name.raw_name, &res) | ||
|
||
if maj_stat == GSS_S_COMPLETE: | ||
py_res = res.value[:res.length] | ||
gss_release_buffer(&min_stat, &res) | ||
return py_res | ||
else: | ||
raise GSSError(maj_stat, min_stat) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
GSSAPI="BASE" # This ensures that a full module is generated by Cython | ||
|
||
from gssapi.raw.cython_types cimport gss_OID | ||
from gssapi.raw.cython_converters cimport c_make_oid | ||
|
||
from gssapi.raw import types as gsstypes | ||
|
||
|
||
# NB(directxman12): this is placed in separate file since the | ||
# GSS_C_NT_COMPOSITE_EXPORT constant didn't appear in MIT | ||
# krb5 until 1.11. However, due to the way that support was | ||
# written for composite tokens, simply using GSS_C_NT_EXPORT_NAME | ||
# will work in prior version which contain support for RFC 6680 | ||
cdef extern from "python_gssapi_ext.h": | ||
gss_OID GSS_C_NT_COMPOSITE_EXPORT | ||
|
||
|
||
gsstypes.NameType.composite_export = c_make_oid(GSS_C_NT_COMPOSITE_EXPORT) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.