Example #1
0
 def test_4(self):
     """
     Test CSR with badly formatted base64-encoded data
     """
     csr = self.read_file("test4.csr")
     with pytest.raises(ValueError):
         pkcs10.load_certificate_request(csr)
Example #2
0
 def test_4(self):
     """
     Test CSR with badly formatted base64-encoded data
     """
     csr = self.read_file("test4.csr")
     with pytest.raises(ValueError):
         pkcs10.load_certificate_request(csr)
Example #3
0
    def test_3(self):
        """
        Test CSR with base64-encoded bogus data
        """
        csr = self.read_file("test3.csr")

        with pytest.raises(ValueError):
            pkcs10.load_certificate_request(csr)
Example #4
0
    def test_3(self):
        """
        Test CSR with base64-encoded bogus data
        """
        csr = self.read_file("test3.csr")

        with pytest.raises(ValueError):
            pkcs10.load_certificate_request(csr)
Example #5
0
 def test_4(self):
     """
     Test CSR with badly formatted base64-encoded data
     """
     csr = self.read_file("test4.csr")
     try:
         pkcs10.load_certificate_request(csr)
     except (TypeError, binascii.Error) as typeerr:
         assert(str(typeerr) == 'Incorrect padding')
Example #6
0
 def test_4(self):
     """
     Test CSR with badly formatted base64-encoded data
     """
     csr = self.read_file("test4.csr")
     try:
         pkcs10.load_certificate_request(csr)
     except (TypeError, binascii.Error) as typeerr:
         assert (str(typeerr) == 'Incorrect padding')
Example #7
0
    def test_3(self):
        """
        Test CSR with base64-encoded bogus data
        """
        csr = self.read_file("test3.csr")

        try:
            pkcs10.load_certificate_request(csr)
        except NSPRError as nsprerr:
            # (SEC_ERROR_BAD_DER) security library: improperly formatted DER-encoded message.
            assert (nsprerr.errno == -8183)
Example #8
0
    def test_3(self):
        """
        Test CSR with base64-encoded bogus data
        """
        csr = self.read_file("test3.csr")

        try:
            pkcs10.load_certificate_request(csr)
        except NSPRError as nsprerr:
            # (SEC_ERROR_BAD_DER) security library: improperly formatted DER-encoded message.
            assert(nsprerr. errno== -8183)
Example #9
0
def validate_csr(ugettext, csr):
    """
    Ensure the CSR is base64-encoded and can be decoded by our PKCS#10
    parser.
    """
    if api.env.context == 'cli':
        # If we are passed in a pointer to a valid file on the client side
        # escape and let the load_files() handle things
        if csr and os.path.exists(csr):
            return
    try:
        pkcs10.load_certificate_request(csr)
    except (TypeError, ValueError) as e:
        raise errors.CertificateOperationError(
            error=_('Failure decoding Certificate Signing Request: %s') % e)
Example #10
0
    def test_2(self):
        """
        Test CSR with subject alt name and a list of CRL distribution points
        """
        csr = self.read_file("test2.csr")
        request = pkcs10.load_certificate_request(csr)

        subject = request.subject

        cn = subject.get_attributes_for_oid(
            cryptography.x509.NameOID.COMMON_NAME)[-1].value
        assert (cn == 'test.example.com')
        st = subject.get_attributes_for_oid(
            cryptography.x509.NameOID.STATE_OR_PROVINCE_NAME)[-1].value
        assert (st == 'California')
        c = subject.get_attributes_for_oid(
            cryptography.x509.NameOID.COUNTRY_NAME)[-1].value
        assert (c == 'US')

        san = request.extensions.get_extension_for_oid(
            cryptography.x509.ExtensionOID.SUBJECT_ALTERNATIVE_NAME).value
        dns = san.get_values_for_type(cryptography.x509.DNSName)
        assert dns[0] == 'testlow.example.com'

        crldps = request.extensions.get_extension_for_oid(
            cryptography.x509.ExtensionOID.CRL_DISTRIBUTION_POINTS).value
        gns = []
        for crldp in crldps:
            gns.extend(crldp.full_name)
        uris = [
            u'http://ca.example.com/my.crl',
            u'http://other.example.com/my.crl',
        ]
        for uri in uris:
            assert cryptography.x509.UniformResourceIdentifier(uri) in gns
Example #11
0
    def test_2(self):
        """
        Test CSR with subject alt name and a list of CRL distribution points
        """
        csr = self.read_file("test2.csr")
        request = pkcs10.load_certificate_request(csr)

        subject = request.subject

        cn = subject.get_attributes_for_oid(
                cryptography.x509.NameOID.COMMON_NAME)[-1].value
        assert(cn == 'test.example.com')
        st = subject.get_attributes_for_oid(
                cryptography.x509.NameOID.STATE_OR_PROVINCE_NAME)[-1].value
        assert(st == 'California')
        c = subject.get_attributes_for_oid(
                cryptography.x509.NameOID.COUNTRY_NAME)[-1].value
        assert(c == 'US')

        san = request.extensions.get_extension_for_oid(
                cryptography.x509.ExtensionOID.SUBJECT_ALTERNATIVE_NAME).value
        dns = san.get_values_for_type(cryptography.x509.DNSName)
        assert dns[0] == 'testlow.example.com'

        crldps = request.extensions.get_extension_for_oid(
                cryptography.x509.ExtensionOID.CRL_DISTRIBUTION_POINTS).value
        gns = []
        for crldp in crldps:
            gns.extend(crldp.full_name)
        uris = [
            u'http://ca.example.com/my.crl',
            u'http://other.example.com/my.crl',
        ]
        for uri in uris:
            assert cryptography.x509.UniformResourceIdentifier(uri) in gns
Example #12
0
def validate_csr(ugettext, csr):
    """
    Ensure the CSR is base64-encoded and can be decoded by our PKCS#10
    parser.
    """
    if api.env.context == 'cli':
        # If we are passed in a pointer to a valid file on the client side
        # escape and let the load_files() handle things
        if csr and os.path.exists(csr):
            return
    try:
        pkcs10.load_certificate_request(csr)
    except (TypeError, binascii.Error) as e:
        raise errors.Base64DecodeError(reason=str(e))
    except Exception as e:
        raise errors.CertificateOperationError(error=_('Failure decoding Certificate Signing Request: %s') % e)
Example #13
0
def get_csr_hostname(csr):
    """
    Return the value of CN in the subject of the request or None
    """
    try:
        request = pkcs10.load_certificate_request(csr)
        subject = pkcs10.get_subject(request)
        return subject.common_name
    except NSPRError, nsprerr:
        raise errors.CertificateOperationError(error=_('Failure decoding Certificate Signing Request:'))
Example #14
0
def get_csr_hostname(csr):
    """
    Return the value of CN in the subject of the request or None
    """
    try:
        request = pkcs10.load_certificate_request(csr)
        subject = pkcs10.get_subject(request)
        return subject.common_name
    except NSPRError, nsprerr:
        raise errors.CertificateOperationError(
            error=_('Failure decoding Certificate Signing Request:'))
Example #15
0
def get_subjectaltname(csr):
    """
    Return the first value of the subject alt name, if any
    """
    try:
        request = pkcs10.load_certificate_request(csr)
        for extension in request.extensions:
            if extension.oid_tag == nss.SEC_OID_X509_SUBJECT_ALT_NAME:
                return nss.x509_alt_name(extension.value)[0]
        return None
    except NSPRError, nsprerr:
        raise errors.CertificateOperationError(error=_('Failure decoding Certificate Signing Request'))
Example #16
0
    def test_0(self):
        """
        Test simple CSR with no attributes
        """
        csr = self.read_file("test0.csr")
        request = pkcs10.load_certificate_request(csr)

        subject = pkcs10.get_subject(request)

        assert (subject.common_name == 'test.example.com')
        assert (subject.state_name == 'California')
        assert (subject.country_name == 'US')
Example #17
0
    def test_0(self):
        """
        Test simple CSR with no attributes
        """
        csr = self.read_file("test0.csr")
        request = pkcs10.load_certificate_request(csr)

        subject = pkcs10.get_subject(request)

        assert(subject.common_name == 'test.example.com')
        assert(subject.state_name == 'California')
        assert(subject.country_name == 'US')
Example #18
0
def get_subjectaltname(csr):
    """
    Return the first value of the subject alt name, if any
    """
    try:
        request = pkcs10.load_certificate_request(csr)
        for extension in request.extensions:
            if extension.oid_tag == nss.SEC_OID_X509_SUBJECT_ALT_NAME:
                return nss.x509_alt_name(extension.value)[0]
        return None
    except NSPRError, nsprerr:
        raise errors.CertificateOperationError(
            error=_('Failure decoding Certificate Signing Request'))
Example #19
0
def validate_csr(ugettext, csr):
    """
    Ensure the CSR is base64-encoded and can be decoded by our PKCS#10
    parser.
    """
    if api.env.context == 'cli':
        # If we are passed in a pointer to a valid file on the client side
        # escape and let the load_files() handle things
        if csr and os.path.exists(csr):
            return
    try:
        request = pkcs10.load_certificate_request(csr)
    except TypeError, e:
        raise errors.Base64DecodeError(reason=str(e))
Example #20
0
def validate_csr(ugettext, csr):
    """
    Ensure the CSR is base64-encoded and can be decoded by our PKCS#10
    parser.
    """
    if api.env.context == 'cli':
        # If we are passed in a pointer to a valid file on the client side
        # escape and let the load_files() handle things
        if csr and os.path.exists(csr):
            return
    try:
        request = pkcs10.load_certificate_request(csr)
    except TypeError, e:
        raise errors.Base64DecodeError(reason=str(e))
Example #21
0
    def test_1(self):
        """
        Test CSR with subject alt name
        """
        csr = self.read_file("test1.csr")
        request = pkcs10.load_certificate_request(csr)

        subject = request.subject

        assert(subject.common_name == 'test.example.com')
        assert(subject.state_name == 'California')
        assert(subject.country_name == 'US')

        for extension in request.extensions:
            if extension.oid_tag == nss.SEC_OID_X509_SUBJECT_ALT_NAME:
                assert nss.x509_alt_name(extension.value)[0] == 'testlow.example.com'
Example #22
0
    def test_1(self):
        """
        Test CSR with subject alt name
        """
        csr = self.read_file("test1.csr")
        request = pkcs10.load_certificate_request(csr)

        subject = request.subject

        assert(subject.common_name == 'test.example.com')
        assert(subject.state_name == 'California')
        assert(subject.country_name == 'US')

        for extension in request.extensions:
            if extension.oid_tag == nss.SEC_OID_X509_SUBJECT_ALT_NAME:
                assert nss.x509_alt_name(extension.value)[0] == 'testlow.example.com'
Example #23
0
    def test_0(self):
        """
        Test simple CSR with no attributes
        """
        csr = pkcs10.load_certificate_request(self.read_file("test0.csr"))

        subject = csr.subject

        cn = subject.get_attributes_for_oid(
            cryptography.x509.NameOID.COMMON_NAME)[-1].value
        assert (cn == 'test.example.com')
        st = subject.get_attributes_for_oid(
            cryptography.x509.NameOID.STATE_OR_PROVINCE_NAME)[-1].value
        assert (st == 'California')
        c = subject.get_attributes_for_oid(
            cryptography.x509.NameOID.COUNTRY_NAME)[-1].value
        assert (c == 'US')
Example #24
0
    def test_0(self):
        """
        Test simple CSR with no attributes
        """
        csr = pkcs10.load_certificate_request(self.read_file("test0.csr"))

        subject = csr.subject

        cn = subject.get_attributes_for_oid(
                cryptography.x509.NameOID.COMMON_NAME)[-1].value
        assert(cn == 'test.example.com')
        st = subject.get_attributes_for_oid(
                cryptography.x509.NameOID.STATE_OR_PROVINCE_NAME)[-1].value
        assert(st == 'California')
        c = subject.get_attributes_for_oid(
                cryptography.x509.NameOID.COUNTRY_NAME)[-1].value
        assert(c == 'US')
Example #25
0
    def request_certificate(self, csr, request_type='pkcs10'):
        """
        :param csr: The certificate signing request.
        :param request_type: The request type (defaults to ``'pkcs10'``).

        Submit certificate signing request.

        The command returns a dict with these possible key/value pairs.
        Some key/value pairs may be absent.

        +---------------+---------------+---------------+
        |result name    |result type    |comments       |
        +===============+===============+===============+
        |serial_number  |unicode [1]_   |               |
        +---------------+---------------+---------------+
        |certificate    |unicode [2]_   |               |
        +---------------+---------------+---------------+
        |request_id     |unicode        |               |
        +---------------+---------------+---------------+
        |subject        |unicode        |               |
        +---------------+---------------+---------------+

        .. [1] Passed through XMLRPC as decimal string. Can convert to
               optimal integer type (int or long) via int(serial_number)

        .. [2] Base64 encoded

        """
        try:
            config = api.Command['config_show']()['result']
            subject_base = EditableDN(config.get('ipacertificatesubjectbase')[0])
            hostname = get_csr_hostname(csr)
            subject_base.insert(0, RDN(('CN', hostname)))
            request = pkcs10.load_certificate_request(csr)
            # python-nss normalizes the request subject
            request_subject = DN(str(pkcs10.get_subject(request)))

            if subject_base != request_subject:
                raise errors.CertificateOperationError(error=_('Request subject "%(request_subject)s" does not match the form "%(subject_base)s"') % \
                {'request_subject' : request_subject, 'subject_base' : subject_base})
        except errors.CertificateOperationError, e:
            raise e
Example #26
0
    def test_2(self):
        """
        Test CSR with subject alt name and a list of CRL distribution points
        """
        csr = self.read_file("test2.csr")
        request = pkcs10.load_certificate_request(csr)

        subject = request.subject

        assert(subject.common_name == 'test.example.com')
        assert(subject.state_name == 'California')
        assert(subject.country_name == 'US')

        for extension in request.extensions:
            if extension.oid_tag == nss.SEC_OID_X509_SUBJECT_ALT_NAME:
                assert nss.x509_alt_name(extension.value)[0] == 'testlow.example.com'
            if extension.oid_tag == nss.SEC_OID_X509_CRL_DIST_POINTS:
                pts = nss.CRLDistributionPts(extension.value)
                urls = pts[0].get_general_names()
                assert('http://ca.example.com/my.crl' in urls)
                assert('http://other.example.com/my.crl' in urls)
Example #27
0
    def test_2(self):
        """
        Test CSR with subject alt name and a list of CRL distribution points
        """
        csr = self.read_file("test2.csr")
        request = pkcs10.load_certificate_request(csr)

        subject = request.subject

        assert(subject.common_name == 'test.example.com')
        assert(subject.state_name == 'California')
        assert(subject.country_name == 'US')

        for extension in request.extensions:
            if extension.oid_tag == nss.SEC_OID_X509_SUBJECT_ALT_NAME:
                assert nss.x509_alt_name(extension.value)[0] == 'testlow.example.com'
            if extension.oid_tag == nss.SEC_OID_X509_CRL_DIST_POINTS:
                pts = nss.CRLDistributionPts(extension.value)
                urls = pts[0].get_general_names()
                assert('http://ca.example.com/my.crl' in urls)
                assert('http://other.example.com/my.crl' in urls)
Example #28
0
    def test_1(self):
        """
        Test CSR with subject alt name
        """
        csr = self.read_file("test1.csr")
        request = pkcs10.load_certificate_request(csr)

        subject = request.subject

        cn = subject.get_attributes_for_oid(
                cryptography.x509.NameOID.COMMON_NAME)[-1].value
        assert(cn == 'test.example.com')
        st = subject.get_attributes_for_oid(
                cryptography.x509.NameOID.STATE_OR_PROVINCE_NAME)[-1].value
        assert(st == 'California')
        c = subject.get_attributes_for_oid(
                cryptography.x509.NameOID.COUNTRY_NAME)[-1].value
        assert(c == 'US')

        san = request.extensions.get_extension_for_oid(
                cryptography.x509.ExtensionOID.SUBJECT_ALTERNATIVE_NAME).value
        dns = san.get_values_for_type(cryptography.x509.DNSName)
        assert dns[0] == 'testlow.example.com'
Example #29
0
    def test_1(self):
        """
        Test CSR with subject alt name
        """
        csr = self.read_file("test1.csr")
        request = pkcs10.load_certificate_request(csr)

        subject = request.subject

        cn = subject.get_attributes_for_oid(
            cryptography.x509.NameOID.COMMON_NAME)[-1].value
        assert (cn == 'test.example.com')
        st = subject.get_attributes_for_oid(
            cryptography.x509.NameOID.STATE_OR_PROVINCE_NAME)[-1].value
        assert (st == 'California')
        c = subject.get_attributes_for_oid(
            cryptography.x509.NameOID.COUNTRY_NAME)[-1].value
        assert (c == 'US')

        san = request.extensions.get_extension_for_oid(
            cryptography.x509.ExtensionOID.SUBJECT_ALTERNATIVE_NAME).value
        dns = san.get_values_for_type(cryptography.x509.DNSName)
        assert dns[0] == 'testlow.example.com'
Example #30
0
    def execute(self, csr, all=False, raw=False, **kw):
        ca_enabled_check()

        ldap = self.api.Backend.ldap2
        add = kw.get('add')
        request_type = kw.get('request_type')
        profile_id = kw.get('profile_id', self.Backend.ra.DEFAULT_PROFILE)

        # Check that requested authority exists (done before CA ACL
        # enforcement so that user gets better error message if
        # referencing nonexistant CA) and look up authority ID.
        #
        ca = kw['cacn']
        ca_obj = api.Command.ca_show(ca)['result']
        ca_id = ca_obj['ipacaid'][0]
        """
        Access control is partially handled by the ACI titled
        'Hosts can modify service userCertificate'. This is for the case
        where a machine binds using a host/ prinicpal. It can only do the
        request if the target hostname is in the managedBy attribute which
        is managed using the add/del member commands.

        Binding with a user principal one needs to be in the request_certs
        taskgroup (directly or indirectly via role membership).
        """

        principal = kw.get('principal')
        principal_string = unicode(principal)

        if principal.is_user:
            principal_type = USER
        elif principal.is_host:
            principal_type = HOST
        else:
            principal_type = SERVICE

        bind_principal = kerberos.Principal(getattr(context, 'principal'))
        bind_principal_string = unicode(bind_principal)

        if bind_principal.is_user:
            bind_principal_type = USER
        elif bind_principal.is_host:
            bind_principal_type = HOST
        else:
            bind_principal_type = SERVICE

        if (bind_principal_string != principal_string
                and bind_principal_type != HOST):
            # Can the bound principal request certs for another principal?
            self.check_access()

        try:
            self.check_access("request certificate ignore caacl")
            bypass_caacl = True
        except errors.ACIError:
            bypass_caacl = False

        if not bypass_caacl:
            caacl_check(principal_type, principal, ca, profile_id)

        try:
            csr_obj = pkcs10.load_certificate_request(csr)
        except ValueError as e:
            raise errors.CertificateOperationError(
                error=_("Failure decoding Certificate Signing Request: %s") %
                e)

        try:
            ext_san = csr_obj.extensions.get_extension_for_oid(
                cryptography.x509.oid.ExtensionOID.SUBJECT_ALTERNATIVE_NAME)
        except cryptography.x509.extensions.ExtensionNotFound:
            ext_san = None

        # self-service and host principals may bypass SAN permission check
        if (bind_principal_string != principal_string
                and bind_principal_type != HOST):
            if ext_san is not None:
                self.check_access('request certificate with subjectaltname')

        dn = None
        principal_obj = None
        # See if the service exists and punt if it doesn't and we aren't
        # going to add it
        try:
            if principal_type == SERVICE:
                principal_obj = api.Command['service_show'](principal_string,
                                                            all=True)
            elif principal_type == HOST:
                principal_obj = api.Command['host_show'](principal.hostname,
                                                         all=True)
            elif principal_type == USER:
                principal_obj = api.Command['user_show'](principal.username,
                                                         all=True)
        except errors.NotFound as e:
            if add:
                if principal_type == SERVICE:
                    principal_obj = api.Command['service_add'](
                        principal_string, force=True)
                else:
                    princtype_str = PRINCIPAL_TYPE_STRING_MAP[principal_type]
                    raise errors.OperationNotSupportedForPrincipalType(
                        operation=_("'add' option"),
                        principal_type=princtype_str)
            else:
                raise errors.NotFound(
                    reason=_("The principal for this request doesn't exist."))
        principal_obj = principal_obj['result']
        dn = principal_obj['dn']

        # Ensure that the DN in the CSR matches the principal
        #
        # We only look at the "most specific" CN value
        cns = csr_obj.subject.get_attributes_for_oid(
            cryptography.x509.oid.NameOID.COMMON_NAME)
        if len(cns) == 0:
            raise errors.ValidationError(
                name='csr',
                error=_("No Common Name was found in subject of request."))
        cn = cns[-1].value  # "most specific" is end of list

        if principal_type in (SERVICE, HOST):
            if cn.lower() != principal.hostname.lower():
                raise errors.ACIError(info=_(
                    "hostname in subject of request '%(cn)s' "
                    "does not match principal hostname '%(hostname)s'") %
                                      dict(cn=cn, hostname=principal.hostname))
        elif principal_type == USER:
            # check user name
            if cn != principal.username:
                raise errors.ValidationError(
                    name='csr',
                    error=_("DN commonName does not match user's login"))

            # check email address
            #
            # fail if any email addr from DN does not appear in ldap entry
            email_addrs = csr_obj.subject.get_attributes_for_oid(
                cryptography.x509.oid.NameOID.EMAIL_ADDRESS)
            if len(set(email_addrs) - set(principal_obj.get('mail', []))) > 0:
                raise errors.ValidationError(
                    name='csr',
                    error=_("DN emailAddress does not match "
                            "any of user's email addresses"))

        # We got this far so the principal entry exists, can we write it?
        if not ldap.can_write(dn, "usercertificate"):
            raise errors.ACIError(
                info=_("Insufficient 'write' privilege "
                       "to the 'userCertificate' attribute of entry '%s'.") %
                dn)

        # Validate the subject alt name, if any
        generalnames = []
        if ext_san is not None:
            generalnames = x509.process_othernames(ext_san.value)
        for gn in generalnames:
            if isinstance(gn, cryptography.x509.general_name.DNSName):
                name = gn.value
                alt_principal = None
                alt_principal_obj = None
                try:
                    if principal_type == HOST:
                        alt_principal = kerberos.Principal((u'host', name),
                                                           principal.realm)
                        alt_principal_obj = api.Command['host_show'](name,
                                                                     all=True)
                    elif principal_type == SERVICE:
                        alt_principal = kerberos.Principal(
                            (principal.service_name, name), principal.realm)
                        alt_principal_obj = api.Command['service_show'](
                            alt_principal, all=True)
                    elif principal_type == USER:
                        raise errors.ValidationError(
                            name='csr',
                            error=_("subject alt name type %s is forbidden "
                                    "for user principals") % "DNSName")
                except errors.NotFound:
                    # We don't want to issue any certificates referencing
                    # machines we don't know about. Nothing is stored in this
                    # host record related to this certificate.
                    raise errors.NotFound(reason=_(
                        'The service principal for '
                        'subject alt name %s in certificate request does not '
                        'exist') % name)
                if alt_principal_obj is not None:
                    altdn = alt_principal_obj['result']['dn']
                    if not ldap.can_write(altdn, "usercertificate"):
                        raise errors.ACIError(info=_(
                            "Insufficient privilege to create a certificate "
                            "with subject alt name '%s'.") % name)
                if alt_principal is not None and not bypass_caacl:
                    caacl_check(principal_type, alt_principal, ca, profile_id)
            elif isinstance(gn, (x509.KRB5PrincipalName, x509.UPN)):
                if gn.name != principal_string:
                    raise errors.ACIError(
                        info=_("Principal '%s' in subject alt name does not "
                               "match requested principal") % gn.name)
            elif isinstance(gn, cryptography.x509.general_name.RFC822Name):
                if principal_type == USER:
                    if gn.value not in principal_obj.get('mail', []):
                        raise errors.ValidationError(
                            name='csr',
                            error=_("RFC822Name does not match "
                                    "any of user's email addresses"))
                else:
                    raise errors.ValidationError(
                        name='csr',
                        error=_("subject alt name type %s is forbidden "
                                "for non-user principals") % "RFC822Name")
            else:
                raise errors.ACIError(
                    info=_("Subject alt name type %s is forbidden") %
                    type(gn).__name__)

        # Request the certificate
        try:
            # re-serialise to PEM, in case the user-supplied data has
            # extraneous material that will cause Dogtag to freak out
            csr_pem = csr_obj.public_bytes(serialization.Encoding.PEM)
            result = self.Backend.ra.request_certificate(
                csr_pem, profile_id, ca_id, request_type=request_type)
        except errors.HTTPRequestError as e:
            if e.status == 409:  # pylint: disable=no-member
                raise errors.CertificateOperationError(
                    error=_("CA '%s' is disabled") % ca)
            else:
                raise e

        if not raw:
            self.obj._parse(result, all)
            result['request_id'] = int(result['request_id'])
            result['cacn'] = ca_obj['cn'][0]

        # Success? Then add it to the principal's entry
        # (unless the profile tells us not to)
        profile = api.Command['certprofile_show'](profile_id)
        store = profile['result']['ipacertprofilestoreissued'][0] == 'TRUE'
        if store and 'certificate' in result:
            cert = str(result.get('certificate'))
            kwargs = dict(addattr=u'usercertificate={}'.format(cert))
            if principal_type == SERVICE:
                api.Command['service_mod'](principal_string, **kwargs)
            elif principal_type == HOST:
                api.Command['host_mod'](principal.hostname, **kwargs)
            elif principal_type == USER:
                api.Command['user_mod'](principal.username, **kwargs)

        return dict(
            result=result,
            value=pkey_to_value(int(result['request_id']), kw),
        )
Example #31
0
class cert_request(VirtualCommand):
    __doc__ = _('Submit a certificate signing request.')

    takes_args = (File(
        'csr',
        validate_csr,
        label=_('CSR'),
        cli_name='csr_file',
        normalizer=normalize_csr,
    ), )
    operation = "request certificate"

    takes_options = (
        Str(
            'principal',
            label=_('Principal'),
            doc=
            _('Service principal for this certificate (e.g. HTTP/test.example.com)'
              ),
        ),
        Str(
            'request_type',
            default=u'pkcs10',
            autofill=True,
        ),
        Flag('add',
             doc=_("automatically add the principal if it doesn't exist"),
             default=False,
             autofill=True),
    )

    has_output_params = (
        Str(
            'certificate',
            label=_('Certificate'),
        ),
        Str(
            'subject',
            label=_('Subject'),
        ),
        Str(
            'issuer',
            label=_('Issuer'),
        ),
        Str(
            'valid_not_before',
            label=_('Not Before'),
        ),
        Str(
            'valid_not_after',
            label=_('Not After'),
        ),
        Str(
            'md5_fingerprint',
            label=_('Fingerprint (MD5)'),
        ),
        Str(
            'sha1_fingerprint',
            label=_('Fingerprint (SHA1)'),
        ),
        Str(
            'serial_number',
            label=_('Serial number'),
        ),
        Str(
            'serial_number_hex',
            label=_('Serial number (hex)'),
        ),
    )

    has_output = (Output(
        'result',
        type=dict,
        doc=_('Dictionary mapping variable name to value'),
    ), )

    def execute(self, csr, **kw):
        ldap = self.api.Backend.ldap2
        principal = kw.get('principal')
        add = kw.get('add')
        del kw['principal']
        del kw['add']
        service = None
        """
        Access control is partially handled by the ACI titled
        'Hosts can modify service userCertificate'. This is for the case
        where a machine binds using a host/ prinicpal. It can only do the
        request if the target hostname is in the managedBy attribute which
        is managed using the add/del member commands.

        Binding with a user principal one needs to be in the request_certs
        taskgroup (directly or indirectly via role membership).
        """

        bind_principal = getattr(context, 'principal')
        # Can this user request certs?
        if not bind_principal.startswith('host/'):
            self.check_access()

        # FIXME: add support for subject alt name

        # Ensure that the hostname in the CSR matches the principal
        subject_host = get_csr_hostname(csr)
        (servicename, hostname, realm) = split_principal(principal)
        if subject_host.lower() != hostname.lower():
            raise errors.ACIError(
                info=_("hostname in subject of request '%(subject_host)s' "
                       "does not match principal hostname '%(hostname)s'") %
                dict(subject_host=subject_host, hostname=hostname))

        dn = None
        service = None
        # See if the service exists and punt if it doesn't and we aren't
        # going to add it
        try:
            if not principal.startswith('host/'):
                service = api.Command['service_show'](principal,
                                                      all=True,
                                                      raw=True)['result']
                dn = service['dn']
            else:
                hostname = get_host_from_principal(principal)
                service = api.Command['host_show'](hostname,
                                                   all=True,
                                                   raw=True)['result']
                dn = service['dn']
        except errors.NotFound, e:
            if not add:
                raise errors.NotFound(reason=_("The service principal for "
                                               "this request doesn't exist."))
            try:
                service = api.Command['service_add'](principal, **{
                    'force': True
                })['result']
                dn = service['dn']
            except errors.ACIError:
                raise errors.ACIError(
                    info=_('You need to be a member of '
                           'the serviceadmin role to add services'))

        # We got this far so the service entry exists, can we write it?
        if not ldap.can_write(dn, "usercertificate"):
            raise errors.ACIError(
                info=_("Insufficient 'write' privilege "
                       "to the 'userCertificate' attribute of entry '%s'.") %
                dn)

        # Validate the subject alt name, if any
        request = pkcs10.load_certificate_request(csr)
        subjectaltname = pkcs10.get_subjectaltname(request)
        if subjectaltname is not None:
            for name in subjectaltname:
                name = unicode(name)
                try:
                    hostentry = api.Command['host_show'](name,
                                                         all=True,
                                                         raw=True)['result']
                    hostdn = hostentry['dn']
                except errors.NotFound:
                    # We don't want to issue any certificates referencing
                    # machines we don't know about. Nothing is stored in this
                    # host record related to this certificate.
                    raise errors.NotFound(reason=_(
                        'no host record for '
                        'subject alt name %s in certificate request') % name)
                authprincipal = getattr(context, 'principal')
                if authprincipal.startswith("host/"):
                    if not hostdn in service.get('managedby', []):
                        raise errors.ACIError(info=_(
                            "Insufficient privilege to create a certificate "
                            "with subject alt name '%s'.") % name)

        if 'usercertificate' in service:
            serial = x509.get_serial_number(service['usercertificate'][0],
                                            datatype=x509.DER)
            # revoke the certificate and remove it from the service
            # entry before proceeding. First we retrieve the certificate to
            # see if it is already revoked, if not then we revoke it.
            try:
                result = api.Command['cert_show'](unicode(serial))['result']
                if 'revocation_reason' not in result:
                    try:
                        api.Command['cert_revoke'](unicode(serial),
                                                   revocation_reason=4)
                    except errors.NotImplementedError:
                        # some CA's might not implement revoke
                        pass
            except errors.NotImplementedError:
                # some CA's might not implement get
                pass
            if not principal.startswith('host/'):
                api.Command['service_mod'](principal, usercertificate=None)
            else:
                hostname = get_host_from_principal(principal)
                api.Command['host_mod'](hostname, usercertificate=None)

        # Request the certificate
        result = self.Backend.ra.request_certificate(csr, **kw)
        cert = x509.load_certificate(result['certificate'])
        result['issuer'] = unicode(cert.issuer)
        result['valid_not_before'] = unicode(cert.valid_not_before_str)
        result['valid_not_after'] = unicode(cert.valid_not_after_str)
        result['md5_fingerprint'] = unicode(
            nss.data_to_hex(nss.md5_digest(cert.der_data), 64)[0])
        result['sha1_fingerprint'] = unicode(
            nss.data_to_hex(nss.sha1_digest(cert.der_data), 64)[0])

        # Success? Then add it to the service entry.
        if 'certificate' in result:
            if not principal.startswith('host/'):
                skw = {"usercertificate": str(result.get('certificate'))}
                api.Command['service_mod'](principal, **skw)
            else:
                hostname = get_host_from_principal(principal)
                skw = {"usercertificate": str(result.get('certificate'))}
                api.Command['host_mod'](hostname, **skw)

        return dict(result=result)