示例#1
0
    def test_handle_message_loop_with_unexpected_error(self, request_mock):
        """
        Test that the correct logging and error handling occurs when an
        unexpected error is generated while processing a request.
        """
        data = utils.BytearrayStream(())

        kmip_engine = engine.KmipEngine()
        kmip_engine._logger = mock.MagicMock()
        kmip_session = session.KmipSession(kmip_engine, None, 'name')
        kmip_session._engine = mock.MagicMock()
        test_exception = Exception("Unexpected error.")
        kmip_session._engine.process_request = mock.MagicMock(
            side_effect=test_exception)
        kmip_session._logger = mock.MagicMock()
        kmip_session._connection = mock.MagicMock()
        kmip_session._receive_request = mock.MagicMock(return_value=data)
        kmip_session._send_response = mock.MagicMock()

        kmip_session._handle_message_loop()

        kmip_session._receive_request.assert_called_once_with()
        kmip_session._logger.info.assert_not_called()
        kmip_session._logger.warning.assert_called_once_with(
            "An unexpected error occurred while processing request.")
        kmip_session._logger.exception.assert_called_once_with(test_exception)
        self.assertTrue(kmip_session._send_response.called)
示例#2
0
    def test_handle_message_loop_with_response_too_long(self,
                                                        request_mock,
                                                        cert_mock):
        """
        Test that the correct logging and error handling occurs during the
        message handling loop.
        """
        data = utils.BytearrayStream(())

        cert_mock.return_value = 'test_certificate'
        kmip_engine = engine.KmipEngine()
        kmip_session = session.KmipSession(
            kmip_engine,
            None,
            None,
            name='name',
            enable_tls_client_auth=False
        )
        kmip_session.authenticate = mock.MagicMock()
        kmip_session.authenticate.return_value = (
            'test',
            ['group A', 'group B']
        )
        kmip_session._logger = mock.MagicMock()
        kmip_session._connection = mock.MagicMock()
        kmip_session._receive_request = mock.MagicMock(return_value=data)
        kmip_session._send_response = mock.MagicMock()
        kmip_session._max_response_size = 0

        kmip_session._handle_message_loop()

        kmip_session._receive_request.assert_called_once_with()
        self.assertTrue(kmip_session._logger.warning.called)
        kmip_session._logger.exception.assert_not_called()
        self.assertTrue(kmip_session._send_response.called)
示例#3
0
    def test_handle_message_loop_with_parse_failure(self):
        """
        Test that the correct logging and error handling occurs during the
        message handling loop.
        """
        data = utils.BytearrayStream(())

        kmip_engine = engine.KmipEngine()
        kmip_session = session.KmipSession(kmip_engine, None, 'name')
        kmip_session._get_client_identity = mock.MagicMock()
        kmip_session._get_client_identity.return_value = 'test'
        kmip_session._logger = mock.MagicMock()
        kmip_session._connection = mock.MagicMock()
        kmip_session._receive_request = mock.MagicMock(return_value=data)
        kmip_session._send_response = mock.MagicMock()

        kmip_session._handle_message_loop()

        kmip_session._receive_request.assert_called_once_with()
        kmip_session._logger.warning.assert_called_once_with(
            "Failure parsing request message."
        )
        self.assertTrue(kmip_session._logger.exception.called)
        kmip_session._logger.error.assert_not_called()
        self.assertTrue(kmip_session._send_response.called)
示例#4
0
    def test_handle_message_loop_with_unexpected_error(self, request_mock,
                                                       cert_mock):
        """
        Test that the correct logging and error handling occurs when an
        unexpected error is generated while processing a request.
        """
        data = utils.BytearrayStream(())

        cert_mock.return_value = 'test_certificate'
        kmip_engine = engine.KmipEngine()
        kmip_engine._logger = mock.MagicMock()
        kmip_session = session.KmipSession(kmip_engine,
                                           None,
                                           None,
                                           name='name',
                                           enable_tls_client_auth=False)
        kmip_session.authenticate = mock.MagicMock()
        kmip_session.authenticate.return_value = ('test',
                                                  ['group A', 'group B'])
        kmip_session._engine = mock.MagicMock()
        test_exception = Exception("Unexpected error.")
        kmip_session._engine.process_request = mock.MagicMock(
            side_effect=test_exception)
        kmip_session._logger = mock.MagicMock()
        kmip_session._connection = mock.MagicMock()
        kmip_session._receive_request = mock.MagicMock(return_value=data)
        kmip_session._send_response = mock.MagicMock()

        kmip_session._handle_message_loop()

        kmip_session._receive_request.assert_called_once_with()
        kmip_session._logger.warning.assert_called_once_with(
            "An unexpected error occurred while processing request.")
        kmip_session._logger.exception.assert_called_once_with(test_exception)
        self.assertTrue(kmip_session._send_response.called)
示例#5
0
    def test_handle_message_loop_with_authentication_failure(
            self, request_mock, cert_mock):
        """
        Test that the correct logging and error handling occurs when an
        authentication error is generated while processing a request.
        """
        data = utils.BytearrayStream(())

        cert_mock.return_value = 'test_certificate'
        kmip_engine = engine.KmipEngine()
        kmip_engine._logger = mock.MagicMock()
        kmip_session = session.KmipSession(kmip_engine,
                                           None,
                                           None,
                                           name='name',
                                           enable_tls_client_auth=False)
        kmip_session.authenticate = mock.MagicMock()
        kmip_session.authenticate.side_effect = exceptions.PermissionDenied(
            "Authentication failed.")
        kmip_session._engine = mock.MagicMock()
        kmip_session._engine.default_protocol_version = \
            kmip_engine.default_protocol_version
        kmip_session._logger = mock.MagicMock()
        kmip_session._connection = mock.MagicMock()
        kmip_session._receive_request = mock.MagicMock(return_value=data)
        kmip_session._send_response = mock.MagicMock()
        fake_version = contents.ProtocolVersion(1, 2)
        fake_credential = objects.Credential(
            credential_type=enums.CredentialType.USERNAME_AND_PASSWORD,
            credential_value=objects.UsernamePasswordCredential(
                username="******", password="******"))
        fake_header = messages.RequestHeader(
            protocol_version=fake_version,
            authentication=contents.Authentication(
                credentials=[fake_credential]))
        fake_request = messages.RequestMessage()
        fake_request.request_header = fake_header
        fake_request.read = mock.MagicMock()
        request_mock.return_value = fake_request

        kmip_session._handle_message_loop()

        kmip_session._receive_request.assert_called_once_with()
        fake_request.read.assert_called_once_with(
            data, kmip_version=enums.KMIPVersion.KMIP_1_2)
        kmip_session.authenticate.assert_called_once_with(
            "test_certificate", fake_request)
        kmip_session._logger.warning.assert_called_once_with(
            "Authentication failed.")
        kmip_session._engine.build_error_response.assert_called_once_with(
            fake_version, enums.ResultReason.AUTHENTICATION_NOT_SUCCESSFUL,
            "An error occurred during client authentication. "
            "See server logs for more information.")
        kmip_session._logger.exception.assert_not_called()
        self.assertTrue(kmip_session._send_response.called)
示例#6
0
    def test_handle_message_loop(self, request_mock):
        """
        Test that the correct logging and error handling occurs during the
        message handling loop.
        """
        data = utils.BytearrayStream()

        # Build a response and use it as a dummy processing result.
        batch_item = messages.ResponseBatchItem(
            result_status=contents.ResultStatus(enums.ResultStatus.SUCCESS),
            result_reason=contents.ResultReason(
                enums.ResultReason.OBJECT_ARCHIVED),
            result_message=contents.ResultMessage("Test message."))
        batch_items = [batch_item]
        header = messages.ResponseHeader(
            protocol_version=contents.ProtocolVersion(1, 0),
            time_stamp=contents.TimeStamp(int(time.time())),
            batch_count=contents.BatchCount(len(batch_items)))
        message = messages.ResponseMessage(response_header=header,
                                           batch_items=batch_items)

        kmip_engine = engine.KmipEngine()
        kmip_engine._logger = mock.MagicMock()
        kmip_session = session.KmipSession(kmip_engine, None, 'name')
        kmip_session._engine = mock.MagicMock()
        kmip_session._get_client_identity = mock.MagicMock()
        kmip_session._get_client_identity.return_value = 'test'
        kmip_session._engine.process_request = mock.MagicMock(
            return_value=(message, kmip_session._max_response_size))
        kmip_session._logger = mock.MagicMock()
        kmip_session._connection = mock.MagicMock()
        kmip_session._connection.shared_ciphers = mock.MagicMock(
            return_value=[('AES128-SHA256', 'TLSv1/SSLv3',
                           128), ('AES256-SHA256', 'TLSv1/SSLv3', 256)])
        kmip_session._connection.cipher = mock.MagicMock(
            return_value=('AES128-SHA256', 'TLSv1/SSLv3', 128))
        kmip_session._receive_request = mock.MagicMock(return_value=data)
        kmip_session._send_response = mock.MagicMock()

        kmip_session._handle_message_loop()

        kmip_session._receive_request.assert_called_once_with()
        kmip_session._logger.info.assert_not_called()
        kmip_session._logger.debug.assert_any_call(
            "Possible session ciphers: 2")
        kmip_session._logger.debug.assert_any_call(
            ('AES128-SHA256', 'TLSv1/SSLv3', 128))
        kmip_session._logger.debug.assert_any_call(
            ('AES256-SHA256', 'TLSv1/SSLv3', 256))
        kmip_session._logger.debug.assert_any_call(
            "Session cipher selected: {0}".format(
                ('AES128-SHA256', 'TLSv1/SSLv3', 128)))
        kmip_session._logger.warning.assert_not_called()
        kmip_session._logger.exception.assert_not_called()
        self.assertTrue(kmip_session._send_response.called)
示例#7
0
    def test_handle_message_loop_invalid_certificate_extension(self,
                                                               request_mock,
                                                               cert_mock,
                                                               ext_mock):
        """
        Test that the correct logging and error handling occurs when an
        invalid certificate is encountered while processing a request.
        """
        data = utils.BytearrayStream(())

        cert_mock.return_value = 'test_certificate'
        ext_mock.return_value = []
        kmip_engine = engine.KmipEngine()
        kmip_engine._logger = mock.MagicMock()
        kmip_session = session.KmipSession(
            kmip_engine,
            None,
            None,
            name='name',
            enable_tls_client_auth=True
        )
        kmip_session.authenticate = mock.MagicMock()
        kmip_session._engine = mock.MagicMock()
        kmip_session._logger = mock.MagicMock()
        kmip_session._connection = mock.MagicMock()
        kmip_session._receive_request = mock.MagicMock(return_value=data)
        kmip_session._send_response = mock.MagicMock()

        kmip_session._handle_message_loop()

        kmip_session._receive_request.assert_called_once_with()
        kmip_session._logger.warning(
            "Failure verifying the client certificate."
        )
        kmip_session._logger.exception.assert_called_once_with(
            exceptions.PermissionDenied(
                "The extended key usage extension is not marked for client "
                "authentication in the client certificate."
            )
        )
        kmip_session._engine.build_error_response.assert_called_once_with(
            contents.ProtocolVersion(1, 0),
            enums.ResultReason.AUTHENTICATION_NOT_SUCCESSFUL,
            "Error verifying the client certificate. "
            "See server logs for more information."
        )
        self.assertTrue(kmip_session._send_response.called)
示例#8
0
    def test_handle_message_loop_with_response_too_long(self, request_mock):
        """
        Test that the correct logging and error handling occurs during the
        message handling loop.
        """
        data = utils.BytearrayStream(())

        kmip_engine = engine.KmipEngine()
        kmip_session = session.KmipSession(kmip_engine, None, 'name')
        kmip_session._logger = mock.MagicMock()
        kmip_session._connection = mock.MagicMock()
        kmip_session._receive_request = mock.MagicMock(return_value=data)
        kmip_session._send_response = mock.MagicMock()
        kmip_session._max_response_size = 0

        kmip_session._handle_message_loop()

        kmip_session._receive_request.assert_called_once_with()
        kmip_session._logger.info.assert_not_called()
        self.assertTrue(kmip_session._logger.warning.called)
        kmip_session._logger.exception.assert_not_called()
        self.assertTrue(kmip_session._send_response.called)
示例#9
0
    def test_handle_message_loop(self, request_mock):
        """
        Test that the correct logging and error handling occurs during the
        message handling loop.
        """
        data = utils.BytearrayStream()

        # Build a response and use it as a dummy processing result.
        batch_item = messages.ResponseBatchItem(
            result_status=contents.ResultStatus(enums.ResultStatus.SUCCESS),
            result_reason=contents.ResultReason(
                enums.ResultReason.OBJECT_ARCHIVED),
            result_message=contents.ResultMessage("Test message."))
        batch_items = [batch_item]
        header = messages.ResponseHeader(
            protocol_version=contents.ProtocolVersion.create(1, 0),
            time_stamp=contents.TimeStamp(int(time.time())),
            batch_count=contents.BatchCount(len(batch_items)))
        message = messages.ResponseMessage(response_header=header,
                                           batch_items=batch_items)

        kmip_engine = engine.KmipEngine()
        kmip_engine._logger = mock.MagicMock()
        kmip_session = session.KmipSession(kmip_engine, None, 'name')
        kmip_session._engine = mock.MagicMock()
        kmip_session._engine.process_request = mock.MagicMock(
            return_value=(message, kmip_session._max_response_size))
        kmip_session._logger = mock.MagicMock()
        kmip_session._connection = mock.MagicMock()
        kmip_session._receive_request = mock.MagicMock(return_value=data)
        kmip_session._send_response = mock.MagicMock()

        kmip_session._handle_message_loop()

        kmip_session._receive_request.assert_called_once_with()
        kmip_session._logger.info.assert_not_called()
        kmip_session._logger.warning.assert_not_called()
        kmip_session._logger.exception.assert_not_called()
        self.assertTrue(kmip_session._send_response.called)
示例#10
0
    def start(self):
        """
        Prepare the server to start serving connections.

        Configure the server socket handler and establish a TLS wrapping
        socket from which all client connections descend. Bind this TLS
        socket to the specified network address for the server.

        Raises:
            NetworkingError: Raised if the TLS socket cannot be bound to the
                network address.
        """
        self.manager = multiprocessing.Manager()
        self.policies = self.manager.dict()
        policies = copy.deepcopy(operation_policy.policies)
        for policy_name, policy_set in six.iteritems(policies):
            self.policies[policy_name] = policy_set

        self.policy_monitor = monitor.PolicyDirectoryMonitor(
            self.config.settings.get('policy_path'), self.policies,
            self.live_policies)

        def interrupt_handler(trigger, frame):
            self.policy_monitor.stop()

        signal.signal(signal.SIGINT, interrupt_handler)
        signal.signal(signal.SIGTERM, interrupt_handler)

        self.policy_monitor.start()

        self._engine = engine.KmipEngine(
            policies=self.policies,
            database_path=self.config.settings.get('database_path'))

        self._logger.info("Starting server socket handler.")

        # Create a TCP stream socket and configure it for immediate reuse.
        socket.setdefaulttimeout(10)
        self._socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        self._socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)

        self._logger.debug("Configured cipher suites: {0}".format(
            len(self.config.settings.get('tls_cipher_suites'))))
        for cipher in self.config.settings.get('tls_cipher_suites'):
            self._logger.debug(cipher)
        auth_suite_ciphers = self.auth_suite.ciphers.split(':')
        self._logger.debug("Authentication suite ciphers to use: {0}".format(
            len(auth_suite_ciphers)))
        for cipher in auth_suite_ciphers:
            self._logger.debug(cipher)

        self._socket = ssl.wrap_socket(
            self._socket,
            keyfile=self.config.settings.get('key_path'),
            certfile=self.config.settings.get('certificate_path'),
            server_side=True,
            cert_reqs=ssl.CERT_REQUIRED,
            ssl_version=self.auth_suite.protocol,
            ca_certs=self.config.settings.get('ca_path'),
            do_handshake_on_connect=False,
            suppress_ragged_eofs=True,
            ciphers=self.auth_suite.ciphers)

        try:
            self._socket.bind((self.config.settings.get('hostname'),
                               int(self.config.settings.get('port'))))
        except Exception as e:
            self._logger.exception(e)
            raise exceptions.NetworkingError(
                "Server failed to bind socket handler to {0}:{1}".format(
                    self.config.settings.get('hostname'),
                    self.config.settings.get('port')))
        else:
            self._logger.info(
                "Server successfully bound socket handler to {0}:{1}".format(
                    self.config.settings.get('hostname'),
                    self.config.settings.get('port')))
            self._is_serving = True
示例#11
0
    def __init__(
            self,
            hostname=None,
            port=None,
            certificate_path=None,
            key_path=None,
            ca_path=None,
            auth_suite=None,
            config_path='/etc/pykmip/server.conf',
            log_path='/var/log/pykmip/server.log',
            policy_path=None,
            enable_tls_client_auth=None,
            tls_cipher_suites=None
    ):
        """
        Create a KmipServer.

        Settings are loaded initially from the configuration file located at
        config_path, if specified. All other configuration options listed
        below, if specified, will override the settings loaded from the
        configuration file.

        A rotating file logger will be set up with the base log file located
        at log_path. The server itself will handle rotating the log files as
        the logs grow. The server process must have permission to read/write
        to the specified log directory.

        The main KmipEngine request processor is created here, along with all
        information required to manage KMIP client connections and sessions.

        Args:
            hostname (string): The host address the server will be bound to
                (e.g., '127.0.0.1'). Optional, defaults to None.
            port (int): The port number the server will be bound to
                (e.g., 5696). Optional, defaults to None.
            certificate_path (string): The path to the server certificate file
                (e.g., '/etc/pykmip/certs/server.crt'). Optional, defaults to
                None.
            key_path (string): The path to the server certificate key file
                (e.g., '/etc/pykmip/certs/server.key'). Optional, defaults to
                None.
            ca_path (string): The path to the certificate authority (CA)
                certificate file (e.g., '/etc/pykmip/certs/ca.crt'). Optional,
                defaults to None.
            auth_suite (string): A string value indicating the type of
                authentication suite to use for establishing TLS connections.
                Accepted values are: 'Basic', 'TLS1.2'. Optional, defaults to
                None.
            config_path (string): The path to the server configuration file
                (e.g., '/etc/pykmip/server.conf'). Optional, defaults to
                '/etc/pykmip/server.conf'.
            log_path (string): The path to the base server log file
                (e.g., '/var/log/pykmip/server.log'). Optional, defaults to
                '/var/log/pykmip/server.log'.
            policy_path (string): The path to the filesystem directory
                containing PyKMIP server operation policy JSON files.
                Optional, defaults to None.
            enable_tls_client_auth (boolean): A boolean indicating if the TLS
                certificate client auth flag should be required for client
                certificates when establishing a new client session. Optional,
                defaults to None.
            tls_cipher_suites (string): A comma-delimited list of cipher suite
                names (e.g., TLS_RSA_WITH_AES_256_CBC_SHA256,TLS_RSA_WITH_AES_
                128_CBC_SHA256), indicating which specific cipher suites should
                be used by the server when establishing a TLS connection with
                a client. Optional, defaults to None. If None, the default set
                of TLS cipher suites will be used.
        """
        self._logger = logging.getLogger('kmip.server')
        self._setup_logging(log_path)

        self.config = config.KmipServerConfig()
        self._setup_configuration(
            config_path,
            hostname,
            port,
            certificate_path,
            key_path,
            ca_path,
            auth_suite,
            policy_path,
            enable_tls_client_auth,
            tls_cipher_suites
        )

        cipher_suites = self.config.settings.get('tls_cipher_suites')
        if self.config.settings.get('auth_suite') == 'TLS1.2':
            self.auth_suite = auth.TLS12AuthenticationSuite(cipher_suites)
        else:
            self.auth_suite = auth.BasicAuthenticationSuite(cipher_suites)

        self._engine = engine.KmipEngine(
            self.config.settings.get('policy_path')
        )
        self._session_id = 1
        self._is_serving = False
示例#12
0
    def __init__(self,
                 hostname=None,
                 port=None,
                 certificate_path=None,
                 key_path=None,
                 ca_path=None,
                 auth_suite=None,
                 config_path='/etc/pykmip/server.conf',
                 log_path='/var/log/pykmip/server.log'):
        """
        Create a KmipServer.

        Settings are loaded initially from the configuration file located at
        config_path, if specified. All other configuration options listed
        below, if specified, will override the settings loaded from the
        configuration file.

        A rotating file logger will be set up with the base log file located
        at log_path. The server itself will handle rotating the log files as
        the logs grow. The server process must have permission to read/write
        to the specified log directory.

        The main KmipEngine request processor is created here, along with all
        information required to manage KMIP client connections and sessions.

        Args:
            hostname (string): The host address the server will be bound to
                (e.g., '127.0.0.1'). Optional, defaults to None.
            port (int): The port number the server will be bound to
                (e.g., 5696). Optional, defaults to None.
            certificate_path (string): The path to the server certificate file
                (e.g., '/etc/pykmip/certs/server.crt'). Optional, defaults to
                None.
            key_path (string): The path to the server certificate key file
                (e.g., '/etc/pykmip/certs/server.key'). Optional, defaults to
                None.
            ca_path (string): The path to the certificate authority (CA)
                certificate file (e.g., '/etc/pykmip/certs/ca.crt'). Optional,
                defaults to None.
            auth_suite (string): A string value indicating the type of
                authentication suite to use for establishing TLS connections.
                Accepted values are: 'Basic', 'TLS1.2'. Optional, defaults to
                None.
            config_path (string): The path to the server configuration file
                (e.g., '/etc/pykmip/server.conf'). Optional, defaults to
                '/etc/pykmip/server.conf'.
            log_path (string): The path to the base server log file
                (e.g., '/var/log/pykmip/server.log'). Optional, defaults to
                '/var/log/pykmip/server.log'.
        """
        self._logger = logging.getLogger('kmip.server')
        self._setup_logging(log_path)

        self.config = config.KmipServerConfig()
        self._setup_configuration(config_path, hostname, port,
                                  certificate_path, key_path, ca_path,
                                  auth_suite)

        if self.config.settings.get('auth_suite') == 'TLS1.2':
            self.auth_suite = auth.TLS12AuthenticationSuite()
        else:
            self.auth_suite = auth.BasicAuthenticationSuite()

        self._engine = engine.KmipEngine()
        self._session_id = 1
        self._is_serving = False