示例#1
0
class Handler:
    def __init__(self, config=None):
        if config is None:
            config = get_config_from_file("config.ini")
        self.config = config
        self.cert_processor = CertProcessor(config)
        self.seed()

    def seed(self):
        """Seeds the User and Admin trust databases."""
        logger.info("Seeding PGP Trust Databases")
        seed_base_dir = self.config.get("mtls",
                                        "seed_dir",
                                        fallback="/tmp/seeds")
        if os.path.isdir(seed_base_dir):
            for trust in ["user", "admin"]:
                seed_dir = os.path.join(seed_base_dir, trust)
                if os.path.isdir(seed_dir):
                    logger.info("Seeding {} Trust Store".format(trust))
                    for f in os.listdir(seed_dir):
                        f_path = os.path.join(seed_dir, f)
                        if os.path.isfile(f_path):
                            fingerprint = f.split(".")[0]
                            with open(f_path, "r") as gpg_data:
                                gpg_data = str(gpg_data.read())
                                if trust == "admin":
                                    self.import_and_trust(
                                        gpg_data,
                                        self.cert_processor.admin_gpg)
                                # If we add an admin, they're also a user,
                                # so we can just pull the fingerprint once
                                # and use that for logging. It will only show
                                # it's being 'added' to the admin store, but
                                # that's fine since that assumption is already
                                # made
                                fingerprint = self.import_and_trust(
                                    gpg_data, self.cert_processor.user_gpg)
                                logger.info("Added {fp} to {t} Store".format(
                                    fp=fingerprint, t=trust))

    def import_and_trust(self, key_data, gpg):
        """Imports a key into a given keyring and trust database as well as
        properly trusting it for use.

        Args:
            key_data (str): The key data in ACSII or binary format.
            gpg (gnupg.GPG): The gpg instance.

        Returns:
            str: The fingerprint of the newly imported and trusted key.
        """
        import_data = gpg.import_keys(key_data)
        fingerprint = import_data.fingerprints[0]
        gpg.trust_keys([fingerprint], "TRUST_ULTIMATE")
        return fingerprint

    def create_cert(self, body):
        """Create a certificate."""
        lifetime = int(body["lifetime"])
        min_lifetime = int(self.config.get("mtls", "min_lifetime",
                                           fallback=60))
        max_lifetime = int(self.config.get("mtls", "max_lifetime", fallback=0))
        if lifetime < min_lifetime:
            logger.info(
                "User requested lifetime less than minimum. {} < {}".format(
                    lifetime, min_lifetime))
            error_response("lifetime must be greater than {} seconds".format(
                min_lifetime))
        if max_lifetime != 0:
            if lifetime > max_lifetime:
                logger.info(
                    "User requested lifetime greater than maximum. {} < {}".
                    format(lifetime, max_lifetime))
                error_response("lifetime must be less than {} seconds".format(
                    max_lifetime))
        csr_str = body["csr"]
        csr = self.cert_processor.get_csr(csr_str)
        if csr is None:
            return error_response("Could not load CSR")
        try:
            csr_public_bytes = csr.public_bytes(serialization.Encoding.PEM)
            sig_path = write_sig_to_file(body["signature"])
            fingerprint = self.cert_processor.verify(csr_public_bytes,
                                                     sig_path)
            os.remove(sig_path)
        except CertProcessorUntrustedSignatureError as e:
            logger.info("Unauthorized: {}".format(e))
            return error_response("Unauthorized", 403)
        except CertProcessorInvalidSignatureError:
            logger.info("Invalid signature in CSR.")
            return error_response("Invalid signature", 401)
        if csr is None:
            logger.info("Invalid CSR.")
            return error_response("Invalid CSR")
        cert = None
        try:
            cert = self.cert_processor.generate_cert(csr, lifetime,
                                                     fingerprint)
            return json.dumps({"cert": cert.decode("UTF-8")}), 200
        except CertProcessorKeyNotFoundError:
            logger.critical("Key missing. Service not properly initialized")
            return error_response("Internal Error")
        except CertProcessorMismatchedPublicKeyError:
            logger.error("CSR Public Key does not match found certificate.")
            return error_response("Internal Error")
        except CertProcessorNotAdminUserError:
            logger.error(
                "User {} is not an admin and attempted ".format(fingerprint) +
                "to generate a certificate they are not allowed to generate.")
            return error_response("Invalid Request", 403)
        except CertProcessorNoPGPKeyFoundError:
            logger.info("PGP Key not found.")
            return error_response("Unauthorized", 401)

    def revoke_cert(self, body):
        """
        A user should be able to revoke their own certificate. An admin should
        be able to revoke the certificate of any user.

        Args:
            body: A dictionary from the JSON input.

        Returns:
            (json, int): a tuple of the json response and http status code.
        """
        is_admin = False
        fingerprint = None
        sig_path = write_sig_to_file(body["signature"])
        try:
            fingerprint = self.cert_processor.admin_verify(
                json.dumps(body["query"]).encode("UTF-8"), sig_path)
            is_admin = True
            logger.info(
                "Admin {adminfp} revoking certificate with query {query}".
                format(adminfp=fingerprint, query=json.dumps(body["query"])))
            os.remove(sig_path)
        except (CertProcessorInvalidSignatureError,
                CertProcessorUntrustedSignatureError):
            try:
                fingerprint = self.cert_processor.verify(
                    json.dumps(body["query"]).encode("UTF-8"), sig_path)
                logger.info(
                    "User {userfp} revoking certificate with query {query}".
                    format(userfp=fingerprint,
                           query=json.dumps(body["query"])))
                os.remove(sig_path)
            except (
                    CertProcessorInvalidSignatureError,
                    CertProcessorUntrustedSignatureError,
            ):
                os.remove(sig_path)
                return error_response("Unauthorized", 403)

        certs = self.cert_processor.storage.get_cert(**body["query"])
        if certs is None:
            return error_response("No Cert to revoke")
        for cert in certs:
            cert = x509.load_pem_x509_certificate(str(cert).encode("UTF-8"),
                                                  backend=default_backend())
            self.cert_processor.revoke_cert(cert.serial_number)
        return json.dumps({"msg": "success"}), 200

    def add_user(self, body, is_admin=False):
        """Add a user or admin."""
        fingerprint = None
        sig_path = write_sig_to_file(body["signature"])
        try:
            fingerprint = self.cert_processor.admin_verify(
                body["fingerprint"].encode("UTF-8"), sig_path)
        except (CertProcessorInvalidSignatureError,
                CertProcessorUntrustedSignatureError):
            os.remove(sig_path)
            logger.error(
                "Invalid signature on adding fingerprint: {fp}".format(
                    fp=body["fingerprint"]))
            return error_response("Unauthorized", 403)
        # Remove signature file
        os.remove(sig_path)

        fingerprint = body["fingerprint"]

        try:
            if is_admin:
                has_user = self.has_user(self.cert_processor.admin_gpg,
                                         fingerprint)
                if not has_user:
                    logger.info(
                        "Admin {adminfp} adding admin user {userfp}".format(
                            adminfp=fingerprint, userfp=body["fingerprint"]))
                    # Add a user to the admin trust store
                    self.add_and_trust_user(self.cert_processor.admin_gpg,
                                            fingerprint)

            has_user = self.has_user(self.cert_processor.user_gpg, fingerprint)

            if not has_user:
                # Add the user to the user trust store
                logger.info(
                    "Admin {adminfp} adding admin user {userfp}".format(
                        adminfp=fingerprint, userfp=body["fingerprint"]))
                self.add_and_trust_user(self.cert_processor.user_gpg,
                                        fingerprint)
            return json.dumps({"msg": "success"}), 201
        except GPGKeyNotFoundException:
            return (
                json.dumps(
                    {"msg": "Key not found on keyserver. Could not import"}),
                422,
            )

    def has_user(self, gpg, fingerprint):
        keys = gpg.list_keys(keys=fingerprint)
        if len(keys) is 0:
            return False
        return True

    def add_and_trust_user(self, gpg, fingerprint):
        result = self.cert_processor.user_gpg.recv_keys(
            self.config.get("gnupg",
                            "keyserver",
                            fallback="keyserver.ubuntu.com"),
            fingerprint,
        )
        if result.count is None or result.count == 0:
            raise GPGKeyNotFoundException()
        self.cert_processor.user_gpg.trust_keys([fingerprint],
                                                "TRUST_ULTIMATE")

    def remove_user(self, body, is_admin=False):
        """Remove a user or admin."""
        fingerprint = None
        sig_path = write_sig_to_file(body["signature"])
        try:
            fingerprint = self.cert_processor.admin_verify(
                body["fingerprint"].encode("UTF-8"), sig_path)
            logger.info("Admin {adminfp} adding user {userfp}".format(
                adminfp=fingerprint, userfp=body["fingerprint"]))
        except (CertProcessorInvalidSignatureError,
                CertProcessorUntrustedSignatureError):
            os.remove(sig_path)
            logger.error(
                "Invalid signature on adding fingerprint: {fp}".format(
                    fp=body["fingerprint"]))
            return error_response("Unauthorized", 403)
        # Remove signature file
        os.remove(sig_path)

        if is_admin:
            # Add a user to the admin trust store
            self.cert_processor.admin_gpg.delete_keys(body["fingerprint"])

        # Add the user to the user trust store
        self.cert_processor.user_gpg.delete_keys(body["fingerprint"])
        return json.dumps({"msg": "success"}), 201
示例#2
0
class Handler:
    def __init__(self, config=None):
        if config is None:
            config = get_config_from_file("config.ini")
        self.config = config
        # Seed the trust stores
        Sync(self.config).seed()
        self.cert_processor = CertProcessor(config)

    def create_cert(self, body):
        """Create a certificate."""
        lifetime = int(body["lifetime"])
        min_lifetime = int(self.config.get("mtls", "min_lifetime",
                                           fallback=60))
        max_lifetime = int(self.config.get("mtls", "max_lifetime", fallback=0))
        if lifetime < min_lifetime:
            logger.info(
                "User requested lifetime less than minimum. {} < {}".format(
                    lifetime, min_lifetime))
            return error_response(
                "lifetime must be greater than {} seconds".format(
                    min_lifetime))
        if max_lifetime != 0:
            if lifetime > max_lifetime:
                logger.info(
                    "User requested lifetime greater than maximum. {} < {}".
                    format(lifetime, max_lifetime))
                return error_response(
                    "lifetime must be less than {} seconds".format(
                        max_lifetime))
        csr_str = body["csr"]
        csr = self.cert_processor.get_csr(csr_str)
        if csr is None:
            return error_response("Could not load CSR")
        try:
            logger.info("create_cert: get csr_public_bytes")
            csr_public_bytes = csr.public_bytes(serialization.Encoding.PEM)
            logger.info("create_cert: write to temp sig file")
            sig_path = write_sig_to_file(body["signature"])
            logger.info("create_cert: get fingerprint")
            fingerprint = self.cert_processor.verify(csr_public_bytes,
                                                     sig_path)
            logger.info("create_cert: remove sig file")
            os.remove(sig_path)
        except CertProcessorUntrustedSignatureError as e:
            logger.info("Unauthorized: {}".format(e))
            return error_response("Unauthorized", 403)
        except CertProcessorInvalidSignatureError:
            logger.info("Invalid signature in CSR.")
            return error_response("Invalid signature", 401)
        except Exception as e:
            logger.critical("Unknown Error: {}".format(e))
            return error_response("Internal Server Error", 500)
        if csr is None:
            logger.info("Invalid CSR.")
            return error_response("Invalid CSR")
        cert = None
        try:
            logger.info(
                f"create_cert: generating certificate for: {fingerprint}")
            cert = self.cert_processor.generate_cert(csr, lifetime,
                                                     fingerprint)
            logger.info(
                f"create_cert: sending certificate to client for: {fingerprint}"
            )
            return json.dumps({"cert": cert.decode("UTF-8")}), 200
        except CertProcessorKeyNotFoundError:
            logger.critical("Key missing. Service not properly initialized")
            return error_response("Internal Error")
        except CertProcessorMismatchedPublicKeyError:
            logger.error("CSR Public Key does not match found certificate.")
            return error_response("Internal Error")
        except CertProcessorNotAdminUserError:
            logger.error(
                "User {} is not an admin and attempted ".format(fingerprint) +
                "to generate a certificate they are not allowed to generate.")
            return error_response("Invalid Request", 403)
        except CertProcessorNoPGPKeyFoundError:
            logger.info("PGP Key not found.")
            return error_response("Unauthorized", 401)
        except Exception as e:
            logger.critical(f"Unhandled Exception: {e}")
            return error_response("Internal Server Error", 500)

    def revoke_cert(self, body):
        """
        A user should be able to revoke their own certificate. An admin should
        be able to revoke the certificate of any user.

        Args:
            body: A dictionary from the JSON input.

        Returns:
            (json, int): a tuple of the json response and http status code.
        """
        is_admin = False
        fingerprint = None
        sig_path = write_sig_to_file(body["signature"])
        try:
            fingerprint = self.cert_processor.admin_verify(
                json.dumps(body["query"]).encode("UTF-8"), sig_path)
            is_admin = True
            logger.info(
                "Admin {adminfp} revoking certificate with query {query}".
                format(adminfp=fingerprint, query=json.dumps(body["query"])))
            os.remove(sig_path)
        except (CertProcessorInvalidSignatureError,
                CertProcessorUntrustedSignatureError):
            try:
                fingerprint = self.cert_processor.verify(
                    json.dumps(body["query"]).encode("UTF-8"), sig_path)
                logger.info(
                    "User {userfp} revoking certificate with query {query}".
                    format(userfp=fingerprint,
                           query=json.dumps(body["query"])))
                os.remove(sig_path)
            except (
                    CertProcessorInvalidSignatureError,
                    CertProcessorUntrustedSignatureError,
            ):
                os.remove(sig_path)
                return error_response("Unauthorized", 403)

        certs = self.cert_processor.storage.get_cert(**body["query"])
        if certs is None:
            return error_response("No Cert to revoke")
        for cert in certs:
            cert = x509.load_pem_x509_certificate(str(cert).encode("UTF-8"),
                                                  backend=default_backend())
            self.cert_processor.revoke_cert(cert.serial_number)
        return json.dumps({"msg": "success"}), 200

    def add_user(self, body, is_admin=False):
        """Add a user or admin."""
        fingerprint = None
        sig_path = write_sig_to_file(body["signature"])
        try:
            fingerprint = self.cert_processor.admin_verify(
                body["fingerprint"].encode("UTF-8"), sig_path)
        except (CertProcessorInvalidSignatureError,
                CertProcessorUntrustedSignatureError):
            os.remove(sig_path)
            logger.error(
                "Invalid signature on adding fingerprint: {fp}".format(
                    fp=body["fingerprint"]))
            return error_response("Unauthorized", 403)
        # Remove signature file
        os.remove(sig_path)

        fingerprint = body["fingerprint"]

        try:
            if is_admin:
                has_user = self.has_user(self.cert_processor.admin_gpg,
                                         fingerprint)
                if not has_user:
                    logger.info(
                        "Admin {adminfp} adding admin user {userfp}".format(
                            adminfp=fingerprint, userfp=body["fingerprint"]))
                    # Add a user to the admin trust store
                    self.add_and_trust_user(self.cert_processor.admin_gpg,
                                            fingerprint)

            has_user = self.has_user(self.cert_processor.user_gpg, fingerprint)

            if not has_user:
                # Add the user to the user trust store
                logger.info(
                    "Admin {adminfp} adding admin user {userfp}".format(
                        adminfp=fingerprint, userfp=body["fingerprint"]))
                self.add_and_trust_user(self.cert_processor.user_gpg,
                                        fingerprint)
            return json.dumps({"msg": "success"}), 201
        except GPGKeyNotFoundException:
            return (
                json.dumps(
                    {"msg": "Key not found on keyserver. Could not import"}),
                422,
            )

    def has_user(self, gpg, fingerprint):
        keys = gpg.list_keys(keys=fingerprint)
        if len(keys) == 0:
            return False
        return True

    def add_and_trust_user(self, gpg, fingerprint):
        result = self.cert_processor.user_gpg.recv_keys(
            self.config.get("gnupg",
                            "keyserver",
                            fallback="keyserver.ubuntu.com"),
            fingerprint,
        )
        if result.count is None or result.count == 0:
            raise GPGKeyNotFoundException()
        self.cert_processor.user_gpg.trust_keys([fingerprint],
                                                "TRUST_ULTIMATE")

    def remove_user(self, body, is_admin=False):
        """Remove a user or admin."""
        fingerprint = None
        sig_path = write_sig_to_file(body["signature"])
        try:
            fingerprint = self.cert_processor.admin_verify(
                body["fingerprint"].encode("UTF-8"), sig_path)
            logger.info("Admin {adminfp} adding user {userfp}".format(
                adminfp=fingerprint, userfp=body["fingerprint"]))
        except (CertProcessorInvalidSignatureError,
                CertProcessorUntrustedSignatureError):
            os.remove(sig_path)
            logger.error(
                "Invalid signature on adding fingerprint: {fp}".format(
                    fp=body["fingerprint"]))
            return error_response("Unauthorized", 403)
        # Remove signature file
        os.remove(sig_path)

        if is_admin:
            # Add a user to the admin trust store
            self.cert_processor.admin_gpg.delete_keys(body["fingerprint"])

        # Add the user to the user trust store
        self.cert_processor.user_gpg.delete_keys(body["fingerprint"])
        return json.dumps({"msg": "success"}), 201