Beispiel #1
0
    def execute(self, serial_number, **kw):
        ca_enabled_check()

        # Make sure that the cert specified by issuer+serial exists.
        # Will raise NotFound if it does not.
        resp = api.Command.cert_show(unicode(serial_number), cacn=kw['cacn'])

        try:
            self.check_access()
        except errors.ACIError as acierr:
            self.debug(
                "Not granted by ACI to revoke certificate, looking at principal"
            )
            try:
                cert = x509.load_certificate(resp['result']['certificate'])
                if not bind_principal_can_manage_cert(cert):
                    raise acierr
            except errors.NotImplementedError:
                raise acierr
        revocation_reason = kw['revocation_reason']
        if revocation_reason == 7:
            raise errors.CertificateOperationError(
                error=_('7 is not a valid revocation reason'))
        return dict(
            # Dogtag lightweight CAs have shared serial number domain, so
            # we don't tell Dogtag the issuer (but we already checked that
            # the given serial was issued by the named ca).
            result=self.Backend.ra.revoke_certificate(
                str(serial_number), revocation_reason=revocation_reason))
Beispiel #2
0
    def verify_ca_cert_validity(self, nickname):
        cert = self.get_cert(nickname)
        cert = x509.load_certificate(cert, x509.DER)

        if not cert.subject:
            raise ValueError("has empty subject")

        try:
            bc = cert.extensions.get_extension_for_class(
                cryptography.x509.BasicConstraints)
        except cryptography.x509.ExtensionNotFound:
            raise ValueError("missing basic constraints")

        if not bc.value.ca:
            raise ValueError("not a CA certificate")

        try:
            cert.extensions.get_extension_for_class(
                cryptography.x509.SubjectKeyIdentifier)
        except cryptography.x509.ExtensionNotFound:
            raise ValueError("missing subject key identifier extension")

        try:
            self.run_certutil(['-V', '-n', nickname, '-u', 'L'],
                              capture_output=True)
        except ipautil.CalledProcessError as e:
            # certutil output in case of error is
            # 'certutil: certificate is invalid: <ERROR_STRING>\n'
            raise ValueError(e.output)
Beispiel #3
0
    def verify_ca_cert_validity(self, nickname):
        cert = self.get_cert(nickname)
        cert = x509.load_certificate(cert, x509.DER)

        if not cert.subject:
            raise ValueError("has empty subject")

        try:
            bc = cert.extensions.get_extension_for_class(
                    cryptography.x509.BasicConstraints)
        except cryptography.x509.ExtensionNotFound:
            raise ValueError("missing basic constraints")

        if not bc.value.ca:
            raise ValueError("not a CA certificate")

        try:
            cert.extensions.get_extension_for_class(
                    cryptography.x509.SubjectKeyIdentifier)
        except cryptography.x509.ExtensionNotFound:
            raise ValueError("missing subject key identifier extension")

        try:
            self.run_certutil(['-V', '-n', nickname, '-u', 'L'],
                              capture_output=True)
        except ipautil.CalledProcessError as e:
            # certutil output in case of error is
            # 'certutil: certificate is invalid: <ERROR_STRING>\n'
            raise ValueError(e.output)
Beispiel #4
0
    def _get_cert_key(self, cert):
        try:
            cert_obj = x509.load_certificate(cert, x509.DER)
        except ValueError as e:
            message = messages.SearchResultTruncated(
                reason=_("failed to load certificate: %s") % e, )
            self.add_message(message)

            raise

        return (DN(cert_obj.issuer), cert_obj.serial)
Beispiel #5
0
    def execute(self,
                serial_number,
                all=False,
                raw=False,
                no_members=False,
                **options):
        ca_enabled_check()

        # Dogtag lightweight CAs have shared serial number domain, so
        # we don't tell Dogtag the issuer (but we check the cert after).
        #
        result = self.Backend.ra.get_certificate(str(serial_number))
        cert = x509.load_certificate(result['certificate'])

        try:
            self.check_access()
        except errors.ACIError as acierr:
            self.debug(
                "Not granted by ACI to retrieve certificate, looking at principal"
            )
            if not bind_principal_can_manage_cert(cert):
                raise acierr  # pylint: disable=E0702

        ca_obj = api.Command.ca_show(options['cacn'])['result']
        if DN(cert.issuer) != DN(ca_obj['ipacasubjectdn'][0]):
            # DN of cert differs from what we requested
            raise errors.NotFound(
                reason=_("Certificate with serial number %(serial)s "
                         "issued by CA '%(ca)s' not found") %
                dict(serial=serial_number, ca=options['cacn']))

        if all or not no_members:
            ldap = self.api.Backend.ldap2
            filter = ldap.make_filter_from_attr(
                'usercertificate', base64.b64decode(result['certificate']))
            try:
                entries = ldap.get_entries(base_dn=self.api.env.basedn,
                                           filter=filter,
                                           attrs_list=[''])
            except errors.EmptyResult:
                entries = []
            for entry in entries:
                result.setdefault('owner', []).append(entry.dn)

        if not raw:
            result['certificate'] = result['certificate'].replace('\r\n', '')
            self.obj._parse(result, all)
            result['revoked'] = ('revocation_reason' in result)
            self.obj._fill_owners(result)
            result['cacn'] = ca_obj['cn'][0]

        return dict(result=result, value=pkey_to_value(serial_number, options))
Beispiel #6
0
    def verify_server_cert_validity(self, nickname, hostname):
        """Verify a certificate is valid for a SSL server with given hostname

        Raises a ValueError if the certificate is invalid.
        """
        cert = self.get_cert(nickname)
        cert = x509.load_certificate(cert, x509.DER)

        try:
            self.run_certutil(['-V', '-n', nickname, '-u', 'V'])
        except ipautil.CalledProcessError:
            raise ValueError('invalid for a SSL server')

        try:
            x509.match_hostname(cert, hostname)
        except ValueError:
            raise ValueError('invalid for server %s' % hostname)
Beispiel #7
0
    def _parse(self, obj, full=True):
        """Extract certificate-specific data into a result object.

        ``obj``
            Result object containing certificate, into which extracted
            data will be inserted.
        ``full``
            Whether to include all fields, or only the ones we guess
            people want to see most of the time.  Also add
            recognised otherNames to the generic ``san_other``
            attribute when ``True`` in addition to the specialised
            attribute.

        """
        if 'certificate' in obj:
            cert = x509.load_certificate(obj['certificate'])
            obj['subject'] = DN(cert.subject)
            obj['issuer'] = DN(cert.issuer)
            obj['serial_number'] = cert.serial
            obj['valid_not_before'] = x509.format_datetime(
                cert.not_valid_before)
            obj['valid_not_after'] = x509.format_datetime(cert.not_valid_after)
            if full:
                obj['md5_fingerprint'] = x509.to_hex_with_colons(
                    cert.fingerprint(hashes.MD5()))
                obj['sha1_fingerprint'] = x509.to_hex_with_colons(
                    cert.fingerprint(hashes.SHA1()))

            general_names = x509.process_othernames(
                x509.get_san_general_names(cert))

            for gn in general_names:
                try:
                    self._add_san_attribute(obj, full, gn)
                except Exception:
                    # Invalid GeneralName (i.e. not a valid X.509 cert);
                    # don't fail but log something about it
                    root_logger.warning(
                        "Encountered bad GeneralName; skipping", exc_info=True)

        serial_number = obj.get('serial_number')
        if serial_number is not None:
            obj['serial_number_hex'] = u'0x%X' % serial_number
Beispiel #8
0
    def verify_ca_cert_validity(self, nickname):
        cert = self.get_cert(nickname)
        cert = x509.load_certificate(cert, x509.DER)

        if not cert.subject:
            raise ValueError("has empty subject")

        try:
            bc = cert.extensions.get_extension_for_class(
                    cryptography.x509.BasicConstraints)
        except cryptography.x509.ExtensionNotFound:
            raise ValueError("missing basic constraints")

        if not bc.value.ca:
            raise ValueError("not a CA certificate")

        try:
            self.run_certutil(['-V', '-n', nickname, '-u', 'L'])
        except ipautil.CalledProcessError:
            raise ValueError('invalid for a CA')
Beispiel #9
0
    def verify_server_cert_validity(self, nickname, hostname):
        """Verify a certificate is valid for a SSL server with given hostname

        Raises a ValueError if the certificate is invalid.
        """
        cert = self.get_cert(nickname)
        cert = x509.load_certificate(cert, x509.DER)

        try:
            self.run_certutil(['-V', '-n', nickname, '-u', 'V'],
                              capture_output=True)
        except ipautil.CalledProcessError as e:
            # certutil output in case of error is
            # 'certutil: certificate is invalid: <ERROR_STRING>\n'
            raise ValueError(e.output)

        try:
            x509.match_hostname(cert, hostname)
        except ValueError:
            raise ValueError('invalid for server %s' % hostname)
Beispiel #10
0
    def verify_server_cert_validity(self, nickname, hostname):
        """Verify a certificate is valid for a SSL server with given hostname

        Raises a ValueError if the certificate is invalid.
        """
        cert = self.get_cert(nickname)
        cert = x509.load_certificate(cert, x509.DER)

        try:
            self.run_certutil(['-V', '-n', nickname, '-u', 'V'],
                              capture_output=True)
        except ipautil.CalledProcessError as e:
            # certutil output in case of error is
            # 'certutil: certificate is invalid: <ERROR_STRING>\n'
            raise ValueError(e.output)

        try:
            x509.match_hostname(cert, hostname)
        except ValueError:
            raise ValueError('invalid for server %s' % hostname)
Beispiel #11
0
    def import_files(self, files, import_keys=False, key_password=None,
                     key_nickname=None):
        """
        Import certificates and a single private key from multiple files

        The files may be in PEM and DER certificate, PKCS#7 certificate chain,
        PKCS#8 and raw private key and PKCS#12 formats.

        :param files: Names of files to import
        :param import_keys: Whether to import private keys
        :param key_password: Password to decrypt private keys
        :param key_nickname: Nickname of the private key to import from PKCS#12
            files
        """
        key_file = None
        extracted_key = None
        extracted_certs = []

        for filename in files:
            try:
                with open(filename, 'rb') as f:
                    data = f.read()
            except IOError as e:
                raise RuntimeError(
                    "Failed to open %s: %s" % (filename, e.strerror))

            # Try to parse the file as PEM file
            matches = list(re.finditer(
                r'-----BEGIN (.+?)-----(.*?)-----END \1-----', data, re.DOTALL))
            if matches:
                loaded = False
                for match in matches:
                    body = match.group()
                    label = match.group(1)
                    line = len(data[:match.start() + 1].splitlines())

                    if label in ('CERTIFICATE', 'X509 CERTIFICATE',
                                 'X.509 CERTIFICATE'):
                        try:
                            x509.load_certificate(match.group(2))
                        except ValueError as e:
                            if label != 'CERTIFICATE':
                                root_logger.warning(
                                    "Skipping certificate in %s at line %s: %s",
                                    filename, line, e)
                                continue
                        else:
                            extracted_certs.append(body)
                            loaded = True
                            continue

                    if label in ('PKCS7', 'PKCS #7 SIGNED DATA', 'CERTIFICATE'):
                        try:
                            certs = x509.pkcs7_to_pems(body)
                        except ipautil.CalledProcessError as e:
                            if label == 'CERTIFICATE':
                                root_logger.warning(
                                    "Skipping certificate in %s at line %s: %s",
                                    filename, line, e)
                            else:
                                root_logger.warning(
                                    "Skipping PKCS#7 in %s at line %s: %s",
                                    filename, line, e)
                            continue
                        else:
                            extracted_certs.extend(certs)
                            loaded = True
                            continue

                    if label in ('PRIVATE KEY', 'ENCRYPTED PRIVATE KEY',
                                 'RSA PRIVATE KEY', 'DSA PRIVATE KEY',
                                 'EC PRIVATE KEY'):
                        if not import_keys:
                            continue

                        if key_file:
                            raise RuntimeError(
                                "Can't load private key from both %s and %s" %
                                (key_file, filename))

                        args = [
                            OPENSSL, 'pkcs8',
                            '-topk8',
                            '-passout', 'file:' + self.pwd_file,
                        ]
                        if ((label != 'PRIVATE KEY' and key_password) or
                            label == 'ENCRYPTED PRIVATE KEY'):
                            key_pwdfile = ipautil.write_tmp_file(key_password)
                            args += [
                                '-passin', 'file:' + key_pwdfile.name,
                            ]
                        try:
                            result = ipautil.run(
                                args, stdin=body, capture_output=True)
                        except ipautil.CalledProcessError as e:
                            root_logger.warning(
                                "Skipping private key in %s at line %s: %s",
                                filename, line, e)
                            continue
                        else:
                            extracted_key = result.output
                            key_file = filename
                            loaded = True
                            continue
                if loaded:
                    continue
                raise RuntimeError("Failed to load %s" % filename)

            # Try to load the file as DER certificate
            try:
                x509.load_certificate(data, x509.DER)
            except ValueError:
                pass
            else:
                data = x509.make_pem(base64.b64encode(data))
                extracted_certs.append(data)
                continue

            # Try to import the file as PKCS#12 file
            if import_keys:
                try:
                    self.import_pkcs12(filename, key_password)
                except RuntimeError:
                    pass
                else:
                    if key_file:
                        raise RuntimeError(
                            "Can't load private key from both %s and %s" %
                            (key_file, filename))
                    key_file = filename

                    server_certs = self.find_server_certs()
                    if key_nickname:
                        for nickname, _trust_flags in server_certs:
                            if nickname == key_nickname:
                                break
                        else:
                            raise RuntimeError(
                                "Server certificate \"%s\" not found in %s" %
                                (key_nickname, filename))
                    else:
                        if len(server_certs) > 1:
                            raise RuntimeError(
                                "%s server certificates found in %s, "
                                "expecting only one" %
                                (len(server_certs), filename))

                    continue

            raise RuntimeError("Failed to load %s" % filename)

        if import_keys and not key_file:
            raise RuntimeError(
                "No server certificates found in %s" % (', '.join(files)))

        for cert_pem in extracted_certs:
            cert = x509.load_certificate(cert_pem)
            nickname = str(DN(cert.subject))
            data = cert.public_bytes(serialization.Encoding.DER)
            self.add_cert(data, nickname, ',,')

        if extracted_key:
            in_file = ipautil.write_tmp_file(
                    '\n'.join(extracted_certs) + '\n' + extracted_key)
            out_file = tempfile.NamedTemporaryFile()
            out_password = ipautil.ipa_generate_password()
            out_pwdfile = ipautil.write_tmp_file(out_password)
            args = [
                OPENSSL, 'pkcs12',
                '-export',
                '-in', in_file.name,
                '-out', out_file.name,
                '-passin', 'file:' + self.pwd_file,
                '-passout', 'file:' + out_pwdfile.name,
            ]
            try:
                ipautil.run(args)
            except ipautil.CalledProcessError as e:
                raise RuntimeError(
                    "No matching certificate found for private key from %s" %
                    key_file)

            self.import_pkcs12(out_file.name, out_password)
Beispiel #12
0
    def verify_kdc_cert_validity(self, nickname, realm):
        nicknames = self.get_trust_chain(nickname)
        certs = [self.get_cert(nickname) for nickname in nicknames]
        certs = [x509.load_certificate(cert, x509.DER) for cert in certs]

        verify_kdc_cert_validity(certs[-1], certs[:-1], realm)
Beispiel #13
0
    def verify_kdc_cert_validity(self, nickname, realm):
        nicknames = self.get_trust_chain(nickname)
        certs = [self.get_cert(nickname) for nickname in nicknames]
        certs = [x509.load_certificate(cert, x509.DER) for cert in certs]

        verify_kdc_cert_validity(certs[-1], certs[:-1], realm)
Beispiel #14
0
    def import_files(self, files, import_keys=False, key_password=None,
                     key_nickname=None):
        """
        Import certificates and a single private key from multiple files

        The files may be in PEM and DER certificate, PKCS#7 certificate chain,
        PKCS#8 and raw private key and PKCS#12 formats.

        :param files: Names of files to import
        :param import_keys: Whether to import private keys
        :param key_password: Password to decrypt private keys
        :param key_nickname: Nickname of the private key to import from PKCS#12
            files
        """
        key_file = None
        extracted_key = None
        extracted_certs = []

        for filename in files:
            try:
                with open(filename, 'rb') as f:
                    data = f.read()
            except IOError as e:
                raise RuntimeError(
                    "Failed to open %s: %s" % (filename, e.strerror))

            # Try to parse the file as PEM file
            matches = list(re.finditer(
                r'-----BEGIN (.+?)-----(.*?)-----END \1-----', data, re.DOTALL))
            if matches:
                loaded = False
                for match in matches:
                    body = match.group()
                    label = match.group(1)
                    line = len(data[:match.start() + 1].splitlines())

                    if label in ('CERTIFICATE', 'X509 CERTIFICATE',
                                 'X.509 CERTIFICATE'):
                        try:
                            x509.load_certificate(match.group(2))
                        except ValueError as e:
                            if label != 'CERTIFICATE':
                                logger.warning(
                                    "Skipping certificate in %s at line %s: "
                                    "%s",
                                    filename, line, e)
                                continue
                        else:
                            extracted_certs.append(body)
                            loaded = True
                            continue

                    if label in ('PKCS7', 'PKCS #7 SIGNED DATA', 'CERTIFICATE'):
                        try:
                            certs = x509.pkcs7_to_pems(body)
                        except ipautil.CalledProcessError as e:
                            if label == 'CERTIFICATE':
                                logger.warning(
                                    "Skipping certificate in %s at line %s: "
                                    "%s",
                                    filename, line, e)
                            else:
                                logger.warning(
                                    "Skipping PKCS#7 in %s at line %s: %s",
                                    filename, line, e)
                            continue
                        else:
                            extracted_certs.extend(certs)
                            loaded = True
                            continue

                    if label in ('PRIVATE KEY', 'ENCRYPTED PRIVATE KEY',
                                 'RSA PRIVATE KEY', 'DSA PRIVATE KEY',
                                 'EC PRIVATE KEY'):
                        if not import_keys:
                            continue

                        if key_file:
                            raise RuntimeError(
                                "Can't load private key from both %s and %s" %
                                (key_file, filename))

                        args = [
                            OPENSSL, 'pkcs8',
                            '-topk8',
                            '-passout', 'file:' + self.pwd_file,
                        ]
                        if ((label != 'PRIVATE KEY' and key_password) or
                            label == 'ENCRYPTED PRIVATE KEY'):
                            key_pwdfile = ipautil.write_tmp_file(key_password)
                            args += [
                                '-passin', 'file:' + key_pwdfile.name,
                            ]
                        try:
                            result = ipautil.run(
                                args, stdin=body, capture_output=True)
                        except ipautil.CalledProcessError as e:
                            logger.warning(
                                "Skipping private key in %s at line %s: %s",
                                filename, line, e)
                            continue
                        else:
                            extracted_key = result.output
                            key_file = filename
                            loaded = True
                            continue
                if loaded:
                    continue
                raise RuntimeError("Failed to load %s" % filename)

            # Try to load the file as DER certificate
            try:
                x509.load_certificate(data, x509.DER)
            except ValueError:
                pass
            else:
                data = x509.make_pem(base64.b64encode(data))
                extracted_certs.append(data)
                continue

            # Try to import the file as PKCS#12 file
            if import_keys:
                try:
                    self.import_pkcs12(filename, key_password)
                except RuntimeError:
                    pass
                else:
                    if key_file:
                        raise RuntimeError(
                            "Can't load private key from both %s and %s" %
                            (key_file, filename))
                    key_file = filename

                    server_certs = self.find_server_certs()
                    if key_nickname:
                        for nickname, _trust_flags in server_certs:
                            if nickname == key_nickname:
                                break
                        else:
                            raise RuntimeError(
                                "Server certificate \"%s\" not found in %s" %
                                (key_nickname, filename))
                    else:
                        if len(server_certs) > 1:
                            raise RuntimeError(
                                "%s server certificates found in %s, "
                                "expecting only one" %
                                (len(server_certs), filename))

                    continue

            raise RuntimeError("Failed to load %s" % filename)

        if import_keys and not key_file:
            raise RuntimeError(
                "No server certificates found in %s" % (', '.join(files)))

        for cert_pem in extracted_certs:
            cert = x509.load_certificate(cert_pem)
            nickname = str(DN(cert.subject))
            data = cert.public_bytes(serialization.Encoding.DER)
            self.add_cert(data, nickname, EMPTY_TRUST_FLAGS)

        if extracted_key:
            in_file = ipautil.write_tmp_file(
                    '\n'.join(extracted_certs) + '\n' + extracted_key)
            out_file = tempfile.NamedTemporaryFile()
            out_password = ipautil.ipa_generate_password()
            out_pwdfile = ipautil.write_tmp_file(out_password)
            args = [
                OPENSSL, 'pkcs12',
                '-export',
                '-in', in_file.name,
                '-out', out_file.name,
                '-passin', 'file:' + self.pwd_file,
                '-passout', 'file:' + out_pwdfile.name,
            ]
            try:
                ipautil.run(args)
            except ipautil.CalledProcessError as e:
                raise RuntimeError(
                    "No matching certificate found for private key from %s" %
                    key_file)

            self.import_pkcs12(out_file.name, out_password)