Example #1
0
    def submit_recovery_request(self, key_id):
        """
        :param key_id: identifier of data to be recovered

        Create a recovery request for a passphrase or symmetric key

        The command returns a dict as described in the comments to
        parse_key_request_info_xml().  This data includes the request_id
        of the created recovery request
        """
        self.debug('%s.submit_recovery_request()', self.fullname)

        # check clientID and security data
        if key_id is None:
            raise CertificateOperationError(
                error=_('Bad argument to archive_security_data'))

        request = self.create_recovery_request(key_id, None, None, None)

        # Call CMS
        http_status, http_reason_phrase, _http_headers, http_body = \
            self._request('/kra/rest/agent/keyrequests/recover',
                          self.kra_agent_port,
                          self.POST,
                          etree.tostring(request.getroot(), encoding='UTF-8'))

        # Parse and handle errors
        if (http_status != 200):
            raise CertificateOperationError(
                error=_('Error in archiving request (%s)') %
                http_reason_phrase)

        parse_result = self.get_parse_result_xml(http_body,
                                                 parse_key_request_info_xml)
        return parse_result
    def test_ca_connection_cert_not_found(self, mock_load_cert,
                                          mock_ca_subject):
        """CA connectivity check for a cert that doesn't exist"""
        m_api.Command.cert_show.reset_mock()
        m_api.Command.config_show.side_effect = subject_base
        m_api.Command.cert_show.side_effect = CertificateOperationError(
            message='Certificate operation cannot be completed: '
                    'EXCEPTION (Certificate serial number 0x0 not found)'
        )
        mock_load_cert.return_value = [IPACertificate()]
        mock_ca_subject.return_value = DN(('cn', 'Certificate Authority'),
                                          f'O={m_api.env.realm}')

        framework = object()
        registry.initialize(framework, config.Config)
        f = DogtagCertsConnectivityCheck(registry)

        self.results = capture_results(f)

        assert len(self.results) == 1

        result = self.results.results[0]
        assert result.result == constants.ERROR
        assert result.source == 'ipahealthcheck.dogtag.ca'
        assert result.check == 'DogtagCertsConnectivityCheck'
        assert result.kw.get('key') == 'cert_show_1'
        assert result.kw.get('serial') == '1'
        assert result.kw.get('msg') == 'Serial number not found: {error}'
    def test_ca_connection_down(self, mock_load_cert, mock_ca_subject):
        """CA connectivity check with the CA down"""
        m_api.Command.cert_show.side_effect = CertificateOperationError(
            message='Certificate operation cannot be completed: '
                    'Unable to communicate with CMS (503)'
        )
        m_api.Command.config_show.side_effect = subject_base
        mock_load_cert.return_value = [IPACertificate()]
        mock_ca_subject.return_value = DN(('cn', 'Certificate Authority'),
                                          f'O={m_api.env.realm}')

        framework = object()
        registry.initialize(framework, config.Config)
        f = DogtagCertsConnectivityCheck(registry)

        self.results = capture_results(f)

        assert len(self.results) == 1

        result = self.results.results[0]
        assert result.result == constants.ERROR
        assert result.source == 'ipahealthcheck.dogtag.ca'
        assert result.check == 'DogtagCertsConnectivityCheck'
        assert result.kw.get('msg') == (
            'Request for certificate failed: {error}'
        )
Example #4
0
    def __init__(self, work_dir, kra_host, kra_port, kra_nickname):
        # crypto
        self.sec_dir = work_dir
        self.pwd_file = work_dir + "/pwdfile.txt"
        self.transport_cert_nickname = kra_nickname
        self.mechanism = nss.CKM_DES3_CBC_PAD
        try:
            with open(self.pwd_file, "r") as f:
                self.password = f.readline().strip()
        except IOError:
            self.password = ''

        # set up key db for crypto functions
        try:
            nss.nss_init(self.sec_dir)
        except Exception as e:
            raise CertificateOperationError(
                error=_('Error in initializing certdb (%s)') + e.strerror)
        self.transport_cert = nss.find_cert_from_nickname(
            self.transport_cert_nickname)

        # DRM info
        self.kra_host = kra_host
        self.kra_agent_port = kra_port
        '''super(kra, self).__init__()'''
Example #5
0
    def get_transport_cert(self, etag=None):
        """
        :param etag:    etag info for last cert retrieval from DRM

        Gets the transport certificate from the DRM

        The command returns a dict as defined in parse_certificate_data_xml()
        """
        self.debug('%s.get_transport_cert()', self.fullname)

        # Call CMS
        http_status, http_reason_phrase, http_headers, http_body = \
            self._request('/kra/rest/config/cert/transport',
                          self.kra_agent_port,
                          self.GET,
                          None)

        self.debug("headers: %s", http_headers)
        # Parse and handle errors
        if (http_status != 200):
            raise CertificateOperationError(
                error=_('Error in archiving request (%s)') %
                http_reason_phrase)

        parse_result = self.get_parse_result_xml(http_body,
                                                 parse_certificate_data_xml)
        return parse_result
Example #6
0
    def issue_server_cert(self, certreq_fname, cert_fname):
        self.setup_cert_request()

        if self.host_name is None:
            raise RuntimeError("CA Host is not set.")

        f = open(certreq_fname, "r")
        csr = f.readlines()
        f.close()
        csr = "".join(csr)

        # We just want the CSR bits, make sure there is nothing else
        csr = pkcs10.strip_header(csr)

        params = {
            'profileId': dogtag.DEFAULT_PROFILE,
            'cert_request_type': 'pkcs10',
            'requestor_name': 'IPA Installer',
            'cert_request': csr,
            'xmlOutput': 'true'
        }

        # Send the request to the CA
        f = open(self.passwd_fname, "r")
        password = f.readline()
        f.close()
        result = dogtag.https_request(self.host_name, 8443,
                                      "/ca/ee/ca/profileSubmitSSLClient",
                                      self.secdir, password, "ipaCert",
                                      **params)
        http_status, _http_headers, http_body = result
        root_logger.debug("CA answer: %s", http_body)

        if http_status != 200:
            raise CertificateOperationError(
                error=_('Unable to communicate with CMS (status %d)') %
                http_status)

        # The result is an XML blob. Pull the certificate out of that
        doc = xml.dom.minidom.parseString(http_body)
        item_node = doc.getElementsByTagName("b64")
        try:
            try:
                cert = item_node[0].childNodes[0].data
            except IndexError:
                raise RuntimeError("Certificate issuance failed")
        finally:
            doc.unlink()

        # base64-decode the result for uniformity
        cert = base64.b64decode(cert)

        # Write the certificate to a file. It will be imported in a later
        # step. This file will be read later to be imported.
        f = open(cert_fname, "w")
        f.write(cert)
        f.close()
Example #7
0
    def archive_security_data(self, client_id, security_data, data_type):
        """
        :param client_id:     identifier to be used for this stored key
        :param security_data: data blob (PKIArchiveOptions) containing passphrase
                              or symmetric key to be archived
        :param data_type:     data type (symmetricKey, pass_phrase, asymmetricKey)

        Archives security data packaged in a PKIArchiveOptions blob

        The command returns a dict with key/value pairs as defined in
        parse_key_request_info_xml().  These include the request_id of the created
        archival request, the status of the request, and the key_id of the archived
        key.
        """
        self.debug('%s.archive_security_data()', self.fullname)

        # check clientID and security data
        if ((client_id is None) or (security_data is None)):
            raise CertificateOperationError(
                error=_('Bad arguments to archive_security_data'))

        request = self.create_archival_request(
            client_id,
            security_data,
            data_type)

        # Call CMS
        http_status, http_reason_phrase, _http_headers, http_body = \
            self._request('/kra/rest/agent/keyrequests/archive',
                          self.kra_agent_port,
                          self.POST,
                          etree.tostring(request.getroot(), encoding='UTF-8'))

        # Parse and handle errors
        if (http_status != 200):
            raise CertificateOperationError(error=_('Error in archiving request (%s)') %
                                            http_reason_phrase)

        parse_result = self.get_parse_result_xml(
            http_body,
            parse_key_request_info_xml)
        return parse_result
Example #8
0
    def issue_server_cert(self, certreq_fname, cert_fname):
        self.setup_cert_request()

        if self.host_name is None:
            raise RuntimeError("CA Host is not set.")

        with open(certreq_fname, "rb") as f:
            csr = f.read()

        # We just want the CSR bits, make sure there is no thing else
        csr = strip_csr_header(csr).decode('utf8')

        params = {
            'profileId': dogtag.DEFAULT_PROFILE,
            'cert_request_type': 'pkcs10',
            'requestor_name': 'IPA Installer',
            'cert_request': csr,
            'xmlOutput': 'true'
        }

        # Send the request to the CA
        result = dogtag.https_request(self.host_name,
                                      8443,
                                      url="/ca/ee/ca/profileSubmitSSLClient",
                                      cafile=api.env.tls_ca_cert,
                                      client_certfile=paths.RA_AGENT_PEM,
                                      client_keyfile=paths.RA_AGENT_KEY,
                                      **params)
        http_status, _http_headers, http_body = result
        logger.debug("CA answer: %r", http_body)

        if http_status != 200:
            raise CertificateOperationError(
                error=_('Unable to communicate with CMS (status %d)') %
                http_status)

        # The result is an XML blob. Pull the certificate out of that
        doc = xml.dom.minidom.parseString(http_body)
        item_node = doc.getElementsByTagName("b64")
        try:
            try:
                cert = item_node[0].childNodes[0].data
            except IndexError:
                raise RuntimeError("Certificate issuance failed")
        finally:
            doc.unlink()

        # base64-decode the result for uniformity
        cert = base64.b64decode(cert)

        # Write the certificate to a file. It will be imported in a later
        # step. This file will be read later to be imported.
        with open(cert_fname, "wb") as f:
            f.write(cert)
Example #9
0
    def cancel_recovery_request(self, request_id):
        """
        :param recovery_request_id:  identifier of key recovery request

        Cancel recovery request
        """
        self.debug('%s.cancel_recovery_request()', self.fullname)
        if request_id is None:
            raise CertificateOperationError(
                error=_('Bad argument to cancel_recovery_request'))

        # Call CMS
        http_status, http_reason_phrase, _http_headers, _http_body = \
            self._request('/kra/rest/agent/keyrequests/' + request_id + '/cancel',
                          self.kra_agent_port,
                          self.POST,
                          None)

        # Parse and handle errors
        if (http_status > 399):
            raise CertificateOperationError(error=_('Error in cancelling request (%s)') %
                                            http_reason_phrase)
Example #10
0
    def list_security_data(self, client_id, key_state=None, next_id=None):
        """
        :param client_id:     identifier to be searched for
        :param key_state:     state for key (active, inactive, all)
        :param next_id:       id for starting key on next page (if more than one page)

        List security data matching the specified client id and state

        The command returns a dict as specified in parse_key_data_infos_xml().
        """
        self.debug('%s.list_security_data()', self.fullname)
        if client_id is None:
            raise CertificateOperationError(
                error=_('Bad argument to list_security_data'))
        get_args = "clientID=" + quote_plus(client_id)

        if key_state is not None:
            get_args = get_args + "&status=" + quote_plus(key_state)

        if next_id is not None:
            # currnently not implemented on server
            get_args = get_args + "&start=" + quote_plus(next_id)

        # Call CMS
        http_status, http_reason_phrase, _http_headers, http_body = \
            self._request('/kra/rest/agent/keys',
                          self.kra_agent_port,
                          self.GET,
                          get_args)

        # Parse and handle errors
        if (http_status != 200):
            raise CertificateOperationError(error=_('Error in listing keys (%s)') %
                                            http_reason_phrase)

        parse_result = self.get_parse_result_xml(
            http_body,
            parse_key_data_infos_xml)
        return parse_result
Example #11
0
    def list_key_requests(self,
                          request_state=None,
                          request_type=None,
                          client_id=None,
                          next_id=None):
        """
        :param request_state:  state of request (pending, complete, cancelled, rejected, approved)
        :param request_type:   request type (enrollment, recovery)
        :param next_id:       id for starting key on next page (if more than one page)

        List security data matching the specified client id and state

        The command returns a dict as specified in parse_key_request_infos_xml().
        """
        self.debug('%s.list_key_requests()', self.fullname)
        get_args = ""

        if request_state is not None:
            get_args = get_args + "&requestState=" + quote_plus(request_state)

        if request_type is not None:
            get_args = get_args + "&requestType=" + quote_plus(request_type)

        if client_id is not None:
            get_args = get_args + "&clientID=" + quote_plus(client_id)

        if next_id is not None:
            # currnently not implemented on server
            get_args = get_args + "&start=" + quote_plus(next_id)

        # Call CMS
        http_status, http_reason_phrase, _http_headers, http_body = \
            self._request('/kra/rest/agent/keyrequests',
                          self.kra_agent_port,
                          self.GET,
                          get_args)

        # Parse and handle errors
        if (http_status != 200):
            raise CertificateOperationError(
                error=_('Error in listing key requests (%s)') %
                http_reason_phrase)

        parse_result = self.get_parse_result_xml(http_body,
                                                 parse_key_request_infos_xml)
        return parse_result
    def test_ca_connection_down(self):
        """CA connectivity check with the CA down"""
        m_api.Command.cert_show.side_effect = CertificateOperationError(
            message='Certificate operation cannot be completed: '
            'Unable to communicate with CMS (503)')

        framework = object()
        registry.initialize(framework, config.Config)
        f = DogtagCertsConnectivityCheck(registry)

        self.results = capture_results(f)

        assert len(self.results) == 1

        result = self.results.results[0]
        assert result.result == constants.ERROR
        assert result.source == 'ipahealthcheck.dogtag.ca'
        assert result.check == 'DogtagCertsConnectivityCheck'
        assert 'Unable to communicate' in result.kw.get('msg')
    def test_ca_connection_cert_not_found(self):
        """CA connectivity check for a cert that doesn't exist"""
        m_api.Command.cert_show.reset_mock()
        m_api.Command.cert_show.side_effect = CertificateOperationError(
            message='Certificate operation cannot be completed: '
            'EXCEPTION (Certificate serial number 0x0 not found)')

        framework = object()
        registry.initialize(framework, config.Config)
        f = DogtagCertsConnectivityCheck(registry)

        self.results = capture_results(f)

        assert len(self.results) == 1

        result = self.results.results[0]
        assert result.result == constants.SUCCESS
        assert result.source == 'ipahealthcheck.dogtag.ca'
        assert result.check == 'DogtagCertsConnectivityCheck'
Example #14
0
    def retrieve_security_data(self, recovery_request_id, passphrase=None):
        """
        :param recovery_request_id:  identifier of key recovery request
        :param passphrase:           passphrase to be used to wrap the data

        Recover the passphrase or symmetric key.  We require an approved
        recovery request.

        If a passphrase is provided, the DRM will return a blob that can be decrypted
        with the passphrase.  If not, then a symmetric key will be created to wrap the
        data for transport to this server.  Upon receipt, the data will be unwrapped
        and returned unencrypted.

        The command returns a dict with the values described in parse_key_data_xml(),
        as well as the following field

        +-----------------+---------------+-------------------------------------- +
        |result name      |result type    |comments                               |
        +=================+===============+=======================================+
        |data             |String         | Key data (either wrapped using        |
        |                 |               | passphrase or unwrapped)              |
        +-----------------+---------------+---------------------------------------+
        """
        self.debug('%s.retrieve_security_data()', self.fullname)

        if recovery_request_id is None:
            raise CertificateOperationError(
                error=_('Bad arguments to retrieve_security_data'))

        # generate symmetric key
        slot = nss.get_best_slot(self.mechanism)
        session_key = slot.key_gen(self.mechanism, None,
                                   slot.get_best_key_length(self.mechanism))

        # wrap this key with the transport cert
        public_key = self.transport_cert.subject_public_key_info.public_key
        wrapped_session_key = b64encode(
            nss.pub_wrap_sym_key(self.mechanism, public_key, session_key))
        wrapped_passphrase = None
        if passphrase is not None:
            # wrap passphrase with session key
            wrapped_session_key = b64encode(
                self.symmetric_wrap(passphrase, session_key))

        request = self.create_recovery_request(None, recovery_request_id,
                                               wrapped_session_key,
                                               wrapped_passphrase)

        # Call CMS
        http_status, http_reason_phrase, _http_headers, http_body = \
            self._request('/kra/rest/agent/keys/retrieve',
                          self.kra_agent_port,
                          self.POST,
                          etree.tostring(request.getroot(), encoding='UTF-8'))

        # Parse and handle errors
        if (http_status != 200):
            raise CertificateOperationError(
                error=_('Error in retrieving security data (%s)') %
                http_reason_phrase)

        parse_result = self.get_parse_result_xml(http_body, parse_key_data_xml)

        if passphrase is None:
            iv = nss.data_to_hex(b64decode(parse_result['nonce_data']))
            parse_result['data'] = self.symmetric_unwrap(
                b64decode(parse_result['wrapped_data']), session_key, iv)

        return parse_result
Example #15
0
    def issue_server_cert(self, certreq_fname, cert_fname):
        self.setup_cert_request()
        if self.self_signed_ca:
            p = subprocess.Popen([
                "/usr/bin/certutil", "-d", self.secdir, "-C", "-c",
                self.cacert_name, "-i", certreq_fname, "-o", cert_fname, "-m",
                next_serial(), "-v", self.valid_months, "-f",
                self.passwd_fname, "-1", "-5"
            ],
                                 stdin=subprocess.PIPE,
                                 stdout=subprocess.PIPE)

            # Bah - this sucks, but I guess it isn't possible to fully
            # control this with command line arguments.
            #
            # What this is requesting is:
            #  -1 (Create key usage extension)
            #     2 - Key encipherment
            #     9 - done
            #     n - not critical
            #
            #  -5 (Create netscape cert type extension)
            #     1 - SSL Server
            #     9 - done
            #     n - not critical
            p.stdin.write("2\n9\nn\n1\n9\nn\n")
            p.wait()
        else:
            if self.host_name is None:
                raise RuntimeError("CA Host is not set.")

            f = open(certreq_fname, "r")
            csr = f.readlines()
            f.close()
            csr = "".join(csr)

            # We just want the CSR bits, make sure there is nothing else
            csr = pkcs10.strip_header(csr)

            params = {
                'profileId': 'caIPAserviceCert',
                'cert_request_type': 'pkcs10',
                'requestor_name': 'IPA Installer',
                'cert_request': csr,
                'xmlOutput': 'true'
            }

            # Send the request to the CA
            f = open(self.passwd_fname, "r")
            password = f.readline()
            f.close()
            result = dogtag.https_request(
                self.host_name, api.env.ca_ee_install_port
                or dogtag.configured_constants().EE_SECURE_PORT,
                "/ca/ee/ca/profileSubmitSSLClient", self.secdir, password,
                "ipaCert", **params)
            http_status, http_reason_phrase, http_headers, http_body = result

            if http_status != 200:
                raise CertificateOperationError(
                    error=_('Unable to communicate with CMS (%s)') %
                    http_reason_phrase)

            # The result is an XML blob. Pull the certificate out of that
            doc = xml.dom.minidom.parseString(http_body)
            item_node = doc.getElementsByTagName("b64")
            try:
                try:
                    cert = item_node[0].childNodes[0].data
                except IndexError:
                    raise RuntimeError("Certificate issuance failed")
            finally:
                doc.unlink()

            # base64-decode the result for uniformity
            cert = base64.b64decode(cert)

            # Write the certificate to a file. It will be imported in a later
            # step. This file will be read later to be imported.
            f = open(cert_fname, "w")
            f.write(cert)
            f.close()

        return