Exemplo n.º 1
0
 def test_pubkey_from_tpm2b_public_ec(self):
     test_ec_cert = base64.b64decode(
         "MIIDEDCCAragAwIBAgIEcYSJiTAKBggqhkjOPQQDAjCBgzELMAkGA1UEBhMCREUxIT"
         "AfBgNVBAoMGEluZmluZW9uIFRlY2hub2xvZ2llcyBBRzEaMBgGA1UECwwRT1BUSUdB"
         "KFRNKSBUUE0yLjAxNTAzBgNVBAMMLEluZmluZW9uIE9QVElHQShUTSkgRUNDIE1hbn"
         "VmYWN0dXJpbmcgQ0EgMDM1MB4XDTE4MDMwMTE0MTkxNloXDTMzMDMwMTE0MTkxNlow"
         "ADBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABNK9AtBnW5bwNG2ZIWDrM8w/h03Ht2"
         "lp3MUosV05DeBHWZEZfmKsHMBqpqDsIKkEgclQawA4BFR5YUvSdrSUDTGjggGYMIIB"
         "lDBbBggrBgEFBQcBAQRPME0wSwYIKwYBBQUHMAKGP2h0dHA6Ly9wa2kuaW5maW5lb2"
         "4uY29tL09wdGlnYUVjY01mckNBMDM1L09wdGlnYUVjY01mckNBMDM1LmNydDAOBgNV"
         "HQ8BAf8EBAMCAAgwWAYDVR0RAQH/BE4wTKRKMEgxFjAUBgVngQUCAQwLaWQ6NDk0Nj"
         "U4MDAxGjAYBgVngQUCAgwPU0xCIDk2NzAgVFBNMi4wMRIwEAYFZ4EFAgMMB2lkOjA3"
         "M2YwDAYDVR0TAQH/BAIwADBQBgNVHR8ESTBHMEWgQ6BBhj9odHRwOi8vcGtpLmluZm"
         "luZW9uLmNvbS9PcHRpZ2FFY2NNZnJDQTAzNS9PcHRpZ2FFY2NNZnJDQTAzNS5jcmww"
         "FQYDVR0gBA4wDDAKBggqghQARAEUATAfBgNVHSMEGDAWgBQ2WY8i7ITDxPZA0hwWfQ"
         "uRE3uQpDAQBgNVHSUECTAHBgVngQUIATAhBgNVHQkEGjAYMBYGBWeBBQIQMQ0wCwwD"
         "Mi4wAgEAAgF0MAoGCCqGSM49BAMCA0gAMEUCIQCdCv3+G+KsM4OiT3SgKqvE8r5ktD"
         "I5elC9xTmS9mDA3AIgcckalMvQVTst1pGMEyAI+OoXTnYA1sBRm27WJ6sZag8="
     )
     correct_ec_obj = base64.b64decode(
         "AHoAIwALAAMAsgAgg3GXZ0SEs/gakMyNRqXXJP1S124GUgtk8qHaGzMUaaoABgCAAE"
         "MAEAADABAAINK9AtBnW5bwNG2ZIWDrM8w/h03Ht2lp3MUosV05DeBHACBZkRl+Yqwc"
         "wGqmoOwgqQSByVBrADgEVHlhS9J2tJQNMQ=="
     )
     test_ec_cert = load_der_x509_certificate(test_ec_cert, backend=default_backend())
     new_ec_pubkey = pubkey_from_tpm2b_public(correct_ec_obj)
     correct_ec_pubkey = test_ec_cert.public_key()
     new_ec_pubkey_n = new_ec_pubkey.public_numbers()
     correct_ec_pubkey_n = correct_ec_pubkey.public_numbers()
     self.assertEqual(new_ec_pubkey_n.curve.name, correct_ec_pubkey_n.curve.name)
     self.assertEqual(new_ec_pubkey_n.x, correct_ec_pubkey_n.x)
     self.assertEqual(new_ec_pubkey_n.y, correct_ec_pubkey_n.y)
Exemplo n.º 2
0
    def test_pubkey_from_tpm2b_public_rsa_without_encryption(self):
        new_rsa_pubkey = pubkey_from_tpm2b_public(
            bytes.fromhex(
                "01180001000b00050072000000100014000b0800000000000100cac43903c6"
                "16bba049ce413c961c901b56181392c7999e672e6e5ecdd7a625d4702c3d78"
                "deac81e1372b0ca1894ac0f16add636bb53d3d5b112d8f3b169ccadef6bac0"
                "d909067d1ff81dae34b26cd538a52fa20ee7bbf3b16214417d35bde80cbb0f"
                "1b3267fd6211ecfb652f771f7eaeff560b91ef2f374ab1d37bba5a7a1c7cd4"
                "4961cdd7351ee060947f43244f45fc42ea6a1ea783aaa18dc8cce90d9a97f8"
                "da09e72637a0167fdbf4cc0d09f2f752d864d45bd34ed387acc0bcddca26c6"
                "1ebe9056013a35cd1d8011336af93579afa424fe50fd7e2b03270518505710"
                "82fcae891e2897e3117fd28bd03d2d2ffdfcfa0ff95f76af9383e3c9e59fe4"
                "dde753"
            )
        )
        new_rsa_pubkey_n = new_rsa_pubkey.public_numbers()

        self.assertEqual(new_rsa_pubkey.key_size, 2048)
        self.assertEqual(new_rsa_pubkey_n.e, 65537)  # pylint: disable=no-member
        self.assertEqual(
            str(new_rsa_pubkey_n.n),  # pylint: disable=no-member
            "255968986296679270326283402717529063492526907681140893873754141432"
            "890531031973586937300971300465026177966018575012122367284728088154"
            "485873651193407172159946655006581809152369460009001515677703036255"
            "837234635576083087037905135410736640524495731191518154258439490758"
            "531740360767515943902821573272461751306668217217601399605319344343"
            "524504419559281243744525835687758392857402638332592577865592671234"
            "679107983328133731582503713366603521336278457142403979969779706740"
            "010077961630324526931687863526905140593203113247551679416434551326"
            "587069716966112452602019925398408142602185862884082705845069125895"
            "71106286823536420841299",
        )
Exemplo n.º 3
0
    def test_pubkey_from_tpm2b_public_ec_without_encryption(self):
        new_ec_pubkey = pubkey_from_tpm2b_public(
            bytes.fromhex(
                "00580023000b00050072000000100018000b000300100020c74568135840f4"
                "97ad575ebeabe6d01f3f098b5a768111ab423d5f26b259a4f000205ec0f586"
                "b53e348bc916b43a015e6ceefd947d685e59ff65357499f2c4788cba"))
        new_ec_pubkey_n = new_ec_pubkey.public_numbers()

        self.assertEqual(new_ec_pubkey_n.curve.name, "secp256r1")
        self.assertEqual(
            str(new_ec_pubkey_n.x),
            "90132887618692975484254453731651094410483286444689191401164175504334705501424",
        )
        self.assertEqual(
            str(new_ec_pubkey_n.y),
            "42858336962839421935559570622369777529185491150475599613778789950332157332666",
        )
Exemplo n.º 4
0
 def test_pubkey_from_tpm2b_public_rsa(self):
     test_rsa_cert = base64.b64decode(
         "MIIEnDCCA4SgAwIBAgIEL8wtHjANBgkqhkiG9w0BAQsFADCBgzELMAkGA1UEBhMCRE"
         "UxITAfBgNVBAoMGEluZmluZW9uIFRlY2hub2xvZ2llcyBBRzEaMBgGA1UECwwRT1BU"
         "SUdBKFRNKSBUUE0yLjAxNTAzBgNVBAMMLEluZmluZW9uIE9QVElHQShUTSkgUlNBIE"
         "1hbnVmYWN0dXJpbmcgQ0EgMDM1MB4XDTE4MDMwMTE0MTkzM1oXDTMzMDMwMTE0MTkz"
         "M1owADCCASIwDQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALaIriXJCSUKdvWRDY"
         "dRbtdTK8i7eCJwHV8NhQ8Cor8NKoVmrnOdDhGqXlrKyJTueA9D2P4yQlWZI+tD9PCV"
         "CHCQiGmqxxHQXgzCzx6z+57HTUNPDi16K6ZFPNs3UkhAQxeGLOy36XD35zpfgadtvc"
         "lxJC8L+UgKfXVAM3/oMj4cDXa4cbVKhlfIQXD9OhcNjvESPWVFw0dj7Q6HM0jEkezM"
         "ew5sJ3I+LET1cIIhUlXvX8fWLu2MHx9+6LIBjkN8SuMLjKBQZjh+rEbHoFuG7Ib9pN"
         "ucrPAycid4EBBQB65j9irZ8C+ZdUUkKM5hsDhcenm/0AdfqAGXsFtsEa8DuDECAwEA"
         "AaOCAZgwggGUMFsGCCsGAQUFBwEBBE8wTTBLBggrBgEFBQcwAoY/aHR0cDovL3BraS"
         "5pbmZpbmVvbi5jb20vT3B0aWdhUnNhTWZyQ0EwMzUvT3B0aWdhUnNhTWZyQ0EwMzUu"
         "Y3J0MA4GA1UdDwEB/wQEAwIAIDBYBgNVHREBAf8ETjBMpEowSDEWMBQGBWeBBQIBDA"
         "tpZDo0OTQ2NTgwMDEaMBgGBWeBBQICDA9TTEIgOTY3MCBUUE0yLjAxEjAQBgVngQUC"
         "AwwHaWQ6MDczZjAMBgNVHRMBAf8EAjAAMFAGA1UdHwRJMEcwRaBDoEGGP2h0dHA6Ly"
         "9wa2kuaW5maW5lb24uY29tL09wdGlnYVJzYU1mckNBMDM1L09wdGlnYVJzYU1mckNB"
         "MDM1LmNybDAVBgNVHSAEDjAMMAoGCCqCFABEARQBMB8GA1UdIwQYMBaAFM53FTtuEQ"
         "ykrilxoJhR70mTJiAqMBAGA1UdJQQJMAcGBWeBBQgBMCEGA1UdCQQaMBgwFgYFZ4EF"
         "AhAxDTALDAMyLjACAQACAXQwDQYJKoZIhvcNAQELBQADggEBAIJ7pvW3yj2wAHO1fq"
         "zOeKg/xQjBMZ2hdpqVmhc+gU7F7zCMF85iWodISkThp9aa6p7VptkNcp5BNE1ojx+3"
         "1aJZRAFTCV0b0QxKXELTVsQLvBVmKGtFuaP3FPDVJYIOnQtb8uF+2LduF5P9K6oXdF"
         "TFuh1kG8GU/UUnltA7h6u2qhnj5uvFEDz7pxX1lt/GbI1nTYB+0SYtveIglpFyZK71"
         "0FH9UAvvR8byEbK+adE+teBUOexdXhTC1ZmPZmTvHSqmeRV3UTZFZRnyOTBnN8QlN0"
         "pMVmwFTak931PqxV0xOSXkMcvTre39jzkhEJ+VMb5EOMFfsVn+b4snob9jank="
     )
     test_rsa_cert = load_der_x509_certificate(
         test_rsa_cert, backend=default_backend()
     )
     correct_rsa_obj = base64.b64decode(
         "AToAAQALAAMAsgAgg3GXZ0SEs/gakMyNRqXXJP1S124GUgtk8qHaGzMUaaoABgCAAE"
         "MAEAgAAAAAAAEAtoiuJckJJQp29ZENh1Fu11MryLt4InAdXw2FDwKivw0qhWauc50O"
         "EapeWsrIlO54D0PY/jJCVZkj60P08JUIcJCIaarHEdBeDMLPHrP7nsdNQ08OLXorpk"
         "U82zdSSEBDF4Ys7LfpcPfnOl+Bp229yXEkLwv5SAp9dUAzf+gyPhwNdrhxtUqGV8hB"
         "cP06Fw2O8RI9ZUXDR2PtDoczSMSR7Mx7Dmwncj4sRPVwgiFSVe9fx9Yu7YwfH37osg"
         "GOQ3xK4wuMoFBmOH6sRsegW4bshv2k25ys8DJyJ3gQEFAHrmP2KtnwL5l1RSQozmGw"
         "OFx6eb/QB1+oAZewW2wRrwO4MQ=="
     )
     new_rsa_pubkey = pubkey_from_tpm2b_public(correct_rsa_obj)
     correct_rsa_pubkey = test_rsa_cert.public_key()
     new_rsa_pubkey_n = new_rsa_pubkey.public_numbers()
     correct_rsa_pubkey_n = correct_rsa_pubkey.public_numbers()
     self.assertEqual(new_rsa_pubkey.key_size, correct_rsa_pubkey.key_size)
     self.assertEqual(new_rsa_pubkey_n.e, correct_rsa_pubkey_n.e)
     self.assertEqual(new_rsa_pubkey_n.n, correct_rsa_pubkey_n.n)
Exemplo n.º 5
0
def main():
    for ML in [config.MEASUREDBOOT_ML, config.IMA_ML]:
        if not os.access(ML, os.F_OK):
            logger.warning(
                'Measurement list path %s not accessible by agent. Any attempt to instruct it to access this path - via "keylime_tenant" CLI - will result in agent process dying',
                ML,
            )

    ima_log_file = None
    if os.path.exists(config.IMA_ML):
        ima_log_file = open(config.IMA_ML, "r", encoding="utf-8")  # pylint: disable=consider-using-with

    tpm_log_file_data = None
    if os.path.exists(config.MEASUREDBOOT_ML):
        with open(config.MEASUREDBOOT_ML, "rb") as tpm_log_file:
            tpm_log_file_data = base64.b64encode(tpm_log_file.read())

    if config.get("cloud_agent", "agent_uuid") == "dmidecode":
        if os.getuid() != 0:
            raise RuntimeError(
                "agent_uuid is configured to use dmidecode, but current process is not running as root."
            )
        cmd = ["which", "dmidecode"]
        ret = cmd_exec.run(cmd, raiseOnError=False)
        if ret["code"] != 0:
            raise RuntimeError(
                "agent_uuid is configured to use dmidecode, but it's is not found on the system."
            )

    # initialize the tmpfs partition to store keys if it isn't already available
    secdir = secure_mount.mount()

    # Now that operations requiring root privileges are done, drop privileges
    # if 'run_as' is available in the configuration.
    if os.getuid() == 0:
        run_as = config.get("cloud_agent", "run_as", fallback="")
        if run_as != "":
            user_utils.chown(secdir, run_as)
            user_utils.change_uidgid(run_as)
            logger.info("Dropped privileges to %s", run_as)
        else:
            logger.warning(
                "Cannot drop privileges since 'run_as' is empty or missing in keylime.conf agent section."
            )

    # Instanitate TPM class

    instance_tpm = tpm()
    # get params for initialization
    registrar_ip = config.get("cloud_agent", "registrar_ip")
    registrar_port = config.get("cloud_agent", "registrar_port")

    # get params for the verifier to contact the agent
    contact_ip = os.getenv("KEYLIME_AGENT_CONTACT_IP", None)
    if contact_ip is None and config.has_option("cloud_agent",
                                                "agent_contact_ip"):
        contact_ip = config.get("cloud_agent", "agent_contact_ip")
    contact_port = os.getenv("KEYLIME_AGENT_CONTACT_PORT", None)
    if contact_port is None and config.has_option("cloud_agent",
                                                  "agent_contact_port"):
        contact_port = config.get("cloud_agent",
                                  "agent_contact_port",
                                  fallback="invalid")

    # change dir to working dir
    fs_util.ch_dir(config.WORK_DIR)

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

    # initialize tpm
    (ekcert, ek_tpm, aik_tpm) = instance_tpm.tpm_init(
        self_activate=False,
        config_pw=config.get("cloud_agent", "tpm_ownerpassword")
    )  # this tells initialize not to self activate the AIK

    # Warn if kernel version is <5.10 and another algorithm than SHA1 is used,
    # because otherwise IMA will not work
    kernel_version = tuple(platform.release().split("-")[0].split("."))
    if tuple(map(int, kernel_version)) < (
            5, 10,
            0) and instance_tpm.defaults["hash"] != algorithms.Hash.SHA1:
        logger.warning(
            "IMA attestation only works on kernel versions <5.10 with SHA1 as hash algorithm. "
            'Even if ascii_runtime_measurements shows "%s" as the '
            "algorithm, it might be just padding zeros",
            (instance_tpm.defaults["hash"]),
        )

    if ekcert is None and instance_tpm.is_emulator():
        ekcert = "emulator"

    # now we need the UUID
    try:
        agent_uuid = config.get("cloud_agent", "agent_uuid")
    except configparser.NoOptionError:
        agent_uuid = None
    if agent_uuid == "hash_ek":
        ek_pubkey = pubkey_from_tpm2b_public(base64.b64decode(ek_tpm))
        ek_pubkey_pem = ek_pubkey.public_bytes(
            encoding=serialization.Encoding.PEM,
            format=serialization.PublicFormat.SubjectPublicKeyInfo)
        agent_uuid = hashlib.sha256(ek_pubkey_pem).hexdigest()
    elif agent_uuid == "generate" or agent_uuid is None:
        agent_uuid = str(uuid.uuid4())
    elif agent_uuid == "dmidecode":
        cmd = ["dmidecode", "-s", "system-uuid"]
        ret = cmd_exec.run(cmd)
        sys_uuid = ret["retout"][0].decode("utf-8")
        agent_uuid = sys_uuid.strip()
        try:
            uuid.UUID(agent_uuid)
        except ValueError as e:
            raise RuntimeError(  # pylint: disable=raise-missing-from
                f"The UUID returned from dmidecode is invalid: {str(e)}")
    elif agent_uuid == "hostname":
        agent_uuid = socket.getfqdn()
    elif agent_uuid == "environment":
        agent_uuid = os.getenv("KEYLIME_AGENT_UUID", None)
        if agent_uuid is None:
            raise RuntimeError(
                "Env variable KEYLIME_AGENT_UUID is empty, but agent_uuid is set to 'environment'"
            )
    elif not validators.valid_uuid(agent_uuid):
        raise RuntimeError("The UUID is not valid")

    if not validators.valid_agent_id(agent_uuid):
        raise RuntimeError(
            "The agent ID set via agent uuid parameter use invalid characters")

    logger.info("Agent UUID: %s", agent_uuid)

    serveraddr = (config.get("cloud_agent", "cloudagent_ip"),
                  config.getint("cloud_agent", "cloudagent_port"))

    keylime_ca = config.get("cloud_agent", "keylime_ca")
    if keylime_ca == "default":
        keylime_ca = os.path.join(config.WORK_DIR, "cv_ca", "cacert.crt")

    server = CloudAgentHTTPServer(serveraddr, Handler, agent_uuid, contact_ip,
                                  ima_log_file, tpm_log_file_data)
    if server.mtls_cert_enabled:
        context = web_util.generate_mtls_context(server.mtls_cert_path,
                                                 server.rsakey_path,
                                                 keylime_ca,
                                                 logger=logger)
        server.socket = context.wrap_socket(server.socket, server_side=True)
    else:
        if (not config.getboolean(
                "cloud_agent", "enable_insecure_payload", fallback=False)
                and config.get("cloud_agent", "payload_script") != ""):
            raise RuntimeError(
                "agent mTLS is disabled, while a tenant can instruct the agent to execute code on the node. "
                'In order to allow the running of the agent, "enable_insecure_payload" has to be set to "True"'
            )

    serverthread = threading.Thread(target=server.serve_forever, daemon=True)

    # register it and get back a blob
    mtls_cert = "disabled"
    if server.mtls_cert:
        mtls_cert = server.mtls_cert.public_bytes(serialization.Encoding.PEM)

    keyblob = registrar_client.doRegisterAgent(registrar_ip, registrar_port,
                                               agent_uuid, ek_tpm, ekcert,
                                               aik_tpm, mtls_cert, contact_ip,
                                               contact_port)

    if keyblob is None:
        instance_tpm.flush_keys()
        raise Exception("Registration failed")

    # get the ephemeral registrar key
    key = instance_tpm.activate_identity(keyblob)

    if key is None:
        instance_tpm.flush_keys()
        raise Exception("Activation failed")

    # tell the registrar server we know the key
    retval = registrar_client.doActivateAgent(registrar_ip, registrar_port,
                                              agent_uuid, key)

    if not retval:
        instance_tpm.flush_keys()
        raise Exception("Registration failed on activate")

    # Start revocation listener in a new process to not interfere with tornado
    revocation_process = multiprocessing.Process(target=revocation_listener,
                                                 daemon=True)
    revocation_process.start()

    logger.info(
        "Starting Cloud Agent on %s:%s with API version %s. Use <Ctrl-C> to stop",
        serveraddr[0],
        serveraddr[1],
        keylime_api_version.current_version(),
    )
    serverthread.start()

    def shutdown_handler(*_):
        logger.info("TERM Signal received, shutting down...")
        logger.debug("Stopping revocation notifier...")
        revocation_process.terminate()
        logger.debug("Shutting down HTTP server...")
        server.shutdown()
        server.server_close()
        serverthread.join()
        logger.debug("HTTP server stopped...")
        revocation_process.join()
        logger.debug("Revocation notifier stopped...")
        secure_mount.umount()
        logger.debug("Umounting directories...")
        instance_tpm.flush_keys()
        logger.debug("Flushed keys successfully")
        sys.exit(0)

    signal.signal(signal.SIGTERM, shutdown_handler)
    signal.signal(signal.SIGQUIT, shutdown_handler)
    signal.signal(signal.SIGINT, shutdown_handler)

    # Keep the main thread alive by waiting for the server thread
    serverthread.join()
Exemplo n.º 6
0
def main():
    for ML in [config.MEASUREDBOOT_ML, config.IMA_ML]:
        if not os.access(ML, os.F_OK):
            logger.warning(
                "Measurement list path %s not accessible by agent. Any attempt to instruct it to access this path - via \"keylime_tenant\" CLI - will result in agent process dying",
                ML)

    ima_log_file = None
    if os.path.exists(config.IMA_ML):
        ima_log_file = open(config.IMA_ML, 'r', encoding="utf-8")

    tpm_log_file_data = None
    if os.path.exists(config.MEASUREDBOOT_ML):
        with open(config.MEASUREDBOOT_ML, 'rb') as tpm_log_file:
            tpm_log_file_data = base64.b64encode(tpm_log_file.read())

    if config.get('cloud_agent', 'agent_uuid') == 'dmidecode':
        if os.getuid() != 0:
            raise RuntimeError('agent_uuid is configured to use dmidecode, '
                               'but current process is not running as root.')
        cmd = ['which', 'dmidecode']
        ret = cmd_exec.run(cmd, raiseOnError=False)
        if ret['code'] != 0:
            raise RuntimeError('agent_uuid is configured to use dmidecode, '
                               'but it\'s is not found on the system.')

    # initialize the tmpfs partition to store keys if it isn't already available
    secdir = secure_mount.mount()

    # Now that operations requiring root privileges are done, drop privileges
    # if 'run_as' is available in the configuration.
    if os.getuid() == 0:
        run_as = config.get('cloud_agent', 'run_as', fallback='')
        if run_as != '':
            user_utils.chown(secdir, run_as)
            user_utils.change_uidgid(run_as)
            logger.info(f"Dropped privileges to {run_as}")
        else:
            logger.warning(
                "Cannot drop privileges since 'run_as' is empty or missing in keylime.conf agent section."
            )

    # Instanitate TPM class

    instance_tpm = tpm()
    # get params for initialization
    registrar_ip = config.get('cloud_agent', 'registrar_ip')
    registrar_port = config.get('cloud_agent', 'registrar_port')

    # get params for the verifier to contact the agent
    contact_ip = os.getenv("KEYLIME_AGENT_CONTACT_IP", None)
    if contact_ip is None and config.has_option('cloud_agent',
                                                'agent_contact_ip'):
        contact_ip = config.get('cloud_agent', 'agent_contact_ip')
    contact_port = os.getenv("KEYLIME_AGENT_CONTACT_PORT", None)
    if contact_port is None and config.has_option('cloud_agent',
                                                  'agent_contact_port'):
        contact_port = config.get('cloud_agent',
                                  'agent_contact_port',
                                  fallback="invalid")

    # change dir to working dir
    fs_util.ch_dir(config.WORK_DIR)

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

    # initialize tpm
    (ekcert, ek_tpm, aik_tpm) = instance_tpm.tpm_init(
        self_activate=False,
        config_pw=config.get('cloud_agent', 'tpm_ownerpassword')
    )  # this tells initialize not to self activate the AIK
    virtual_agent = instance_tpm.is_vtpm()

    # Warn if kernel version is <5.10 and another algorithm than SHA1 is used,
    # because otherwise IMA will not work
    kernel_version = tuple(platform.release().split("-")[0].split("."))
    if tuple(map(int, kernel_version)) < (
            5, 10,
            0) and instance_tpm.defaults["hash"] != algorithms.Hash.SHA1:
        logger.warning(
            "IMA attestation only works on kernel versions <5.10 with SHA1 as hash algorithm. "
            "Even if ascii_runtime_measurements shows \"%s\" as the "
            "algorithm, it might be just padding zeros",
            (instance_tpm.defaults["hash"]))

    if ekcert is None:
        if virtual_agent:
            ekcert = 'virtual'
        elif instance_tpm.is_emulator():
            ekcert = 'emulator'

    # now we need the UUID
    try:
        agent_uuid = config.get('cloud_agent', 'agent_uuid')
    except configparser.NoOptionError:
        agent_uuid = None
    if agent_uuid == 'hash_ek':
        ek_pubkey = pubkey_from_tpm2b_public(base64.b64decode(ek_tpm))
        ek_pubkey_pem = ek_pubkey.public_bytes(
            encoding=serialization.Encoding.PEM,
            format=serialization.PublicFormat.SubjectPublicKeyInfo)
        agent_uuid = hashlib.sha256(ek_pubkey_pem).hexdigest()
    elif agent_uuid == 'generate' or agent_uuid is None:
        agent_uuid = str(uuid.uuid4())
    elif agent_uuid == 'dmidecode':
        cmd = ['dmidecode', '-s', 'system-uuid']
        ret = cmd_exec.run(cmd)
        sys_uuid = ret['retout'][0].decode('utf-8')
        agent_uuid = sys_uuid.strip()
        try:
            uuid.UUID(agent_uuid)
        except ValueError as e:
            raise RuntimeError(
                "The UUID returned from dmidecode is invalid: %s" % e)  # pylint: disable=raise-missing-from
    elif agent_uuid == 'hostname':
        agent_uuid = socket.getfqdn()
    elif agent_uuid == 'environment':
        agent_uuid = os.getenv("KEYLIME_AGENT_UUID", None)
        if agent_uuid is None:
            raise RuntimeError(
                "Env variable KEYLIME_AGENT_UUID is empty, but agent_uuid is set to 'environment'"
            )
    elif not validators.valid_uuid(agent_uuid):
        raise RuntimeError("The UUID is not valid")

    if not validators.valid_agent_id(agent_uuid):
        raise RuntimeError(
            "The agent ID set via agent uuid parameter use invalid characters")

    if config.STUB_VTPM and config.TPM_CANNED_VALUES is not None:
        # Use canned values for stubbing
        jsonIn = config.TPM_CANNED_VALUES
        if "add_vtpm_to_group" in jsonIn:
            # The value we're looking for has been canned!
            agent_uuid = jsonIn['add_vtpm_to_group']['retout']
        else:
            # Our command hasn't been canned!
            raise Exception("Command %s not found in canned json!" %
                            ("add_vtpm_to_group"))

    logger.info("Agent UUID: %s", agent_uuid)

    serveraddr = (config.get('cloud_agent', 'cloudagent_ip'),
                  config.getint('cloud_agent', 'cloudagent_port'))

    keylime_ca = config.get('cloud_agent', 'keylime_ca')
    if keylime_ca == "default":
        keylime_ca = os.path.join(config.WORK_DIR, 'cv_ca', 'cacert.crt')

    server = CloudAgentHTTPServer(serveraddr, Handler, agent_uuid, contact_ip,
                                  ima_log_file, tpm_log_file_data)
    context = web_util.generate_mtls_context(server.mtls_cert_path,
                                             server.rsakey_path,
                                             keylime_ca,
                                             logger=logger)
    server.socket = context.wrap_socket(server.socket, server_side=True)
    serverthread = threading.Thread(target=server.serve_forever, daemon=True)

    # register it and get back a blob
    mtls_cert = server.mtls_cert.public_bytes(serialization.Encoding.PEM)
    keyblob = registrar_client.doRegisterAgent(registrar_ip, registrar_port,
                                               agent_uuid, ek_tpm, ekcert,
                                               aik_tpm, mtls_cert, contact_ip,
                                               contact_port)

    if keyblob is None:
        instance_tpm.flush_keys()
        raise Exception("Registration failed")

    # get the ephemeral registrar key
    key = instance_tpm.activate_identity(keyblob)

    if key is None:
        instance_tpm.flush_keys()
        raise Exception("Activation failed")

    # tell the registrar server we know the key
    retval = registrar_client.doActivateAgent(registrar_ip, registrar_port,
                                              agent_uuid, key)

    if not retval:
        instance_tpm.flush_keys()
        raise Exception("Registration failed on activate")

    # Start revocation listener in a new process to not interfere with tornado
    revocation_process = multiprocessing.Process(target=revocation_listener,
                                                 daemon=True)
    revocation_process.start()

    logger.info(
        "Starting Cloud Agent on %s:%s with API version %s. Use <Ctrl-C> to stop",
        serveraddr[0], serveraddr[1], keylime_api_version.current_version())
    serverthread.start()

    def shutdown_handler(*_):
        logger.info("TERM Signal received, shutting down...")
        logger.debug("Stopping revocation notifier...")
        revocation_process.terminate()
        logger.debug("Shutting down HTTP server...")
        server.shutdown()
        server.server_close()
        serverthread.join()
        logger.debug("HTTP server stopped...")
        revocation_process.join()
        logger.debug("Revocation notifier stopped...")
        secure_mount.umount()
        logger.debug("Umounting directories...")
        instance_tpm.flush_keys()
        logger.debug("Flushed keys successfully")
        sys.exit(0)

    signal.signal(signal.SIGTERM, shutdown_handler)
    signal.signal(signal.SIGQUIT, shutdown_handler)
    signal.signal(signal.SIGINT, shutdown_handler)

    # Keep the main thread alive by waiting for the server thread
    serverthread.join()
Exemplo n.º 7
0
    def validate_tpm_quote(self, public_key, quote, hash_alg):
        """ Validate TPM Quote received from the Agent

        Arguments:
            public_key {[type]} -- [description]
            quote {[type]} -- [description]
            hash_alg {bool} -- [description]

        Raises:
            UserError: [description]

        Returns:
            [type] -- [description]
        """
        registrar_client.init_client_tls('tenant')
        reg_keys = registrar_client.getKeys(
            self.registrar_ip, self.registrar_port, self.agent_uuid)
        if reg_keys is None:
            logger.warning("AIK not found in registrar, quote not validated")
            return False

        if not self.tpm_instance.check_quote(self.agent_uuid, self.nonce, public_key, quote, reg_keys['aik_tpm'], hash_alg=hash_alg):
            if reg_keys['regcount'] > 1:
                logger.error("WARNING: This UUID had more than one ek-ekcert registered to it! This might indicate that your system is misconfigured or a malicious host is present. Run 'regdelete' for this agent and restart")
                sys.exit()
            return False

        if reg_keys['regcount'] > 1:
            logger.warning("WARNING: This UUID had more than one ek-ekcert registered to it! This might indicate that your system is misconfigured. Run 'regdelete' for this agent and restart")

        if not config.STUB_TPM and (not config.getboolean('tenant', 'require_ek_cert') and config.get('tenant', 'ek_check_script') == ""):
            logger.warning(
                "DANGER: EK cert checking is disabled and no additional checks on EKs have been specified with ek_check_script option. Keylime is not secure!!")

        # check EK cert and make sure it matches EK
        if not self.check_ek(reg_keys['ekcert']):
            return False
        # if agent is virtual, check phyisical EK cert and make sure it matches phyiscal EK
        if 'provider_keys' in reg_keys:
            if not self.check_ek(reg_keys['provider_keys']['ekcert']):
                return False

        # check all EKs with optional script:
        script = config.get('tenant', 'ek_check_script')
        if not script:
            return True

        if script[0] != '/':
            script = "%s/%s" % (config.WORK_DIR, script)

        logger.info("Checking EK with script %s", script)
        # now we need to exec the script with the ek and ek cert in vars
        env = os.environ.copy()
        env['AGENT_UUID'] = self.agent_uuid
        env['EK'] = tpm2_objects.pubkey_from_tpm2b_public(
            base64.b64decode(reg_keys['ek_tpm']),
            ).public_bytes(
                crypto_serialization.Encoding.PEM,
                crypto_serialization.PublicFormat.SubjectPublicKeyInfo,
            )
        env['EK_TPM'] = reg_keys['ek_tpm']
        if reg_keys['ekcert'] is not None:
            env['EK_CERT'] = reg_keys['ekcert']
        else:
            env['EK_CERT'] = ""

        env['PROVKEYS'] = json.dumps(reg_keys.get('provider_keys', {}))
        proc = subprocess.Popen(script, env=env, shell=True,
                                cwd=config.WORK_DIR, stdout=subprocess.PIPE,
                                stderr=subprocess.STDOUT)
        retval = proc.wait()
        if retval != 0:
            raise UserError("External check script failed to validate EK")
        logger.debug("External check script successfully to validated EK")
        while True:
            line = proc.stdout.readline().decode()
            if line == "":
                break
            logger.debug("ek_check output: %s", line.strip())
        return True