Skip to content

Commit

Permalink
Add krb5_cc_set_config() and krb5_cc_get_config() (#37)
Browse files Browse the repository at this point in the history
  • Loading branch information
steffen-kiess committed Mar 13, 2024
1 parent d30aeba commit 035cd7e
Show file tree
Hide file tree
Showing 5 changed files with 164 additions and 0 deletions.
4 changes: 4 additions & 0 deletions src/krb5/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
cc_default,
cc_default_name,
cc_destroy,
cc_get_config,
cc_get_name,
cc_get_principal,
cc_get_type,
Expand All @@ -15,6 +16,7 @@
cc_remove_cred,
cc_resolve,
cc_retrieve_cred,
cc_set_config,
cc_set_default_name,
cc_store_cred,
cc_switch,
Expand Down Expand Up @@ -90,6 +92,7 @@
"cc_default",
"cc_default_name",
"cc_destroy",
"cc_get_config",
"cc_get_name",
"cc_get_principal",
"cc_get_type",
Expand All @@ -98,6 +101,7 @@
"cc_remove_cred",
"cc_resolve",
"cc_retrieve_cred",
"cc_set_config",
"cc_set_default_name",
"cc_store_cred",
"cc_switch",
Expand Down
32 changes: 32 additions & 0 deletions src/krb5/_ccache.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -277,3 +277,35 @@ def cc_switch(
context: Krb5 context.
cache: The credential cache to set as the primary in its collection.
"""

def cc_get_config(
context: Context,
cache: CCache,
principal: typing.Optional[Principal],
key: bytes,
) -> bytes:
"""Get a configuration value from a credential cache.
Args:
context: Krb5 context.
cache: The credential cache to get the data from.
principal: The principal to get the configuration for or None for global values.
key: Name of the variable.
"""

def cc_set_config(
context: Context,
cache: CCache,
principal: typing.Optional[Principal],
key: bytes,
data: bytes,
) -> bytes:
"""Store a configuration value in a credential cache.
Args:
context: Krb5 context.
cache: The credential cache to store the data in.
principal: The principal to set the configuration for or None for global values.
key: Name of the variable.
data: Data to store or None to remove.
"""
88 changes: 88 additions & 0 deletions src/krb5/_ccache.pyx
Original file line number Diff line number Diff line change
Expand Up @@ -120,6 +120,22 @@ cdef extern from "python_krb5.h":
krb5_ccache cache,
) nogil

krb5_error_code krb5_cc_get_config(
krb5_context context,
krb5_ccache id,
krb5_const_principal principal,
const char *key,
krb5_data *data,
);

krb5_error_code krb5_cc_set_config(
krb5_context context,
krb5_ccache id,
krb5_const_principal principal,
const char *key,
krb5_data *data,
) nogil

int32_t KRB5_TC_MATCH_TIMES
int32_t KRB5_TC_MATCH_IS_SKEY
int32_t KRB5_TC_MATCH_FLAGS
Expand Down Expand Up @@ -411,3 +427,75 @@ def cc_switch(
err = krb5_cc_switch(context.raw, cache.raw)
if err:
raise Krb5Error(context, err)


def cc_get_config(
Context context not None,
CCache cache not None,
Principal principal,
const unsigned char[:] key not None,
) -> bytes:
cdef krb5_error_code err = 0

cdef krb5_principal principal_raw = NULL
if principal is not None:
principal_raw = principal.raw

cdef const char *key_ptr
if len(key):
key_ptr = <const char*>&key[0]
else:
key_ptr = ""

cdef krb5_data data
err = krb5_cc_get_config(context.raw, cache.raw, principal_raw, key_ptr, &data)
if err:
raise Krb5Error(context, err)

cdef size_t length
cdef char *value
pykrb5_get_krb5_data(&data, &length, &value)

if length == 0:
data_bytes = b""
else:
data_bytes = <bytes>value[:length]

pykrb5_free_data_contents(context.raw, &data)

return data_bytes


def cc_set_config(
Context context not None,
CCache cache not None,
Principal principal,
const unsigned char[:] key not None,
const unsigned char[:] data,
) -> None:
cdef krb5_error_code err = 0

cdef krb5_principal principal_raw = NULL
if principal is not None:
principal_raw = principal.raw

cdef const char *key_ptr
if len(key):
key_ptr = <const char*>&key[0]
else:
key_ptr = ""

cdef krb5_data data_raw
cdef krb5_data* data_ptr
if data is None:
data_ptr = NULL
else:
if len(data) == 0:
pykrb5_set_krb5_data(&data_raw, 0, "")
else:
pykrb5_set_krb5_data(&data_raw, len(data), <char *>&data[0])
data_ptr = &data_raw

err = krb5_cc_set_config(context.raw, cache.raw, principal_raw, key_ptr, data_ptr)
if err:
raise Krb5Error(context, err)
5 changes: 5 additions & 0 deletions src/krb5/_krb5_types.pxd
Original file line number Diff line number Diff line change
Expand Up @@ -132,3 +132,8 @@ cdef extern from "python_krb5.h":
size_t *length,
char **value,
) nogil

void pykrb5_free_data_contents(
krb5_context context,
krb5_data *val,
) nogil
35 changes: 35 additions & 0 deletions tests/test_ccache.py
Original file line number Diff line number Diff line change
Expand Up @@ -267,6 +267,41 @@ def test_cc_cache_match(realm: k5test.K5Realm, tmp_path: pathlib.Path, monkeypat
assert user_actual.principal.name == user_princ.name


def test_cc_config(realm: k5test.K5Realm, tmp_path: pathlib.Path) -> None:
ctx = krb5.init_context()
princ = krb5.parse_name_flags(ctx, realm.user_princ.encode())

cc = krb5.cc_resolve(ctx, f"{tmp_path / 'ccache'}".encode())
krb5.cc_initialize(ctx, cc, princ)

princ2 = krb5.parse_name_flags(ctx, b"other_principal")

key1 = b"ConfigKey1"
key2 = b"SecondConfigKey"

msg_pattern = "Matching credential not found|End of credential cache reached|Did not find credential for"
with pytest.raises(krb5.Krb5Error, match=msg_pattern):
c = krb5.cc_get_config(ctx, cc, None, key1)
with pytest.raises(krb5.Krb5Error, match=msg_pattern):
c = krb5.cc_get_config(ctx, cc, None, key2)
with pytest.raises(krb5.Krb5Error, match=msg_pattern):
c = krb5.cc_get_config(ctx, cc, princ2, key1)
with pytest.raises(krb5.Krb5Error, match=msg_pattern):
c = krb5.cc_get_config(ctx, cc, princ2, key2)

krb5.cc_set_config(ctx, cc, None, key1, b"Value1")
krb5.cc_set_config(ctx, cc, princ2, key2, b"Value2")

value1 = krb5.cc_get_config(ctx, cc, None, key1)
assert value1 == b"Value1"
with pytest.raises(krb5.Krb5Error, match=msg_pattern):
c = krb5.cc_get_config(ctx, cc, None, key2)
with pytest.raises(krb5.Krb5Error, match=msg_pattern):
c = krb5.cc_get_config(ctx, cc, princ2, key1)
value2 = krb5.cc_get_config(ctx, cc, princ2, key2)
assert value2 == b"Value2"


def test_cc_retrieve_remove_cred(realm: k5test.K5Realm, tmp_path: pathlib.Path) -> None:
ctx = krb5.init_context()
princ = krb5.parse_name_flags(ctx, realm.user_princ.encode())
Expand Down

0 comments on commit 035cd7e

Please sign in to comment.