Exemple #1
0
    def test_010_reg_agent_post(self):
        """Test registrar's POST /v2/agents/{UUID} Interface"""
        global keyblob, aik, vtpm, ek

        # Change CWD for TPM-related operations
        cwd = os.getcwd()
        config.ch_dir(config.WORK_DIR, None)
        _ = secure_mount.mount()

        # Initialize the TPM with AIK
        (ek, ekcert, aik, ek_tpm,
         aik_name) = tpm.tpm_init(self_activate=False,
                                  config_pw=config.get('cloud_agent',
                                                       'tpm_ownerpassword'))
        vtpm = tpm.is_vtpm()

        # Seed RNG (root only)
        if config.REQUIRE_ROOT:
            tpm.init_system_rand()

        # Handle virtualized and emulated TPMs
        if ekcert is None:
            if vtpm:
                ekcert = 'virtual'
            elif tpm.is_emulator():
                ekcert = 'emulator'

        # Get back to our original CWD
        config.ch_dir(cwd, None)

        data = {
            'ek': ek,
            'ekcert': ekcert,
            'aik': aik,
            'aik_name': aik_name,
            'ek_tpm': ek_tpm,
            'tpm_version': tpm.VERSION,
        }

        test_010_reg_agent_post = RequestsClient(
            tenant_templ.registrar_base_url, tls_enabled=False)
        response = test_010_reg_agent_post.post(
            f'/v{self.api_version}/agents/{tenant_templ.agent_uuid}',
            data=json.dumps(data),
            cert="",
            verify=False)

        self.assertEqual(response.status_code, 200,
                         "Non-successful Registrar agent Add return code!")
        json_response = response.json()

        # Ensure response is well-formed
        self.assertIn("results", json_response, "Malformed response body!")
        self.assertIn("blob", json_response["results"],
                      "Malformed response body!")

        keyblob = json_response["results"]["blob"]
        self.assertIsNotNone(keyblob, "Malformed response body!")
    def test_010_reg_agent_post(self):
        """Test registrar's POST /agents/{UUID} Interface"""
        global keyblob, vtpm, tpm_instance, ek_tpm, aik_tpm
        contact_ip = "127.0.0.1"
        contact_port = 9002
        tpm_instance = tpm_main.tpm()

        # Change CWD for TPM-related operations
        cwd = os.getcwd()
        config.ch_dir(config.WORK_DIR, None)
        _ = secure_mount.mount()

        # Initialize the TPM with AIK
        (ekcert, ek_tpm, aik_tpm) = tpm_instance.tpm_init(
            self_activate=False,
            config_pw=config.get('cloud_agent', 'tpm_ownerpassword'))
        vtpm = tpm_instance.is_vtpm()

        # Handle virtualized and emulated TPMs
        if ekcert is None:
            if vtpm:
                ekcert = 'virtual'
            elif tpm_instance.is_emulator():
                ekcert = 'emulator'

        # Get back to our original CWD
        config.ch_dir(cwd, None)

        data = {
            'ekcert': ekcert,
            'aik_tpm': aik_tpm,
            'ip': contact_ip,
            'port': contact_port
        }
        if ekcert is None or ekcert == 'emulator':
            data['ek_tpm'] = ek_tpm

        test_010_reg_agent_post = RequestsClient(
            tenant_templ.registrar_base_url, tls_enabled=False)
        response = test_010_reg_agent_post.post(
            f'/v{self.api_version}/agents/{tenant_templ.agent_uuid}',
            data=json.dumps(data),
            cert="",
            verify=False)

        self.assertEqual(response.status_code, 200,
                         "Non-successful Registrar agent Add return code!")
        json_response = response.json()

        # Ensure response is well-formed
        self.assertIn("results", json_response, "Malformed response body!")
        self.assertIn("blob", json_response["results"],
                      "Malformed response body!")

        keyblob = json_response["results"]["blob"]
        self.assertIsNotNone(keyblob, "Malformed response body!")
Exemple #3
0
def cmd_mkcert(workingdir, name):
    cwd = os.getcwd()
    try:
        config.ch_dir(workingdir, logger)
        priv = read_private()
        cacert = X509.load_cert('cacert.crt')
        ca_pk = EVP.load_key_string(priv[0]['ca'])

        cert, pk = ca_impl.mk_signed_cert(cacert, ca_pk, name,
                                          priv[0]['lastserial'] + 1)

        with open('%s-cert.crt' % name, 'wb') as f:
            f.write(cert.as_pem())

        f = BIO.MemoryBuffer()
        pk.save_key_bio(f, None)
        priv[0][name] = f.getvalue()
        f.close()

        # increment serial number after successful creation
        priv[0]['lastserial'] += 1

        write_private(priv)

        # write out the private key with password
        with os.fdopen(
                os.open("%s-private.pem" % name, os.O_WRONLY | os.O_CREAT,
                        0o600), 'wb') as f:
            biofile = BIO.File(f)
            pk.save_key_bio(biofile, None)
            biofile.close()

        pk.get_rsa().save_pub_key('%s-public.pem' % name)

        cc = X509.load_cert('%s-cert.crt' % name)

        if cc.verify(cacert.get_pubkey()):
            logger.info(
                f"Created certificate for name {name} successfully in {workingdir}"
            )
        else:
            logger.error("ERROR: Cert does not validate against CA")
    finally:
        os.chdir(cwd)
Exemple #4
0
def cmd_revoke(workingdir, name=None, serial=None):
    cwd = os.getcwd()
    try:
        config.ch_dir(workingdir, logger)
        priv = read_private()

        if name is not None and serial is not None:
            raise Exception(
                "You may not specify a cert and a serial at the same time")
        if name is None and serial is None:
            raise Exception("You must specify a cert or a serial to revoke")
        if name is not None:
            # load up the cert
            cert = X509.load_cert("%s-cert.crt" % name)
            serial = cert.get_serial_number()

        # convert serial to string
        serial = str(serial)

        # get the ca key cert and keys as strings
        with open('cacert.crt', 'r') as f:
            cacert = f.read()
        ca_pk = priv[0]['ca'].decode('utf-8')

        if serial not in priv[0]['revoked_keys']:
            priv[0]['revoked_keys'].append(serial)

        crl = ca_impl.gencrl(priv[0]['revoked_keys'], cacert, ca_pk)

        write_private(priv)

        # write out the CRL to the disk
        if os.stat('cacrl.der').st_size:
            with open('cacrl.der', 'wb') as f:
                f.write(crl)
            convert_crl_to_pem("cacrl.der", "cacrl.pem")

    finally:
        os.chdir(cwd)
    return crl
Exemple #5
0
def cmd_regencrl(workingdir):
    cwd = os.getcwd()
    try:
        config.ch_dir(workingdir, logger)
        priv = read_private()

        # get the ca key cert and keys as strings
        with open('cacert.crt', 'r') as f:
            cacert = f.read()
        ca_pk = str(priv[0]['ca'])

        crl = ca_impl.gencrl(priv[0]['revoked_keys'], cacert, ca_pk)

        write_private(priv)

        # write out the CRL to the disk
        with open('cacrl.der', 'wb') as f:
            f.write(crl)
        convert_crl_to_pem("cacrl.der", "cacrl.pem")

    finally:
        os.chdir(cwd)
    return crl
Exemple #6
0
def cmd_listen(workingdir, cert_path):
    cwd = os.getcwd()
    try:
        config.ch_dir(workingdir, logger)
        # just load up the password for later
        read_private(True)

        serveraddr = ('', config.CRL_PORT)
        server = ThreadedCRLServer(serveraddr, CRLHandler)
        if os.path.exists('cacrl.der'):
            logger.info("Loading existing crl: %s" %
                        os.path.abspath("cacrl.der"))
            with open('cacrl.der', 'rb') as f:
                server.setcrl(f.read())
        t = threading.Thread(target=server.serve_forever)
        logger.info("Hosting CRL on %s:%d" %
                    (socket.getfqdn(), config.CRL_PORT))
        t.start()

        def check_expiration():
            logger.info("checking CRL for expiration every hour")
            while True:  # pylint: disable=R1702
                try:
                    if (os.path.exists('cacrl.der')
                            and os.stat('cacrl.der').st_size):
                        cmd = ('openssl', 'crl', '-inform', 'der', '-in',
                               'cacrl.der', '-text', '-noout')
                        retout = cmd_exec.run(cmd, lock=False)['retout']
                        for line in retout:
                            line = line.strip()
                            if line.startswith(b"Next Update:"):
                                expire = datetime.datetime.strptime(
                                    line[13:].decode('utf-8'),
                                    "%b %d %H:%M:%S %Y %Z")
                                # check expiration within 6 hours
                                in1hour = datetime.datetime.utcnow(
                                ) + datetime.timedelta(hours=6)
                                if expire <= in1hour:
                                    logger.info(
                                        "Certificate to expire soon %s, re-issuing"
                                        % expire)
                                    cmd_regencrl(workingdir)
                    # check a little less than every hour
                    time.sleep(3540)

                except KeyboardInterrupt:
                    logger.info("TERM Signal received, shutting down...")
                    # server.shutdown()
                    break

        t2 = threading.Thread(target=check_expiration)
        t2.setDaemon(True)
        t2.start()

        def revoke_callback(revocation):
            json_meta = json.loads(revocation['meta_data'])
            serial = json_meta['cert_serial']
            if revocation.get('type', None) != 'revocation' or serial is None:
                logger.error("Unsupported revocation message: %s" % revocation)
                return

            logger.info("Revoking certificate: %s" % serial)
            server.setcrl(cmd_revoke(workingdir, None, serial))

        try:
            while True:
                try:
                    revocation_notifier.await_notifications(
                        revoke_callback, revocation_cert_path=cert_path)
                except Exception as e:
                    logger.exception(e)
                    logger.warning(
                        "No connection to revocation server, retrying in 10s..."
                    )
                    time.sleep(10)
        except KeyboardInterrupt:
            logger.info("TERM Signal received, shutting down...")
            server.shutdown()
            sys.exit()
    finally:
        os.chdir(cwd)
Exemple #7
0
def cmd_certpkg(workingdir, name, insecure=False):
    cwd = os.getcwd()
    try:
        config.ch_dir(workingdir, logger)
        # zip up the crt, private key, and public key

        with open('cacert.crt', 'r') as f:
            cacert = f.read()

        with open("%s-public.pem" % name, 'r') as f:
            pub = f.read()

        with open("%s-cert.crt" % name, 'r') as f:
            cert = f.read()

        with open('cacrl.der', 'rb') as f:
            crl = f.read()

        with open('cacrl.pem', 'r') as f:
            crlpem = f.read()

        cert_obj = X509.load_cert_string(cert)
        serial = cert_obj.get_serial_number()
        subject = str(cert_obj.get_subject())

        priv = read_private()
        private = priv[0][name]

        with open("%s-private.pem" % name, 'r') as f:
            prot_priv = f.read()

        # code to create a pem formatted protected private key using the keystore password
    #     pk = EVP.load_key_string(str(priv[0][name]))
    #     f = BIO.MemoryBuffer()
    #     # globalcb will return the global password provided by the user
    #     pk.save_key_bio(f, 'aes_256_cbc', globalcb)
    #     prot_priv = f.getvalue()
    #     f.close()

    # no compression to avoid extraction errors in tmpfs
        sf = io.BytesIO()
        with zipfile.ZipFile(sf, 'w', compression=zipfile.ZIP_STORED) as f:
            f.writestr('%s-public.pem' % name, pub)
            f.writestr('%s-cert.crt' % name, cert)
            f.writestr('%s-private.pem' % name, private)
            f.writestr('cacert.crt', cacert)
            f.writestr('cacrl.der', crl)
            f.writestr('cacrl.pem', crlpem)
        pkg = sf.getvalue()

        if insecure:
            logger.warn(
                "Unprotected private keys in cert package being written to disk"
            )
            with open('%s-pkg.zip' % name, 'w') as f:
                f.write(pkg)
        else:
            # actually output the package to disk with a protected private key
            with zipfile.ZipFile('%s-pkg.zip' % name,
                                 'w',
                                 compression=zipfile.ZIP_STORED) as f:
                f.writestr('%s-public.pem' % name, pub)
                f.writestr('%s-cert.crt' % name, cert)
                f.writestr('%s-private.pem' % name, prot_priv)
                f.writestr('cacert.crt', cacert)
                f.writestr('cacrl.der', crl)
                f.writestr('cacrl.pem', crlpem)

        logger.info("Creating cert package for %s in %s-pkg.zip" %
                    (name, name))

        return pkg, serial, subject
    finally:
        os.chdir(cwd)
Exemple #8
0
def cmd_init(workingdir):
    cwd = os.getcwd()
    try:
        config.ch_dir(workingdir, logger)

        rmfiles("*.pem")
        rmfiles("*.crt")
        rmfiles("*.zip")
        rmfiles("*.der")
        rmfiles("private.yml")

        if config.CA_IMPL == 'cfssl':
            pk_str, cacert, ca_pk, _ = ca_impl.mk_cacert()
        elif config.CA_IMPL == 'openssl':
            cacert, ca_pk, _ = ca_impl.mk_cacert()  # pylint: disable=W0632
        else:
            raise Exception("Unknown CA implementation: %s" % config.CA_IMPL)

        priv = read_private()

        # write out keys
        with open('cacert.crt', 'wb') as f:
            f.write(cacert.as_pem())

        f = BIO.MemoryBuffer()
        ca_pk.save_key_bio(f, None)
        priv[0]['ca'] = f.getvalue()
        f.close()

        # store the last serial number created.
        # the CA is always serial # 1
        priv[0]['lastserial'] = 1

        write_private(priv)

        ca_pk.get_rsa().save_pub_key('ca-public.pem')

        # generate an empty crl
        if config.CA_IMPL == 'cfssl':
            crl = ca_impl.gencrl([], cacert.as_pem(), pk_str)
        elif config.CA_IMPL == 'openssl':
            crl = ca_impl.gencrl([], cacert.as_pem(), str(priv[0]['ca']))
        else:
            raise Exception("Unknown CA implementation: %s" % config.CA_IMPL)

        if isinstance(crl, str):
            crl = crl.encode('utf-8')

        with open('cacrl.der', 'wb') as f:
            f.write(crl)
        convert_crl_to_pem("cacrl.der", "cacrl.pem")

        # Sanity checks...
        cac = X509.load_cert('cacert.crt')
        if cac.verify():
            logger.info("CA certificate created successfully in %s" %
                        workingdir)
        else:
            logger.error("ERROR: Cert does not self validate")
    finally:
        os.chdir(cwd)
Exemple #9
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)

    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.')

    # 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')

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

    # change dir to working dir
    config.ch_dir(config.WORK_DIR, logger)

    # 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()
    # try to get some TPM randomness into the system entropy pool
    instance_tpm.init_system_rand()

    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 == 'openstack':
        agent_uuid = openstack.get_openstack_uuid()
    elif agent_uuid == 'hash_ek':
        agent_uuid = hashlib.sha256(ek_tpm).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'].decode('utf-8')
        agent_uuid = sys_uuid.strip()
    elif agent_uuid == 'hostname':
        agent_uuid = socket.getfqdn()
    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)

    # register it and get back a blob
    keyblob = registrar_client.doRegisterAgent(registrar_ip, registrar_port,
                                               agent_uuid, ek_tpm, ekcert,
                                               aik_tpm)

    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 = False
    retval = registrar_client.doActivateAgent(registrar_ip, registrar_port,
                                              agent_uuid, key)

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

    serveraddr = (config.get('cloud_agent', 'cloudagent_ip'),
                  config.getint('cloud_agent', 'cloudagent_port'))
    server = CloudAgentHTTPServer(serveraddr, Handler, agent_uuid)
    serverthread = threading.Thread(target=server.serve_forever)

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

    # want to listen for revocations?
    if config.getboolean('cloud_agent', 'listen_notfications'):
        cert_path = config.get('cloud_agent', 'revocation_cert')
        if cert_path == "default":
            cert_path = '%s/unzipped/RevocationNotifier-cert.crt' % (secdir)
        elif cert_path[0] != '/':
            # if it is a relative, convert to absolute in work_dir
            cert_path = os.path.abspath('%s/%s' % (config.WORK_DIR, cert_path))

        def perform_actions(revocation):
            actionlist = []

            # load the actions from inside the keylime module
            actionlisttxt = config.get('cloud_agent', 'revocation_actions')
            if actionlisttxt.strip() != "":
                actionlist = actionlisttxt.split(',')
                actionlist = ["revocation_actions.%s" % i for i in actionlist]

            # load actions from unzipped
            if os.path.exists("%s/unzipped/action_list" % secdir):
                with open("%s/unzipped/action_list" % secdir, 'r') as f:
                    actionlisttxt = f.read()
                if actionlisttxt.strip() != "":
                    localactions = actionlisttxt.strip().split(',')
                    for action in localactions:
                        if not action.startswith('local_action_'):
                            logger.warning(
                                "Invalid local action: %s. Must start with local_action_",
                                action)
                        else:
                            actionlist.append(action)

                    uzpath = "%s/unzipped" % secdir
                    if uzpath not in sys.path:
                        sys.path.append(uzpath)

            for action in actionlist:
                logger.info("Executing revocation action %s", action)
                try:
                    module = importlib.import_module(action)
                    execute = getattr(module, 'execute')
                    asyncio.get_event_loop().run_until_complete(
                        execute(revocation))
                except Exception as e:
                    logger.warning(
                        "Exception during execution of revocation action %s: %s",
                        action, e)

        try:
            while True:
                try:
                    revocation_notifier.await_notifications(
                        perform_actions, revocation_cert_path=cert_path)
                except Exception as e:
                    logger.exception(e)
                    logger.warning(
                        "No connection to revocation server, retrying in 10s..."
                    )
                    time.sleep(10)
        except KeyboardInterrupt:
            logger.info("TERM Signal received, shutting down...")
            instance_tpm.flush_keys()
            server.shutdown()
    else:
        try:
            while True:
                time.sleep(1)
        except KeyboardInterrupt:
            logger.info("TERM Signal received, shutting down...")
            instance_tpm.flush_keys()
            server.shutdown()
Exemple #10
0
def cmd_certpkg(workingdir, name, insecure=False):
    cwd = os.getcwd()
    try:
        config.ch_dir(workingdir, logger)
        # zip up the crt, private key, and public key

        with open('cacert.crt', 'r') as f:
            cacert = f.read()

        with open("%s-public.pem" % name, 'r') as f:
            pub = f.read()

        with open("%s-cert.crt" % name, 'r') as f:
            cert = f.read()

        with open('cacrl.der', 'rb') as f:
            crl = f.read()

        with open('cacrl.pem', 'r') as f:
            crlpem = f.read()

        cert_obj = X509.load_cert_string(cert)
        serial = cert_obj.get_serial_number()
        subject = str(cert_obj.get_subject())

        priv = read_private()
        private = priv[0][name]

        with open("%s-private.pem" % name, 'r') as f:
            prot_priv = f.read()

        # no compression to avoid extraction errors in tmpfs
        sf = io.BytesIO()
        with zipfile.ZipFile(sf, 'w', compression=zipfile.ZIP_STORED) as f:
            f.writestr('%s-public.pem' % name, pub)
            f.writestr('%s-cert.crt' % name, cert)
            f.writestr('%s-private.pem' % name, private)
            f.writestr('cacert.crt', cacert)
            f.writestr('cacrl.der', crl)
            f.writestr('cacrl.pem', crlpem)
        pkg = sf.getvalue()

        if insecure:
            logger.warning(
                "Unprotected private keys in cert package being written to disk")
            with open('%s-pkg.zip' % name, 'w') as f:
                f.write(pkg)
        else:
            # actually output the package to disk with a protected private key
            with zipfile.ZipFile('%s-pkg.zip' % name, 'w', compression=zipfile.ZIP_STORED) as f:
                f.writestr('%s-public.pem' % name, pub)
                f.writestr('%s-cert.crt' % name, cert)
                f.writestr('%s-private.pem' % name, prot_priv)
                f.writestr('cacert.crt', cacert)
                f.writestr('cacrl.der', crl)
                f.writestr('cacrl.pem', crlpem)

        logger.info("Creating cert package for %s in %s-pkg.zip" %
                    (name, name))

        return pkg, serial, subject
    finally:
        os.chdir(cwd)