Beispiel #1
0
    def put(self):
        """This method handles the PUT requests to add agents to the Agent Monitor.

        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
        """
        common.echo_json_response(self, 405, "PUT not supported")
Beispiel #2
0
    def delete(self):
        """This method handles the DELETE requests to remove agents from the Agent Monitor.

        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.
        """
        common.echo_json_response(self, 405, "DELETE not supported")
Beispiel #3
0
    def do_DELETE(self):
        """This method handles the DELETE requests to remove agents from the Registrar Server. 
        
        Currently, only agents resources are available for DELETEing, i.e. /agents. All other DELETE uri's will return errors.
        agents requests require a single agent_id parameter which identifies the agent to be deleted.    
        """
        rest_params = common.get_restful_params(self.path)
        if rest_params is None:
            common.echo_json_response(
                self, 405, "Not Implemented: Use /agents/ interface")
            return

        if "agents" not in rest_params:
            common.echo_json_response(self, 400, "uri not supported")
            logger.warning(
                'DELETE agent returning 400 response. uri not supported: ' +
                self.path)
            return

        agent_id = rest_params["agents"]

        if agent_id is not None:
            if self.server.db.remove_agent(agent_id):
                #send response
                common.echo_json_response(self, 200, "Success")
                return
            else:
                #send response
                common.echo_json_response(self, 404)
                return
        else:
            common.echo_json_response(self, 404)
            return
Beispiel #4
0
    def post(self):
        """This method handles the POST requests to add agents to the Agent Monitor.

        Currently, only agents resources are available for POSTing, i.e. /agents. All other POST uri's will return errors.
        agents requests require a json block sent in the body
        """
        logger.info('Agent Monitor POST')
        try:
            rest_params = common.get_restful_params(self.request.path)

            if "agents" not in rest_params:
                common.echo_json_response(self, 400, "uri not supported")
                logger.warning('POST returning 400 response. uri not supported: ' + self.request.path)
                return

            agent_id = rest_params["agents"]

            if agent_id is not None: # we have to know who phoned home
                content_length = len(self.request.body)
                if content_length==0:
                    common.echo_json_response(self, 400, "Expected non zero content length")
                    logger.warning('POST returning 400 response. Expected non zero content length.')
                else:
                    json_body = json.loads(self.request.body)

                    # VERIFY CLIENT CERT ID MATCHES AGENT ID (agent_id)
                    client_cert = self.request.get_ssl_certificate()
                    ssl.match_hostname(client_cert, agent_id)

                    # Execute specified script if all is well
                    global initscript
                    if initscript is not None and initscript is not "":
                        def initthread():
                            import subprocess
                            logger.debug("Executing specified script: %s"%initscript)
                            env = os.environ.copy()
                            env['AGENT_UUID']=agent_id
                            proc= subprocess.Popen(["/bin/sh",initscript],env=env,shell=False,
                                                    stdout=subprocess.PIPE,stderr=subprocess.STDOUT)
                            proc.wait()
                            while True:
                                line = proc.stdout.readline()
                                if line=="":
                                    break
                                logger.debug("init-output: %s"%line.strip())
                        t = threading.Thread(target=initthread)
                        t.start()

                    common.echo_json_response(self, 200, "Success", json_body)
                    logger.info('POST returning 200 response for Agent Monitor connection as ' + agent_id)
            else:
                common.echo_json_response(self, 400, "uri not supported")
                logger.warning("POST returning 400 response. uri not supported")
        except Exception as e:
            common.echo_json_response(self, 400, "Exception error: %s"%e)
            logger.warning("POST returning 400 response. Exception error: %s"%e)
            logger.exception(e)
Beispiel #5
0
    def get(self):
        """This method handles the GET requests to retrieve status on agents from the Agent Monitor.

        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.  If the agent_id
        was not found, it either completed successfully, or failed.  If found, the agent_id is still polling
        to contact the Cloud Agent.
        """
        common.echo_json_response(self, 405, "GET not supported")
Beispiel #6
0
    def write_error(self, status_code, **kwargs):

        if self.settings.get("serve_traceback") and "exc_info" in kwargs:
            # in debug mode, try to send a traceback
            lines = []
            for line in traceback.format_exception(*kwargs["exc_info"]):
                lines.append(line)
            common.echo_json_response(self, status_code, self._reason, lines)
        else:
            common.echo_json_response(self, status_code, self._reason)
    def delete(self):
        """This method handles the DELETE requests to remove instances from the Cloud Verifier. 
         
        Currently, only instances resources are available for DELETEing, i.e. /v2/instances. All other DELETE uri's will return errors.
        instances requests require a single instance_id parameter which identifies the instance to be deleted.    
        """
        rest_params = common.get_restful_params(self.request.path)
        if rest_params is None:
            common.echo_json_response(
                self, 405, "Not Implemented: Use /v2/instances/ interface")
            return

        if "instances" not in rest_params:
            common.echo_json_response(self, 400, "uri not supported")
            return

        instance_id = rest_params["instances"]

        if instance_id is None:
            common.echo_json_response(self, 400, "uri not supported")
            logger.warning(
                'DELETE returning 400 response. uri not supported: ' +
                self.request.path)

        instance = self.db.get_instance(instance_id)

        if instance is None:
            common.echo_json_response(self, 404, "instance id not found")
            logger.info('DELETE returning 404 response. instance id: ' +
                        instance_id + ' not found.')
            return

        op_state = instance['operational_state']
        if op_state == cloud_verifier_common.CloudInstance_Operational_State.SAVED or \
        op_state == cloud_verifier_common.CloudInstance_Operational_State.FAILED or \
        op_state == cloud_verifier_common.CloudInstance_Operational_State.INVALID_QUOTE:
            self.db.remove_instance(instance_id)
        else:
            self.db.update_instance(
                instance_id, 'operational_state', cloud_verifier_common.
                CloudInstance_Operational_State.TERMINATED)

        common.echo_json_response(self, 200, "Success")
        logger.info('DELETE returning 200 response for instance id: ' +
                    instance_id)
Beispiel #8
0
    def get_instance_state(self, instance_id):
        try:
            response = tornado_requests.request(
                "GET",
                "http://%s:%s/v2/instances/%s" %
                (tenant_templ.cloudverifier_ip,
                 tenant_templ.cloudverifier_port, instance_id),
                context=tenant_templ.context)
        except Exception as e:
            logger.error(
                "Status command response: %s:%s Unexpected response from Cloud Verifier."
                % (tenant_templ.cloudverifier_ip,
                   tenant_templ.cloudverifier_port))
            logger.error(traceback.print_exc())
            logger.error("Error: %s " % str(e))
            common.echo_json_response(
                self, 500, "Unexpected response from Cloud Verifier", str(e))
            return

        inst_response_body = response.json()

        if response.status_code != 200 and response.status_code != 404:
            logger.error(
                "Status command response: %d Unexpected response from Cloud Verifier."
                % response.status_code)
            common.log_http_response(logger, logging.ERROR, inst_response_body)
            return None

        if "results" not in inst_response_body:
            logger.critical(
                "Error: unexpected http response body from Cloud Verifier: %s"
                % str(response.status_code))
            return None

        # Node not added to CV (but still registered)
        if response.status_code == 404:
            return {
                "operational_state":
                cloud_verifier_common.CloudInstance_Operational_State.
                REGISTERED
            }
        else:
            return inst_response_body["results"]

        return None
Beispiel #9
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.
        """

        rest_params = common.get_restful_params(self.request.uri)
        if rest_params is None:
            common.echo_json_response(
                self, 405, "Not Implemented: Use /agents/ interface")
            return

        if "agents" not in rest_params:
            common.echo_json_response(self, 400, "uri not supported")
            logger.warning('PUT returning 400 response. uri not supported: ' +
                           self.request.path)
            return

        agent_id = rest_params["agents"]

        # let Tenant do dirty work of reactivating agent
        mytenant = tenant.Tenant()
        mytenant.agent_uuid = agent_id
        mytenant.do_cvreactivate()

        common.echo_json_response(self, 200, "Success")
Beispiel #10
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")
            logger.warning(
                'DELETE returning 400 response. uri not supported: ' +
                self.request.path)
            return

        agent_id = rest_params["agents"]

        # let Tenant do dirty work of deleting agent
        mytenant = tenant.Tenant()
        mytenant.agent_uuid = agent_id
        mytenant.do_cvdelete()

        common.echo_json_response(self, 200, "Success")
    def get(self):
        """This method handles the GET requests to retrieve status on instances from the Cloud Verifier. 
        
        Currently, only instances resources are available for GETing, i.e. /v2/instances. All other GET uri's 
        will return errors. instances requests require a single instance_id parameter which identifies the 
        instance to be returned. If the instance_id is not found, a 404 response is returned.  If the instance_id
        was not found, it either completed successfully, or failed.  If found, the instance_id is still polling 
        to contact the Cloud Node. 
        """
        rest_params = common.get_restful_params(self.request.path)
        if rest_params is None:
            common.echo_json_response(
                self, 405, "Not Implemented: Use /v2/instances/ interface")
            return

        if "instances" not in rest_params:
            common.echo_json_response(self, 400, "uri not supported")
            logger.warning('GET returning 400 response. uri not supported: ' +
                           self.request.path)
            return

        instance_id = rest_params["instances"]

        if instance_id is not None:
            instance = self.db.get_instance(instance_id)
            if instance != None:
                response = cloud_verifier_common.process_get_status(instance)
                common.echo_json_response(self, 200, "Success", response)
                #logger.info('GET returning 200 response for instance_id: ' + instance_id)

            else:
                #logger.info('GET returning 404 response. instance id: ' + instance_id + ' not found.')
                common.echo_json_response(self, 404, "instance id not found")
        else:
            # return the available keys in the DB
            json_response = self.db.get_instance_ids()
            common.echo_json_response(self, 200, "Success",
                                      {'uuids': json_response})
            logger.info('GET returning 200 response for instance_id list')
Beispiel #12
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
        """

        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")
Beispiel #13
0
    def get(self):
        """This method handles the GET requests to retrieve status on agents from the WebApp. 
        
        Currently, only the web app is available for GETing, i.e. /agents. All other GET uri's 
        will return errors. 
        """

        rest_params = common.get_restful_params(self.request.uri)
        if rest_params is None:
            common.echo_json_response(
                self, 405, "Not Implemented: Use /agents/ or /logs/ interface")
            return

        if "logs" in rest_params and rest_params["logs"] == "tenant":
            offset = 0
            if "pos" in rest_params and rest_params[
                    "pos"] is not None and rest_params["pos"].isdigit():
                offset = int(rest_params["pos"])
            # intercept requests for logs
            with open(keylime_logging.LOGSTREAM, 'r') as f:
                logValue = f.readlines()
                common.echo_json_response(self, 200, "Success",
                                          {'log': logValue[offset:]})
            return
        elif "agents" not in rest_params:
            # otherwise they must be looking for agent info
            common.echo_json_response(self, 400, "uri not supported")
            logger.warning('GET returning 400 response. uri not supported: ' +
                           self.request.path)
            return

        agent_id = rest_params["agents"]
        if agent_id is not None:
            # Handle request for specific agent data separately
            agents = self.get_agent_state(agent_id)
            agents["id"] = agent_id

            common.echo_json_response(self, 200, "Success", agents)
            return

        # If no agent ID, get list of all agents from Registrar
        try:
            response = tornado_requests.request(
                "GET",
                "http://%s:%s/agents/" %
                (tenant_templ.registrar_ip, tenant_templ.registrar_port),
                context=tenant_templ.context)
        except Exception as e:
            logger.error(
                "Status command response: %s:%s Unexpected response from Registrar."
                % (tenant_templ.registrar_ip, tenant_templ.registrar_port))
            logger.exception(e)
            common.echo_json_response(self, 500,
                                      "Unexpected response from Registrar",
                                      str(e))
            return

        response_body = response.json()

        if response.status_code != 200:
            logger.error(
                "Status command response: %d Unexpected response from Registrar."
                % response.status_code)
            common.log_http_response(logger, logging.ERROR, response_body)
            return None

        if ("results"
                not in response_body) or ("uuids"
                                          not in response_body["results"]):
            logger.critical(
                "Error: unexpected http response body from Registrar: %s" %
                str(response.status_code))
            return None

        agent_list = response_body["results"]["uuids"]

        # Loop through each agent and ask for status
        agents = {}
        for agent in agent_list:
            agents[agent] = self.get_agent_state(agent_id)

        # Pre-create sorted agents list
        sorted_by_state = {}
        states = cloud_verifier_common.CloudAgent_Operational_State.STR_MAPPINGS
        for state in states:
            sorted_by_state[state] = {}

        # Build sorted agents list
        for agent_id in agents:
            state = agents[agent_id]["operational_state"]
            sorted_by_state[state][agent_id] = agents[agent_id]

        print_order = [10, 9, 7, 3, 4, 5, 6, 2, 1, 8, 0]
        sorted_agents = []
        for state in print_order:
            for agent_id in sorted_by_state[state]:
                sorted_agents.append(agent_id)

        common.echo_json_response(self, 200, "Success",
                                  {'uuids': sorted_agents})
Beispiel #14
0
 def head(self):
     """HEAD not supported"""
     common.echo_json_response(self, 405, "HEAD not supported")
Beispiel #15
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
Beispiel #16
0
 def do_PATCH(self):
     """PATCH not supported"""
     common.echo_json_response(self, 405, "PATCH not supported")
     return
    def post(self):
        """This method handles the POST requests to add instances to the Cloud Verifier. 
         
        Currently, only instances resources are available for POSTing, i.e. /v2/instances. All other POST uri's will return errors.
        instances requests require a json block sent in the body
        """
        try:
            rest_params = common.get_restful_params(self.request.path)
            if rest_params is None:
                common.echo_json_response(
                    self, 405, "Not Implemented: Use /v2/instances/ interface")
                return

            if "instances" not in rest_params:
                common.echo_json_response(self, 400, "uri not supported")
                logger.warning(
                    'POST returning 400 response. uri not supported: ' +
                    self.request.path)
                return

            instance_id = rest_params["instances"]

            if instance_id is not None:  # this is for new items
                content_length = len(self.request.body)
                if content_length == 0:
                    common.echo_json_response(
                        self, 400, "Expected non zero content length")
                    logger.warning(
                        'POST returning 400 response. Expected non zero content length.'
                    )
                else:
                    json_body = json.loads(self.request.body)
                    d = {}
                    d['v'] = json_body['v']
                    d['ip'] = json_body['cloudnode_ip']
                    d['port'] = int(json_body['cloudnode_port'])
                    d['operational_state'] = cloud_verifier_common.CloudInstance_Operational_State.START
                    d['public_key'] = ""
                    d['tpm_policy'] = json_body['tpm_policy']
                    d['vtpm_policy'] = json_body['vtpm_policy']
                    d['metadata'] = json_body['metadata']
                    d['ima_whitelist'] = json_body['ima_whitelist']
                    d['revocation_key'] = json_body['revocation_key']

                    new_instance = self.db.add_instance(instance_id, d)

                    # don't allow overwriting
                    if new_instance is None:
                        common.echo_json_response(
                            self, 409,
                            "Node of uuid %s already exists" % (instance_id))
                        logger.warning("Node of uuid %s already exists" %
                                       (instance_id))
                    else:
                        self.process_instance(
                            new_instance, cloud_verifier_common.
                            CloudInstance_Operational_State.GET_QUOTE)
                        common.echo_json_response(self, 200, "Success")
                        logger.info(
                            'POST returning 200 response for adding instance id: '
                            + instance_id)
            else:
                common.echo_json_response(self, 400, "uri not supported")
                logger.warning(
                    "POST returning 400 response. uri not supported")
        except Exception as e:
            common.echo_json_response(self, 400, "Exception error: %s" % e)
            logger.warning("POST returning 400 response. Exception error: %s" %
                           e)
            logger.warning(traceback.format_exc())

        self.finish()
Beispiel #18
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(base64.b64decode(agent['key']),
                                            agent_id)
                    if ex_mac == auth_tag:
                        self.server.db.update_agent(agent_id, 'active', True)
                    else:
                        raise Exception(
                            "Auth tag %s does not match expected value %s" %
                            (auth_tag, ex_mac))

                common.echo_json_response(self, 200, "Success")
                logger.info('PUT activated: ' + agent_id)
            elif "vactivate" in rest_params:
                deepquote = json_body.get('deepquote', None)

                agent = self.server.db.get_agent(agent_id)
                if agent is None:
                    raise Exception(
                        "attempting to activate agent before requesting registrar for %s"
                        % agent_id)

                if not agent['virtual']:
                    raise Exception(
                        "attempting to activate physical AIK using virtual interface for %s"
                        % agent_id)

                # get an physical AIK for this host
                registrar_client.init_client_tls(config, 'registrar')
                provider_keys = registrar_client.getKeys(
                    config.get('general', 'provider_registrar_ip'),
                    config.get('general', 'provider_registrar_tls_port'),
                    agent_id)
                # we already have the vaik
                tpm = tpm_obj.getTPM(need_hw_tpm=False,
                                     tpm_version=agent['tpm_version'])
                if not tpm.check_deep_quote(
                        hashlib.sha1(agent['key']).hexdigest(),
                        agent_id + agent['aik'] + agent['ek'], deepquote,
                        agent['aik'], provider_keys['aik']):
                    raise Exception("Deep quote invalid")

                self.server.db.update_agent(agent_id, 'active', True)
                self.server.db.update_agent(agent_id, 'provider_keys',
                                            provider_keys)

                common.echo_json_response(self, 200, "Success")
                logger.info('PUT activated: ' + agent_id)
            else:
                pass
        except Exception as e:
            common.echo_json_response(self, 400, "Error: %s" % e)
            logger.warning("PUT for " + agent_id +
                           " returning 400 response. Error: %s" % e)
            logger.exception(e)
            return
Beispiel #19
0
 def do_HEAD(self):
     """Not supported"""
     common.echo_json_response(self, 405, "HEAD not supported")
Beispiel #20
0
 def delete(self):
     common.echo_json_response(
         self, 405,
         "Not Implemented: Use /webapp/, /agents/ or /logs/  interface instead"
     )
Beispiel #21
0
 def do_PUT(self):
     """PUT not supported"""
     common.echo_json_response(self, 405,
                               "PUT not supported via TLS interface")
     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
             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 post(self):
     """This method handles the POST requests to add agents to the Cloud Verifier. 
      
     Currently, only agents resources are available for POSTing, i.e. /agents. All other POST uri's will return errors.
     agents requests require a json block sent in the body
     """
     try:
         rest_params = common.get_restful_params(self.request.uri)
         if rest_params is None:
             common.echo_json_response(self, 405, "Not Implemented: Use /agents/ interface")
             return
         
         if "agents" not in rest_params:
             common.echo_json_response(self, 400, "uri not supported")
             logger.warning('POST returning 400 response. uri not supported: ' + self.request.path)
             return
         
         agent_id = rest_params["agents"]
         
         if agent_id is not None: # this is for new items
             content_length = len(self.request.body)
             if content_length==0:
                 common.echo_json_response(self, 400, "Expected non zero content length")
                 logger.warning('POST returning 400 response. Expected non zero content length.')
             else:
                 json_body = json.loads(self.request.body)
                 d = {}
                 d['v'] = json_body['v']
                 d['ip'] = json_body['cloudagent_ip']
                 d['port'] = int(json_body['cloudagent_port'])
                 d['operational_state'] = cloud_verifier_common.CloudAgent_Operational_State.START
                 d['public_key'] = ""
                 d['tpm_policy'] = json_body['tpm_policy']
                 d['vtpm_policy'] = json_body['vtpm_policy']
                 d['metadata'] = json_body['metadata']
                 d['ima_whitelist'] = json_body['ima_whitelist']
                 d['revocation_key'] = json_body['revocation_key']
                 d['tpm_version'] = 0
                 d['accept_tpm_hash_algs'] = json_body['accept_tpm_hash_algs']
                 d['accept_tpm_encryption_algs'] = json_body['accept_tpm_encryption_algs']
                 d['accept_tpm_signing_algs'] = json_body['accept_tpm_signing_algs']
                 d['hash_alg'] = ""
                 d['enc_alg'] = ""
                 d['sign_alg'] = ""
                 
                 new_agent = self.db.add_agent(agent_id,d)
                 
                 # don't allow overwriting
                 if new_agent is None:
                     common.echo_json_response(self, 409, "Agent of uuid %s already exists"%(agent_id))
                     logger.warning("Agent of uuid %s already exists"%(agent_id))
                 else:    
                     self.process_agent(new_agent, cloud_verifier_common.CloudAgent_Operational_State.GET_QUOTE)
                     common.echo_json_response(self, 200, "Success")
                     logger.info('POST returning 200 response for adding agent id: ' + agent_id)
         else:
             common.echo_json_response(self, 400, "uri not supported")
             logger.warning("POST returning 400 response. uri not supported")
     except Exception as e:
         common.echo_json_response(self, 400, "Exception error: %s"%e)
         logger.warning("POST returning 400 response. Exception error: %s"%e)
         logger.exception(e)
     
     self.finish()
Beispiel #24
0
 def do_DELETE(self):
     """DELETE not supported"""
     common.echo_json_response(self, 405, "DELETE not supported")
     return
 def put(self):
     common.echo_json_response(
         self, 405, "Not Implemented: Use /v2/instances/ interface instead")
Beispiel #26
0
 def get(self):
     common.echo_json_response(
         self, 405, "Not Implemented: Use /agents/ interface instead")
Beispiel #27
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))
        f.close()
        
        #stow the U value for later
        tpm.write_key_nvram(self.server.final_U)
        
        # optionally extend a hash of they key and payload into specified PCR
        tomeasure = self.server.K
        
        # if we have a good key, now attempt to write out the encrypted payload
        dec_path = "%s/%s"%(secdir, config.get('cloud_agent',"dec_payload_file"))
        enc_path = "%s/encrypted_payload"%common.WORK_DIR
        
        dec_payload = None
        enc_payload = None
        
        if self.server.payload is not None:
            dec_payload = crypto.decrypt(self.server.payload, str(self.server.K))
            enc_payload = self.server.payload
        elif os.path.exists(enc_path):
            # if no payload provided, try to decrypt one from a previous run stored in encrypted_payload
            with open(enc_path,'r') as f:
                enc_payload = f.read()
            try:
                dec_payload = crypto.decrypt(enc_payload,str(self.server.K))
                logger.info("Decrypted previous payload in %s to %s"%(enc_path,dec_path))
            except Exception as e:
                logger.warning("Unable to decrypt previous payload %s with derived key: %s"%(enc_path,e))
                os.remove(enc_path)
                enc_payload=None
        
        # also write out encrypted payload to be decrytped next time
        if enc_payload is not None:
            with open(enc_path,'w') as f:
                f.write(self.server.payload)

        # deal with payload
        payload_thread = None
        if dec_payload is not None:
            tomeasure += dec_payload
            # see if payload is a zip
            zfio = cStringIO.StringIO(dec_payload)
            if config.getboolean('cloud_agent','extract_payload_zip') and zipfile.is_zipfile(zfio):
                logger.info("Decrypting and unzipping payload to %s/unzipped"%secdir)
                with zipfile.ZipFile(zfio,'r')as f:
                    f.extractall('%s/unzipped'%secdir)
                
                # run an included script if one has been provided
                initscript = config.get('cloud_agent','payload_script')
                if initscript is not "":
                    def initthread():
                        import subprocess
                        env = os.environ.copy()
                        env['AGENT_UUID']=self.server.agent_uuid
                        proc= subprocess.Popen(["/bin/bash",initscript],env=env,shell=False,cwd='%s/unzipped'%secdir,
                                                stdout=subprocess.PIPE,stderr=subprocess.STDOUT)
                        while True:
                            line = proc.stdout.readline();
                            if line == '' and proc.poll() is not None:
                                break
                            if line:
                                logger.debug("init-output: %s"%line.strip())
                        # should be a no-op as poll already told us it's done
                        proc.wait()                            
                            
                    if not os.path.exists("%s/unzipped/%s"%(secdir,initscript)):
                        logger.info("No payload script %s found in %s/unzipped"%(initscript,secdir))
                    else:
                        logger.info("Executing payload script: %s/unzipped/%s"%(secdir,initscript))
                        payload_thread = threading.Thread(target=initthread)
            else:
                logger.info("Decrypting payload to %s"%dec_path)
                with open(dec_path,'w') as f:
                    f.write(dec_payload)
            zfio.close()

        # now extend a measurement of the payload and key if there was one
        pcr = config.getint('cloud_agent','measure_payload_pcr')
        if pcr>0 and pcr<24:
            logger.info("extending measurement of payload into PCR %s"%pcr)
            measured = tpm.hashdigest(tomeasure)
            tpm.extendPCR(pcr,measured)
            
        if payload_thread is not None:
            payload_thread.start()
            
        return
Beispiel #28
0
 def do_GET(self):
     """GET not supported"""
     common.echo_json_response(self, 405, "GET not supported")
     return
Beispiel #29
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
Beispiel #30
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)

            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