added command line client
authorScott Baker <bakers@cs.arizona.edu>
Tue, 8 Jul 2008 23:44:27 +0000 (23:44 +0000)
committerScott Baker <bakers@cs.arizona.edu>
Tue, 8 Jul 2008 23:44:27 +0000 (23:44 +0000)
cmdline/clientstub.py [new file with mode: 0644]
cmdline/cliexcep.py [new file with mode: 0644]
cmdline/genicli.py [new file with mode: 0644]
cmdline/report.py [new file with mode: 0644]

diff --git a/cmdline/clientstub.py b/cmdline/clientstub.py
new file mode 100644 (file)
index 0000000..957737a
--- /dev/null
@@ -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 (file)
index 0000000..0c95d12
--- /dev/null
@@ -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 (file)
index 0000000..06cb466
--- /dev/null
@@ -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 <options> command <args>"
+   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 <type> <hrn>"
+   print "    dumpCredential"
+   print "    getCredential <cred_name>"
+   print "    start <hrn>"
+
+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 <type> <hrn>")
+           sys.exit(-1)
+       type = args[1]
+       hrn = args[2]
+
+   elif opname == "getCredential":
+       if len(args) < 1:
+           report.error("syntax: getcredential <cred_name>")
+           sys.exit(-1)
+       cred_name = args[1]
+
+   elif opname == "start":
+       if len(args) < 1:
+           report.error("syntax: start <hrn>")
+           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 (file)
index 0000000..28dd79a
--- /dev/null
@@ -0,0 +1,5 @@
+def trace(x):
+    print x
+
+def error(x):
+    print x