Blame src/tests/kcmserver.py

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()