示例#1
0
    def init_add(self, args):
        """ Set up required values. Command line options can overwrite these config values

        Arguments:
            args {[string]} -- agent_ip|agent_port|cv_agent_ip
        """
        if "agent_ip" in args:
            self.agent_ip = args["agent_ip"]

        if 'agent_port' in args and args['agent_port'] is not None:
            self.agent_port = args['agent_port']

        if 'cv_agent_ip' in args and args['cv_agent_ip'] is not None:
            self.cv_cloudagent_ip = args['cv_agent_ip']
        else:
            self.cv_cloudagent_ip = self.agent_ip

        # Make sure all keys exist in dictionary
        if "file" not in args:
            args["file"] = None
        if "keyfile" not in args:
            args["keyfile"] = None
        if "payload" not in args:
            args["payload"] = None
        if "ca_dir" not in args:
            args["ca_dir"] = None
        if "incl_dir" not in args:
            args["incl_dir"] = None
        if "ca_dir_pw" not in args:
            args["ca_dir_pw"] = None

        # Set up accepted algorithms
        self.accept_tpm_hash_algs = config.get(
            'tenant', 'accept_tpm_hash_algs').split(',')
        self.accept_tpm_encryption_algs = config.get(
            'tenant', 'accept_tpm_encryption_algs').split(',')
        self.accept_tpm_signing_algs = config.get(
            'tenant', 'accept_tpm_signing_algs').split(',')

        # Set up PCR values
        tpm_policy = config.get('tenant', 'tpm_policy')
        if "tpm_policy" in args and args["tpm_policy"] is not None:
            tpm_policy = args["tpm_policy"]
        self.tpm_policy = TPM_Utilities.readPolicy(tpm_policy)
        logger.info(f"TPM PCR Mask from policy is {self.tpm_policy['mask']}")

        vtpm_policy = config.get('tenant', 'vtpm_policy')
        if "vtpm_policy" in args and args["vtpm_policy"] is not None:
            vtpm_policy = args["vtpm_policy"]
        self.vtpm_policy = TPM_Utilities.readPolicy(vtpm_policy)
        logger.info(f"TPM PCR Mask from policy is {self.vtpm_policy['mask']}")

        # Read command-line path string allowlist
        al_data = None
        if "allowlist" in args and args["allowlist"] is not None:

            # Auto-enable IMA (or-bit mask)
            self.tpm_policy['mask'] = "0x%X" % (int(self.tpm_policy['mask'], 0)
                                                | (1 << config.IMA_PCR))

            if isinstance(args["allowlist"], str):
                if args["allowlist"] == "default":
                    args["allowlist"] = config.get('tenant', 'allowlist')
                al_data = ima.read_allowlist(args["allowlist"])
            elif isinstance(args["allowlist"], list):
                al_data = args["allowlist"]
            else:
                raise UserError("Invalid allowlist provided")

        # Read command-line path string IMA exclude list
        excl_data = None
        if "ima_exclude" in args and args["ima_exclude"] is not None:
            if isinstance(args["ima_exclude"], str):
                if args["ima_exclude"] == "default":
                    args["ima_exclude"] = config.get('tenant',
                                                     'ima_excludelist')
                excl_data = ima.read_excllist(args["ima_exclude"])
            elif isinstance(args["ima_exclude"], list):
                excl_data = args["ima_exclude"]
            else:
                raise UserError("Invalid exclude list provided")

        # Set up IMA
        if TPM_Utilities.check_mask(self.tpm_policy['mask'], config.IMA_PCR) or \
                TPM_Utilities.check_mask(self.vtpm_policy['mask'], config.IMA_PCR):

            # Process allowlists
            self.allowlist = ima.process_allowlists(al_data, excl_data)

        # if none
        if (args["file"] is None and args["keyfile"] is None
                and args["ca_dir"] is None):
            raise UserError(
                "You must specify one of -k, -f, or --cert to specify the key/contents to be securely delivered to the agent"
            )

        if args["keyfile"] is not None:
            if args["file"] is not None or args["ca_dir"] is not None:
                raise UserError(
                    "You must specify one of -k, -f, or --cert to specify the key/contents to be securely delivered to the agent"
                )

            # read the keys in
            if isinstance(args["keyfile"], dict) and "data" in args["keyfile"]:
                if isinstance(args["keyfile"]["data"], list) and len(
                        args["keyfile"]["data"]) == 1:
                    keyfile = args["keyfile"]["data"][0]
                    if keyfile is None:
                        raise UserError("Invalid key file contents")
                    f = io.StringIO(keyfile)
                else:
                    raise UserError("Invalid key file provided")
            else:
                f = open(args["keyfile"], 'r')
            self.K = base64.b64decode(f.readline())
            self.U = base64.b64decode(f.readline())
            self.V = base64.b64decode(f.readline())
            f.close()

            # read the payload in (opt.)
            if isinstance(args["payload"], dict) and "data" in args["payload"]:
                if isinstance(args["payload"]["data"],
                              list) and len(args["payload"]["data"]) > 0:
                    self.payload = args["payload"]["data"][0]
            else:
                if args["payload"] is not None:
                    f = open(args["payload"], 'r')
                    self.payload = f.read()
                    f.close()

        if args["file"] is not None:
            if args["keyfile"] is not None or args["ca_dir"] is not None:
                raise UserError(
                    "You must specify one of -k, -f, or --cert to specify the key/contents to be securely delivered to the agent"
                )

            if isinstance(args["file"], dict) and "data" in args["file"]:
                if isinstance(args["file"]["data"],
                              list) and len(args["file"]["data"]) > 0:
                    contents = args["file"]["data"][0]
                    if contents is None:
                        raise UserError("Invalid file payload contents")
                else:
                    raise UserError("Invalid file payload provided")
            else:
                with open(args["file"], 'r') as f:
                    contents = f.read()
            ret = user_data_encrypt.encrypt(contents)
            self.K = ret['k']
            self.U = ret['u']
            self.V = ret['v']
            self.payload = ret['ciphertext']

        if args["ca_dir"] is None and args["incl_dir"] is not None:
            raise UserError(
                "--include option is only valid when used with --cert")
        if args["ca_dir"] is not None:
            if args["file"] is not None or args["keyfile"] is not None:
                raise UserError(
                    "You must specify one of -k, -f, or --cert to specify the key/contents to be securely delivered to the agent"
                )
            if args["ca_dir"] == 'default':
                args["ca_dir"] = config.CA_WORK_DIR

            if "ca_dir_pw" in args and args["ca_dir_pw"] is not None:
                ca_util.setpassword(args["ca_dir_pw"])

            if not os.path.exists(args["ca_dir"]) or not os.path.exists(
                    "%s/cacert.crt" % args["ca_dir"]):
                logger.warning(" CA directory does not exist.  Creating...")
                ca_util.cmd_init(args["ca_dir"])
            if not os.path.exists("%s/%s-private.pem" %
                                  (args["ca_dir"], self.agent_uuid)):
                ca_util.cmd_mkcert(args["ca_dir"], self.agent_uuid)

            cert_pkg, serial, subject = ca_util.cmd_certpkg(
                args["ca_dir"], self.agent_uuid)

            # support revocation
            if not os.path.exists(
                    "%s/RevocationNotifier-private.pem" % args["ca_dir"]):
                ca_util.cmd_mkcert(args["ca_dir"], "RevocationNotifier")
            rev_package, _, _ = ca_util.cmd_certpkg(args["ca_dir"],
                                                    "RevocationNotifier")

            # extract public and private keys from package
            sf = io.BytesIO(rev_package)
            with zipfile.ZipFile(sf) as zf:
                privkey = zf.read("RevocationNotifier-private.pem")
                cert = zf.read("RevocationNotifier-cert.crt")

            # put the cert of the revoker into the cert package
            sf = io.BytesIO(cert_pkg)
            with zipfile.ZipFile(sf, 'a',
                                 compression=zipfile.ZIP_STORED) as zf:
                zf.writestr('RevocationNotifier-cert.crt', cert)

                # add additional files to zip
                if args["incl_dir"] is not None:
                    if isinstance(args["incl_dir"], dict) and "data" in args[
                            "incl_dir"] and "name" in args["incl_dir"]:
                        if isinstance(args["incl_dir"]["data"],
                                      list) and isinstance(
                                          args["incl_dir"]["name"], list):
                            if len(args["incl_dir"]["data"]) != len(
                                    args["incl_dir"]["name"]):
                                raise UserError("Invalid incl_dir provided")
                            for i in range(len(args["incl_dir"]["data"])):
                                zf.writestr(
                                    os.path.basename(
                                        args["incl_dir"]["name"][i]),
                                    args["incl_dir"]["data"][i])
                    else:
                        if os.path.exists(args["incl_dir"]):
                            files = next(os.walk(args["incl_dir"]))[2]
                            for filename in files:
                                with open(
                                        "%s/%s" % (args["incl_dir"], filename),
                                        'rb') as f:
                                    zf.writestr(os.path.basename(f.name),
                                                f.read())
                        else:
                            logger.warn(
                                f'Specified include directory {args["incl_dir"]} does not exist.  Skipping...'
                            )

            cert_pkg = sf.getvalue()

            # put the private key into the data to be send to the CV
            self.revocation_key = privkey

            # encrypt up the cert package
            ret = user_data_encrypt.encrypt(cert_pkg)
            self.K = ret['k']
            self.U = ret['u']
            self.V = ret['v']
            self.metadata = {'cert_serial': serial, 'subject': subject}
            self.payload = ret['ciphertext']

        if self.payload is not None and len(self.payload) > config.getint(
                'tenant', 'max_payload_size'):
            raise UserError("Payload size %s exceeds max size %d" % (len(
                self.payload), config.getint('tenant', 'max_payload_size')))
示例#2
0
def init_mtls(section='cloud_verifier', generatedir='cv_ca'):
    if not config.getboolean('general', "enable_tls"):
        logger.warning(
            "TLS is currently disabled, keys will be sent in the clear! Should only be used for testing."
        )
        return None

    logger.info("Setting up TLS...")
    my_cert = config.get(section, 'my_cert')
    ca_cert = config.get(section, 'ca_cert')
    my_priv_key = config.get(section, 'private_key')
    my_key_pw = config.get(section, 'private_key_pw')
    tls_dir = config.get(section, 'tls_dir')

    if tls_dir == 'generate':
        if my_cert != 'default' or my_priv_key != 'default' or ca_cert != 'default':
            raise Exception(
                "To use tls_dir=generate, options ca_cert, my_cert, and private_key must all be set to 'default'"
            )

        if generatedir[0] != '/':
            generatedir = os.path.abspath('%s/%s' %
                                          (common.WORK_DIR, generatedir))
        tls_dir = generatedir
        ca_path = "%s/cacert.crt" % (tls_dir)
        if os.path.exists(ca_path):
            logger.info(
                "Existing CA certificate found in %s, not generating a new one"
                % (tls_dir))
        else:
            logger.info(
                "Generating a new CA in %s and a client certificate for connecting"
                % tls_dir)
            logger.info("use keylime_ca -d %s to manage this CA" % tls_dir)
            if not os.path.exists(tls_dir):
                os.makedirs(tls_dir, 0o700)
            if my_key_pw == 'default':
                logger.warning(
                    "CAUTION: using default password for CA, please set private_key_pw to a strong password"
                )
            ca_util.setpassword(my_key_pw)
            ca_util.cmd_init(tls_dir)
            ca_util.cmd_mkcert(tls_dir, socket.gethostname())
            ca_util.cmd_mkcert(tls_dir, 'client')

    if tls_dir == 'CV':
        if section != 'registrar':
            raise Exception(
                "You only use the CV option to tls_dir for the registrar not %s"
                % section)
        tls_dir = os.path.abspath('%s/%s' % (common.WORK_DIR, 'cv_ca'))
        if not os.path.exists("%s/cacert.crt" % (tls_dir)):
            raise Exception(
                "It appears that the verifier has not yet created a CA and certificates, please run the verifier first"
            )

    # if it is relative path, convert to absolute in WORK_DIR
    if tls_dir[0] != '/':
        tls_dir = os.path.abspath('%s/%s' % (common.WORK_DIR, tls_dir))

    if ca_cert == 'default':
        ca_path = "%s/cacert.crt" % (tls_dir)
    else:
        ca_path = "%s/%s" % (tls_dir, ca_cert)

    if my_cert == 'default':
        my_cert = "%s/%s-cert.crt" % (tls_dir, socket.gethostname())
    else:
        my_cert = "%s/%s" % (tls_dir, my_cert)

    if my_priv_key == 'default':
        my_priv_key = "%s/%s-private.pem" % (tls_dir, socket.gethostname())
    else:
        my_priv_key = "%s/%s" % (tls_dir, my_priv_key)

    context = ssl.create_default_context(ssl.Purpose.CLIENT_AUTH)
    context.load_verify_locations(cafile=ca_path)
    context.load_cert_chain(certfile=my_cert,
                            keyfile=my_priv_key,
                            password=my_key_pw)
    context.verify_mode = ssl.CERT_REQUIRED
    return context
示例#3
0
文件: tenant.py 项目: nkinder/keylime
def main(argv=sys.argv):
    parser = argparse.ArgumentParser(argv[0])
    parser.add_argument('-c', '--command',action='store',dest='command',default='add',help="valid commands are add,delete,update,status,list,reactivate,regdelete. defaults to add")
    parser.add_argument('-t', '--targethost',action='store',dest='agent_ip',help="the IP address of the host to provision")
    parser.add_argument('-tp', '--targetport',action='store',dest='agent_port',help="the Port of the host to provision")
    parser.add_argument('--cv_targethost',action='store',default=None,dest='cv_agent_ip',help='the IP address of the host to provision that the verifier will use (optional).  Use only if different than argument to option -t/--targethost')
    parser.add_argument('-v', '--cv',action='store',dest='verifier_ip',help="the IP address of the cloud verifier")
    parser.add_argument('-u', '--uuid',action='store',dest='agent_uuid',help="UUID for the agent to provision")
    parser.add_argument('-f', '--file', action='store',default=None,help='Deliver the specified plaintext to the provisioned agent')
    parser.add_argument('--cert',action='store',dest='ca_dir',default=None,help='Create and deliver a certificate using a CA created by ca-util. Pass in the CA directory or use "default" to use the standard dir')
    parser.add_argument('-k', '--key',action='store',dest='keyfile',help='an intermedia key file produced by user_data_encrypt')
    parser.add_argument('-p', '--payload', action='store',default=None,help='Specify the encrypted payload to deliver with encrypted keys specified by -k')
    parser.add_argument('--include',action='store',dest='incl_dir',default=None,help="Include additional files in provided directory in certificate zip file.  Must be specified with --cert")
    parser.add_argument('--whitelist',action='store',dest='ima_whitelist',default=None,help="Specify the location of an IMA whitelist")
    parser.add_argument('--exclude',action='store',dest='ima_exclude',default=None,help="Specify the location of an IMA exclude list")
    parser.add_argument('--tpm_policy',action='store',dest='tpm_policy',default=None,help="Specify a TPM policy in JSON format. e.g., {\"15\":\"0000000000000000000000000000000000000000\"}")
    parser.add_argument('--vtpm_policy',action='store',dest='vtpm_policy',default=None,help="Specify a vTPM policy in JSON format")
    parser.add_argument('--verify',action='store_true',default=False,help='Block on cryptographically checked key derivation confirmation from the agent once it has been provisioned')

    if common.DEVELOP_IN_ECLIPSE and len(argv)==1:
        ca_util.setpassword('default')
        #tmp = ['-c','add','-t','127.0.0.1','-v', '127.0.0.1','-u','C432FBB3-D2F1-4A97-9EF7-75BD81C866E9','-p','content_payload.txt','-k','content_keys.txt']
        #tmp = ['-c','add','-t','127.0.0.1','-v','127.0.0.1','-u','C432FBB3-D2F1-4A97-9EF7-75BD81C866E9','-f','tenant.py']
        tmp = ['-c','add','-t','127.0.0.1','-v','127.0.0.1','-u','C432FBB3-D2F1-4A97-9EF7-75BD81C866E9','--cert','ca/']
        #tmp = ['-c','delete','-t','127.0.0.1','-v','127.0.0.1','-u','C432FBB3-D2F1-4A97-9EF7-75BD81C866E9']
        #tmp = ['-c','reactivate','-t','127.0.0.1','-v','127.0.0.1','-u','C432FBB3-D2F1-4A97-9EF7-75BD81C866E9']
        #tmp = ['-c','list','-v', '127.0.0.1','-u','C432FBB3-D2F1-4A97-9EF7-75BD81C866E9']
        #tmp = ['-c','regdelete','-u','C432FBB3-D2F1-4A97-9EF7-75BD81C866E9']
    else:
        tmp = argv[1:]

    args = parser.parse_args(tmp)
    mytenant = Tenant()

    if args.command not in ['list','regdelete'] and args.agent_ip is None:
        raise UserError("-t/--targethost is required for command %s"%args.command)

    if args.agent_uuid is not None:
        mytenant.agent_uuid = args.agent_uuid
        # if the uuid is actually a public key, then hash it
        if mytenant.agent_uuid.startswith('-----BEGIN PUBLIC KEY-----'):
            mytenant.agent_uuid = hashlib.sha256(mytenant.agent_uuid).hexdigest()
    else:
        logger.warning("Using default UUID D432FBB3-D2F1-4A97-9EF7-75BD81C00000")
        mytenant.agent_uuid = "D432FBB3-D2F1-4A97-9EF7-75BD81C00000"

    if common.STUB_VTPM and common.TPM_CANNED_VALUES is not None:
        # Use canned values for agent UUID
        jsonIn = common.TPM_CANNED_VALUES
        if "add_vtpm_to_group" in jsonIn:
            mytenant.agent_uuid = jsonIn['add_vtpm_to_group']['retout']
        else:
            # Our command hasn't been canned!
            raise UserError("Command %s not found in canned JSON!"%("add_vtpm_to_group"))

    if args.verifier_ip is not None:
        mytenant.cloudverifier_ip = args.verifier_ip

    if args.command=='add':
        mytenant.init_add(vars(args))
        mytenant.preloop()
        mytenant.do_cv()
        mytenant.do_quote()
        if args.verify:
            mytenant.do_verify()

        if common.DEVELOP_IN_ECLIPSE:
            time.sleep(2)
            mytenant.do_cvstatus()
            time.sleep(1)
            #invalidate it eventually
            logger.debug("invalidating PCR 15, forcing revocation")
            tpm = tpm_obj.getTPM(need_hw_tpm=True)
            tpm.extendPCR(15, tpm.hashdigest(b"garbage"))
            time.sleep(5)
            logger.debug("Deleting agent from verifier")
            mytenant.do_cvdelete()
    elif args.command=='update':
        mytenant.init_add(vars(args))
        mytenant.do_cvdelete()
        mytenant.preloop()
        mytenant.do_cv()
        mytenant.do_quote()
        if args.verify:
            mytenant.do_verify()
    elif args.command=='delete':
        mytenant.do_cvdelete()
    elif args.command=='status':
        mytenant.do_cvstatus()
    elif args.command=='list':
        mytenant.do_cvstatus(listing=True)
    elif args.command=='reactivate':
        mytenant.do_cvreactivate()
    elif args.command=='regdelete':
        mytenant.do_regdelete()
    else:
        raise UserError("Invalid command specified: %s"%(args.command))
示例#4
0
def init_mtls(section='cloud_verifier', generatedir='cv_ca', logger=None):
    """
    Generates mTLS SSLContext for either the cloud verifier or the registrar.
    """

    if not config.getboolean('general', "enable_tls"):
        logger.warning(
            "Warning: TLS is currently disabled, keys will be sent in the clear! This should only be used for testing.")
        return None

    if logger:
        logger.info("Setting up TLS...")
    my_cert = config.get(section, 'my_cert')
    ca_cert = config.get(section, 'ca_cert')
    my_priv_key = config.get(section, 'private_key')
    my_key_pw = config.get(section, 'private_key_pw')
    tls_dir = config.get(section, 'tls_dir')

    if tls_dir == 'generate':
        if my_cert != 'default' or my_priv_key != 'default' or ca_cert != 'default':
            raise Exception(
                "To use tls_dir=generate, options ca_cert, my_cert, and private_key must all be set to 'default'")

        if generatedir[0] != '/':
            generatedir = os.path.abspath(os.path.join(config.WORK_DIR,
                                                       generatedir))
        tls_dir = generatedir
        ca_path = os.path.join(tls_dir, "cacert.crt")
        if os.path.exists(ca_path):
            if logger:
                logger.info("Existing CA certificate found in %s, not generating a new one", tls_dir)
        else:
            if logger:
                logger.info("Generating a new CA in %s and a client certificate for connecting", tls_dir)
                logger.info("use keylime_ca -d %s to manage this CA", tls_dir)
            if not os.path.exists(tls_dir):
                os.makedirs(tls_dir, 0o700)
            if my_key_pw == 'default':
                if logger:
                    logger.warning("CAUTION: using default password for CA, please set private_key_pw to a strong password")
            ca_util.setpassword(my_key_pw)
            ca_util.cmd_init(tls_dir)
            ca_util.cmd_mkcert(tls_dir, socket.gethostname())
            ca_util.cmd_mkcert(tls_dir, 'client')

    if tls_dir == 'CV':
        if section != 'registrar':
            raise Exception(
                f"You only use the CV option to tls_dir for the registrar not {section}")
        tls_dir = os.path.abspath(os.path.join(config.WORK_DIR, 'cv_ca'))
        if not os.path.exists(os.path.join(tls_dir, "cacert.crt")):
            raise Exception(
                "It appears that the verifier has not yet created a CA and certificates, please run the verifier first")

    # if it is relative path, convert to absolute in WORK_DIR
    if tls_dir[0] != '/':
        tls_dir = os.path.abspath(os.path.join(config.WORK_DIR, tls_dir))

    if ca_cert == 'default':
        ca_path = os.path.join(tls_dir, "cacert.crt")
    elif not os.path.isabs(ca_cert):
        ca_path = os.path.join(tls_dir, ca_cert)
    else:
        ca_path = ca_cert

    if my_cert == 'default':
        my_cert = os.path.join(tls_dir, f"{socket.gethostname()}-cert.crt")
    elif not os.path.isabs(my_cert):
        my_cert = os.path.join(tls_dir, my_cert)
    else:
        pass

    if my_priv_key == 'default':
        my_priv_key = os.path.join(tls_dir,
                                   f"{socket.gethostname()}-private.pem")
    elif not os.path.isabs(my_priv_key):
        my_priv_key = os.path.join(tls_dir, my_priv_key)

    check_client_cert = (config.has_option(section, 'check_client_cert')
                         and config.getboolean(section, 'check_client_cert'))
    context = generate_mtls_context(my_cert, my_priv_key, ca_path, check_client_cert, my_key_pw, logger=logger)

    return context, (my_cert, my_priv_key, my_key_pw)