def delete(self): """This method handles the DELETE requests to remove agents from the Cloud Verifier. Currently, only agents resources are available for DELETEing, i.e. /agents. All other DELETE uri's will return errors. agents requests require a single agent_id parameter which identifies the agent to be deleted. """ rest_params = common.get_restful_params(self.request.uri) if rest_params is None: common.echo_json_response( self, 405, "Not Implemented: Use /agents/ interface") return if "agents" not in rest_params: common.echo_json_response(self, 400, "uri not supported") logger.warning( 'DELETE returning 400 response. uri not supported: ' + self.request.path) return agent_id = rest_params["agents"] # let Tenant do dirty work of deleting agent mytenant = tenant.Tenant() mytenant.agent_uuid = agent_id mytenant.do_cvdelete() common.echo_json_response(self, 200, "Success")
def put(self): """This method handles the PUT requests to add instances to the Cloud Verifier. Currently, only instances resources are available for PUTing, i.e. /v2/instances. All other PUT uri's will return errors. instances requests require a json block sent in the body """ try: rest_params = common.get_restful_params(self.request.path) if rest_params is None: common.echo_json_response( self, 405, "Not Implemented: Use /v2/instances/ interface") return if "instances" not in rest_params: common.echo_json_response(self, 400, "uri not supported") logger.warning( 'PUT returning 400 response. uri not supported: ' + self.request.path) return instance_id = rest_params["instances"] if instance_id is None: common.echo_json_response(self, 400, "uri not supported") logger.warning("PUT returning 400 response. uri not supported") instance = self.db.get_instance(instance_id) if instance is not None: common.echo_json_response(self, 404, "instance id not found") logger.info('PUT returning 404 response. instance id: ' + instance_id + ' not found.') if "reactivate" in rest_params: instance[ 'operational_state'] = cloud_verifier_common.CloudInstance_Operational_State.START self.process_instance( instance, cloud_verifier_common. CloudInstance_Operational_State.GET_QUOTE) common.echo_json_response(self, 200, "Success") logger.info('PUT returning 200 response for instance id: ' + instance_id) elif "stop" in rest_params: # do stuff for terminate logger.debug("Stopping polling on %s" % instance_id) self.db.update_instance( instance_id, 'operational_state', cloud_verifier_common. CloudInstance_Operational_State.TENANT_FAILED) common.echo_json_response(self, 200, "Success") logger.info('PUT returning 200 response for instance id: ' + instance_id) else: common.echo_json_response(self, 400, "uri not supported") logger.warning("PUT returning 400 response. uri not supported") except Exception as e: common.echo_json_response(self, 400, "Exception error: %s" % e) logger.warning("PUT returning 400 response. Exception error: %s" % e) logger.warning(traceback.format_exc()) self.finish()
def put(self): """This method handles the PUT requests to add agents to the Cloud Verifier. Currently, only agents resources are available for PUTing, i.e. /agents. All other PUT uri's will return errors. """ rest_params = common.get_restful_params(self.request.uri) if rest_params is None: common.echo_json_response( self, 405, "Not Implemented: Use /agents/ interface") return if "agents" not in rest_params: common.echo_json_response(self, 400, "uri not supported") logger.warning('PUT returning 400 response. uri not supported: ' + self.request.path) return agent_id = rest_params["agents"] # let Tenant do dirty work of reactivating agent mytenant = tenant.Tenant() mytenant.agent_uuid = agent_id mytenant.do_cvreactivate() common.echo_json_response(self, 200, "Success")
def do_DELETE(self): """This method handles the DELETE requests to remove agents from the Registrar Server. Currently, only agents resources are available for DELETEing, i.e. /agents. All other DELETE uri's will return errors. agents requests require a single agent_id parameter which identifies the agent to be deleted. """ rest_params = common.get_restful_params(self.path) if rest_params is None: common.echo_json_response( self, 405, "Not Implemented: Use /agents/ interface") return if "agents" not in rest_params: common.echo_json_response(self, 400, "uri not supported") logger.warning( 'DELETE agent returning 400 response. uri not supported: ' + self.path) return agent_id = rest_params["agents"] if agent_id is not None: if self.server.db.remove_agent(agent_id): #send response common.echo_json_response(self, 200, "Success") return else: #send response common.echo_json_response(self, 404) return else: common.echo_json_response(self, 404) return
def do_GET(self): """This method handles the GET requests to retrieve status on instances from the Registrar Server. Currently, only instances resources are available for GETing, i.e. /v2/instances. All other GET uri's will return errors. instances requests require a single instance_id parameter which identifies the instance to be returned. If the instance_id is not found, a 404 response is returned. """ rest_params = common.get_restful_params(self.path) if rest_params is None: common.echo_json_response( self, 405, "Not Implemented: Use /v2/instances/ interface") return if "instances" not in rest_params: common.echo_json_response(self, 400, "uri not supported") logger.warning('GET returning 400 response. uri not supported: ' + self.path) return instance_id = rest_params["instances"] if instance_id is not None: instance = self.server.db.get_instance(instance_id) if instance is None: common.echo_json_response(self, 404, "instance_id not found") logger.warning('GET returning 404 response. instance_id ' + instance_id + ' not found.') return if not instance['active']: common.echo_json_response(self, 404, "instance_id not yet active") logger.warning('GET returning 404 response. instance_id ' + instance_id + ' not yet active.') return response = { 'aik': instance['aik'], 'ek': instance['ek'], 'ekcert': instance['ekcert'], 'regcount': instance['regcount'], } if instance['virtual']: response['provider_keys'] = instance['provider_keys'] common.echo_json_response(self, 200, "Success", response) logger.info('GET returning 200 response for instance_id:' + instance_id) else: # return the available registered uuids from the DB json_response = self.server.db.get_instance_ids() common.echo_json_response(self, 200, "Success", {'uuids': json_response}) logger.info('GET returning 200 response for instance_id list') return
def post(self): """This method handles the POST requests to add instances to the Cloud Verifier. Currently, only instances resources are available for POSTing, i.e. /v2/instances. All other POST uri's will return errors. instances requests require a json block sent in the body """ try: rest_params = common.get_restful_params(self.request.path) if rest_params is None: common.echo_json_response(self, 405, "Not Implemented: Use /v2/instances/ interface") return if "instances" not in rest_params: common.echo_json_response(self, 400, "uri not supported") logger.warning('POST returning 400 response. uri not supported: ' + self.request.path) return instance_id = rest_params["instances"] if instance_id is not None: # this is for new items content_length = len(self.request.body) if content_length==0: common.echo_json_response(self, 400, "Expected non zero content length") logger.warning('POST returning 400 response. Expected non zero content length.') else: json_body = json.loads(self.request.body) d = {} d['v'] = json_body['v'] d['ip'] = json_body['cloudnode_ip'] d['port'] = int(json_body['cloudnode_port']) d['operational_state'] = cloud_verifier_common.CloudInstance_Operational_State.START d['public_key'] = "" d['tpm_policy'] = json_body['tpm_policy'] d['vtpm_policy'] = json_body['vtpm_policy'] d['metadata'] = json_body['metadata'] d['ima_whitelist'] = json_body['ima_whitelist'] d['revocation_key'] = json_body['revocation_key'] new_instance = self.db.add_instance(instance_id,d) # don't allow overwriting if new_instance is None: common.echo_json_response(self, 409, "Node of uuid %s already exists"%(instance_id)) logger.warning("Node of uuid %s already exists"%(instance_id)) else: self.process_instance(new_instance, cloud_verifier_common.CloudInstance_Operational_State.GET_QUOTE) common.echo_json_response(self, 200, "Success") logger.info('POST returning 200 response for adding instance id: ' + instance_id) else: common.echo_json_response(self, 400, "uri not supported") logger.warning("POST returning 400 response. uri not supported") except Exception as e: common.echo_json_response(self, 400, "Exception error: %s"%e) logger.warning("POST returning 400 response. Exception error: %s"%e) logger.warning(traceback.format_exc()) self.finish()
def post(self): """This method handles the POST requests to add agents to the Agent Monitor. Currently, only agents resources are available for POSTing, i.e. /agents. All other POST uri's will return errors. agents requests require a json block sent in the body """ logger.info('Agent Monitor POST') try: rest_params = common.get_restful_params(self.request.path) if "agents" not in rest_params: common.echo_json_response(self, 400, "uri not supported") logger.warning('POST returning 400 response. uri not supported: ' + self.request.path) return agent_id = rest_params["agents"] if agent_id is not None: # we have to know who phoned home content_length = len(self.request.body) if content_length==0: common.echo_json_response(self, 400, "Expected non zero content length") logger.warning('POST returning 400 response. Expected non zero content length.') else: json_body = json.loads(self.request.body) # VERIFY CLIENT CERT ID MATCHES AGENT ID (agent_id) client_cert = self.request.get_ssl_certificate() ssl.match_hostname(client_cert, agent_id) # Execute specified script if all is well global initscript if initscript is not None and initscript is not "": def initthread(): import subprocess logger.debug("Executing specified script: %s"%initscript) env = os.environ.copy() env['AGENT_UUID']=agent_id proc= subprocess.Popen(["/bin/sh",initscript],env=env,shell=False, stdout=subprocess.PIPE,stderr=subprocess.STDOUT) proc.wait() while True: line = proc.stdout.readline() if line=="": break logger.debug("init-output: %s"%line.strip()) t = threading.Thread(target=initthread) t.start() common.echo_json_response(self, 200, "Success", json_body) logger.info('POST returning 200 response for Agent Monitor connection as ' + agent_id) else: common.echo_json_response(self, 400, "uri not supported") logger.warning("POST returning 400 response. uri not supported") except Exception as e: common.echo_json_response(self, 400, "Exception error: %s"%e) logger.warning("POST returning 400 response. Exception error: %s"%e) logger.exception(e)
def delete(self): """This method handles the DELETE requests to remove agents from the Cloud Verifier. Currently, only agents resources are available for DELETEing, i.e. /agents. All other DELETE uri's will return errors. agents requests require a single agent_id parameter which identifies the agent to be deleted. """ rest_params = common.get_restful_params(self.request.uri) if rest_params is None: common.echo_json_response( self, 405, "Not Implemented: Use /agents/ interface") return if "agents" not in rest_params: common.echo_json_response(self, 400, "uri not supported") return agent_id = rest_params["agents"] if agent_id is None: common.echo_json_response(self, 400, "uri not supported") logger.warning( 'DELETE returning 400 response. uri not supported: ' + self.request.path) agent = self.db.get_agent(agent_id) if agent is None: common.echo_json_response(self, 404, "agent id not found") logger.info('DELETE returning 404 response. agent id: ' + agent_id + ' not found.') return op_state = agent['operational_state'] if op_state == cloud_verifier_common.CloudAgent_Operational_State.SAVED or \ op_state == cloud_verifier_common.CloudAgent_Operational_State.FAILED or \ op_state == cloud_verifier_common.CloudAgent_Operational_State.INVALID_QUOTE: self.db.remove_agent(agent_id) common.echo_json_response(self, 200, "Success") logger.info('DELETE returning 200 response for agent id: ' + agent_id) else: self.db.update_agent( agent_id, 'operational_state', cloud_verifier_common.CloudAgent_Operational_State.TERMINATED) common.echo_json_response(self, 202, "Accepted") logger.info('DELETE returning 202 response for agent id: ' + agent_id)
def get(self): """This method handles the GET requests to retrieve status on instances from the Cloud Verifier. Currently, only instances resources are available for GETing, i.e. /v2/instances. All other GET uri's will return errors. instances requests require a single instance_id parameter which identifies the instance to be returned. If the instance_id is not found, a 404 response is returned. If the instance_id was not found, it either completed successfully, or failed. If found, the instance_id is still polling to contact the Cloud Node. """ rest_params = common.get_restful_params(self.request.path) if rest_params is None: common.echo_json_response( self, 405, "Not Implemented: Use /v2/instances/ interface") return if "instances" not in rest_params: common.echo_json_response(self, 400, "uri not supported") logger.warning('GET returning 400 response. uri not supported: ' + self.request.path) return instance_id = rest_params["instances"] if instance_id is not None: instance = self.db.get_instance(instance_id) if instance != None: response = cloud_verifier_common.process_get_status(instance) common.echo_json_response(self, 200, "Success", response) #logger.info('GET returning 200 response for instance_id: ' + instance_id) else: #logger.info('GET returning 404 response. instance id: ' + instance_id + ' not found.') common.echo_json_response(self, 404, "instance id not found") else: # return the available keys in the DB json_response = self.db.get_instance_ids() common.echo_json_response(self, 200, "Success", {'uuids': json_response}) logger.info('GET returning 200 response for instance_id list')
def do_GET(self): """This method services the GET request typically from either the Tenant or the Cloud Verifier. Only tenant and cloudverifier uri's are supported. Both requests require a nonce parameter. The Cloud verifier requires an additional mask paramter. If the uri or parameters are incorrect, a 400 response is returned. """ logger.info('GET invoked from ' + str(self.client_address) + ' with uri:' + self.path) rest_params = common.get_restful_params(self.path) if rest_params is None: common.echo_json_response(self, 405, "Not Implemented: Use /keys/ or /quotes/ interfaces") return if "keys" in rest_params and rest_params['keys']=='verify': if self.server.K is None: logger.info('GET key challenge returning 400 response. bootstrap key not available') common.echo_json_response(self, 400, "Bootstrap key not yet available.") return challenge = rest_params['challenge'] response={} response['hmac'] = crypto.do_hmac(self.server.K, challenge) common.echo_json_response(self, 200, "Success", response) logger.info('GET key challenge returning 200 response.') # If agent pubkey requested elif "keys" in rest_params and rest_params["keys"] == "pubkey": response = {} response['pubkey'] = self.server.rsapublickey_exportable common.echo_json_response(self, 200, "Success", response) logger.info('GET pubkey returning 200 response.') return elif "quotes" in rest_params: nonce = rest_params['nonce'] pcrmask = rest_params['mask'] if 'mask' in rest_params else None vpcrmask = rest_params['vmask'] if 'vmask' in rest_params else None # if the query is not messed up if nonce is None: logger.warning('GET quote returning 400 response. nonce not provided as an HTTP parameter in request') common.echo_json_response(self, 400, "nonce not provided as an HTTP parameter in request") return # Sanitization assurance (for tpm.run() tasks below) if not (nonce.isalnum() and (pcrmask is None or pcrmask.isalnum()) and (vpcrmask is None or vpcrmask.isalnum())): logger.warning('GET quote returning 400 response. parameters should be strictly alphanumeric') common.echo_json_response(self, 400, "parameters should be strictly alphanumeric") return # identity quotes are always shallow hash_alg = tpm.defaults['hash'] if not tpm.is_vtpm() or rest_params["quotes"]=='identity': quote = tpm.create_quote(nonce, self.server.rsapublickey_exportable, pcrmask, hash_alg) imaMask = pcrmask else: quote = tpm.create_deep_quote(nonce, self.server.rsapublickey_exportable, vpcrmask, pcrmask) imaMask = vpcrmask # Allow for a partial quote response (without pubkey) enc_alg = tpm.defaults['encrypt'] sign_alg = tpm.defaults['sign'] if "partial" in rest_params and (rest_params["partial"] is None or int(rest_params["partial"],0) == 1): response = { 'quote': quote, 'tpm_version': tpm_version, 'hash_alg': hash_alg, 'enc_alg': enc_alg, 'sign_alg': sign_alg, } else: response = { 'quote': quote, 'tpm_version': tpm_version, 'hash_alg': hash_alg, 'enc_alg': enc_alg, 'sign_alg': sign_alg, 'pubkey': self.server.rsapublickey_exportable, } # return a measurement list if available if TPM_Utilities.check_mask(imaMask, common.IMA_PCR): if not os.path.exists(common.IMA_ML): logger.warn("IMA measurement list not available: %s"%(common.IMA_ML)) else: with open(common.IMA_ML,'r') as f: ml = f.read() response['ima_measurement_list']=ml common.echo_json_response(self, 200, "Success", response) logger.info('GET %s quote returning 200 response.'%(rest_params["quotes"])) return else: logger.warning('GET returning 400 response. uri not supported: ' + self.path) common.echo_json_response(self, 400, "uri not supported") return
def do_POST(self): """This method services the POST request typically from either the Tenant or the Cloud Verifier. Only tenant and cloudverifier uri's are supported. Both requests require a nonce parameter. The Cloud verifier requires an additional mask parameter. If the uri or parameters are incorrect, a 400 response is returned. """ rest_params = common.get_restful_params(self.path) if rest_params is None: common.echo_json_response(self, 405, "Not Implemented: Use /keys/ interface") return content_length = int(self.headers.get('Content-Length', 0)) if content_length <= 0: logger.warning('POST returning 400 response, expected content in message. url: ' + self.path) common.echo_json_response(self, 400, "expected content in message") return post_body = self.rfile.read(content_length) json_body = json.loads(post_body) b64_encrypted_key = json_body['encrypted_key'] decrypted_key = crypto.rsa_decrypt(self.server.rsaprivatekey,base64.b64decode(b64_encrypted_key)) have_derived_key = False if rest_params["keys"] == "ukey": self.server.add_U(decrypted_key) self.server.auth_tag = json_body['auth_tag'] self.server.payload = json_body.get('payload',None) have_derived_key = self.server.attempt_decryption(self) elif rest_params["keys"] == "vkey": self.server.add_V(decrypted_key) have_derived_key = self.server.attempt_decryption(self) else: logger.warning('POST returning response. uri not supported: ' + self.path) common.echo_json_response(self, 400, "uri not supported") return logger.info('POST of %s key returning 200'%(('V','U')[rest_params["keys"] == "ukey"])) common.echo_json_response(self, 200, "Success") # no key yet, then we're done if not have_derived_key: return # woo hoo we have a key # ok lets write out the key now secdir = secure_mount.mount() # confirm that storage is still securely mounted # clean out the secure dir of any previous info before we extract files if os.path.isdir("%s/unzipped"%secdir): shutil.rmtree("%s/unzipped"%secdir) # write out key file f = open(secdir+"/"+self.server.enc_keyname,'w') f.write(base64.b64encode(self.server.K)) f.close() #stow the U value for later tpm.write_key_nvram(self.server.final_U) # optionally extend a hash of they key and payload into specified PCR tomeasure = self.server.K # if we have a good key, now attempt to write out the encrypted payload dec_path = "%s/%s"%(secdir, config.get('cloud_agent',"dec_payload_file")) enc_path = "%s/encrypted_payload"%common.WORK_DIR dec_payload = None enc_payload = None if self.server.payload is not None: dec_payload = crypto.decrypt(self.server.payload, str(self.server.K)) enc_payload = self.server.payload elif os.path.exists(enc_path): # if no payload provided, try to decrypt one from a previous run stored in encrypted_payload with open(enc_path,'r') as f: enc_payload = f.read() try: dec_payload = crypto.decrypt(enc_payload,str(self.server.K)) logger.info("Decrypted previous payload in %s to %s"%(enc_path,dec_path)) except Exception as e: logger.warning("Unable to decrypt previous payload %s with derived key: %s"%(enc_path,e)) os.remove(enc_path) enc_payload=None # also write out encrypted payload to be decrytped next time if enc_payload is not None: with open(enc_path,'w') as f: f.write(self.server.payload) # deal with payload payload_thread = None if dec_payload is not None: tomeasure += dec_payload # see if payload is a zip zfio = cStringIO.StringIO(dec_payload) if config.getboolean('cloud_agent','extract_payload_zip') and zipfile.is_zipfile(zfio): logger.info("Decrypting and unzipping payload to %s/unzipped"%secdir) with zipfile.ZipFile(zfio,'r')as f: f.extractall('%s/unzipped'%secdir) # run an included script if one has been provided initscript = config.get('cloud_agent','payload_script') if initscript is not "": def initthread(): import subprocess env = os.environ.copy() env['AGENT_UUID']=self.server.agent_uuid proc= subprocess.Popen(["/bin/bash",initscript],env=env,shell=False,cwd='%s/unzipped'%secdir, stdout=subprocess.PIPE,stderr=subprocess.STDOUT) while True: line = proc.stdout.readline(); if line == '' and proc.poll() is not None: break if line: logger.debug("init-output: %s"%line.strip()) # should be a no-op as poll already told us it's done proc.wait() if not os.path.exists("%s/unzipped/%s"%(secdir,initscript)): logger.info("No payload script %s found in %s/unzipped"%(initscript,secdir)) else: logger.info("Executing payload script: %s/unzipped/%s"%(secdir,initscript)) payload_thread = threading.Thread(target=initthread) else: logger.info("Decrypting payload to %s"%dec_path) with open(dec_path,'w') as f: f.write(dec_payload) zfio.close() # now extend a measurement of the payload and key if there was one pcr = config.getint('cloud_agent','measure_payload_pcr') if pcr>0 and pcr<24: logger.info("extending measurement of payload into PCR %s"%pcr) measured = tpm.hashdigest(tomeasure) tpm.extendPCR(pcr,measured) if payload_thread is not None: payload_thread.start() return
def do_POST(self): """This method handles the POST requests to add instances to the Registrar Server. Currently, only instances resources are available for POSTing, i.e. /v2/instances. All other POST uri's will return errors. POST requests require an an instance_id identifying the instance to add, and json block sent in the body with 2 entries: ek and aik. """ rest_params = common.get_restful_params(self.path) if rest_params is None: common.echo_json_response( self, 405, "Not Implemented: Use /v2/instances/ interface") return if "instances" not in rest_params: common.echo_json_response(self, 400, "uri not supported") logger.warning( 'POST instance returning 400 response. uri not supported: ' + self.path) return instance_id = rest_params["instances"] if instance_id is None: common.echo_json_response(self, 400, "instance id not found in uri") logger.warning( 'POST instance returning 400 response. instance id not found in uri ' + self.path) return try: content_length = int(self.headers.get('Content-Length', 0)) if content_length == 0: common.echo_json_response(self, 400, "Expected non zero content length") logger.warning( 'POST for ' + instance_id + ' returning 400 response. Expected non zero content length.' ) return post_body = self.rfile.read(content_length) json_body = json.loads(post_body) ek = json_body['ek'] ekcert = json_body['ekcert'] aik = json_body['aik'] # try to encrypt the AIK (blob, key) = tpm_initialize.encryptAIK(instance_id, aik, ek) d = {} d['ek'] = ek d['aik'] = aik d['ekcert'] = ekcert d['virtual'] = int(ekcert == 'virtual') d['active'] = int(False) d['key'] = key d['provider_keys'] = {} # force overwrite if self.server.db.get_instance(instance_id) is not None: self.server.db.remove_instance(instance_id) self.server.db.add_instance(instance_id, d) response = { 'blob': blob, } common.echo_json_response(self, 200, "Success", response) logger.info('POST returning key blob for instance_id: ' + instance_id) return except Exception as e: common.echo_json_response(self, 400, "Error: %s" % e) logger.warning("POST for " + instance_id + " returning 400 response. Error: %s" % e) logger.warning(traceback.format_exc()) return
def post(self): """This method handles the POST requests to add agents to the Cloud Verifier. Currently, only agents resources are available for POSTing, i.e. /agents. All other POST uri's will return errors. agents requests require a json block sent in the body """ rest_params = common.get_restful_params(self.request.uri) if rest_params is None: common.echo_json_response( self, 405, "Not Implemented: Use /agents/ interface") return if "agents" not in rest_params: common.echo_json_response(self, 400, "uri not supported") logger.warning('POST returning 400 response. uri not supported: ' + self.request.path) return agent_id = rest_params["agents"] # Parse payload files (base64 data-uri) if self.get_argument("ptype", Agent_Init_Types.FILE, True) == Agent_Init_Types.FILE: keyfile = None payload = None data = { 'data': parse_data_uri(self.get_argument("file_data", None, True)) } ca_dir = None incl_dir = None ca_dir_pw = None elif self.get_argument("ptype", Agent_Init_Types.FILE, True) == Agent_Init_Types.KEYFILE: keyfile = { 'data': parse_data_uri(self.get_argument("keyfile_data", None, True)), } payload = { 'data': parse_data_uri(self.get_argument("file_data", None, True)) } data = None ca_dir = None incl_dir = None ca_dir_pw = None elif self.get_argument("ptype", Agent_Init_Types.FILE, True) == Agent_Init_Types.CA_DIR: keyfile = None payload = None data = None incl_dir = { 'data': parse_data_uri( self.get_argument("include_dir_data", None, True)), 'name': self.get_argument("include_dir_name", "", True).splitlines() } ca_dir = self.get_argument("ca_dir", 'default', True) if ca_dir == "": ca_dir = 'default' ca_dir_pw = self.get_argument("ca_dir_pw", 'default', True) if ca_dir_pw == "": ca_dir_pw = 'default' else: common.echo_json_response(self, 400, "invalid payload type chosen") logger.warning('POST returning 400 response. malformed query') return # Pull in user-defined v/TPM policies tpm_policy = self.get_argument("tpm_policy", "", True) if tpm_policy == "": tpm_policy = None vtpm_policy = self.get_argument("vtpm_policy", "", True) if vtpm_policy == "": vtpm_policy = None # Pull in IMA white list ima_whitelist = None w_list_data = self.get_argument("w_list_data", None, True) if w_list_data != "": ima_whitelist_str = parse_data_uri(w_list_data) if ima_whitelist_str is not None: ima_whitelist = ima_whitelist_str[0].splitlines() # Pull in IMA exclude list ima_exclude = None e_list_data = self.get_argument("e_list_data", None, True) if e_list_data != "": ima_exclude_str = parse_data_uri(e_list_data) if ima_exclude_str is not None: ima_exclude = ima_exclude_str[0].splitlines() # Build args to give to Tenant's init_add method args = { 'agent_ip': self.get_argument("agent_ip", None, True), 'file': data, 'keyfile': keyfile, 'payload': payload, 'ca_dir': ca_dir, 'incl_dir': incl_dir, 'ca_dir_pw': ca_dir_pw, 'tpm_policy': tpm_policy, 'vtpm_policy': vtpm_policy, 'ima_whitelist': ima_whitelist, 'ima_exclude': ima_exclude, } # let Tenant do dirty work of adding agent try: mytenant = tenant.Tenant() mytenant.agent_uuid = agent_id mytenant.init_add(args) mytenant.preloop() mytenant.do_cv() mytenant.do_quote() except Exception as e: logger.exception(e) logger.warning('POST returning 500 response. Tenant error: %s' % str(e)) common.echo_json_response(self, 500, "Request failure", str(e)) return common.echo_json_response(self, 200, "Success")
def get(self): """This method handles the GET requests to retrieve status on agents from the WebApp. Currently, only the web app is available for GETing, i.e. /agents. All other GET uri's will return errors. """ rest_params = common.get_restful_params(self.request.uri) if rest_params is None: common.echo_json_response( self, 405, "Not Implemented: Use /agents/ or /logs/ interface") return if "logs" in rest_params and rest_params["logs"] == "tenant": offset = 0 if "pos" in rest_params and rest_params[ "pos"] is not None and rest_params["pos"].isdigit(): offset = int(rest_params["pos"]) # intercept requests for logs with open(keylime_logging.LOGSTREAM, 'r') as f: logValue = f.readlines() common.echo_json_response(self, 200, "Success", {'log': logValue[offset:]}) return elif "agents" not in rest_params: # otherwise they must be looking for agent info common.echo_json_response(self, 400, "uri not supported") logger.warning('GET returning 400 response. uri not supported: ' + self.request.path) return agent_id = rest_params["agents"] if agent_id is not None: # Handle request for specific agent data separately agents = self.get_agent_state(agent_id) agents["id"] = agent_id common.echo_json_response(self, 200, "Success", agents) return # If no agent ID, get list of all agents from Registrar try: response = tornado_requests.request( "GET", "http://%s:%s/agents/" % (tenant_templ.registrar_ip, tenant_templ.registrar_port), context=tenant_templ.context) except Exception as e: logger.error( "Status command response: %s:%s Unexpected response from Registrar." % (tenant_templ.registrar_ip, tenant_templ.registrar_port)) logger.exception(e) common.echo_json_response(self, 500, "Unexpected response from Registrar", str(e)) return response_body = response.json() if response.status_code != 200: logger.error( "Status command response: %d Unexpected response from Registrar." % response.status_code) common.log_http_response(logger, logging.ERROR, response_body) return None if ("results" not in response_body) or ("uuids" not in response_body["results"]): logger.critical( "Error: unexpected http response body from Registrar: %s" % str(response.status_code)) return None agent_list = response_body["results"]["uuids"] # Loop through each agent and ask for status agents = {} for agent in agent_list: agents[agent] = self.get_agent_state(agent_id) # Pre-create sorted agents list sorted_by_state = {} states = cloud_verifier_common.CloudAgent_Operational_State.STR_MAPPINGS for state in states: sorted_by_state[state] = {} # Build sorted agents list for agent_id in agents: state = agents[agent_id]["operational_state"] sorted_by_state[state][agent_id] = agents[agent_id] print_order = [10, 9, 7, 3, 4, 5, 6, 2, 1, 8, 0] sorted_agents = [] for state in print_order: for agent_id in sorted_by_state[state]: sorted_agents.append(agent_id) common.echo_json_response(self, 200, "Success", {'uuids': sorted_agents})
def do_PUT(self): """This method handles the PUT requests to add agents to the Registrar Server. Currently, only agents resources are available for PUTing, i.e. /agents. All other PUT uri's will return errors. """ rest_params = common.get_restful_params(self.path) if rest_params is None: common.echo_json_response( self, 405, "Not Implemented: Use /agents/ interface") return if "agents" not in rest_params: common.echo_json_response(self, 400, "uri not supported") logger.warning( 'PUT agent returning 400 response. uri not supported: ' + self.path) return agent_id = rest_params["agents"] if agent_id is None: common.echo_json_response(self, 400, "agent id not found in uri") logger.warning( 'PUT agent returning 400 response. agent id not found in uri ' + self.path) return try: content_length = int(self.headers.get('Content-Length', 0)) if content_length == 0: common.echo_json_response(self, 400, "Expected non zero content length") logger.warning( 'PUT for ' + agent_id + ' returning 400 response. Expected non zero content length.' ) return post_body = self.rfile.read(content_length) json_body = json.loads(post_body) if "activate" in rest_params: auth_tag = json_body['auth_tag'] agent = self.server.db.get_agent(agent_id) if agent is None: raise Exception( "attempting to activate agent before requesting registrar for %s" % agent_id) if agent['virtual']: raise Exception( "attempting to activate virtual AIK using physical interface for %s" % agent_id) if common.STUB_TPM: self.server.db.update_agent(agent_id, 'active', True) else: ex_mac = crypto.do_hmac(base64.b64decode(agent['key']), agent_id) if ex_mac == auth_tag: self.server.db.update_agent(agent_id, 'active', True) else: raise Exception( "Auth tag %s does not match expected value %s" % (auth_tag, ex_mac)) common.echo_json_response(self, 200, "Success") logger.info('PUT activated: ' + agent_id) elif "vactivate" in rest_params: deepquote = json_body.get('deepquote', None) agent = self.server.db.get_agent(agent_id) if agent is None: raise Exception( "attempting to activate agent before requesting registrar for %s" % agent_id) if not agent['virtual']: raise Exception( "attempting to activate physical AIK using virtual interface for %s" % agent_id) # get an physical AIK for this host registrar_client.init_client_tls(config, 'registrar') provider_keys = registrar_client.getKeys( config.get('general', 'provider_registrar_ip'), config.get('general', 'provider_registrar_tls_port'), agent_id) # we already have the vaik tpm = tpm_obj.getTPM(need_hw_tpm=False, tpm_version=agent['tpm_version']) if not tpm.check_deep_quote( hashlib.sha1(agent['key']).hexdigest(), agent_id + agent['aik'] + agent['ek'], deepquote, agent['aik'], provider_keys['aik']): raise Exception("Deep quote invalid") self.server.db.update_agent(agent_id, 'active', True) self.server.db.update_agent(agent_id, 'provider_keys', provider_keys) common.echo_json_response(self, 200, "Success") logger.info('PUT activated: ' + agent_id) else: pass except Exception as e: common.echo_json_response(self, 400, "Error: %s" % e) logger.warning("PUT for " + agent_id + " returning 400 response. Error: %s" % e) logger.exception(e) return
def do_POST(self): """This method handles the POST requests to add agents to the Registrar Server. Currently, only agents resources are available for POSTing, i.e. /agents. All other POST uri's will return errors. POST requests require an an agent_id identifying the agent to add, and json block sent in the body with 2 entries: ek and aik. """ rest_params = common.get_restful_params(self.path) if rest_params is None: common.echo_json_response( self, 405, "Not Implemented: Use /agents/ interface") return if "agents" not in rest_params: common.echo_json_response(self, 400, "uri not supported") logger.warning( 'POST agent returning 400 response. uri not supported: ' + self.path) return agent_id = rest_params["agents"] if agent_id is None: common.echo_json_response(self, 400, "agent id not found in uri") logger.warning( 'POST agent returning 400 response. agent id not found in uri ' + self.path) return try: content_length = int(self.headers.get('Content-Length', 0)) if content_length == 0: common.echo_json_response(self, 400, "Expected non zero content length") logger.warning( 'POST for ' + agent_id + ' returning 400 response. Expected non zero content length.' ) return post_body = self.rfile.read(content_length) json_body = json.loads(post_body) ek = json_body['ek'] ek_tpm = json_body['ek_tpm'] ekcert = json_body['ekcert'] aik = json_body['aik'] aik_name = json_body['aik_name'] tpm_version = int(json_body['tpm_version']) # try to encrypt the AIK tpm = tpm_obj.getTPM(need_hw_tpm=False, tpm_version=tpm_version) (blob, key) = tpm.encryptAIK(agent_id, aik, ek, ek_tpm, aik_name) # special behavior if we've registered this uuid before regcount = 1 agent = self.server.db.get_agent(agent_id) if agent is not None: # keep track of how many ek-ekcerts have registered on this uuid regcount = agent['regcount'] if agent['ek'] != ek or agent['ekcert'] != ekcert: logger.warning( 'WARNING: Overwriting previous registration for this UUID with new ek-ekcert pair!' ) regcount += 1 # force overwrite logger.info('Overwriting previous registration for this UUID.') self.server.db.remove_agent(agent_id) d = {} d['ek'] = ek d['aik'] = aik d['ekcert'] = ekcert d['virtual'] = int(ekcert == 'virtual') d['active'] = int(False) d['key'] = key d['tpm_version'] = tpm_version d['provider_keys'] = {} d['regcount'] = regcount self.server.db.add_agent(agent_id, d) response = { 'blob': blob, } common.echo_json_response(self, 200, "Success", response) logger.info('POST returning key blob for agent_id: ' + agent_id) return except Exception as e: common.echo_json_response(self, 400, "Error: %s" % e) logger.warning("POST for " + agent_id + " returning 400 response. Error: %s" % e) logger.exception(e) return
def get(self): """This method handles the GET requests to retrieve status on instances from the WebApp. Currently, only the web app is available for GETing, i.e. /v2/nodes. All other GET uri's will return errors. """ rest_params = common.get_restful_params(self.request.path) if rest_params is None: common.echo_json_response(self, 405, "Not Implemented: Use /v2/nodes/ interface") return if "nodes" not in rest_params: common.echo_json_response(self, 400, "uri not supported") logger.warning('GET returning 400 response. uri not supported: ' + self.request.path) return instance_id = rest_params["nodes"] if instance_id is not None: instances = self.get_instance_state(instance_id) instances["id"] = instance_id common.echo_json_response(self, 200, "Success", instances) else: # Get list of instances from Registrar try: response = tornado_requests.request("GET", "http://%s:%s/v2/instances/"%(tenant_templ.registrar_ip,tenant_templ.registrar_port),context=tenant_templ.context) except Exception as e: logger.error("Status command response: %s:%s Unexpected response from Registrar."%(tenant_templ.registrar_ip,tenant_templ.registrar_port)) logger.error(traceback.print_exc()) logger.error("Error: %s "%str(e)) raise e response_body = response.json() if response.status_code != 200: logger.error("Status command response: %d Unexpected response from Registrar."%response.status_code) common.log_http_response(logger,logging.ERROR,response_body) return None if ("results" not in response_body) or ("uuids" not in response_body["results"]): logger.critical("Error: unexpected http response body from Registrar: %s"%str(response.status_code)) return None instance_list = response_body["results"]["uuids"] # Loop through each instance and ask for status instances = {} for instance in instance_list: instances[instance] = self.get_instance_state(instance_id) # Pre-create sorted instances list sorted_by_state = {} states = cloud_verifier_common.CloudInstance_Operational_State.STR_MAPPINGS for state in states: sorted_by_state[state] = {} # Build sorted instances list for instance_id in instances: state = instances[instance_id]["operational_state"] sorted_by_state[state][instance_id] = instances[instance_id] print_order = [9,7,3,4,5,6,2,1,8,0] sorted_instances = [] for state in print_order: for instance_id in sorted_by_state[state]: sorted_instances.append(instance_id) common.echo_json_response(self, 200, "Success", {'uuids':sorted_instances})
def post(self): """This method handles the POST requests to add agents to the Cloud Verifier. Currently, only agents resources are available for POSTing, i.e. /agents. All other POST uri's will return errors. agents requests require a json block sent in the body """ try: rest_params = common.get_restful_params(self.request.uri) if rest_params is None: common.echo_json_response(self, 405, "Not Implemented: Use /agents/ interface") return if "agents" not in rest_params: common.echo_json_response(self, 400, "uri not supported") logger.warning('POST returning 400 response. uri not supported: ' + self.request.path) return agent_id = rest_params["agents"] if agent_id is not None: # this is for new items content_length = len(self.request.body) if content_length==0: common.echo_json_response(self, 400, "Expected non zero content length") logger.warning('POST returning 400 response. Expected non zero content length.') else: json_body = json.loads(self.request.body) d = {} d['v'] = json_body['v'] d['ip'] = json_body['cloudagent_ip'] d['port'] = int(json_body['cloudagent_port']) d['operational_state'] = cloud_verifier_common.CloudAgent_Operational_State.START d['public_key'] = "" d['tpm_policy'] = json_body['tpm_policy'] d['vtpm_policy'] = json_body['vtpm_policy'] d['metadata'] = json_body['metadata'] d['ima_whitelist'] = json_body['ima_whitelist'] d['revocation_key'] = json_body['revocation_key'] d['tpm_version'] = 0 d['accept_tpm_hash_algs'] = json_body['accept_tpm_hash_algs'] d['accept_tpm_encryption_algs'] = json_body['accept_tpm_encryption_algs'] d['accept_tpm_signing_algs'] = json_body['accept_tpm_signing_algs'] d['hash_alg'] = "" d['enc_alg'] = "" d['sign_alg'] = "" new_agent = self.db.add_agent(agent_id,d) # don't allow overwriting if new_agent is None: common.echo_json_response(self, 409, "Agent of uuid %s already exists"%(agent_id)) logger.warning("Agent of uuid %s already exists"%(agent_id)) else: self.process_agent(new_agent, cloud_verifier_common.CloudAgent_Operational_State.GET_QUOTE) common.echo_json_response(self, 200, "Success") logger.info('POST returning 200 response for adding agent id: ' + agent_id) else: common.echo_json_response(self, 400, "uri not supported") logger.warning("POST returning 400 response. uri not supported") except Exception as e: common.echo_json_response(self, 400, "Exception error: %s"%e) logger.warning("POST returning 400 response. Exception error: %s"%e) logger.exception(e) self.finish()