<commit_before>
import click
import falcon
import logging
import ipaddress
import json
import os
import hashlib
from base64 import b64decode
from certidude import config, authority, helpers, push, errors
from certidude.auth import login_required, login_optional, authorize_admin
from certidude.decorators import serialize, csrf_protection
from certidude.firewall import whitelist_subnets, whitelist_content_types
from cryptography import x509
from cryptography.hazmat.backends import default_backend
from cryptography.hazmat.primitives import hashes
from cryptography.hazmat.primitives.asymmetric import padding
from cryptography.exceptions import InvalidSignature
from cryptography.x509.oid import NameOID
from datetime import datetime

logger = logging.getLogger(__name__)

class RequestListResource(object):
    @login_optional
    @whitelist_subnets(config.REQUEST_SUBNETS)
    @whitelist_content_types("application/pkcs10")
    def on_post(self, req, resp):
        """
        Validate and parse certificate signing request
        """
        body = req.stream.read(req.content_length)
        csr = x509.load_pem_x509_csr(body, default_backend())
        try:
            common_name, = csr.subject.get_attributes_for_oid(NameOID.COMMON_NAME)
        except: # ValueError?
            logger.warning(u"Rejected signing request without common name from %s",
                req.context.get("remote_addr"))
            raise falcon.HTTPBadRequest(
                "Bad request",
                "No common name specified!")

        """
        Handle domain computer automatic enrollment
        """
        machine = req.context.get("machine")
        if config.MACHINE_ENROLLMENT_ALLOWED and machine:
            if common_name.value != machine:
                raise falcon.HTTPBadRequest(
                    "Bad request",
                    "Common name %s differs from Kerberos credential %s!" % (common_name.value, machine))

            # Automatic enroll with Kerberos machine cerdentials
            resp.set_header("Content-Type", "application/x-x509-user-cert")
            cert, resp.body = authority._sign(csr, body, overwrite=True)
            logger.info(u"Automatically enrolled Kerberos authenticated machine %s from %s",
                machine, req.context.get("remote_addr"))
            return

        """
        Attempt to renew certificate using currently valid key pair
        """
        try:
            path, buf, cert = authority.get_signed(common_name.value)
        except EnvironmentError:
            pass
        else:
            if cert.public_key().public_numbers() == csr.public_key().public_numbers():
                try:
                    renewal_signature = b64decode(req.get_header("X-Renewal-Signature"))
                except TypeError, ValueError: # No header supplied, redirect to signed API call
                    resp.status = falcon.HTTP_SEE_OTHER
                    resp.location = os.path.join(os.path.dirname(req.relative_uri), "signed", common_name.value)
                    return
                else:
                    try:
                        verifier = cert.public_key().verifier(
                            renewal_signature,
                            padding.PSS(
                                mgf=padding.MGF1(hashes.SHA512()),
                                salt_length=padding.PSS.MAX_LENGTH
                            ),
                            hashes.SHA512()
                        )
                        verifier.update(buf)
                        verifier.update(body)
                        verifier.verify()
                    except InvalidSignature:
                        logger.error("Renewal failed, invalid signature supplied for %s", common_name.value)
                    else:
                        # At this point renewal signature was valid but we need to perform some extra checks
                        if datetime.utcnow() > cert.not_valid_after:
                            logger.error("Renewal failed, current certificate for %s has expired", common_name.value)
                            # Put on hold
                        elif not config.CERTIFICATE_RENEWAL_ALLOWED:
                            logger.error("Renewal requested for %s, but not allowed by authority settings", common_name.value)
                            # Put on hold
                        else:
                            resp.set_header("Content-Type", "application/x-x509-user-cert")
                            _, resp.body = authority._sign(csr, body, overwrite=True)
                            logger.info("Renewed certificate for %s", common_name.value)
                            return


        """
        Process automatic signing if the IP address is whitelisted,
        autosigning was requested and certificate can be automatically signed
        """
        if req.get_param_as_bool("autosign") and "." not in common_name.value:
            for subnet in config.AUTOSIGN_SUBNETS:
                if req.context.get("remote_addr") in subnet:
                    try:
                        resp.set_header("Content-Type", "application/x-x509-user-cert")
                        _, resp.body = authority._sign(csr, body)
                        logger.info("Autosigned %s as %s is whitelisted", common_name.value, req.context.get("remote_addr"))
                        return
                    except EnvironmentError:
                        logger.info("Autosign for %s failed, signed certificate already exists",
                            common_name.value, req.context.get("remote_addr"))
                    break

        # Attempt to save the request otherwise
        try:
            csr = authority.store_request(body)
        except errors.RequestExists:
            # We should still redirect client to long poll URL below
            pass
        except errors.DuplicateCommonNameError:
            # TODO: Certificate renewal
            logger.warning(u"Rejected signing request with overlapping common name from %s",
                req.context.get("remote_addr"))
            raise falcon.HTTPConflict(
                "CSR with such CN already exists",
                "Will not overwrite existing certificate signing request, explicitly delete CSR and try again")
        else:
            push.publish("request-submitted", common_name.value)

        # Wait the certificate to be signed if waiting is requested
        logger.info(u"Signing request %s from %s stored", common_name.value, req.context.get("remote_addr"))
        if req.get_param("wait"):
            # Redirect to nginx pub/sub
            url = config.LONG_POLL_SUBSCRIBE % hashlib.sha256(body).hexdigest()
            click.echo("Redirecting to: %s"  % url)
            resp.status = falcon.HTTP_SEE_OTHER<commit_msg>Use application/x-pem-file mimetype for user certs<commit_after>
import click
import falcon
import logging
import ipaddress
import json
import os
import hashlib
from base64 import b64decode
from certidude import config, authority, helpers, push, errors
from certidude.auth import login_required, login_optional, authorize_admin
from certidude.decorators import serialize, csrf_protection
from certidude.firewall import whitelist_subnets, whitelist_content_types
from cryptography import x509
from cryptography.hazmat.backends import default_backend
from cryptography.hazmat.primitives import hashes
from cryptography.hazmat.primitives.asymmetric import padding
from cryptography.exceptions import InvalidSignature
from cryptography.x509.oid import NameOID
from datetime import datetime

logger = logging.getLogger(__name__)

class RequestListResource(object):
    @login_optional
    @whitelist_subnets(config.REQUEST_SUBNETS)
    @whitelist_content_types("application/pkcs10")
    def on_post(self, req, resp):
        """
        Validate and parse certificate signing request
        """
        body = req.stream.read(req.content_length)
        csr = x509.load_pem_x509_csr(body, default_backend())
        try:
            common_name, = csr.subject.get_attributes_for_oid(NameOID.COMMON_NAME)
        except: # ValueError?
            logger.warning(u"Rejected signing request without common name from %s",
                req.context.get("remote_addr"))
            raise falcon.HTTPBadRequest(
                "Bad request",
                "No common name specified!")

        """
        Handle domain computer automatic enrollment
        """
        machine = req.context.get("machine")
        if config.MACHINE_ENROLLMENT_ALLOWED and machine:
            if common_name.value != machine:
                raise falcon.HTTPBadRequest(
                    "Bad request",
                    "Common name %s differs from Kerberos credential %s!" % (common_name.value, machine))

            # Automatic enroll with Kerberos machine cerdentials
            resp.set_header("Content-Type", "application/x-pem-file")
            cert, resp.body = authority._sign(csr, body, overwrite=True)
            logger.info(u"Automatically enrolled Kerberos authenticated machine %s from %s",
                machine, req.context.get("remote_addr"))
            return

        """
        Attempt to renew certificate using currently valid key pair
        """
        try:
            path, buf, cert = authority.get_signed(common_name.value)
        except EnvironmentError:
            pass
        else:
            if cert.public_key().public_numbers() == csr.public_key().public_numbers():
                try:
                    renewal_signature = b64decode(req.get_header("X-Renewal-Signature"))
                except TypeError, ValueError: # No header supplied, redirect to signed API call
                    resp.status = falcon.HTTP_SEE_OTHER
                    resp.location = os.path.join(os.path.dirname(req.relative_uri), "signed", common_name.value)
                    return
                else:
                    try:
                        verifier = cert.public_key().verifier(
                            renewal_signature,
                            padding.PSS(
                                mgf=padding.MGF1(hashes.SHA512()),
                                salt_length=padding.PSS.MAX_LENGTH
                            ),
                            hashes.SHA512()
                        )
                        verifier.update(buf)
                        verifier.update(body)
                        verifier.verify()
                    except InvalidSignature:
                        logger.error("Renewal failed, invalid signature supplied for %s", common_name.value)
                    else:
                        # At this point renewal signature was valid but we need to perform some extra checks
                        if datetime.utcnow() > cert.not_valid_after:
                            logger.error("Renewal failed, current certificate for %s has expired", common_name.value)
                            # Put on hold
                        elif not config.CERTIFICATE_RENEWAL_ALLOWED:
                            logger.error("Renewal requested for %s, but not allowed by authority settings", common_name.value)
                            # Put on hold
                        else:
                            resp.set_header("Content-Type", "application/x-x509-user-cert")
                            _, resp.body = authority._sign(csr, body, overwrite=True)
                            logger.info("Renewed certificate for %s", common_name.value)
                            return


        """
        Process automatic signing if the IP address is whitelisted,
        autosigning was requested and certificate can be automatically signed
        """
        if req.get_param_as_bool("autosign") and "." not in common_name.value:
            for subnet in config.AUTOSIGN_SUBNETS:
                if req.context.get("remote_addr") in subnet:
                    try:
                        resp.set_header("Content-Type", "application/x-pem-file")
                        _, resp.body = authority._sign(csr, body)
                        logger.info("Autosigned %s as %s is whitelisted", common_name.value, req.context.get("remote_addr"))
                        return
                    except EnvironmentError:
                        logger.info("Autosign for %s failed, signed certificate already exists",
                            common_name.value, req.context.get("remote_addr"))
                    break

        # Attempt to save the request otherwise
        try:
            csr = authority.store_request(body)
        except errors.RequestExists:
            # We should still redirect client to long poll URL below
            pass
        except errors.DuplicateCommonNameError:
            # TODO: Certificate renewal
            logger.warning(u"Rejected signing request with overlapping common name from %s",
                req.context.get("remote_addr"))
            raise falcon.HTTPConflict(
                "CSR with such CN already exists",
                "Will not overwrite existing certificate signing request, explicitly delete CSR and try again")
        else:
            push.publish("request-submitted", common_name.value)

        # Wait the certificate to be signed if waiting is requested
        logger.info(u"Signing request %s from %s stored", common_name.value, req.context.get("remote_addr"))
        if req.get_param("wait"):
            # Redirect to nginx pub/sub
            url = config.LONG_POLL_SUBSCRIBE % hashlib.sha256(body).hexdigest()
            click.echo("Redirecting to: %s"  % url)
            resp.status = falcon.HTTP_SEE_OTHER
{'fraction_non_alphanumeric': 0.07508208682962422, 'fraction_numerical': 0.005253557095950383, 'mean_word_length': 1.8406217616580312, 'pattern_counts': {'":': 0, '<': 3, '<?xml version=': 0, '>': 5, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 4, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '1171055', 'n_tokens_mistral': 3532, 'n_tokens_neox': 3304, 'n_words': 896}