Ejemplo n.º 1
0
def start(host, tlsport, port):
    """Main method of the Registrar Server.  This method is encapsulated in a function for packaging to allow it to be
    called as a function by an external program."""

    threads = []
    servers = []
    serveraddr = (host, tlsport)

    RegistrarMain.metadata.create_all(engine, checkfirst=True)
    session = SessionManager().make_session(engine)
    try:
        count = session.query(RegistrarMain.agent_id).count()
    except SQLAlchemyError as e:
        logger.error(f'SQLAlchemy Error: {e}')
    if count > 0:
        logger.info("Loaded %d public keys from database" % count)

    server = RegistrarServer(serveraddr, ProtectedHandler)
    context = cloud_verifier_common.init_mtls(section='registrar',
                                              generatedir='reg_ca')
    if context is not None:
        server.socket = context.wrap_socket(server.socket, server_side=True)
    thread = threading.Thread(target=server.serve_forever)
    threads.append(thread)

    # start up the unprotected registrar server
    serveraddr2 = (host, port)
    server2 = RegistrarServer(serveraddr2, UnprotectedHandler)
    thread2 = threading.Thread(target=server2.serve_forever)
    threads.append(thread2)

    servers.append(server)
    servers.append(server2)

    logger.info(
        'Starting Cloud Registrar Server on ports %s and %s (TLS) use <Ctrl-C> to stop' % (port, tlsport))
    for thread in threads:
        thread.start()

    def signal_handler(signal, frame):
        do_shutdown(servers)
        sys.exit(0)

    # Catch these signals.  Note that a SIGKILL cannot be caught, so
    # killing this process with "kill -9" may result in improper shutdown
    signal.signal(signal.SIGTERM, signal_handler)
    signal.signal(signal.SIGQUIT, signal_handler)
    signal.signal(signal.SIGINT, signal_handler)

    # keep the main thread active, so it can process the signals and gracefully shutdown
    while True:
        if not any([thread.isAlive() for thread in threads]):
            # All threads have stopped
            break
        # Some threads are still going
        time.sleep(1)

    for thread in threads:
        thread.join()
Ejemplo n.º 2
0
def main(argv=sys.argv):
    """Main method of the Cloud Verifier Server.  This method is encapsulated in a function for packaging to allow it to be
    called as a function by an external program."""

    config = common.get_config()
    cloudverifier_port = config.get('cloud_verifier', 'cloudverifier_port')

    VerfierMain.metadata.create_all(engine, checkfirst=True)
    session = SessionManager().make_session(engine)
    try:
        query_all = session.query(VerfierMain).all()
    except SQLAlchemyError as e:
        logger.error(f'SQLAlchemy Error: {e}')
    for row in query_all:
        row.operational_state = cloud_verifier_common.CloudAgent_Operational_State.SAVED
    try:
        session.commit()
    except SQLAlchemyError as e:
        logger.error(f'SQLAlchemy Error: {e}')
    num = session.query(VerfierMain.agent_id).count()
    if num > 0:
        agent_ids = session.query(VerfierMain.agent_id).all()
        logger.info("agent ids in db loaded from file: %s" % agent_ids)

    logger.info('Starting Cloud Verifier (tornado) on port ' +
                cloudverifier_port + ', use <Ctrl-C> to stop')

    app = tornado.web.Application([
        (r"/(?:v[0-9]/)?agents/.*", AgentsHandler),
        (r".*", MainHandler),
    ])

    context = cloud_verifier_common.init_mtls()

    # after TLS is up, start revocation notifier
    if config.getboolean('cloud_verifier', 'revocation_notifier'):
        logger.info(
            "Starting service for revocation notifications on port %s" %
            config.getint('cloud_verifier', 'revocation_notifier_port'))
        revocation_notifier.start_broker()

    sockets = tornado.netutil.bind_sockets(int(cloudverifier_port),
                                           address='0.0.0.0')
    tornado.process.fork_processes(
        config.getint('cloud_verifier', 'multiprocessing_pool_num_workers'))
    asyncio.set_event_loop(asyncio.new_event_loop())
    server = tornado.httpserver.HTTPServer(app, ssl_options=context)
    server.add_sockets(sockets)

    try:
        tornado.ioloop.IOLoop.instance().start()
    except KeyboardInterrupt:
        tornado.ioloop.IOLoop.instance().stop()
        if config.getboolean('cloud_verifier', 'revocation_notifier'):
            revocation_notifier.stop_broker()
Ejemplo n.º 3
0
def start(host, tlsport, port):
    """Main method of the Registrar Server.  This method is encapsulated in a function for packaging to allow it to be
    called as a function by an external program."""

    # set a conservative general umask
    os.umask(0o077)

    RegistrarMain.metadata.create_all(engine, checkfirst=True)
    session = SessionManager().make_session(engine)
    try:
        count = session.query(RegistrarMain.agent_id).count()
        if count > 0:
            logger.info("Loaded %d public keys from database", count)
    except SQLAlchemyError as e:
        logger.error('SQLAlchemy Error: %s', e)

    # Set up the protected registrar server
    protected_server = RegistrarServer((host, tlsport), ProtectedHandler)
    context, _ = web_util.init_mtls(section='registrar',
                                    generatedir='reg_ca',
                                    logger=logger)
    if context is not None:
        protected_server.socket = context.wrap_socket(protected_server.socket,
                                                      server_side=True)
    thread_protected_server = threading.Thread(
        target=protected_server.serve_forever)

    # Set up the unprotected registrar server
    unprotected_server = RegistrarServer((host, port), UnprotectedHandler)
    thread_unprotected_server = threading.Thread(
        target=unprotected_server.serve_forever)

    logger.info(
        'Starting Cloud Registrar Server on ports %s and %s (TLS) use <Ctrl-C> to stop',
        port, tlsport)
    keylime_api_version.log_api_versions(logger)
    thread_protected_server.start()
    thread_unprotected_server.start()

    def signal_handler(signum, frame):
        del signum, frame
        logger.info("Shutting down Registrar Server...")
        protected_server.shutdown()
        unprotected_server.shutdown()
        sys.exit(0)

    # Catch these signals.  Note that a SIGKILL cannot be caught, so
    # killing this process with "kill -9" may result in improper shutdown
    signal.signal(signal.SIGTERM, signal_handler)
    signal.signal(signal.SIGQUIT, signal_handler)
    signal.signal(signal.SIGINT, signal_handler)

    thread_protected_server.join()
    thread_unprotected_server.join()
Ejemplo n.º 4
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.
        """
        session = SessionManager().make_session(engine)
        rest_params = web_util.get_restful_params(self.path)
        if rest_params is None:
            web_util.echo_json_response(
                self, 405, "Not Implemented: Use /agents/ interface")
            return

        if not rest_params["api_version"]:
            web_util.echo_json_response(self, 400, "API Version not supported")
            return

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

        agent_id = rest_params["agents"]

        if agent_id is not None:
            # If the agent ID is not valid (wrong set of characters),
            # just do nothing.
            if not validators.valid_agent_id(agent_id):
                web_util.echo_json_response(self, 400,
                                            "agent_id not not valid")
                logger.error("DELETE received an invalid agent ID: %s",
                             agent_id)
                return

            if session.query(RegistrarMain).filter_by(
                    agent_id=agent_id).delete():
                # send response
                try:
                    session.commit()
                except SQLAlchemyError as e:
                    logger.error('SQLAlchemy Error: %s', e)
                web_util.echo_json_response(self, 200, "Success")
                return

            # send response
            web_util.echo_json_response(self, 404)
            return

        web_util.echo_json_response(self, 404)
Ejemplo n.º 5
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.
        """
        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(
                'DELETE agent returning 400 response. uri not supported: ' +
                self.path)
            return

        agent_id = rest_params["agents"]

        if agent_id is not None:
            if session.query(RegistrarMain).filter_by(
                    agent_id=agent_id).delete():
                # send response
                try:
                    session.commit()
                except SQLAlchemyError as e:
                    logger.error(f'SQLAlchemy Error: {e}')
                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
Ejemplo n.º 6
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 = config.get_restful_params(self.path)
        if rest_params is None:
            config.echo_json_response(
                self, 405, "Not Implemented: Use /agents/ interface")
            return

        if "agents" not in rest_params:
            config.echo_json_response(self, 400, "uri not supported")
            logger.warning('GET returning 400 response. uri not supported: %s',
                           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('SQLAlchemy Error: %s', e)

            if agent is None:
                config.echo_json_response(self, 404, "agent_id not found")
                logger.warning(
                    'GET returning 404 response. agent_id %s not found.',
                    agent_id)
                return

            if not agent.active:
                config.echo_json_response(self, 404, "agent_id not yet active")
                logger.warning(
                    'GET returning 404 response. agent_id %s not yet active.',
                    agent_id)
                return

            response = {
                'aik_tpm': agent.aik_tpm,
                'ek_tpm': agent.ek_tpm,
                'ekcert': agent.ekcert,
                'regcount': agent.regcount,
            }

            if agent.virtual:
                response['provider_keys'] = agent.provider_keys

            config.echo_json_response(self, 200, "Success", response)
            logger.info('GET returning 200 response for agent_id: %s',
                        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]
            config.echo_json_response(self, 200, "Success",
                                      {'uuids': return_response})
            logger.info('GET returning 200 response for agent_id list')

        return
Ejemplo n.º 7
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.
        """
        session = SessionManager().make_session(engine)
        rest_params = config.get_restful_params(self.path)
        if rest_params is None:
            config.echo_json_response(
                self, 405, "Not Implemented: Use /agents/ interface")
            return

        if "agents" not in rest_params:
            config.echo_json_response(self, 400, "uri not supported")
            logger.warning(
                'PUT agent returning 400 response. uri not supported: %s',
                self.path)
            return

        agent_id = rest_params["agents"]

        if agent_id is None:
            config.echo_json_response(self, 400, "agent id not found in uri")
            logger.warning(
                'PUT agent returning 400 response. agent id not found in uri %s',
                self.path)
            return

        try:
            content_length = int(self.headers.get('Content-Length', 0))
            if content_length == 0:
                config.echo_json_response(self, 400,
                                          "Expected non zero content length")
                logger.warning(
                    'PUT for %s returning 400 response. Expected non zero content length.',
                    agent_id)
                return

            post_body = self.rfile.read(content_length)
            json_body = json.loads(post_body)

            auth_tag = json_body['auth_tag']
            try:
                agent = session.query(RegistrarMain).filter_by(
                    agent_id=agent_id).first()
            except NoResultFound as e:
                raise Exception(
                    "attempting to activate agent before requesting "
                    "registrar for %s" % agent_id) from e
            except SQLAlchemyError as e:
                logger.error('SQLAlchemy Error: %s', e)
                raise

            if config.STUB_TPM:
                try:
                    session.query(RegistrarMain).filter(
                        RegistrarMain.agent_id == agent_id).update(
                            {'active': True})
                    session.commit()
                except SQLAlchemyError as e:
                    logger.error('SQLAlchemy Error: %s', e)
                    raise
            else:
                # TODO(kaifeng) Special handling should be removed
                if engine.dialect.name == "mysql":
                    agent.key = agent.key.encode('utf-8')

                ex_mac = crypto.do_hmac(agent.key, agent_id)
                if ex_mac == auth_tag:
                    try:
                        session.query(RegistrarMain).filter(
                            RegistrarMain.agent_id == agent_id).update(
                                {'active': True})
                        session.commit()
                    except SQLAlchemyError as e:
                        logger.error('SQLAlchemy Error: %s', e)
                        raise
                else:
                    raise Exception(
                        "Auth tag %s does not match expected value %s" %
                        (auth_tag, ex_mac))

            config.echo_json_response(self, 200, "Success")
            logger.info('PUT activated: %s', agent_id)
        except Exception as e:
            config.echo_json_response(self, 400, "Error: %s" % e)
            logger.warning("PUT for %s returning 400 response. Error: %s",
                           agent_id, e)
            logger.exception(e)
            return
Ejemplo n.º 8
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.
        """
        session = SessionManager().make_session(engine)
        rest_params = config.get_restful_params(self.path)
        if rest_params is None:
            config.echo_json_response(
                self, 405, "Not Implemented: Use /agents/ interface")
            return

        if "agents" not in rest_params:
            config.echo_json_response(self, 400, "uri not supported")
            logger.warning(
                'POST agent returning 400 response. uri not supported: %s',
                self.path)
            return

        agent_id = rest_params["agents"]

        if agent_id is None:
            config.echo_json_response(self, 400, "agent id not found in uri")
            logger.warning(
                'POST agent returning 400 response. agent id not found in uri %s',
                self.path)
            return

        try:
            content_length = int(self.headers.get('Content-Length', 0))
            if content_length == 0:
                config.echo_json_response(self, 400,
                                          "Expected non zero content length")
                logger.warning(
                    'POST for %s returning 400 response. Expected non zero content length.',
                    agent_id)
                return

            post_body = self.rfile.read(content_length)
            json_body = json.loads(post_body)

            ekcert = json_body['ekcert']
            aik_tpm = json_body['aik_tpm']

            initialize_tpm = tpm()

            if ekcert is None or ekcert == 'emulator':
                logger.warning('Agent %s did not submit an ekcert' % agent_id)
                ek_tpm = json_body['ek_tpm']
            else:
                if 'ek_tpm' in json_body:
                    # This would mean the agent submitted both a non-None ekcert, *and*
                    #  an ek_tpm... We can deal with it by just ignoring the ek_tpm they sent
                    logger.warning(
                        'Overriding ek_tpm for agent %s from ekcert' %
                        agent_id)
                # If there's an EKCert, we just overwrite their ek_tpm
                # Note, we don't validate the EKCert here, other than the implicit
                #  "is it a valid x509 cert" check. So it's still untrusted.
                # This will be validated by the tenant.
                ek509 = load_der_x509_certificate(
                    base64.b64decode(ekcert),
                    backend=default_backend(),
                )
                ek_tpm = base64.b64encode(
                    tpm2_objects.ek_low_tpm2b_public_from_pubkey(
                        ek509.public_key(), ))

            aik_attrs = tpm2_objects.get_tpm2b_public_object_attributes(
                base64.b64decode(aik_tpm), )
            if aik_attrs != tpm2_objects.AK_EXPECTED_ATTRS:
                config.echo_json_response(self, 400, "Invalid AK attributes")
                logger.warning(
                    "Agent %s submitted AIK with invalid attributes! %s (provided) != %s (expected)",
                    agent_id,
                    tpm2_objects.object_attributes_description(aik_attrs),
                    tpm2_objects.object_attributes_description(
                        tpm2_objects.AK_EXPECTED_ATTRS),
                )
                return

            # try to encrypt the AIK
            (blob, key) = initialize_tpm.encryptAIK(
                agent_id,
                base64.b64decode(ek_tpm),
                base64.b64decode(aik_tpm),
            )

            # special behavior if we've registered this uuid before
            regcount = 1
            try:
                agent = session.query(RegistrarMain).filter_by(
                    agent_id=agent_id).first()
            except NoResultFound:
                agent = None
            except SQLAlchemyError as e:
                logger.error('SQLAlchemy Error: %s', e)
                raise

            if agent is not None:

                # keep track of how many ek-ekcerts have registered on this uuid
                regcount = agent.regcount
                if agent.ek_tpm != ek_tpm 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.')
                try:
                    session.query(RegistrarMain).filter_by(
                        agent_id=agent_id).delete()
                    session.commit()
                except SQLAlchemyError as e:
                    logger.error('SQLAlchemy Error: %s', e)
                    raise

            # Add values to database
            d = {}
            d['agent_id'] = agent_id
            d['ek_tpm'] = ek_tpm
            d['aik_tpm'] = aik_tpm
            d['ekcert'] = ekcert
            d['virtual'] = int(ekcert == 'virtual')
            d['active'] = int(False)
            d['key'] = key
            d['provider_keys'] = {}
            d['regcount'] = regcount

            try:
                session.add(RegistrarMain(**d))
                session.commit()
            except SQLAlchemyError as e:
                logger.error('SQLAlchemy Error: %s', e)
                raise

            response = {
                'blob': blob,
            }
            config.echo_json_response(self, 200, "Success", response)

            logger.info('POST returning key blob for agent_id: %s', agent_id)
        except Exception as e:
            config.echo_json_response(self, 400, "Error: %s" % e)
            logger.warning("POST for %s returning 400 response. Error: %s",
                           agent_id, e)
            logger.exception(e)
Ejemplo n.º 9
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.
        """
        session = SessionManager().make_session(engine)
        rest_params = web_util.get_restful_params(self.path)
        if rest_params is None:
            web_util.echo_json_response(
                self, 405, "Not Implemented: Use /agents/ interface")
            return

        if not rest_params["api_version"]:
            web_util.echo_json_response(self, 400, "API Version not supported")
            return

        if "agents" not in rest_params:
            web_util.echo_json_response(self, 400, "uri not supported")
            logger.warning(
                'PUT agent returning 400 response. uri not supported: %s',
                self.path)
            return

        agent_id = rest_params["agents"]

        if agent_id is None:
            web_util.echo_json_response(self, 400, "agent id not found in uri")
            logger.warning(
                'PUT agent returning 400 response. agent id not found in uri %s',
                self.path)
            return

        # If the agent ID is not valid (wrong set of characters), just
        # do nothing.
        if not validators.valid_agent_id(agent_id):
            web_util.echo_json_response(self, 400, "agent_id not not valid")
            logger.error("PUT received an invalid agent ID: %s", agent_id)
            return

        try:
            content_length = int(self.headers.get('Content-Length', 0))
            if content_length == 0:
                web_util.echo_json_response(
                    self, 400, "Expected non zero content length")
                logger.warning(
                    'PUT for %s returning 400 response. Expected non zero content length.',
                    agent_id)
                return

            post_body = self.rfile.read(content_length)
            json_body = json.loads(post_body)

            auth_tag = json_body['auth_tag']
            try:
                agent = session.query(RegistrarMain).filter_by(
                    agent_id=agent_id).first()
            except NoResultFound as e:
                raise Exception(
                    "attempting to activate agent before requesting "
                    "registrar for %s" % agent_id) from e
            except SQLAlchemyError as e:
                logger.error('SQLAlchemy Error: %s', e)
                raise

            if config.STUB_TPM:
                try:
                    session.query(RegistrarMain).filter(
                        RegistrarMain.agent_id == agent_id).update(
                            {'active': int(True)})
                    session.commit()
                except SQLAlchemyError as e:
                    logger.error('SQLAlchemy Error: %s', e)
                    raise
            else:
                ex_mac = crypto.do_hmac(agent.key.encode(), agent_id)
                if ex_mac == auth_tag:
                    try:
                        session.query(RegistrarMain).filter(
                            RegistrarMain.agent_id == agent_id).update(
                                {'active': int(True)})
                        session.commit()
                    except SQLAlchemyError as e:
                        logger.error('SQLAlchemy Error: %s', e)
                        raise
                else:
                    raise Exception(
                        f"Auth tag {auth_tag} does not match expected value {ex_mac}"
                    )

            web_util.echo_json_response(self, 200, "Success")
            logger.info('PUT activated: %s', agent_id)
        except Exception as e:
            web_util.echo_json_response(self, 400, "Error: %s" % e)
            logger.warning("PUT for %s returning 400 response. Error: %s",
                           agent_id, e)
            logger.exception(e)
            return
Ejemplo n.º 10
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.
        """
        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(
                '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']
                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:
                    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:
                    try:
                        session.query(RegistrarMain).filter(
                            agent_id == agent_id).update({'active': True})
                        session.commit()
                    except SQLAlchemyError as e:
                        logger.error(f'SQLAlchemy Error: {e}')
                else:
                    ex_mac = crypto.do_hmac(agent.key, agent_id)
                    if ex_mac == auth_tag:
                        try:
                            session.query(RegistrarMain).filter(
                                agent_id == agent_id).update({'active': True})
                            session.commit()
                        except SQLAlchemyError as e:
                            logger.error(f'SQLAlchemy Error: {e}')
                    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)
                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:
                    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")
                try:
                    session.query(RegistrarMain).filter(
                        agent_id == agent_id).update({'active': True})
                except SQLAlchemyError as e:
                    logger.error(f'SQLAlchemy Error: {e}')
                try:
                    session.query(RegistrarMain).filter(
                        agent_id == agent_id).update(
                            {'provider_keys': provider_keys})
                except SQLAlchemyError as e:
                    logger.error(f'SQLAlchemy Error: {e}')

                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
Ejemplo n.º 11
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.
        """
        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(
                '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
            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 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.')
                try:
                    session.query(RegistrarMain).filter_by(
                        agent_id=agent_id).delete()
                    session.commit()
                except SQLAlchemyError as e:
                    logger.error(f'SQLAlchemy Error: {e}')

            # Add values to database
            d = {}
            d['agent_id'] = agent_id
            d['ek'] = ek
            d['aik'] = aik
            d['ekcert'] = ekcert
            d['virtual'] = int(ekcert == 'virtual')
            d['active'] = int(False)
            d['key'] = key
            d['provider_keys'] = {}
            d['regcount'] = regcount

            try:
                session.add(RegistrarMain(**d))
                session.commit()
            except SQLAlchemyError as e:
                logger.error(f'SQLAlchemy Error: {e}')

            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
Ejemplo n.º 12
0
 def setUp(self):
     self.engine = create_engine("sqlite://")
     VerfierMain.metadata.create_all(self.engine, checkfirst=True)
     self.session = SessionManager().make_session(self.engine)
     self.populate_tables()
Ejemplo n.º 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.
        """
        session = SessionManager().make_session(engine)
        rest_params = web_util.get_restful_params(self.path)
        if rest_params is None:
            web_util.echo_json_response(
                self, 405, "Not Implemented: Use /agents/ interface")
            return

        if not web_util.validate_api_version(self, rest_params["api_version"],
                                             logger):
            return

        if "agents" not in rest_params:
            web_util.echo_json_response(self, 400, "uri not supported")
            logger.warning("GET returning 400 response. uri not supported: %s",
                           self.path)
            return

        agent_id = rest_params["agents"]

        if agent_id is not None:
            # If the agent ID is not valid (wrong set of characters),
            # just do nothing.
            if not validators.valid_agent_id(agent_id):
                web_util.echo_json_response(self, 400,
                                            "agent_id not not valid")
                logger.error("GET received an invalid agent ID: %s", agent_id)
                return

            try:
                agent = session.query(RegistrarMain).filter_by(
                    agent_id=agent_id).first()
            except SQLAlchemyError as e:
                logger.error("SQLAlchemy Error: %s", e)

            if agent is None:
                web_util.echo_json_response(self, 404,
                                            f"agent {agent_id} not found")
                logger.warning(
                    "GET returning 404 response. agent %s not found.",
                    agent_id)
                return

            if not bool(agent.active):
                web_util.echo_json_response(
                    self, 404, f"agent {agent_id} not yet active")
                logger.warning(
                    "GET returning 404 response. agent %s not yet active.",
                    agent_id)
                return

            response = {
                "aik_tpm": agent.aik_tpm,
                "ek_tpm": agent.ek_tpm,
                "ekcert": agent.ekcert,
                "mtls_cert": agent.mtls_cert,
                "ip": agent.ip,
                "port": agent.port,
                "regcount": agent.regcount,
            }

            if agent.virtual:
                response["provider_keys"] = agent.provider_keys

            web_util.echo_json_response(self, 200, "Success", response)
            logger.info("GET returning 200 response for agent_id: %s",
                        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]
            web_util.echo_json_response(self, 200, "Success",
                                        {"uuids": return_response})
            logger.info("GET returning 200 response for agent_id list")

        return
Ejemplo n.º 14
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.
        """
        session = SessionManager().make_session(engine)
        rest_params = web_util.get_restful_params(self.path)
        if rest_params is None:
            web_util.echo_json_response(
                self, 405, "Not Implemented: Use /agents/ interface")
            return

        if not web_util.validate_api_version(self, rest_params["api_version"],
                                             logger):
            return

        if "agents" not in rest_params:
            web_util.echo_json_response(self, 400, "uri not supported")
            logger.warning(
                "POST agent returning 400 response. uri not supported: %s",
                self.path)
            return

        agent_id = rest_params["agents"]

        if agent_id is None:
            web_util.echo_json_response(self, 400, "agent id not found in uri")
            logger.warning(
                "POST agent returning 400 response. agent id not found in uri %s",
                self.path)
            return

        # If the agent ID is not valid (wrong set of characters), just
        # do nothing.
        if not validators.valid_agent_id(agent_id):
            web_util.echo_json_response(self, 400, "agent id not valid")
            logger.error("POST received an invalid agent ID: %s", agent_id)
            return

        try:
            content_length = int(self.headers.get("Content-Length", 0))
            if content_length == 0:
                web_util.echo_json_response(
                    self, 400, "Expected non zero content length")
                logger.warning(
                    "POST for %s returning 400 response. Expected non zero content length.",
                    agent_id)
                return

            post_body = self.rfile.read(content_length)
            json_body = json.loads(post_body)

            ekcert = json_body["ekcert"]
            aik_tpm = json_body["aik_tpm"]

            initialize_tpm = tpm()

            if ekcert is None or ekcert == "emulator":
                logger.warning("Agent %s did not submit an ekcert", agent_id)
                ek_tpm = json_body["ek_tpm"]
            else:
                if "ek_tpm" in json_body:
                    # This would mean the agent submitted both a non-None ekcert, *and*
                    #  an ek_tpm... We can deal with it by just ignoring the ek_tpm they sent
                    logger.warning(
                        "Overriding ek_tpm for agent %s from ekcert", agent_id)
                # If there's an EKCert, we just overwrite their ek_tpm
                # Note, we don't validate the EKCert here, other than the implicit
                #  "is it a valid x509 cert" check. So it's still untrusted.
                # This will be validated by the tenant.
                ek509 = load_der_x509_certificate(
                    base64.b64decode(ekcert),
                    backend=default_backend(),
                )
                ek_tpm = base64.b64encode(
                    tpm2_objects.ek_low_tpm2b_public_from_pubkey(
                        ek509.public_key(), )).decode()

            aik_attrs = tpm2_objects.get_tpm2b_public_object_attributes(
                base64.b64decode(aik_tpm), )
            if aik_attrs != tpm2_objects.AK_EXPECTED_ATTRS:
                web_util.echo_json_response(self, 400, "Invalid AK attributes")
                logger.warning(
                    "Agent %s submitted AIK with invalid attributes! %s (provided) != %s (expected)",
                    agent_id,
                    tpm2_objects.object_attributes_description(aik_attrs),
                    tpm2_objects.object_attributes_description(
                        tpm2_objects.AK_EXPECTED_ATTRS),
                )
                return

            # try to encrypt the AIK
            (blob, key) = initialize_tpm.encryptAIK(
                agent_id,
                base64.b64decode(ek_tpm),
                base64.b64decode(aik_tpm),
            )

            # special behavior if we've registered this uuid before
            regcount = 1
            try:
                agent = session.query(RegistrarMain).filter_by(
                    agent_id=agent_id).first()
            except NoResultFound:
                agent = None
            except SQLAlchemyError as e:
                logger.error("SQLAlchemy Error: %s", e)
                raise

            if agent is not None:

                # keep track of how many ek-ekcerts have registered on this uuid
                regcount = agent.regcount
                if agent.ek_tpm != ek_tpm 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.")
                try:
                    session.query(RegistrarMain).filter_by(
                        agent_id=agent_id).delete()
                    session.commit()
                except SQLAlchemyError as e:
                    logger.error("SQLAlchemy Error: %s", e)
                    raise
            # Check for ip and port
            contact_ip = json_body.get("ip", None)
            contact_port = json_body.get("port", None)

            # Validate ip and port
            if contact_ip is not None:
                try:
                    # Use parser from the standard library instead of implementing our own
                    ipaddress.ip_address(contact_ip)
                except ValueError:
                    logger.warning(
                        "Contact ip for agent %s is not a valid ip got: %s.",
                        agent_id, contact_ip)
                    contact_ip = None
            if contact_port is not None:
                try:
                    contact_port = int(contact_port)
                    if contact_port < 1 or contact_port > 65535:
                        logger.warning(
                            "Contact port for agent %s is not a number between 1 and got: %s.",
                            agent_id, contact_port)
                        contact_port = None
                except ValueError:
                    logger.warning(
                        "Contact port for agent %s is not a valid number got: %s.",
                        agent_id, contact_port)
                    contact_port = None

            # Check for mTLS cert
            mtls_cert = json_body.get("mtls_cert", None)
            if mtls_cert is None:
                logger.warning(
                    "Agent %s did not send a mTLS certificate. Most operations will not work!",
                    agent_id)

            # Add values to database
            d = {}
            d["agent_id"] = agent_id
            d["ek_tpm"] = ek_tpm
            d["aik_tpm"] = aik_tpm
            d["ekcert"] = ekcert
            d["ip"] = contact_ip
            d["mtls_cert"] = mtls_cert
            d["port"] = contact_port
            d["virtual"] = int(ekcert == "virtual")
            d["active"] = int(False)
            d["key"] = key
            d["provider_keys"] = {}
            d["regcount"] = regcount

            try:
                session.add(RegistrarMain(**d))
                session.commit()
            except SQLAlchemyError as e:
                logger.error("SQLAlchemy Error: %s", e)
                raise

            response = {
                "blob": blob,
            }
            web_util.echo_json_response(self, 200, "Success", response)

            logger.info("POST returning key blob for agent_id: %s", agent_id)
        except Exception as e:
            web_util.echo_json_response(self, 400, f"Error: {str(e)}")
            logger.warning("POST for %s returning 400 response. Error: %s",
                           agent_id, e)
            logger.exception(e)
Ejemplo n.º 15
0
def get_session():
    return SessionManager().make_session(engine)
 def setUp(self):
     self.engine = create_engine('sqlite://')
     VerfierMain.metadata.create_all(self.engine, checkfirst=True)
     self.session = SessionManager().make_session(self.engine)
     self.populate_agent()
     self.populate_allowlist()
Ejemplo n.º 17
0
 def setUp(self):
     self.engine = create_engine("sqlite://")
     RegistrarMain.metadata.create_all(self.engine, checkfirst=True)
     self.session = SessionManager().make_session(self.engine)
     self.populate_agent()