Example #1
0
    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
Example #2
0
    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
Example #3
0
 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")
Example #5
0
 def do_PATCH(self):
     """PATCH not supported"""
     common.echo_json_response(self, 405, "PATCH not supported")
     return
Example #6
0
    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()
Example #8
0
 def do_PUT(self):
     """PUT not supported"""
     common.echo_json_response(self, 405,
                               "PUT not supported via TLS interface")
     return
Example #9
0
 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}')
Example #11
0
    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")
Example #12
0
    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})
Example #13
0
    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
Example #14
0
 def do_DELETE(self):
     """DELETE not supported"""
     common.echo_json_response(self, 405, "DELETE not supported")
     return
Example #15
0
 def head(self):
     """HEAD not supported"""
     common.echo_json_response(self, 405, "HEAD not supported")
Example #16
0
    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
Example #17
0
    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)
Example #18
0
 def do_HEAD(self):
     """HEAD not supported"""
     common.echo_json_response(self, 405, "HEAD not supported")
     return
Example #19
0
    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()
Example #20
0
 def do_GET(self):
     """GET not supported"""
     common.echo_json_response(self, 405, "GET not supported")
     return
Example #21
0
    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()
Example #22
0
    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)