From 0cba0ea72c986e2108436e447662d96006fa4638 Mon Sep 17 00:00:00 2001 From: Scott Baker Date: Tue, 23 Sep 2008 05:45:43 +0000 Subject: [PATCH] added tickets --- util/credential.py | 23 +++++--- util/geniclient.py | 104 ++++++++++++++++++++++++++++++++---- util/geniserver.py | 33 +++++++++++- util/geniticket.py | 128 +++++++++++++++++++++++++++++++++++++++++++++ util/hierarchy.py | 85 +++++++++++++++++++++++++++--- 5 files changed, 347 insertions(+), 26 deletions(-) create mode 100644 util/geniticket.py diff --git a/util/credential.py b/util/credential.py index 7419076c..c4e16ea2 100644 --- a/util/credential.py +++ b/util/credential.py @@ -27,6 +27,9 @@ class Credential(Certificate): def __init__(self, create=False, subject=None, string=None, filename=None): Certificate.__init__(self, create, subject, string, filename) + def create_similar(self): + return Credential() + def set_gid_caller(self, gid): self.gidCaller = gid @@ -83,9 +86,9 @@ class Credential(Certificate): "privileges": None, "delegate": self.delegate} if self.gidCaller: - dict["gidCaller"] = self.gidCaller.save_to_string() + dict["gidCaller"] = self.gidCaller.save_to_string(save_parents=True) if self.gidObject: - dict["gidObject"] = self.gidObject.save_to_string() + dict["gidObject"] = self.gidObject.save_to_string(save_parents=True) if self.privileges: dict["privileges"] = self.privileges.save_to_string() str = xmlrpclib.dumps((dict,), allow_none=True) @@ -123,16 +126,16 @@ class Credential(Certificate): # do the normal certificate verification stuff Certificate.verify_chain(self, trusted_certs) - if parent: + if self.parent: # make sure the parent delegated rights to the child - if not parent.delegate: - raise MissingDelegateBit(self.get_subject()) + if not self.parent.get_delegate(): + raise MissingDelegateBit(self.parent.get_subject()) # XXX todo: make sure child rights are a subset of parent rights return - def dump(self): + def dump(self, dump_parents=False): print "CREDENTIAL", self.get_subject() print " privs:", self.get_privileges().save_to_string() @@ -140,15 +143,19 @@ class Credential(Certificate): print " gidCaller:" gidCaller = self.get_gid_caller() if gidCaller: - gidCaller.dump(indent=8) + gidCaller.dump(8, dump_parents) print " gidObject:" gidObject = self.get_gid_object() if gidObject: - gidObject.dump(indent=8) + gidObject.dump(8, dump_parents) print " delegate:", self.get_delegate() + if self.parent and dump_parents: + print "PARENT", + self.parent.dump(dump_parents) + diff --git a/util/geniclient.py b/util/geniclient.py index c85b8c11..2de52e2f 100644 --- a/util/geniclient.py +++ b/util/geniclient.py @@ -11,6 +11,22 @@ import xmlrpclib from gid import * from credential import * from record import * +from geniticket import * + +# ServerException, ExceptionUnmarshaller +# +# Used to convert server exception strings back to an exception. +# from usenet, Raghuram Devarakonda + +class ServerException(Exception): + pass + +class ExceptionUnmarshaller(xmlrpclib.Unmarshaller): + def close(self): + try: + return xmlrpclib.Unmarshaller.close(self) + except xmlrpclib.Fault, e: + raise ServerException(e.faultString) # GeniTransport # @@ -33,6 +49,11 @@ class GeniTransport(xmlrpclib.Transport): else: return httplib.HTTPS(host, None, key_file=self.key_file, cert_file=self.cert_file) #**(x509 or {})) + def getparser(self): + unmarshaller = ExceptionUnmarshaller() + parser = xmlrpclib.ExpatParser(unmarshaller) + return parser, unmarshaller + # GeniClient: # # Class for performing GeniClient operations. @@ -48,7 +69,15 @@ class GeniClient(): self.transport = GeniTransport() self.transport.key_file = self.key_file self.transport.cert_file = self.cert_file - self.server = xmlrpclib.ServerProxy(self.url, self.transport) + self.server = xmlrpclib.ServerProxy(self.url, self.transport, allow_none=True) + + # ------------------------------------------------------------------------- + # Registry Interface + # ------------------------------------------------------------------------- + + def create_gid(self, cred, name, uuid, pkey_string): + gid_str = self.server.create_gid(cred.save_to_string(save_parents=True), name, uuid, pkey_string) + return GID(string=gid_str) def get_gid(self, name): gid_str_list = self.server.get_gid(name) @@ -64,17 +93,72 @@ class GeniClient(): # get_credential(..., cred=None,...) def get_self_credential(self, type, name): - cred_str = self.server.get_self_credential(type, name) - return Credential(string = cred_str) + cred_str = self.server.get_self_credential(type, name) + return Credential(string = cred_str) def get_credential(self, cred, type, name): - cred_str = self.server.get_credential(cred.save_to_string(), type, name) - return Credential(string = cred_str) + if cred == None: + return self.get_self_credential(type, name) + cred_str = self.server.get_credential(cred.save_to_string(save_parents=True), type, name) + return Credential(string = cred_str) + + def list(self, cred): + result_dict_list = self.server.list(cred.save_to_string(save_parents=True)) + result_rec_list = [] + for dict in result_dict_list: + result_rec_list.append(GeniRecord(dict=dict)) + return result_rec_list + + def register(self, cred, record): + gid_str = self.server.register(cred.save_to_string(save_parents=True), record.as_dict()) + return GID(string = gid_str) + + def remove(self, cred, record): + result = self.server.remove(cred.save_to_string(save_parents=True), record.as_dict()) + return result def resolve(self, cred, name): - result_dict_list = self.server.resolve(cred.save_to_string(), name) - result_rec_list = [] - for dict in result_dict_list: - result_rec_list.append(GeniRecord(dict=dict)) - return result_rec_list + result_dict_list = self.server.resolve(cred.save_to_string(save_parents=True), name) + result_rec_list = [] + for dict in result_dict_list: + result_rec_list.append(GeniRecord(dict=dict)) + return result_rec_list + + def update(self, cred, record): + result = self.server.update(cred.save_to_string(save_parents=True), record.as_dict()) + return result + + # ------------------------------------------------------------------------ + # Slice Interface + # ------------------------------------------------------------------------ + + def start_slice(self, cred): + result = self.server.start_slice(cred.save_to_string(save_parents=True)) + return result + + def stop_slice(self, cred): + result = self.server.stop_slice(cred.save_to_string(save_parents=True)) + return result + + def reset_slice(self, cred): + result = self.server.reset_slice(cred.save_to_string(save_parents=True)) + return result + + def delete_slice(self, cred): + result = self.server.delete_slice(cred.save_to_string(save_parents=True)) + return result + + def list_slices(self, cred): + result = self.server.list_slices(cred.save_to_string(save_parents=True)) + return result + + def get_ticket(self, cred, name, rspec): + ticket_str = self.server.get_ticket(cred.save_to_string(save_parents=True), name, rspec) + ticket = Ticket(string=ticket_str) + return ticket + + def redeem_ticket(self, ticket): + result = self.server.redeem_ticket(ticket.save_to_string(save_parents=True)) + return result + diff --git a/util/geniserver.py b/util/geniserver.py index c4cd60a5..b58080db 100644 --- a/util/geniserver.py +++ b/util/geniserver.py @@ -9,6 +9,8 @@ import SimpleXMLRPCServer +import sys +import traceback import SocketServer import BaseHTTPServer import SimpleHTTPServer @@ -84,7 +86,7 @@ class SecureXMLRPCServer(BaseHTTPServer.HTTPServer,SimpleXMLRPCServer.SimpleXMLR """ self.logRequests = logRequests - SimpleXMLRPCServer.SimpleXMLRPCDispatcher.__init__(self, None, None) + SimpleXMLRPCServer.SimpleXMLRPCDispatcher.__init__(self, True, None) SocketServer.BaseServer.__init__(self, server_address, HandlerClass) ctx = SSL.Context(SSL.SSLv23_METHOD) ctx.use_privatekey_file(key_file) @@ -96,6 +98,20 @@ class SecureXMLRPCServer(BaseHTTPServer.HTTPServer,SimpleXMLRPCServer.SimpleXMLR self.server_bind() self.server_activate() + # _dispatch + # + # Convert an exception on the server to a full stack trace and send it to + # the client. + + def _dispatch(self, method, params): + try: + return SimpleXMLRPCServer.SimpleXMLRPCDispatcher._dispatch(self, method, params) + except: + # can't use format_exc() as it is not available in jython yet (even + # in trunk). + type, value, tb = sys.exc_info() + raise xmlrpclib.Fault(1,''.join(traceback.format_exception(type, value, tb))) + # SecureXMLRpcRequestHandler # # taken from the web (XXX find reference). Implents HTTPS xmlrpc request handler @@ -130,6 +146,7 @@ class SecureXMLRpcRequestHandler(SimpleXMLRPCServer.SimpleXMLRPCRequestHandler): except: # This should only happen if the module is buggy # internal error, report as HTTP server error self.send_response(500) + self.end_headers() else: # got a valid XML RPC response @@ -158,9 +175,10 @@ class GeniServer(): self.key = Keypair(filename = key_file) self.cert = Certificate(filename = cert_file) self.server = SecureXMLRPCServer((ip, port), SecureXMLRpcRequestHandler, key_file, cert_file) + self.trusted_cert_list = None self.register_functions() - def decode_authentication(self, cred_string): + def decode_authentication(self, cred_string, operation): self.client_cred = Credential(string = cred_string) self.client_gid = self.client_cred.get_gid_caller() self.object_gid = self.client_cred.get_gid_object() @@ -174,6 +192,17 @@ class GeniServer(): if not peer_cert.is_pubkey(self.client_gid.get_pubkey()): raise ConnectionKeyGIDMismatch(self.client_gid.get_subject()) + # make sure the client is allowed to perform the operation + if not self.client_cred.can_perform(operation): + raise InsufficientRights(operation) + + if self.trusted_cert_list: + self.client_cred.verify_chain(self.trusted_cert_list) + if self.client_gid: + self.client_gid.verify_chain(self.trusted_cert_list) + if self.object_gid: + self.object_gid.verify_chain(self.trusted_cert_list) + # register_functions override this to add more functions def register_functions(self): self.server.register_function(self.noop) diff --git a/util/geniticket.py b/util/geniticket.py new file mode 100644 index 00000000..1869e581 --- /dev/null +++ b/util/geniticket.py @@ -0,0 +1,128 @@ +# tickets.py +# +# implements GENI tickets +# + +from cert import * +from rights import * +from gid import * +import xmlrpclib + +# Ticket is tuple: +# (gidCaller, gidObject, attributes, rspec, delegate) +# +# gidCaller = GID of the caller performing the operation +# gidObject = GID of the slice +# attributes = slice attributes (keys, vref, instantiation, etc) +# rspec = resources + +class Ticket(Certificate): + gidCaller = None + gidObject = None + attributes = {} + rspec = {} + delegate = False + + def __init__(self, create=False, subject=None, string=None, filename=None): + Certificate.__init__(self, create, subject, string, filename) + + def set_gid_caller(self, gid): + self.gidCaller = gid + + def get_gid_caller(self): + if not self.gidCaller: + self.decode() + return self.gidCaller + + def set_gid_object(self, gid): + self.gidObject = gid + + def get_gid_object(self): + if not self.gidObject: + self.decode() + return self.gidObject + + def set_attributes(self, gid): + self.attributes = gid + + def get_attributes(self): + if not self.attributes: + self.decode() + return self.attributes + + def set_rspec(self, gid): + self.rspec = gid + + def get_rspec(self): + if not self.rspec: + self.decode() + return self.rspec + + def set_delegate(self, delegate): + self.delegate = delegate + + def get_delegate(self): + if not self.delegate: + self.decode() + return self.delegate + + def encode(self): + dict = {"gidCaller": None, + "gidObject": None, + "attributes": self.attributes, + "rspec": self.rspec, + "delegate": self.delegate} + if self.gidCaller: + dict["gidCaller"] = self.gidCaller.save_to_string(save_parents=True) + if self.gidObject: + dict["gidObject"] = self.gidObject.save_to_string(save_parents=True) + str = xmlrpclib.dumps((dict,), allow_none=True) + self.set_data(str) + + def decode(self): + data = self.get_data() + if data: + dict = xmlrpclib.loads(self.get_data())[0][0] + else: + dict = {} + + self.attributes = dict.get("attributes", {}) + self.rspec = dict.get("rspec", {}) + self.delegate = dict.get("delegate", False) + + gidCallerStr = dict.get("gidCaller", None) + if gidCallerStr: + self.gidCaller = GID(string=gidCallerStr) + else: + self.gidCaller = None + + gidObjectStr = dict.get("gidObject", None) + if gidObjectStr: + self.gidObject = GID(string=gidObjectStr) + else: + self.gidObject = None + + def dump(self, dump_parents=False): + print "TICKET", self.get_subject() + + print " gidCaller:" + gidCaller = self.get_gid_caller() + if gidCaller: + gidCaller.dump(8, dump_parents) + + print " gidObject:" + gidObject = self.get_gid_object() + if gidObject: + gidObject.dump(8, dump_parents) + + print " attributes:" + for attrname in self.get_attributes().keys(): + print " ", attrname, self.get_attributes()[attrname] + + print " rspec:" + for attrname in self.get_rspec().keys(): + print " ", attrname, self.get_rspec()[attrname] + + if self.parent and dump_parents: + print "PARENT", + self.parent.dump(dump_parents) diff --git a/util/hierarchy.py b/util/hierarchy.py index f76f8191..e83b4d4b 100644 --- a/util/hierarchy.py +++ b/util/hierarchy.py @@ -12,24 +12,33 @@ import os import report from cert import * +from credential import * from gid import * from misc import * from config import * +from geniticket import * class AuthInfo(): hrn = None + gid_object = None gid_filename = None privkey_filename = None dbinfo_filename = None def __init__(self, hrn, gid_filename, privkey_filename, dbinfo_filename): self.hrn = hrn - self.gid_filename = gid_filename + self.set_gid_filename(gid_filename) self.privkey_filename = privkey_filename self.dbinfo_filename = dbinfo_filename + def set_gid_filename(self, fn): + self.gid_filename = fn + self.gid_object = None + def get_gid_object(self): - return GID(filename = self.gid_filename) + if not self.gid_object: + self.gid_object = GID(filename = self.gid_filename) + return self.gid_object def get_pkey_object(self): return Keypair(filename = self.privkey_filename) @@ -39,10 +48,14 @@ class AuthInfo(): dict = eval(f.read()) f.close() return dict + + def update_gid_object(self, gid): + gid.save_to_file(self.gid_filename) + self.gid_object = gid class Hierarchy(): def __init__(self, basedir="."): - self.basedir = basedir + self.basedir = os.path.join(basedir, "authorities") def get_auth_filenames(self, hrn): leaf = get_leaf(hrn) @@ -86,7 +99,7 @@ class Hierarchy(): pkey.save_to_file(privkey_filename) gid = self.create_gid(hrn, create_uuid(), pkey) - gid.save_to_file(gid_filename) + gid.save_to_file(gid_filename, save_parents=True) # XXX TODO: think up a better way for the dbinfo to work @@ -106,13 +119,18 @@ class Hierarchy(): auth_info = AuthInfo(hrn, gid_filename, privkey_filename, dbinfo_filename) + # check the GID and see if it needs to be refreshed + gid = auth_info.get_gid_object() + gid_refreshed = self.refresh_gid(gid) + if gid != gid_refreshed: + auth_info.update_gid_object(gid_refreshed) + return auth_info def create_gid(self, hrn, uuid, pkey): - parent_hrn = get_authority(hrn) - gid = GID(subject=hrn, uuid=uuid, hrn=hrn) + parent_hrn = get_authority(hrn) if not parent_hrn: # if there is no parent hrn, then it must be self-signed. this # is where we terminate the recursion @@ -146,3 +164,58 @@ class Hierarchy(): return gid + def get_auth_cred(self, hrn): + auth_info = self.get_auth_info(hrn) + gid = auth_info.get_gid_object() + + cred = Credential(subject=hrn) + cred.set_gid_caller(gid) + cred.set_gid_object(gid) + cred.set_privileges("authority") + cred.set_delegate(True) + cred.set_pubkey(auth_info.get_gid_object().get_pubkey()) + + parent_hrn = get_authority(hrn) + if not parent_hrn: + # if there is no parent hrn, then it must be self-signed. this + # is where we terminate the recursion + cred.set_issuer(auth_info.get_pkey_object(), hrn) + else: + # we need the parent's private key in order to sign this GID + parent_auth_info = self.get_auth_info(parent_hrn) + cred.set_issuer(parent_auth_info.get_pkey_object(), parent_auth_info.hrn) + cred.set_parent(self.get_auth_cred(parent_hrn)) + + cred.encode() + cred.sign() + + return cred + + # this looks almost the same as get_auth_cred, but works for tickets + # XXX does similarity imply there should be more code re-use? + def get_auth_ticket(self, hrn): + auth_info = self.get_auth_info(hrn) + gid = auth_info.get_gid_object() + + ticket = Ticket(subject=hrn) + ticket.set_gid_caller(gid) + ticket.set_gid_object(gid) + ticket.set_delegate(True) + ticket.set_pubkey(auth_info.get_gid_object().get_pubkey()) + + parent_hrn = get_authority(hrn) + if not parent_hrn: + # if there is no parent hrn, then it must be self-signed. this + # is where we terminate the recursion + ticket.set_issuer(auth_info.get_pkey_object(), hrn) + else: + # we need the parent's private key in order to sign this GID + parent_auth_info = self.get_auth_info(parent_hrn) + ticket.set_issuer(parent_auth_info.get_pkey_object(), parent_auth_info.hrn) + ticket.set_parent(self.get_auth_cred(parent_hrn)) + + ticket.encode() + ticket.sign() + + return ticket + -- 2.43.0