From fbade8296f8113b846a2e2cc5191fc241f550d66 Mon Sep 17 00:00:00 2001 From: Scott Baker Date: Tue, 8 Jul 2008 23:44:27 +0000 Subject: [PATCH] added command line client --- cmdline/clientstub.py | 176 ++++++++++++++++++++++++++++++++++ cmdline/cliexcep.py | 13 +++ cmdline/genicli.py | 218 ++++++++++++++++++++++++++++++++++++++++++ cmdline/report.py | 5 + 4 files changed, 412 insertions(+) create mode 100644 cmdline/clientstub.py create mode 100644 cmdline/cliexcep.py create mode 100644 cmdline/genicli.py create mode 100644 cmdline/report.py diff --git a/cmdline/clientstub.py b/cmdline/clientstub.py new file mode 100644 index 00000000..957737a1 --- /dev/null +++ b/cmdline/clientstub.py @@ -0,0 +1,176 @@ +#!/usr/bin/python + +import os, sys +from M2Crypto import SSL +from sec import * +from cliexcep import * +import report + +# XXX SMBAKER: changed MAX_RESULT from 3000B to 32KB +MAX_RESULT = 32768 + +def verify_callback(preverify_ok, ctx): + return 1 + +class GENIClient(): + def __init__(self, hrn, type, id_file, id_key_file, acc_file, cred_file): + self.hrn = hrn + self.type = type + + #check if the certificate and the private key exists, terminate if not + if not os.path.exists(id_file): + report.error("Certificate file " + id_file + " does not exist") + raise NonexistingFile(id_file) + + if not os.path.exists(id_key_file): + report.error("Key file: " + id_key_file + " does not exist") + raise NonexistingFile(key_file) + + report.trace("cert: " + id_file + ", key_file: " + id_key_file) + + #check the acc and cred files + if not os.path.exists(acc_file) or not is_valid_chain(acc_file): + report.trace("replacing acc_file: " + acc_file + " with anonymous acc") + open(acc_file, 'w').write('ANONYM') + + if not os.path.exists(cred_file) or not is_valid_chain(cred_file): + report.trace("replacing cred_file: " + cred_file + " with no_cred") + open(cred_file, 'w').write('NO_CRED') + + #initialize the security system + self.sec = Sec('client', id_file, id_key_file, acc_file, cred_file) + #ssl parameters + self.ctx = SSL.Context() + self.ctx.load_cert(self.sec.id_file, self.sec.id_key_file) + self.ctx.set_verify(SSL.verify_peer | SSL.verify_fail_if_no_peer_cert, depth=9, callback=verify_callback) + + def connect(self, host, port): + #if the acc and cred needs renewal then do call to authority + if self.type == 'user' or self.type == 'slice' or self.type == 'SA': + reg_type = 'slice' + else: + reg_type ='component' + + auth_host = host + auth_port = port + + report.trace("renewing accounting") + renew_res1 = renew_cert('accounting', '.', reg_type, self.hrn, None, None, (auth_host, auth_port), self.sec) + if renew_res1 == None: + report.error("There is no certificate in the directory .") + raise NoCertInDirectory(".") + + report.trace("renewing credential") + renew_res2 = renew_cert('credential', '.', reg_type, self.hrn, None, None, (auth_host, auth_port), self.sec) + # XXX check result of renew_res2 ? + + #connect to server + server = SSL.Connection(self.ctx) + + report.trace("connecting") + server.connect((host,port)) + + report.trace("authenticating") + peer = self.sec.auth_protocol(server) + if peer: + report.trace("Authentication successful") + return server + else: + report.error("Authentication failed") + raise AuthenticationFailed() + +def toFileFormat(res_str): + out_str = "" + try: + res_dict = eval(res_str) + if res_dict['geni'].has_key('pubkey'): # in public key, replace '\n' with ' ' + pubkey = res_dict['geni']['pubkey'] + pubkey = pubkey.split('-----BEGIN RSA PRIVATE KEY-----')[1].split('-----END RSA PRIVATE KEY-----')[0].replace('\n',' ') + pubkey = '-----BEGIN RSA PRIVATE KEY-----'+pubkey+'-----END RSA PRIVATE KEY-----' + res_dict['geni']['pubkey'] = pubkey + + if res_dict.has_key('message'): + out_str = res_dict['message']+'\n' + else: + out_str = "{'geni':{\n" + for key in res_dict['geni']: + val = '' + if res_dict['geni'][key] == None: + val = '' + elif isinstance(res_dict['geni'][key], str): + val = res_dict['geni'][key] + else: + val = str(res_dict['geni'][key]) + out_str = out_str+"'"+key+"':"+val+"\n" + out_str = out_str + "}\n" + out_str = out_str + "'pl':{\n" + for key in res_dict['pl']: + val = '' + if res_dict['pl'][key] == None: + val = '' + if isinstance(res_dict['pl'][key], str): + val = res_dict['pl'][key] + else: + val = str(res_dict['pl'][key]) + out_str = out_str+"'"+key+"':"+val+"\n" + out_str = out_str + "}}" + except: + out_str = res_str + return out_str + +def evaluate(call_data): + call_data = eval(call_data) + #adjust the key format to obey server's storage format + if call_data['g_params'].has_key('pubkey'): #replace the ' ' with '\n' + pubkey = call_data['g_params']['pubkey'] + pubkey = pubkey.split('-----BEGIN RSA PRIVATE KEY-----')[1].split('-----END RSA PRIVATE KEY-----')[0].replace(' ','\n') + pubkey = '-----BEGIN RSA PRIVATE KEY-----'+pubkey+'-----END RSA PRIVATE KEY-----' + call_data['g_params']['pubkey'] = pubkey + return call_data + +def oldmain(): + try: + #read the input file + fp = open('tmp_input.txt', 'r') + user_data = fp.readline() + call_data = fp.read() + print 'Read file.\n' + + #client related info + HRN = user_data.split(' ')[0] + TYPE = user_data.split(' ')[1].split('\n')[0] + name = get_leaf(HRN) + ID_FILE = name+'.cert' + ID_KEY_FILE = name+'.pkey' + ACC_FILE = 'acc_file' + CRED_FILE = 'cred_file' + my_client = GENIClient(HRN, TYPE, ID_FILE, ID_KEY_FILE, ACC_FILE, CRED_FILE) + print 'Constructed client.\n' + + #operation call + message = evaluate(call_data) + server = my_client.connect(SERVER_HOST, SERVER_PORT) + if server: + server.write(str(message)) + result = toFileFormat(server.read(MAX_RESULT)) + server.close() + print 'Performed the call.\n' + else: + result = "Error in client data structures.\n" + + #write result to output file + open('tmp_output.txt','w').write(result) + print 'Written to file.\n' + except "XXX": # XXX smbaker + #write result to output file + open('tmp_output.txt','w').write("An error occurred in client stub.\n") + print 'Exception occurred.\n' + +#if __name__=="__main__": +# print 'Client started.\n' +# os.system("echo foo > foo.txt") +# os.system("mv tmp_input.3 tmp_input.4") +# os.system("mv tmp_input.2 tmp_input.3") +# os.system("mv tmp_input.1 tmp_input.2") +# os.system("cp tmp_input.txt tmp_input.1") +# main() diff --git a/cmdline/cliexcep.py b/cmdline/cliexcep.py new file mode 100644 index 00000000..0c95d128 --- /dev/null +++ b/cmdline/cliexcep.py @@ -0,0 +1,13 @@ +from excep import * + +class NoCertInDirectory(Exception): + def __init__(self, value): + self.value = value + def __str__(self): + return repr(self.value) + +class AuthenticationFailed(Exception): + def __init__(self, value): + self.value = value + def __str__(self): + return repr(self.value) diff --git a/cmdline/genicli.py b/cmdline/genicli.py new file mode 100644 index 00000000..06cb4663 --- /dev/null +++ b/cmdline/genicli.py @@ -0,0 +1,218 @@ +# command line interface + +import getopt +import sys +from clientstub import * + +long_opts = ["username=", "usertype=", "help", "outfile=", "credfile="] + +# default command line options +username = "planetlab.scott.pl.smbaker" +usertype = "user" +opname = "lookup" +type = None +hrn = None +cred_name = None +id_file = None +key_file = None +acc_file = None +cred_file = None +leaf_name = None +server_host = "127.0.0.1" +server_port = 8002 +out_file = None + +def showhelp(): + print "syntax: cli command " + print "options:" + print " --username ... hrn of user performing op" + print " --usertype ... kind of user performing op (user, slice, ...)" + print " --outfile ... write reply to file" + print " --credfile ... credential to pass" + print "commands:" + print " lookup " + print " dumpCredential" + print " getCredential " + print " start " + +def process_options(): + global username, usertype, opname + global type, hrn, cred_name + global leaf_name + global id_file, cred_file + global acc_file, key_file, out_file + + (options, args) = getopt.getopt(sys.argv[1:], '', long_opts) + for opt in options: + name = opt[0] + val = opt[1] + + if name == "--username": + username = val + elif name == "--usertype": + usertype = val + elif name == "--help": + showhelp() + sys.exit(0) + elif name == "--outfile": + out_file = val + elif name == "--credfile": + cred_file = val + + if not args: + report.error("no operation specified") + sys.exit(-1) + + opname = args[0] + + if opname == "lookup": + if len(args) < 3: + report.error("syntax: lookup ") + sys.exit(-1) + type = args[1] + hrn = args[2] + + elif opname == "getCredential": + if len(args) < 1: + report.error("syntax: getcredential ") + sys.exit(-1) + cred_name = args[1] + + elif opname == "start": + if len(args) < 1: + report.error("syntax: start ") + sys.exit(-1) + hrn = args[1] + + if not leaf_name: + leaf_name = get_leaf(username) + + if id_file == None: + id_file = leaf_name + ".cert" + + if key_file == None: + key_file = leaf_name + ".pkey" + + if acc_file == None: + acc_file = "acc_file" + + if cred_file == None: + cred_file = "cred_file" + +def show_options(): + print " username:", username + print " leaf:", leaf_name + print " usertype:", usertype + print " id_file:", id_file + print " key_file:", key_file + print " acc_file:", acc_file + print "cred_file:", cred_file + print "operation:", opname + print " type:", type + print " hrn:", hrn + print "cred_name:", cred_name + print " out_file:", out_file + +def get_authority(x): + parts = x.split(".") + return ".".join(parts[:3]) + +def compose_message(): + g_params = {} + p_params = {} + dict = {"opname": opname} + + if opname == "lookup": + g_params["hrn"] = hrn + g_params["type"] = type + + elif opname == "getCredential": + g_params["cred_name"] = cred_name + + parts = cred_name.split(":") + if len(parts) < 2: + report.error("bad format for getCredential (slice:hrn.of.slice, ...)") + + # XXX smbaker: this looks redundant + if parts[0] == "slice": + g_params["hrn"] = get_authority(parts[1]) + g_params["type"] = "slice" + + elif opname == "start": + g_params["hrn"] = hrn + g_params["type"] = "slice" + + dict["g_params"] = g_params + dict["p_params"] = p_params + + return dict + +def do_remote_op(): + message = compose_message() + + client = GENIClient(username, usertype, id_file, key_file, acc_file, cred_file) + + server = client.connect(server_host, server_port) + if not server: + report.error("failed to connect to server") + sys.exit(-1) + + report.trace("message:" + str(message)) + + server.write(str(message)) + + reply = server.read(MAX_RESULT) + if not reply: + report.error("No reply") + sys.exit(-1) + + if out_file: + open(out_file, "w").write(reply) + else: + print "////// RESULT: //////" + print reply + +def dumpCredential(): + cred_str = open(cred_file).read() + c_pem = X509.load_cert_string(cred_str) + subjectAltName = c_pem.get_ext("subjectAltName").get_value() + info_cert = get_cred_info(subjectAltName) + + print "subject:", c_pem.get_subject().CN + print "issuer:", c_pem.get_issuer().CN + print "cred_str:" + print " ", subjectAltName + print "rights:" + op_set = info_cert['operation_set'] + for item in op_set.keys(): + rights = op_set[item] + print " ", item, ", ".join(rights) + + print "interfaces:" + interfaces = info_cert['on_interfaces'] + for item in interfaces: + print " ", item['lbl'], item['type'], item['name'] + +def main(): + process_options() + show_options() + + if opname == "dumpCredential": + dumpCredential() + sys.exit(0) + + elif opname == "help": + showhelp() + sys.exit(0) + + elif (opname == "lookup") or \ + (opname == "getCredential") or \ + (opname == "start"): + do_remote_op() + + else: + report.error("unknown operation: " + opname) + +if __name__=="__main__": + main() + diff --git a/cmdline/report.py b/cmdline/report.py new file mode 100644 index 00000000..28dd79ae --- /dev/null +++ b/cmdline/report.py @@ -0,0 +1,5 @@ +def trace(x): + print x + +def error(x): + print x -- 2.43.0