|
|
ac7d03 |
From 2f8033174775f55cb2377baf524fc36914aa38fa Mon Sep 17 00:00:00 2001
|
|
|
ac7d03 |
From: Simo Sorce <simo@redhat.com>
|
|
|
ac7d03 |
Date: Thu, 23 Mar 2017 13:02:00 -0400
|
|
|
ac7d03 |
Subject: [PATCH] Work around issues fetching session data
|
|
|
ac7d03 |
|
|
|
ac7d03 |
Unfortunately the MIT krb5 library has a severe limitation with FILE
|
|
|
ac7d03 |
ccaches when retrieving config data. It will always only search until
|
|
|
ac7d03 |
the first entry is found and return that one.
|
|
|
ac7d03 |
|
|
|
ac7d03 |
For FILE caches MIT krb5 does not support removing old entries when a
|
|
|
ac7d03 |
new one is stored, and storage happens only in append mode, so the end
|
|
|
ac7d03 |
result is that even if an update is stored it is never returned with the
|
|
|
ac7d03 |
standard krb5_cc_get_config() call.
|
|
|
ac7d03 |
|
|
|
ac7d03 |
To work around this issue we simply implement what krb5_cc_get_config()
|
|
|
ac7d03 |
does under the hood with the difference that we do not stop at the first
|
|
|
ac7d03 |
match but keep going until all ccache entries have been checked.
|
|
|
ac7d03 |
|
|
|
ac7d03 |
Related https://pagure.io/freeipa/issue/6775
|
|
|
ac7d03 |
|
|
|
ac7d03 |
Signed-off-by: Simo Sorce <simo@redhat.com>
|
|
|
ac7d03 |
Reviewed-By: Christian Heimes <cheimes@redhat.com>
|
|
|
ac7d03 |
Reviewed-By: Alexander Bokovoy <abokovoy@redhat.com>
|
|
|
ac7d03 |
---
|
|
|
ac7d03 |
ipapython/session_storage.py | 213 ++++++++++++++++++++++++++++++++++++++-----
|
|
|
ac7d03 |
1 file changed, 190 insertions(+), 23 deletions(-)
|
|
|
ac7d03 |
|
|
|
ac7d03 |
diff --git a/ipapython/session_storage.py b/ipapython/session_storage.py
|
|
|
ac7d03 |
index a88f9f7a75c73d4dc753183a100350d197d02199..f3094f60000aa6e3f4b27fe91092c4214936f651 100644
|
|
|
ac7d03 |
--- a/ipapython/session_storage.py
|
|
|
ac7d03 |
+++ b/ipapython/session_storage.py
|
|
|
ac7d03 |
@@ -13,6 +13,12 @@ try:
|
|
|
ac7d03 |
except OSError as e: # pragma: no cover
|
|
|
ac7d03 |
raise ImportError(str(e))
|
|
|
ac7d03 |
|
|
|
ac7d03 |
+krb5_int32 = ctypes.c_int32
|
|
|
ac7d03 |
+krb5_error_code = krb5_int32
|
|
|
ac7d03 |
+krb5_magic = krb5_error_code
|
|
|
ac7d03 |
+krb5_enctype = krb5_int32
|
|
|
ac7d03 |
+krb5_octet = ctypes.c_uint8
|
|
|
ac7d03 |
+krb5_timestamp = krb5_int32
|
|
|
ac7d03 |
|
|
|
ac7d03 |
class _krb5_context(ctypes.Structure): # noqa
|
|
|
ac7d03 |
"""krb5/krb5.h struct _krb5_context"""
|
|
|
ac7d03 |
@@ -27,7 +33,7 @@ class _krb5_ccache(ctypes.Structure): # noqa
|
|
|
ac7d03 |
class _krb5_data(ctypes.Structure): # noqa
|
|
|
ac7d03 |
"""krb5/krb5.h struct _krb5_data"""
|
|
|
ac7d03 |
_fields_ = [
|
|
|
ac7d03 |
- ("magic", ctypes.c_int32),
|
|
|
ac7d03 |
+ ("magic", krb5_magic),
|
|
|
ac7d03 |
("length", ctypes.c_uint),
|
|
|
ac7d03 |
("data", ctypes.c_char_p),
|
|
|
ac7d03 |
]
|
|
|
ac7d03 |
@@ -38,6 +44,63 @@ class krb5_principal_data(ctypes.Structure): # noqa
|
|
|
ac7d03 |
_fields_ = []
|
|
|
ac7d03 |
|
|
|
ac7d03 |
|
|
|
ac7d03 |
+class _krb5_keyblock(ctypes.Structure): # noqa
|
|
|
ac7d03 |
+ """krb5/krb5.h struct _krb5_keyblock"""
|
|
|
ac7d03 |
+ _fields_ = [
|
|
|
ac7d03 |
+ ("magic", krb5_magic),
|
|
|
ac7d03 |
+ ("enctype", krb5_enctype),
|
|
|
ac7d03 |
+ ("length", ctypes.c_uint),
|
|
|
ac7d03 |
+ ("contents", ctypes.POINTER(krb5_octet))
|
|
|
ac7d03 |
+ ]
|
|
|
ac7d03 |
+
|
|
|
ac7d03 |
+
|
|
|
ac7d03 |
+class _krb5_ticket_times(ctypes.Structure): # noqa
|
|
|
ac7d03 |
+ """krb5/krb5.h struct _krb5_ticket_times"""
|
|
|
ac7d03 |
+ _fields_ = [
|
|
|
ac7d03 |
+ ("authtime", krb5_timestamp),
|
|
|
ac7d03 |
+ ("starttime", krb5_timestamp),
|
|
|
ac7d03 |
+ ("endtime", krb5_timestamp),
|
|
|
ac7d03 |
+ ("renew_till", krb5_timestamp),
|
|
|
ac7d03 |
+ ]
|
|
|
ac7d03 |
+
|
|
|
ac7d03 |
+
|
|
|
ac7d03 |
+class _krb5_address(ctypes.Structure): # noqa
|
|
|
ac7d03 |
+ """krb5/krb5.h struct _krb5_address"""
|
|
|
ac7d03 |
+ _fields_ = []
|
|
|
ac7d03 |
+
|
|
|
ac7d03 |
+
|
|
|
ac7d03 |
+class _krb5_authdata(ctypes.Structure): # noqa
|
|
|
ac7d03 |
+ """krb5/krb5.h struct _krb5_authdata"""
|
|
|
ac7d03 |
+ _fields_ = []
|
|
|
ac7d03 |
+
|
|
|
ac7d03 |
+
|
|
|
ac7d03 |
+krb5_principal = ctypes.POINTER(krb5_principal_data)
|
|
|
ac7d03 |
+krb5_keyblock = _krb5_keyblock
|
|
|
ac7d03 |
+krb5_ticket_times = _krb5_ticket_times
|
|
|
ac7d03 |
+krb5_boolean = ctypes.c_uint
|
|
|
ac7d03 |
+krb5_flags = krb5_int32
|
|
|
ac7d03 |
+krb5_data = _krb5_data
|
|
|
ac7d03 |
+krb5_address_p = ctypes.POINTER(_krb5_address)
|
|
|
ac7d03 |
+krb5_authdata_p = ctypes.POINTER(_krb5_authdata)
|
|
|
ac7d03 |
+
|
|
|
ac7d03 |
+
|
|
|
ac7d03 |
+class _krb5_creds(ctypes.Structure): # noqa
|
|
|
ac7d03 |
+ """krb5/krb5.h struct _krb5_creds"""
|
|
|
ac7d03 |
+ _fields_ = [
|
|
|
ac7d03 |
+ ("magic", krb5_magic),
|
|
|
ac7d03 |
+ ("client", krb5_principal),
|
|
|
ac7d03 |
+ ("server", krb5_principal),
|
|
|
ac7d03 |
+ ("keyblock", krb5_keyblock),
|
|
|
ac7d03 |
+ ("times", krb5_ticket_times),
|
|
|
ac7d03 |
+ ("is_skey", krb5_boolean),
|
|
|
ac7d03 |
+ ("ticket_flags", krb5_flags),
|
|
|
ac7d03 |
+ ("addresses", ctypes.POINTER(krb5_address_p)),
|
|
|
ac7d03 |
+ ("ticket", krb5_data),
|
|
|
ac7d03 |
+ ("second_ticket", krb5_data),
|
|
|
ac7d03 |
+ ("authdata", ctypes.POINTER(krb5_authdata_p))
|
|
|
ac7d03 |
+ ]
|
|
|
ac7d03 |
+
|
|
|
ac7d03 |
+
|
|
|
ac7d03 |
class KRB5Error(Exception):
|
|
|
ac7d03 |
pass
|
|
|
ac7d03 |
|
|
|
ac7d03 |
@@ -48,11 +111,13 @@ def krb5_errcheck(result, func, arguments):
|
|
|
ac7d03 |
raise KRB5Error(result, func.__name__, arguments)
|
|
|
ac7d03 |
|
|
|
ac7d03 |
|
|
|
ac7d03 |
-krb5_principal = ctypes.POINTER(krb5_principal_data)
|
|
|
ac7d03 |
krb5_context = ctypes.POINTER(_krb5_context)
|
|
|
ac7d03 |
krb5_ccache = ctypes.POINTER(_krb5_ccache)
|
|
|
ac7d03 |
krb5_data_p = ctypes.POINTER(_krb5_data)
|
|
|
ac7d03 |
krb5_error = ctypes.c_int32
|
|
|
ac7d03 |
+krb5_creds = _krb5_creds
|
|
|
ac7d03 |
+krb5_pointer = ctypes.c_void_p
|
|
|
ac7d03 |
+krb5_cc_cursor = krb5_pointer
|
|
|
ac7d03 |
|
|
|
ac7d03 |
krb5_init_context = LIBKRB5.krb5_init_context
|
|
|
ac7d03 |
krb5_init_context.argtypes = (ctypes.POINTER(krb5_context), )
|
|
|
ac7d03 |
@@ -61,15 +126,15 @@ krb5_init_context.errcheck = krb5_errcheck
|
|
|
ac7d03 |
|
|
|
ac7d03 |
krb5_free_context = LIBKRB5.krb5_free_context
|
|
|
ac7d03 |
krb5_free_context.argtypes = (krb5_context, )
|
|
|
ac7d03 |
-krb5_free_context.retval = None
|
|
|
ac7d03 |
+krb5_free_context.restype = None
|
|
|
ac7d03 |
|
|
|
ac7d03 |
krb5_free_principal = LIBKRB5.krb5_free_principal
|
|
|
ac7d03 |
krb5_free_principal.argtypes = (krb5_context, krb5_principal)
|
|
|
ac7d03 |
-krb5_free_principal.retval = None
|
|
|
ac7d03 |
+krb5_free_principal.restype = None
|
|
|
ac7d03 |
|
|
|
ac7d03 |
krb5_free_data_contents = LIBKRB5.krb5_free_data_contents
|
|
|
ac7d03 |
krb5_free_data_contents.argtypes = (krb5_context, krb5_data_p)
|
|
|
ac7d03 |
-krb5_free_data_contents.retval = None
|
|
|
ac7d03 |
+krb5_free_data_contents.restype = None
|
|
|
ac7d03 |
|
|
|
ac7d03 |
krb5_cc_default = LIBKRB5.krb5_cc_default
|
|
|
ac7d03 |
krb5_cc_default.argtypes = (krb5_context, ctypes.POINTER(krb5_ccache), )
|
|
|
ac7d03 |
@@ -78,26 +143,79 @@ krb5_cc_default.errcheck = krb5_errcheck
|
|
|
ac7d03 |
|
|
|
ac7d03 |
krb5_cc_close = LIBKRB5.krb5_cc_close
|
|
|
ac7d03 |
krb5_cc_close.argtypes = (krb5_context, krb5_ccache, )
|
|
|
ac7d03 |
-krb5_cc_close.retval = krb5_error
|
|
|
ac7d03 |
+krb5_cc_close.restype = krb5_error
|
|
|
ac7d03 |
krb5_cc_close.errcheck = krb5_errcheck
|
|
|
ac7d03 |
|
|
|
ac7d03 |
krb5_parse_name = LIBKRB5.krb5_parse_name
|
|
|
ac7d03 |
krb5_parse_name.argtypes = (krb5_context, ctypes.c_char_p,
|
|
|
ac7d03 |
ctypes.POINTER(krb5_principal), )
|
|
|
ac7d03 |
-krb5_parse_name.retval = krb5_error
|
|
|
ac7d03 |
+krb5_parse_name.restype = krb5_error
|
|
|
ac7d03 |
krb5_parse_name.errcheck = krb5_errcheck
|
|
|
ac7d03 |
|
|
|
ac7d03 |
krb5_cc_set_config = LIBKRB5.krb5_cc_set_config
|
|
|
ac7d03 |
krb5_cc_set_config.argtypes = (krb5_context, krb5_ccache, krb5_principal,
|
|
|
ac7d03 |
ctypes.c_char_p, krb5_data_p, )
|
|
|
ac7d03 |
-krb5_cc_set_config.retval = krb5_error
|
|
|
ac7d03 |
+krb5_cc_set_config.restype = krb5_error
|
|
|
ac7d03 |
krb5_cc_set_config.errcheck = krb5_errcheck
|
|
|
ac7d03 |
|
|
|
ac7d03 |
-krb5_cc_get_config = LIBKRB5.krb5_cc_get_config
|
|
|
ac7d03 |
-krb5_cc_get_config.argtypes = (krb5_context, krb5_ccache, krb5_principal,
|
|
|
ac7d03 |
- ctypes.c_char_p, krb5_data_p, )
|
|
|
ac7d03 |
-krb5_cc_get_config.retval = krb5_error
|
|
|
ac7d03 |
-krb5_cc_get_config.errcheck = krb5_errcheck
|
|
|
ac7d03 |
+krb5_cc_get_principal = LIBKRB5.krb5_cc_get_principal
|
|
|
ac7d03 |
+krb5_cc_get_principal.argtypes = (krb5_context, krb5_ccache,
|
|
|
ac7d03 |
+ ctypes.POINTER(krb5_principal), )
|
|
|
ac7d03 |
+krb5_cc_get_principal.restype = krb5_error
|
|
|
ac7d03 |
+krb5_cc_get_principal.errcheck = krb5_errcheck
|
|
|
ac7d03 |
+
|
|
|
ac7d03 |
+# krb5_build_principal is a variadic function but that can't be expressed
|
|
|
ac7d03 |
+# in a ctypes argtypes definition, so I explicitly listed the number of
|
|
|
ac7d03 |
+# arguments we actually use through the code for type checking purposes
|
|
|
ac7d03 |
+krb5_build_principal = LIBKRB5.krb5_build_principal
|
|
|
ac7d03 |
+krb5_build_principal.argtypes = (krb5_context, ctypes.POINTER(krb5_principal),
|
|
|
ac7d03 |
+ ctypes.c_uint, ctypes.c_char_p,
|
|
|
ac7d03 |
+ ctypes.c_char_p, ctypes.c_char_p,
|
|
|
ac7d03 |
+ ctypes.c_char_p, ctypes.c_char_p, )
|
|
|
ac7d03 |
+krb5_build_principal.restype = krb5_error
|
|
|
ac7d03 |
+krb5_build_principal.errcheck = krb5_errcheck
|
|
|
ac7d03 |
+
|
|
|
ac7d03 |
+krb5_cc_start_seq_get = LIBKRB5.krb5_cc_start_seq_get
|
|
|
ac7d03 |
+krb5_cc_start_seq_get.argtypes = (krb5_context, krb5_ccache,
|
|
|
ac7d03 |
+ ctypes.POINTER(krb5_cc_cursor), )
|
|
|
ac7d03 |
+krb5_cc_start_seq_get.restype = krb5_error
|
|
|
ac7d03 |
+krb5_cc_start_seq_get.errcheck = krb5_errcheck
|
|
|
ac7d03 |
+
|
|
|
ac7d03 |
+krb5_cc_next_cred = LIBKRB5.krb5_cc_next_cred
|
|
|
ac7d03 |
+krb5_cc_next_cred.argtypes = (krb5_context, krb5_ccache,
|
|
|
ac7d03 |
+ ctypes.POINTER(krb5_cc_cursor),
|
|
|
ac7d03 |
+ ctypes.POINTER(krb5_creds), )
|
|
|
ac7d03 |
+krb5_cc_next_cred.restype = krb5_error
|
|
|
ac7d03 |
+krb5_cc_next_cred.errcheck = krb5_errcheck
|
|
|
ac7d03 |
+
|
|
|
ac7d03 |
+krb5_cc_end_seq_get = LIBKRB5.krb5_cc_end_seq_get
|
|
|
ac7d03 |
+krb5_cc_end_seq_get.argtypes = (krb5_context, krb5_ccache,
|
|
|
ac7d03 |
+ ctypes.POINTER(krb5_cc_cursor), )
|
|
|
ac7d03 |
+krb5_cc_end_seq_get.restype = krb5_error
|
|
|
ac7d03 |
+krb5_cc_end_seq_get.errcheck = krb5_errcheck
|
|
|
ac7d03 |
+
|
|
|
ac7d03 |
+krb5_free_cred_contents = LIBKRB5.krb5_free_cred_contents
|
|
|
ac7d03 |
+krb5_free_cred_contents.argtypes = (krb5_context, ctypes.POINTER(krb5_creds))
|
|
|
ac7d03 |
+krb5_free_cred_contents.restype = krb5_error
|
|
|
ac7d03 |
+krb5_free_cred_contents.errcheck = krb5_errcheck
|
|
|
ac7d03 |
+
|
|
|
ac7d03 |
+krb5_principal_compare = LIBKRB5.krb5_principal_compare
|
|
|
ac7d03 |
+krb5_principal_compare.argtypes = (krb5_context, krb5_principal,
|
|
|
ac7d03 |
+ krb5_principal, )
|
|
|
ac7d03 |
+krb5_principal_compare.restype = krb5_boolean
|
|
|
ac7d03 |
+
|
|
|
ac7d03 |
+krb5_unparse_name = LIBKRB5.krb5_unparse_name
|
|
|
ac7d03 |
+krb5_unparse_name.argtypes = (krb5_context, krb5_principal,
|
|
|
ac7d03 |
+ ctypes.POINTER(ctypes.c_char_p), )
|
|
|
ac7d03 |
+krb5_unparse_name.restype = krb5_error
|
|
|
ac7d03 |
+krb5_unparse_name.errcheck = krb5_errcheck
|
|
|
ac7d03 |
+
|
|
|
ac7d03 |
+krb5_free_unparsed_name = LIBKRB5.krb5_free_unparsed_name
|
|
|
ac7d03 |
+krb5_free_unparsed_name.argtypes = (krb5_context, ctypes.c_char_p, )
|
|
|
ac7d03 |
+krb5_free_unparsed_name.restype = None
|
|
|
ac7d03 |
+
|
|
|
ac7d03 |
+CONF_REALM = "X-CACHECONF:"
|
|
|
ac7d03 |
+CONF_NAME = "krb5_ccache_conf_data"
|
|
|
ac7d03 |
|
|
|
ac7d03 |
|
|
|
ac7d03 |
def store_data(princ_name, key, value):
|
|
|
ac7d03 |
@@ -144,29 +262,78 @@ def get_data(princ_name, key):
|
|
|
ac7d03 |
"""
|
|
|
ac7d03 |
context = krb5_context()
|
|
|
ac7d03 |
principal = krb5_principal()
|
|
|
ac7d03 |
+ srv_princ = krb5_principal()
|
|
|
ac7d03 |
ccache = krb5_ccache()
|
|
|
ac7d03 |
- data = _krb5_data()
|
|
|
ac7d03 |
+ pname_princ = krb5_principal()
|
|
|
ac7d03 |
+ pname = ctypes.c_char_p()
|
|
|
ac7d03 |
|
|
|
ac7d03 |
try:
|
|
|
ac7d03 |
krb5_init_context(ctypes.byref(context))
|
|
|
ac7d03 |
|
|
|
ac7d03 |
- krb5_parse_name(context, ctypes.c_char_p(princ_name),
|
|
|
ac7d03 |
- ctypes.byref(principal))
|
|
|
ac7d03 |
-
|
|
|
ac7d03 |
krb5_cc_default(context, ctypes.byref(ccache))
|
|
|
ac7d03 |
+ krb5_cc_get_principal(context, ccache, ctypes.byref(principal))
|
|
|
ac7d03 |
|
|
|
ac7d03 |
- krb5_cc_get_config(context, ccache, principal, key,
|
|
|
ac7d03 |
- ctypes.byref(data))
|
|
|
ac7d03 |
-
|
|
|
ac7d03 |
- return str(data.data)
|
|
|
ac7d03 |
+ # We need to parse and then unparse the name in case the pric_name
|
|
|
ac7d03 |
+ # passed in comes w/o a realm attached
|
|
|
ac7d03 |
+ krb5_parse_name(context, ctypes.c_char_p(princ_name),
|
|
|
ac7d03 |
+ ctypes.byref(pname_princ))
|
|
|
ac7d03 |
+ krb5_unparse_name(context, pname_princ, ctypes.byref(pname))
|
|
|
ac7d03 |
+
|
|
|
ac7d03 |
+ krb5_build_principal(context, ctypes.byref(srv_princ),
|
|
|
ac7d03 |
+ len(CONF_REALM), ctypes.c_char_p(CONF_REALM),
|
|
|
ac7d03 |
+ ctypes.c_char_p(CONF_NAME), ctypes.c_char_p(key),
|
|
|
ac7d03 |
+ pname, ctypes.c_char_p(None))
|
|
|
ac7d03 |
+
|
|
|
ac7d03 |
+ # Unfortunately we can't just use krb5_cc_get_config()
|
|
|
ac7d03 |
+ # because of bugs in some ccache handling code in krb5
|
|
|
ac7d03 |
+ # libraries that would always return the first entry
|
|
|
ac7d03 |
+ # stored and not the last one, which is the one we want.
|
|
|
ac7d03 |
+ cursor = krb5_cc_cursor()
|
|
|
ac7d03 |
+ creds = krb5_creds()
|
|
|
ac7d03 |
+ got_creds = False
|
|
|
ac7d03 |
+ krb5_cc_start_seq_get(context, ccache, ctypes.byref(cursor))
|
|
|
ac7d03 |
+ try:
|
|
|
ac7d03 |
+ while True:
|
|
|
ac7d03 |
+ checkcreds = krb5_creds()
|
|
|
ac7d03 |
+ # the next function will throw an error and break out of the
|
|
|
ac7d03 |
+ # while loop when we try to access past the last cred
|
|
|
ac7d03 |
+ krb5_cc_next_cred(context, ccache, ctypes.byref(cursor),
|
|
|
ac7d03 |
+ ctypes.byref(checkcreds))
|
|
|
ac7d03 |
+ if (krb5_principal_compare(context, principal,
|
|
|
ac7d03 |
+ checkcreds.client) == 1 and
|
|
|
ac7d03 |
+ krb5_principal_compare(context, srv_princ,
|
|
|
ac7d03 |
+ checkcreds.server) == 1):
|
|
|
ac7d03 |
+ if got_creds:
|
|
|
ac7d03 |
+ krb5_free_cred_contents(context, ctypes.byref(creds))
|
|
|
ac7d03 |
+ creds = checkcreds
|
|
|
ac7d03 |
+ got_creds = True
|
|
|
ac7d03 |
+ # We do not stop here, as we want the LAST entry
|
|
|
ac7d03 |
+ # in the ccache for those ccaches that cannot delete
|
|
|
ac7d03 |
+ # but only always append, like FILE
|
|
|
ac7d03 |
+ else:
|
|
|
ac7d03 |
+ krb5_free_cred_contents(context,
|
|
|
ac7d03 |
+ ctypes.byref(checkcreds))
|
|
|
ac7d03 |
+ except KRB5Error:
|
|
|
ac7d03 |
+ pass
|
|
|
ac7d03 |
+ finally:
|
|
|
ac7d03 |
+ krb5_cc_end_seq_get(context, ccache, ctypes.byref(cursor))
|
|
|
ac7d03 |
+
|
|
|
ac7d03 |
+ if got_creds:
|
|
|
ac7d03 |
+ data = creds.ticket.data.decode('utf-8')
|
|
|
ac7d03 |
+ krb5_free_cred_contents(context, ctypes.byref(creds))
|
|
|
ac7d03 |
+ return data
|
|
|
ac7d03 |
|
|
|
ac7d03 |
finally:
|
|
|
ac7d03 |
if principal:
|
|
|
ac7d03 |
krb5_free_principal(context, principal)
|
|
|
ac7d03 |
+ if srv_princ:
|
|
|
ac7d03 |
+ krb5_free_principal(context, srv_princ)
|
|
|
ac7d03 |
+ if pname_princ:
|
|
|
ac7d03 |
+ krb5_free_principal(context, pname_princ)
|
|
|
ac7d03 |
+ if pname:
|
|
|
ac7d03 |
+ krb5_free_unparsed_name(context, pname)
|
|
|
ac7d03 |
if ccache:
|
|
|
ac7d03 |
krb5_cc_close(context, ccache)
|
|
|
ac7d03 |
- if data:
|
|
|
ac7d03 |
- krb5_free_data_contents(context, data)
|
|
|
ac7d03 |
if context:
|
|
|
ac7d03 |
krb5_free_context(context)
|
|
|
ac7d03 |
|
|
|
ac7d03 |
--
|
|
|
ac7d03 |
2.12.1
|
|
|
ac7d03 |
|