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).decode()) 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, bytes(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, 'rb') as f: enc_payload = f.read() try: dec_payload = crypto.decrypt(enc_payload, 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, 'wb') as f: f.write(self.server.payload.encode('utf-8')) # deal with payload payload_thread = None if dec_payload is not None: tomeasure = tomeasure + dec_payload # see if payload is a zip zfio = io.BytesIO(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, 'wb') 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_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 get(self): common.echo_json_response( self, 405, "Not Implemented: Use /agents/ interface instead") logger.info("405 error")
def put(self): common.echo_json_response( self, 405, "Not Implemented: Use /agents/ interface instead")
def do_PATCH(self): """PATCH not supported""" common.echo_json_response(self, 405, "PATCH not supported") 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) self.server.db.remove_agent(agent_id) # Add values to database 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 post(self): session = self.make_session(engine) """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: 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) agent_data = {} agent_data['v'] = json_body['v'] agent_data['ip'] = json_body['cloudagent_ip'] agent_data['port'] = int(json_body['cloudagent_port']) agent_data[ 'operational_state'] = cloud_verifier_common.CloudAgent_Operational_State.START agent_data['public_key'] = "" agent_data['tpm_policy'] = json_body['tpm_policy'] agent_data['vtpm_policy'] = json_body['vtpm_policy'] agent_data['meta_data'] = json_body['metadata'] agent_data['ima_whitelist'] = json_body['ima_whitelist'] agent_data['revocation_key'] = json_body['revocation_key'] agent_data['tpm_version'] = 0 agent_data['accept_tpm_hash_algs'] = json_body[ 'accept_tpm_hash_algs'] agent_data['accept_tpm_encryption_algs'] = json_body[ 'accept_tpm_encryption_algs'] agent_data['accept_tpm_signing_algs'] = json_body[ 'accept_tpm_signing_algs'] agent_data['hash_alg'] = "" agent_data['enc_alg'] = "" agent_data['sign_alg'] = "" agent_data['agent_id'] = agent_id try: new_agent_count = session.query(VerfierMain).filter_by( agent_id=agent_id).count() except SQLAlchemyError as e: logger.error(f'SQLAlchemy Error: {e}') # don't allow overwriting if new_agent_count > 0: 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: try: # Add the agent and data session.add(VerfierMain(**agent_data)) session.commit() except SQLAlchemyError as e: logger.error(f'SQLAlchemy Error: {e}') for key in list(exclude_db.keys()): agent_data[key] = exclude_db[key] asyncio.ensure_future( self.process_agent( agent_data, 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()
def do_PUT(self): """PUT not supported""" common.echo_json_response(self, 405, "PUT not supported via TLS interface") return
def delete(self): common.echo_json_response( self, 405, "Not Implemented: Use /webapp/, /agents/ or /logs/ interface instead" )
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. """ session = self.make_session(engine) 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) try: agent = session.query(VerfierMain).filter_by( agent_id=agent_id).first() except SQLAlchemyError as e: logger.error(f'SQLAlchemy Error: {e}') 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.TERMINATED or \ op_state == cloud_verifier_common.CloudAgent_Operational_State.TENANT_FAILED or \ op_state == cloud_verifier_common.CloudAgent_Operational_State.INVALID_QUOTE: try: session.query(VerfierMain).filter_by( agent_id=agent_id).delete() session.commit() except SQLAlchemyError as e: logger.error(f'SQLAlchemy Error: {e}') common.echo_json_response(self, 200, "Success") logger.info('DELETE returning 200 response for agent id: ' + agent_id) else: try: update_agent = session.query(VerfierMain).get(agent_id) update_agent.operational_state = cloud_verifier_common.CloudAgent_Operational_State.TERMINATED try: session.add(update_agent) except SQLAlchemyError as e: logger.error(f'SQLAlchemy Error: {e}') session.commit() common.echo_json_response(self, 202, "Accepted") logger.info('DELETE returning 202 response for agent id: ' + agent_id) except SQLAlchemyError as e: logger.error(f'SQLAlchemy Error: {e}')
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 yaml 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")
async 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 = await 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: res = tornado_requests.request( "GET", "http://%s:%s/agents/" % (tenant_templ.registrar_ip, tenant_templ.registrar_port), context=tenant_templ.registrar_context) response = await res 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) keylime_logging.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] = await 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_GET(self): """This method handles the GET requests to retrieve status on agents from the Registrar Server. Currently, only agents resources are available for GETing, i.e. /agents. All other GET uri's will return errors. agents requests require a single agent_id parameter which identifies the agent to be returned. If the agent_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 /agents/ interface") return if "agents" 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 agent_id = rest_params["agents"] if agent_id is not None: agent = self.server.db.get_agent(agent_id) if agent is None: common.echo_json_response(self, 404, "agent_id not found") logger.warning('GET returning 404 response. agent_id ' + agent_id + ' not found.') return if not agent['active']: common.echo_json_response(self, 404, "agent_id not yet active") logger.warning('GET returning 404 response. agent_id ' + agent_id + ' not yet active.') return response = { 'aik': agent['aik'], 'ek': agent['ek'], 'ekcert': agent['ekcert'], 'regcount': agent['regcount'], } if agent['virtual']: response['provider_keys'] = agent['provider_keys'] common.echo_json_response(self, 200, "Success", response) logger.info('GET returning 200 response for agent_id:' + agent_id) else: # return the available registered uuids from the DB json_response = self.server.db.get_agent_ids() common.echo_json_response(self, 200, "Success", {'uuids': json_response}) logger.info('GET returning 200 response for agent_id list') return
def do_DELETE(self): """DELETE not supported""" common.echo_json_response(self, 405, "DELETE not supported") return
def head(self): """HEAD not supported""" common.echo_json_response(self, 405, "HEAD not supported")
def do_GET(self): """This method handles the GET requests to retrieve status on agents from the Registrar Server. Currently, only agents resources are available for GETing, i.e. /agents. All other GET uri's will return errors. agents requests require a single agent_id parameter which identifies the agent to be returned. If the agent_id is not found, a 404 response is returned. """ session = SessionManager().make_session(engine) 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('GET returning 400 response. uri not supported: ' + self.path) return agent_id = rest_params["agents"] if agent_id is not None: try: agent = session.query(RegistrarMain).filter_by( agent_id=agent_id).first() except SQLAlchemyError as e: logger.error(f'SQLAlchemy Error: {e}') if agent is None: common.echo_json_response(self, 404, "agent_id not found") logger.warning('GET returning 404 response. agent_id ' + agent_id + ' not found.') return if not agent.active: common.echo_json_response(self, 404, "agent_id not yet active") logger.warning('GET returning 404 response. agent_id ' + agent_id + ' not yet active.') return response = { 'aik': agent.aik, 'ek': agent.ek, 'ekcert': agent.ekcert, 'regcount': agent.regcount, } if agent.virtual: response['provider_keys'] = agent.provider_keys common.echo_json_response(self, 200, "Success", response) logger.info('GET returning 200 response for agent_id:' + agent_id) else: # return the available registered uuids from the DB json_response = session.query(RegistrarMain.agent_id).all() return_response = [item[0] for item in json_response] common.echo_json_response(self, 200, "Success", {'uuids': return_response}) logger.info('GET returning 200 response for agent_id list') return
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.TERMINATED or \ op_state == cloud_verifier_common.CloudAgent_Operational_State.TENANT_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 do_HEAD(self): """HEAD not supported""" common.echo_json_response(self, 405, "HEAD not supported") 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 """ 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: asyncio.ensure_future( 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()
def do_GET(self): """GET not supported""" common.echo_json_response(self, 405, "GET not supported") return
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. 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( 'PUT returning 400 response. uri not supported: ' + self.request.path) return agent_id = rest_params["agents"] if agent_id is None: common.echo_json_response(self, 400, "uri not supported") logger.warning("PUT returning 400 response. uri not supported") agent = self.db.get_agent(agent_id) if agent is not None: common.echo_json_response(self, 404, "agent id not found") logger.info('PUT returning 404 response. agent id: ' + agent_id + ' not found.') if "reactivate" in rest_params: agent[ 'operational_state'] = cloud_verifier_common.CloudAgent_Operational_State.START asyncio.ensure_future( self.process_agent( agent, cloud_verifier_common. CloudAgent_Operational_State.GET_QUOTE)) common.echo_json_response(self, 200, "Success") logger.info('PUT returning 200 response for agent id: ' + agent_id) elif "stop" in rest_params: # do stuff for terminate logger.debug("Stopping polling on %s" % agent_id) self.db.update_agent( agent_id, 'operational_state', cloud_verifier_common. CloudAgent_Operational_State.TENANT_FAILED) common.echo_json_response(self, 200, "Success") logger.info('PUT returning 200 response for agent id: ' + agent_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.exception(e) self.finish()
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(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('registrar', 'provider_registrar_ip'), config.get('registrar', '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( agent_id, 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
async def get(self): """This method handles the GET requests for Tenant verifiers to talk with Provider verifiers """ rest_params = common.get_restful_params(self.request.uri) global tree if rest_params is None: common.echo_json_response( self, 405, "Not Implemented: Use /agents/interface") return if "agents" in rest_params: agent_id = rest_params["agents"] if agent_id is not None: agent = self.db.get_agent(agent_id) if agent != None: response = cloud_verifier_common.process_get_status(agent) common.echo_json_response(self, 200, "Success", response) else: #logger.info('GET returning 404 response. agent id: ' + agent_id + ' not found.') common.echo_json_response(self, 404, "agent id not found") else: # return the available keys in the DB json_response = self.db.get_agent_ids() common.echo_json_response(self, 200, "Success", {'uuids': json_response}) logger.info('GET returning 200 response for agent_id list') elif "verifier" in rest_params: partial_req = "1" # don't need a pub key agent = self.db.get_agent_ids() new_agent = self.db.get_agent(agent[0]) try: await nonce_col.put(rest_params["nonce"]) except Exception as e: print('error: ', e) new_agent['quote_col'].append(rest_params["nonce"]) self.db.update_all_agents('quote_col', new_agent['quote_col']) #Simulate busy TPM with async sleep function to allow testing of quote batching functionality await asyncio.sleep(10) try: agent = self.db.get_agent_ids() new_agent2 = self.db.get_agent(agent[0]) tree = MerkleTree([], hashfunc) logger.info("Concurrent Nonces: " + str(nonce_col.qsize())) for nonce in range(nonce_col.qsize()): temp = nonce_col.get() t = await temp logger.debug(t) tree.append(t) await nonce_col.put(t) logger.info("Making Merkle Tree in Provider Verifier") logger.info(beautify(tree)) nonce_proof = tree.get_proof(rest_params['nonce']) except Exception as e: print('error: ', e) rest_params['nonce'] = tree.merkle_root url = "http://%s:%d/quotes/integrity?nonce=%s&mask=%s&vmask=%s&partial=%s" % ( new_agent['ip'], new_agent['port'], rest_params["nonce"], rest_params["mask"], rest_params['vmask'], partial_req) res = tornado_requests.request("GET", url, context=None) response = await res json_response = json.loads(response.body) json_response_result = json_response["results"] json_response_result['nonce_proof'] = proof_to_string(nonce_proof) json_response_result['merkle_head'] = tree.merkle_root logger.debug("To string of Nonce Proof", json_response_result['merkle_head']) common.echo_json_response(self, 200, "Success", json_response_result) else: common.echo_json_response(self, 400, "uri not supported") logger.warning('GET returning 400 response. uri not supported: ' + self.request.path)