|
Packit |
fd8b60 |
# This is a simple KCM test server, used to exercise the KCM ccache
|
|
Packit |
fd8b60 |
# client code. It will generally throw an uncaught exception if the
|
|
Packit |
fd8b60 |
# client sends anything unexpected, so is unsuitable for production.
|
|
Packit |
fd8b60 |
# (It also imposes no namespace or access constraints, and blocks
|
|
Packit |
fd8b60 |
# while reading requests and writing responses.)
|
|
Packit |
fd8b60 |
|
|
Packit |
fd8b60 |
# This code knows nothing about how to marshal and unmarshal principal
|
|
Packit |
fd8b60 |
# names and credentials as is required in the KCM protocol; instead,
|
|
Packit |
fd8b60 |
# it just remembers the marshalled forms and replays them to the
|
|
Packit |
fd8b60 |
# client when asked. This works because marshalled creds and
|
|
Packit |
fd8b60 |
# principal names are always the last part of marshalled request
|
|
Packit |
fd8b60 |
# arguments, and because we don't need to implement remove_cred (which
|
|
Packit |
fd8b60 |
# would need to know how to match a cred tag against previously stored
|
|
Packit |
fd8b60 |
# credentials).
|
|
Packit |
fd8b60 |
|
|
Packit |
fd8b60 |
# The following code is useful for debugging if anything appears to be
|
|
Packit |
fd8b60 |
# going wrong in the server, since daemon output is generally not
|
|
Packit |
fd8b60 |
# visible in Python test scripts.
|
|
Packit |
fd8b60 |
#
|
|
Packit |
fd8b60 |
# import sys, traceback
|
|
Packit |
fd8b60 |
# def ehook(etype, value, tb):
|
|
Packit |
fd8b60 |
# with open('/tmp/exception', 'w') as f:
|
|
Packit |
fd8b60 |
# traceback.print_exception(etype, value, tb, file=f)
|
|
Packit |
fd8b60 |
# sys.excepthook = ehook
|
|
Packit |
fd8b60 |
|
|
Packit |
fd8b60 |
import select
|
|
Packit |
fd8b60 |
import socket
|
|
Packit |
fd8b60 |
import struct
|
|
Packit |
fd8b60 |
import sys
|
|
Packit |
fd8b60 |
|
|
Packit |
fd8b60 |
caches = {}
|
|
Packit |
fd8b60 |
cache_uuidmap = {}
|
|
Packit |
fd8b60 |
defname = b'default'
|
|
Packit |
fd8b60 |
next_unique = 1
|
|
Packit |
fd8b60 |
next_uuid = 1
|
|
Packit |
fd8b60 |
|
|
Packit |
fd8b60 |
class KCMOpcodes(object):
|
|
Packit |
fd8b60 |
GEN_NEW = 3
|
|
Packit |
fd8b60 |
INITIALIZE = 4
|
|
Packit |
fd8b60 |
DESTROY = 5
|
|
Packit |
fd8b60 |
STORE = 6
|
|
Packit |
fd8b60 |
GET_PRINCIPAL = 8
|
|
Packit |
fd8b60 |
GET_CRED_UUID_LIST = 9
|
|
Packit |
fd8b60 |
GET_CRED_BY_UUID = 10
|
|
Packit |
fd8b60 |
REMOVE_CRED = 11
|
|
Packit |
fd8b60 |
GET_CACHE_UUID_LIST = 18
|
|
Packit |
fd8b60 |
GET_CACHE_BY_UUID = 19
|
|
Packit |
fd8b60 |
GET_DEFAULT_CACHE = 20
|
|
Packit |
fd8b60 |
SET_DEFAULT_CACHE = 21
|
|
Packit |
fd8b60 |
GET_KDC_OFFSET = 22
|
|
Packit |
fd8b60 |
SET_KDC_OFFSET = 23
|
|
Packit |
fd8b60 |
|
|
Packit |
fd8b60 |
|
|
Packit |
fd8b60 |
class KRB5Errors(object):
|
|
Packit |
fd8b60 |
KRB5_CC_END = -1765328242
|
|
Packit |
fd8b60 |
KRB5_CC_NOSUPP = -1765328137
|
|
Packit |
fd8b60 |
KRB5_FCC_NOFILE = -1765328189
|
|
Packit |
fd8b60 |
|
|
Packit |
fd8b60 |
|
|
Packit |
fd8b60 |
def make_uuid():
|
|
Packit |
fd8b60 |
global next_uuid
|
|
Packit |
fd8b60 |
uuid = bytes(12) + struct.pack('>L', next_uuid)
|
|
Packit |
fd8b60 |
next_uuid = next_uuid + 1
|
|
Packit |
fd8b60 |
return uuid
|
|
Packit |
fd8b60 |
|
|
Packit |
fd8b60 |
|
|
Packit |
fd8b60 |
class Cache(object):
|
|
Packit |
fd8b60 |
def __init__(self, name):
|
|
Packit |
fd8b60 |
self.name = name
|
|
Packit |
fd8b60 |
self.princ = None
|
|
Packit |
fd8b60 |
self.uuid = make_uuid()
|
|
Packit |
fd8b60 |
self.cred_uuids = []
|
|
Packit |
fd8b60 |
self.creds = {}
|
|
Packit |
fd8b60 |
self.time_offset = 0
|
|
Packit |
fd8b60 |
|
|
Packit |
fd8b60 |
|
|
Packit |
fd8b60 |
def get_cache(name):
|
|
Packit |
fd8b60 |
if name in caches:
|
|
Packit |
fd8b60 |
return caches[name]
|
|
Packit |
fd8b60 |
cache = Cache(name)
|
|
Packit |
fd8b60 |
caches[name] = cache
|
|
Packit |
fd8b60 |
cache_uuidmap[cache.uuid] = cache
|
|
Packit |
fd8b60 |
return cache
|
|
Packit |
fd8b60 |
|
|
Packit |
fd8b60 |
|
|
Packit |
fd8b60 |
def unmarshal_name(argbytes):
|
|
Packit |
fd8b60 |
offset = argbytes.find(b'\0')
|
|
Packit |
fd8b60 |
return argbytes[0:offset], argbytes[offset+1:]
|
|
Packit |
fd8b60 |
|
|
Packit |
fd8b60 |
|
|
Packit |
fd8b60 |
def op_gen_new(argbytes):
|
|
Packit |
fd8b60 |
# Does not actually check for uniqueness.
|
|
Packit |
fd8b60 |
global next_unique
|
|
Packit |
fd8b60 |
name = b'unique' + str(next_unique).encode('ascii')
|
|
Packit |
fd8b60 |
next_unique += 1
|
|
Packit |
fd8b60 |
return 0, name + b'\0'
|
|
Packit |
fd8b60 |
|
|
Packit |
fd8b60 |
|
|
Packit |
fd8b60 |
def op_initialize(argbytes):
|
|
Packit |
fd8b60 |
name, princ = unmarshal_name(argbytes)
|
|
Packit |
fd8b60 |
cache = get_cache(name)
|
|
Packit |
fd8b60 |
cache.princ = princ
|
|
Packit |
fd8b60 |
cache.cred_uuids = []
|
|
Packit |
fd8b60 |
cache.creds = {}
|
|
Packit |
fd8b60 |
cache.time_offset = 0
|
|
Packit |
fd8b60 |
return 0, b''
|
|
Packit |
fd8b60 |
|
|
Packit |
fd8b60 |
|
|
Packit |
fd8b60 |
def op_destroy(argbytes):
|
|
Packit |
fd8b60 |
name, rest = unmarshal_name(argbytes)
|
|
Packit |
fd8b60 |
cache = get_cache(name)
|
|
Packit |
fd8b60 |
del cache_uuidmap[cache.uuid]
|
|
Packit |
fd8b60 |
del caches[name]
|
|
Packit |
fd8b60 |
return 0, b''
|
|
Packit |
fd8b60 |
|
|
Packit |
fd8b60 |
|
|
Packit |
fd8b60 |
def op_store(argbytes):
|
|
Packit |
fd8b60 |
name, cred = unmarshal_name(argbytes)
|
|
Packit |
fd8b60 |
cache = get_cache(name)
|
|
Packit |
fd8b60 |
uuid = make_uuid()
|
|
Packit |
fd8b60 |
cache.creds[uuid] = cred
|
|
Packit |
fd8b60 |
cache.cred_uuids.append(uuid)
|
|
Packit |
fd8b60 |
return 0, b''
|
|
Packit |
fd8b60 |
|
|
Packit |
fd8b60 |
|
|
Packit |
fd8b60 |
def op_get_principal(argbytes):
|
|
Packit |
fd8b60 |
name, rest = unmarshal_name(argbytes)
|
|
Packit |
fd8b60 |
cache = get_cache(name)
|
|
Packit |
fd8b60 |
if cache.princ is None:
|
|
Packit |
fd8b60 |
return KRB5Errors.KRB5_FCC_NOFILE, b''
|
|
Packit |
fd8b60 |
return 0, cache.princ + b'\0'
|
|
Packit |
fd8b60 |
|
|
Packit |
fd8b60 |
|
|
Packit |
fd8b60 |
def op_get_cred_uuid_list(argbytes):
|
|
Packit |
fd8b60 |
name, rest = unmarshal_name(argbytes)
|
|
Packit |
fd8b60 |
cache = get_cache(name)
|
|
Packit |
fd8b60 |
return 0, b''.join(cache.cred_uuids)
|
|
Packit |
fd8b60 |
|
|
Packit |
fd8b60 |
|
|
Packit |
fd8b60 |
def op_get_cred_by_uuid(argbytes):
|
|
Packit |
fd8b60 |
name, uuid = unmarshal_name(argbytes)
|
|
Packit |
fd8b60 |
cache = get_cache(name)
|
|
Packit |
fd8b60 |
if uuid not in cache.creds:
|
|
Packit |
fd8b60 |
return KRB5Errors.KRB5_CC_END, b''
|
|
Packit |
fd8b60 |
return 0, cache.creds[uuid]
|
|
Packit |
fd8b60 |
|
|
Packit |
fd8b60 |
|
|
Packit |
fd8b60 |
def op_remove_cred(argbytes):
|
|
Packit |
fd8b60 |
return KRB5Errors.KRB5_CC_NOSUPP, b''
|
|
Packit |
fd8b60 |
|
|
Packit |
fd8b60 |
|
|
Packit |
fd8b60 |
def op_get_cache_uuid_list(argbytes):
|
|
Packit |
fd8b60 |
return 0, b''.join(cache_uuidmap.keys())
|
|
Packit |
fd8b60 |
|
|
Packit |
fd8b60 |
|
|
Packit |
fd8b60 |
def op_get_cache_by_uuid(argbytes):
|
|
Packit |
fd8b60 |
uuid = argbytes
|
|
Packit |
fd8b60 |
if uuid not in cache_uuidmap:
|
|
Packit |
fd8b60 |
return KRB5Errors.KRB5_CC_END, b''
|
|
Packit |
fd8b60 |
return 0, cache_uuidmap[uuid].name + b'\0'
|
|
Packit |
fd8b60 |
|
|
Packit |
fd8b60 |
|
|
Packit |
fd8b60 |
def op_get_default_cache(argbytes):
|
|
Packit |
fd8b60 |
return 0, defname + b'\0'
|
|
Packit |
fd8b60 |
|
|
Packit |
fd8b60 |
|
|
Packit |
fd8b60 |
def op_set_default_cache(argbytes):
|
|
Packit |
fd8b60 |
global defname
|
|
Packit |
fd8b60 |
defname, rest = unmarshal_name(argbytes)
|
|
Packit |
fd8b60 |
return 0, b''
|
|
Packit |
fd8b60 |
|
|
Packit |
fd8b60 |
|
|
Packit |
fd8b60 |
def op_get_kdc_offset(argbytes):
|
|
Packit |
fd8b60 |
name, rest = unmarshal_name(argbytes)
|
|
Packit |
fd8b60 |
cache = get_cache(name)
|
|
Packit |
fd8b60 |
return 0, struct.pack('>l', cache.time_offset)
|
|
Packit |
fd8b60 |
|
|
Packit |
fd8b60 |
|
|
Packit |
fd8b60 |
def op_set_kdc_offset(argbytes):
|
|
Packit |
fd8b60 |
name, obytes = unmarshal_name(argbytes)
|
|
Packit |
fd8b60 |
cache = get_cache(name)
|
|
Packit |
fd8b60 |
cache.time_offset, = struct.unpack('>l', obytes)
|
|
Packit |
fd8b60 |
return 0, b''
|
|
Packit |
fd8b60 |
|
|
Packit |
fd8b60 |
|
|
Packit |
fd8b60 |
ophandlers = {
|
|
Packit |
fd8b60 |
KCMOpcodes.GEN_NEW : op_gen_new,
|
|
Packit |
fd8b60 |
KCMOpcodes.INITIALIZE : op_initialize,
|
|
Packit |
fd8b60 |
KCMOpcodes.DESTROY : op_destroy,
|
|
Packit |
fd8b60 |
KCMOpcodes.STORE : op_store,
|
|
Packit |
fd8b60 |
KCMOpcodes.GET_PRINCIPAL : op_get_principal,
|
|
Packit |
fd8b60 |
KCMOpcodes.GET_CRED_UUID_LIST : op_get_cred_uuid_list,
|
|
Packit |
fd8b60 |
KCMOpcodes.GET_CRED_BY_UUID : op_get_cred_by_uuid,
|
|
Packit |
fd8b60 |
KCMOpcodes.REMOVE_CRED : op_remove_cred,
|
|
Packit |
fd8b60 |
KCMOpcodes.GET_CACHE_UUID_LIST : op_get_cache_uuid_list,
|
|
Packit |
fd8b60 |
KCMOpcodes.GET_CACHE_BY_UUID : op_get_cache_by_uuid,
|
|
Packit |
fd8b60 |
KCMOpcodes.GET_DEFAULT_CACHE : op_get_default_cache,
|
|
Packit |
fd8b60 |
KCMOpcodes.SET_DEFAULT_CACHE : op_set_default_cache,
|
|
Packit |
fd8b60 |
KCMOpcodes.GET_KDC_OFFSET : op_get_kdc_offset,
|
|
Packit |
fd8b60 |
KCMOpcodes.SET_KDC_OFFSET : op_set_kdc_offset
|
|
Packit |
fd8b60 |
}
|
|
Packit |
fd8b60 |
|
|
Packit |
fd8b60 |
# Read and respond to a request from the socket s.
|
|
Packit |
fd8b60 |
def service_request(s):
|
|
Packit |
fd8b60 |
lenbytes = b''
|
|
Packit |
fd8b60 |
while len(lenbytes) < 4:
|
|
Packit |
fd8b60 |
lenbytes += s.recv(4 - len(lenbytes))
|
|
Packit |
fd8b60 |
if lenbytes == b'':
|
|
Packit |
fd8b60 |
return False
|
|
Packit |
fd8b60 |
|
|
Packit |
fd8b60 |
reqlen, = struct.unpack('>L', lenbytes)
|
|
Packit |
fd8b60 |
req = b''
|
|
Packit |
fd8b60 |
while len(req) < reqlen:
|
|
Packit |
fd8b60 |
req += s.recv(reqlen - len(req))
|
|
Packit |
fd8b60 |
|
|
Packit |
fd8b60 |
majver, minver, op = struct.unpack('>BBH', req[:4])
|
|
Packit |
fd8b60 |
argbytes = req[4:]
|
|
Packit |
fd8b60 |
code, payload = ophandlers[op](argbytes)
|
|
Packit |
fd8b60 |
|
|
Packit |
fd8b60 |
# The KCM response is the code (4 bytes) and the response payload.
|
|
Packit |
fd8b60 |
# The Heimdal IPC response is the length of the KCM response (4
|
|
Packit |
fd8b60 |
# bytes), a status code which is essentially always 0 (4 bytes),
|
|
Packit |
fd8b60 |
# and the KCM response.
|
|
Packit |
fd8b60 |
kcm_response = struct.pack('>l', code) + payload
|
|
Packit |
fd8b60 |
hipc_response = struct.pack('>LL', len(kcm_response), 0) + kcm_response
|
|
Packit |
fd8b60 |
s.sendall(hipc_response)
|
|
Packit |
fd8b60 |
return True
|
|
Packit |
fd8b60 |
|
|
Packit |
fd8b60 |
|
|
Packit |
fd8b60 |
server = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
|
|
Packit |
fd8b60 |
server.bind(sys.argv[1])
|
|
Packit |
fd8b60 |
server.listen(5)
|
|
Packit |
fd8b60 |
select_input = [server,]
|
|
Packit |
fd8b60 |
sys.stderr.write('starting...\n')
|
|
Packit |
fd8b60 |
sys.stderr.flush()
|
|
Packit |
fd8b60 |
|
|
Packit |
fd8b60 |
while True:
|
|
Packit |
fd8b60 |
iready, oready, xready = select.select(select_input, [], [])
|
|
Packit |
fd8b60 |
for s in iready:
|
|
Packit |
fd8b60 |
if s == server:
|
|
Packit |
fd8b60 |
client, addr = server.accept()
|
|
Packit |
fd8b60 |
select_input.append(client)
|
|
Packit |
fd8b60 |
else:
|
|
Packit |
fd8b60 |
if not service_request(s):
|
|
Packit |
fd8b60 |
select_input.remove(s)
|
|
Packit |
fd8b60 |
s.close()
|