def _test_session_resumption_rate(self, server_info,
                                      resumption_attempts_nb):
        # type: (ServerConnectivityInfo, int) -> Tuple[int, List[Text]]
        """Attempt several session ID resumption with the server.
        """
        thread_pool = ThreadPool()

        for _ in range(resumption_attempts_nb):
            thread_pool.add_job(
                (self._resume_with_session_id, (server_info, )))
        thread_pool.start(
            nb_threads=min(resumption_attempts_nb, self.MAX_THREADS_NB))

        # Count successful/failed resumptions
        successful_resumptions_nb = 0
        for completed_job in thread_pool.get_result():
            (job, was_resumption_successful) = completed_job
            if was_resumption_successful:
                successful_resumptions_nb += 1

        # Count errors and store error messages
        errored_resumptions_list = []
        for failed_job in thread_pool.get_error():
            (job, exception) = failed_job
            error_msg = u'{} - {}'.format(str(exception.__class__.__name__),
                                          str(exception))
            errored_resumptions_list.append(error_msg)

        thread_pool.join()
        return successful_resumptions_nb, errored_resumptions_list
    def _test_session_resumption_rate(
            self, server_info: ServerConnectivityInfo,
            ssl_version_to_use: OpenSslVersionEnum,
            resumption_attempts_nb: int) -> Tuple[int, List[str]]:
        """Attempt several session ID resumption with the server.
        """
        thread_pool = ThreadPool()

        for _ in range(resumption_attempts_nb):
            thread_pool.add_job((self._resume_with_session_id,
                                 [server_info, ssl_version_to_use]))
        thread_pool.start(
            nb_threads=min(resumption_attempts_nb, self.MAX_THREADS_NB))

        # Count successful/failed resumptions
        successful_resumptions_nb = 0
        for completed_job in thread_pool.get_result():
            (job, was_resumption_successful) = completed_job
            if was_resumption_successful:
                successful_resumptions_nb += 1

        # Count errors and store error messages
        errored_resumptions_list = []
        for failed_job in thread_pool.get_error():
            (job, exception) = failed_job
            error_msg = f'{str(exception.__class__.__name__)} - {str(exception)}'
            errored_resumptions_list.append(error_msg)

        thread_pool.join()
        return successful_resumptions_nb, errored_resumptions_list
Esempio n. 3
0
    def process_task(
            self,
            server_info: ServerConnectivityInfo,
            scan_command: PluginScanCommand
    ) -> 'CertificateInfoScanResult':
        if not isinstance(scan_command, CertificateInfoScanCommand):
            raise ValueError('Unexpected scan command')

        final_trust_store_list = TrustStoresRepository.get_default().get_all_stores()
        if scan_command.custom_ca_file:
            if not os.path.isfile(scan_command.custom_ca_file):
                raise ValueError('Could not open supplied CA file at "{}"'.format(scan_command.custom_ca_file))
            final_trust_store_list.append(TrustStore(scan_command.custom_ca_file, 'Custom --ca_file', 'N/A'))

        # Workaround for https://github.com/pyca/cryptography/issues/3495
        default_backend()

        thread_pool = ThreadPool()
        for trust_store in final_trust_store_list:
            # Try to connect with each trust store
            thread_pool.add_job((self._get_and_verify_certificate_chain, [server_info, trust_store]))

        # Start processing the jobs; one thread per trust
        thread_pool.start(len(final_trust_store_list))

        # Store the results as they come
        certificate_chain: List[Certificate] = []
        path_validation_result_list = []
        path_validation_error_list = []
        ocsp_response = None

        for (job, result) in thread_pool.get_result():
            (_, (_, trust_store)) = job
            certificate_chain, validation_result, _ocsp_response = result

            # Keep the OCSP response if the validation was succesful and a response was returned
            if _ocsp_response:
                ocsp_response = _ocsp_response

            # Store the returned verify string for each trust store
            path_validation_result_list.append(PathValidationResult(trust_store, validation_result))

        # Store thread pool errors
        last_exception = None
        for (job, exception) in thread_pool.get_error():
            (_, (_, trust_store)) = job
            path_validation_error_list.append(PathValidationError(trust_store, exception))
            last_exception = exception

        thread_pool.join()

        if len(path_validation_error_list) == len(final_trust_store_list):
            # All connections failed unexpectedly; raise an exception instead of returning a result
            raise last_exception  # type: ignore

        # All done
        return CertificateInfoScanResult(server_info, scan_command, certificate_chain, path_validation_result_list,
                                         path_validation_error_list, ocsp_response)
    def process_task(self,
                     server_connectivity_info,
                     plugin_command,
                     option_dict=None):
        if option_dict and 'verbose' in option_dict.keys():
            verbose_mode = option_dict['verbose']
        else:
            verbose_mode = False
        ssl3_support = self.test_SSLv3_support(server_connectivity_info)
        support_vulnerable_ciphers = None
        if ssl3_support:
            cipher_list = self.get_ssl3_cipher_list()
            thread_pool = ThreadPool()
            for cipher in cipher_list:
                thread_pool.add_job((self._test_ciphersuite,
                                     (server_connectivity_info, cipher)))
            thread_pool.start(
                nb_threads=min(len(cipher_list), self.MAX_THREADS))

            accept_ciphers = []
            reject_ciphers = []
            if verbose_mode:
                print '  VERBOSE MODE PRINT'
                print '  ------------------'
            for completed_job in thread_pool.get_result():
                (job, cipher_result) = completed_job
                if isinstance(cipher_result, AcceptCipher):
                    accept_ciphers.append(cipher_result)
                elif isinstance(cipher_result, RejectCipher):
                    reject_ciphers.append(cipher_result)
                else:
                    raise ValueError("Unexpected result")
                if verbose_mode:
                    cipher_result.print_cipher()

            if verbose_mode:
                print '  ----------------------'
                print '  END VERBOSE MODE PRINT'
                print '  ----------------------'

            for error_job in thread_pool.get_error():
                (_, exception) = error_job
                raise exception

            thread_pool.join()
            support_vulnerable_ciphers = self.get_vulnerable_ciphers(
                accept_ciphers)

        is_vulnerable = ssl3_support and (
            support_vulnerable_ciphers is not None
            or len(support_vulnerable_ciphers) > 0)

        return POODLEVulnerabilityTesterResult(server_connectivity_info,
                                               plugin_command, option_dict,
                                               ssl3_support, is_vulnerable,
                                               support_vulnerable_ciphers)
    def process_task(self,
                     server_connectivity_info,
                     plugin_command,
                     options_dict=None):
        ssl_version = self.SSL_VERSIONS_MAPPING[plugin_command]

        # Get the list of available cipher suites for the given ssl version
        ssl_client = SslClient(ssl_version=ssl_version)
        ssl_client.set_cipher_list('ALL:COMPLEMENTOFALL')
        cipher_list = ssl_client.get_cipher_list()

        # Scan for every available cipher suite
        thread_pool = ThreadPool()
        for cipher in cipher_list:
            thread_pool.add_job(
                (self._test_cipher_suite, (server_connectivity_info,
                                           ssl_version, cipher)))

        # Start processing the jobs; One thread per cipher
        thread_pool.start(nb_threads=min(len(cipher_list), self.MAX_THREADS))

        accepted_cipher_list = []
        rejected_cipher_list = []
        errored_cipher_list = []

        # Store the results as they come
        for completed_job in thread_pool.get_result():
            (job, cipher_result) = completed_job
            if isinstance(cipher_result, AcceptedCipherSuite):
                accepted_cipher_list.append(cipher_result)
            elif isinstance(cipher_result, RejectedCipherSuite):
                rejected_cipher_list.append(cipher_result)
            elif isinstance(cipher_result, ErroredCipherSuite):
                errored_cipher_list.append(cipher_result)
            else:
                raise ValueError('Unexpected result')

        # Store thread pool errors; only something completely unexpected would trigger an error
        for failed_job in thread_pool.get_error():
            (_, exception) = failed_job
            raise exception

        thread_pool.join()

        # Test for the cipher suite preference
        preferred_cipher = self._get_preferred_cipher_suite(
            server_connectivity_info, ssl_version, accepted_cipher_list)

        # Generate the results
        plugin_result = OpenSSLCipherSuitesResult(server_connectivity_info,
                                                  plugin_command, options_dict,
                                                  preferred_cipher,
                                                  accepted_cipher_list,
                                                  rejected_cipher_list,
                                                  errored_cipher_list)
        return plugin_result
Esempio n. 6
0
    def process_task(self, server_info, command, options_dict=None):

        if command == 'certinfo_basic':
            result_class = CertInfoBasicResult
        elif command == 'certinfo_full':
            result_class = CertInfoFullResult
        else:
            raise ValueError("PluginCertInfo: Unknown command.")

        final_trust_store_list = list(DEFAULT_TRUST_STORE_LIST)
        if options_dict and 'ca_file' in options_dict.keys():
            final_trust_store_list.append(
                TrustStore(options_dict['ca_file'], 'Custom --ca_file', 'N/A'))

        thread_pool = ThreadPool()
        for trust_store in final_trust_store_list:
            # Try to connect with each trust store
            thread_pool.add_job(
                (self._get_certificate_chain, (server_info, trust_store)))

        # Start processing the jobs; one thread per trust
        thread_pool.start(len(final_trust_store_list))

        # Store the results as they come
        certificate_chain = []
        path_validation_result_list = []
        path_validation_error_list = []
        ocsp_response = None

        for (job, result) in thread_pool.get_result():
            (_, (_, trust_store)) = job
            certificate_chain, validation_result, ocsp_response = result
            # Store the returned verify string for each trust store
            path_validation_result_list.append(
                PathValidationResult(trust_store, validation_result))

        # Store thread pool errors
        last_exception = None
        for (job, exception) in thread_pool.get_error():
            (_, (_, trust_store)) = job
            path_validation_error_list.append(
                PathValidationError(trust_store, exception))
            last_exception = exception

        thread_pool.join()

        if len(path_validation_error_list) == len(final_trust_store_list):
            # All connections failed unexpectedly; raise an exception instead of returning a result
            raise RuntimeError(
                'Could not connect to the server; last error: {}'.format(
                    last_exception))

        # All done
        return result_class(server_info, command, options_dict,
                            certificate_chain, path_validation_result_list,
                            path_validation_error_list, ocsp_response)
    def _test_session_resumption_rate(
            self,
            server_info: ServerConnectivityInfo,
            ssl_version_to_use: OpenSslVersionEnum,
            resumption_attempts_nb: int
    ) -> Tuple[int, List[str]]:
        """Attempt several session ID resumption with the server.
        """
        thread_pool = ThreadPool()

        for _ in range(resumption_attempts_nb):
            thread_pool.add_job((self._resume_with_session_id, [server_info, ssl_version_to_use]))
        thread_pool.start(nb_threads=min(resumption_attempts_nb, self.MAX_THREADS_NB))

        # Count successful/failed resumptions
        successful_resumptions_nb = 0
        for completed_job in thread_pool.get_result():
            (job, was_resumption_successful) = completed_job
            if was_resumption_successful:
                successful_resumptions_nb += 1

        # Count errors and store error messages
        errored_resumptions_list = []
        for failed_job in thread_pool.get_error():
            (job, exception) = failed_job
            error_msg = f'{str(exception.__class__.__name__)} - {str(exception)}'
            errored_resumptions_list.append(error_msg)

        thread_pool.join()
        return successful_resumptions_nb, errored_resumptions_list
    def process_task(self, server_connectivity_info, plugin_command, options_dict=None):
        if options_dict and "verbose" in options_dict.keys():
            verbose_mode = options_dict["verbose"]
        else:
            verbose_mode = False
        test_protocols = {"SSLv3", "TLSv1"}
        thread_pool = ThreadPool()
        ciphers_list = []
        support_protocol_list = []
        for proto in test_protocols:
            if self.test_protocol_support(PROTOCOL_VERSION[proto], server_connectivity_info):
                ssl_client = SslClient(ssl_version=PROTOCOL_VERSION[proto])
                ssl_client.set_cipher_list(proto)
                ciphers_list = ssl_client.get_cipher_list()
                for cipher in ciphers_list:
                    thread_pool.add_job(
                        (self._test_ciphersuite, (server_connectivity_info, PROTOCOL_VERSION[proto], cipher))
                    )
                support_protocol_list.append(proto)
        thread_pool.start(nb_threads=min(len(ciphers_list), self.MAX_THREADS))

        accept_ciphers = []
        reject_ciphers = []
        if verbose_mode:
            print "  VERBOSE MODE PRINT"
            print "  ------------------"

        for completed_job in thread_pool.get_result():
            (job, cipher_result) = completed_job
            if isinstance(cipher_result, AcceptCipher):
                accept_ciphers.append(cipher_result)
            elif isinstance(cipher_result, RejectCipher):
                reject_ciphers.append(cipher_result)
            else:
                raise ValueError("Unexpected result")
            if verbose_mode:
                cipher_result.print_cipher()

        if verbose_mode:
            print "  ----------------------"
            print "  END VERBOSE MODE PRINT"
            print "  ----------------------"

        for error_job in thread_pool.get_error():
            (_, exception) = error_job
            raise exception

        thread_pool.join()

        support_vulnerable_ciphers_set = self.get_vulnerable_ciphers(accept_ciphers)
        is_vulnerable = True if len(support_vulnerable_ciphers_set) > 0 else False

        return BEASTVulnerabilityTesterResult(
            server_connectivity_info,
            plugin_command,
            options_dict,
            support_vulnerable_ciphers_set,
            is_vulnerable,
            support_protocol_list,
        )
    def _test_session_resumption_rate(self, server_info, resumption_attempts_nb):
        # type: (ServerConnectivityInfo, int) -> Tuple[int, List[Text]]
        """Attempt several session ID resumption with the server.
        """
        thread_pool = ThreadPool()

        for _ in range(resumption_attempts_nb):
            thread_pool.add_job((self._resume_with_session_id, (server_info, )))
        thread_pool.start(nb_threads=min(resumption_attempts_nb, self.MAX_THREADS_NB))

        # Count successful/failed resumptions
        successful_resumptions_nb = 0
        for completed_job in thread_pool.get_result():
            (job, was_resumption_successful) = completed_job
            if was_resumption_successful:
                successful_resumptions_nb += 1

        # Count errors and store error messages
        errored_resumptions_list = []
        for failed_job in thread_pool.get_error():
            (job, exception) = failed_job
            error_msg = '{} - {}'.format(str(exception.__class__.__name__), str(exception))
            errored_resumptions_list.append(error_msg)

        thread_pool.join()
        return successful_resumptions_nb, errored_resumptions_list
Esempio n. 10
0
    def process_task(
            self,
            server_info: ServerConnectivityInfo,
            scan_command: PluginScanCommand
    ) -> 'CertificateInfoScanResult':
        if not isinstance(scan_command, CertificateInfoScanCommand):
            raise ValueError('Unexpected scan command')

        final_trust_store_list = TrustStoresRepository.get_default().get_all_stores()
        if scan_command.custom_ca_file:
            if not os.path.isfile(scan_command.custom_ca_file):
                raise ValueError('Could not open supplied CA file at "{}"'.format(scan_command.custom_ca_file))
            final_trust_store_list.append(TrustStore(scan_command.custom_ca_file, 'Custom --ca_file', 'N/A'))

        # Workaround for https://github.com/pyca/cryptography/issues/3495
        default_backend()

        thread_pool = ThreadPool()
        for trust_store in final_trust_store_list:
            # Try to connect with each trust store
            thread_pool.add_job((self._get_and_verify_certificate_chain, [server_info, trust_store]))

        # Start processing the jobs; one thread per trust
        thread_pool.start(len(final_trust_store_list))

        # Store the results as they come
        certificate_chain: List[Certificate] = []
        path_validation_result_list = []
        path_validation_error_list = []
        ocsp_response = None

        for (job, result) in thread_pool.get_result():
            (_, (_, trust_store)) = job
            certificate_chain, validation_result, _ocsp_response = result

            # Keep the OCSP response if the validation was succesful and a response was returned
            if _ocsp_response:
                ocsp_response = _ocsp_response

            # Store the returned verify string for each trust store
            path_validation_result_list.append(PathValidationResult(trust_store, validation_result))

        # Store thread pool errors
        last_exception = None
        for (job, exception) in thread_pool.get_error():
            (_, (_, trust_store)) = job
            path_validation_error_list.append(PathValidationError(trust_store, exception))
            last_exception = exception

        thread_pool.join()

        if len(path_validation_error_list) == len(final_trust_store_list):
            # All connections failed unexpectedly; raise an exception instead of returning a result
            raise last_exception  # type: ignore

        # All done
        return CertificateInfoScanResult(server_info, scan_command, certificate_chain, path_validation_result_list,
                                         path_validation_error_list, ocsp_response)
Esempio n. 11
0
    def _run_oracle_over_threads(
        cls,
        server_info: ServerConnectivityInfo,
        ssl_version_to_use: OpenSslVersionEnum,
        cipher_string: str,
        rsa_modulus: int,
        rsa_exponent: int,
        should_complete_handshake: bool,
    ) -> RobotScanResultEnum:
        # Use threads to speed things up
        thread_pool = ThreadPool()

        for payload_enum in RobotPmsPaddingPayloadEnum:
            # Run each payload three times to ensure the results are consistent
            for _ in range(3):
                thread_pool.add_job((
                    cls._send_robot_payload,
                    [
                        server_info,
                        ssl_version_to_use,
                        cipher_string,
                        payload_enum,
                        should_complete_handshake,
                        rsa_modulus,
                        rsa_exponent,
                    ],
                ))

        # Use one thread per check
        thread_pool.start(nb_threads=len(RobotPmsPaddingPayloadEnum))

        # Store the results - two attempts per ROBOT payload
        payload_responses: Dict[RobotPmsPaddingPayloadEnum, List[str]] = {
            RobotPmsPaddingPayloadEnum.VALID: [],
            RobotPmsPaddingPayloadEnum.WRONG_FIRST_TWO_BYTES: [],
            RobotPmsPaddingPayloadEnum.WRONG_POSITION_00: [],
            RobotPmsPaddingPayloadEnum.NO_00_IN_THE_MIDDLE: [],
            RobotPmsPaddingPayloadEnum.WRONG_VERSION_NUMBER: [],
        }

        for completed_job in thread_pool.get_result():
            (job, (payload_enum, server_response)) = completed_job
            payload_responses[payload_enum].append(server_response)

        for failed_job in thread_pool.get_error():
            # Should never happen when running the Robot check as we catch all exceptions in the handshake
            (_, exception) = failed_job
            raise exception

        thread_pool.join()
        return RobotServerResponsesAnalyzer(
            payload_responses).compute_result_enum()
    def create_thread_pool_for_protocol_tls(self, server_connectivity_info):
        """ Creates and returns instance of ThreadPool class. Adds into ThreadPool new jobs for each cipher suite, which is available for protocol TLS 1.1 and TLS 1.2
    
            Args:
            server_connectivity_info (ServerConnectivityInfo): contains information for connection on server.
        """
        thread_pool=ThreadPool()
        protocols = ['TLSv1.1', 'TLSv1.2']
        cipher_list = []
        for protocol in protocols:
            if self.test_protocol_support(server_connectivity_info, protocol):
                cipher_list = self.get_cipher_list(protocol)
                for cipher in cipher_list:
                    thread_pool.add_job((self._test_ciphersuite,(server_connectivity_info, protocol, cipher)))

        return (thread_pool, min(self.MAX_THREADS,len(cipher)))
    def create_thread_pool_for_protocol_dtls(self,server_connectivity_info, port):
        """ Creates and returns instance of ThreadPool class. Adds into ThreadPool new jobs for each cipher suite, which is available for protocol DTLS 1.0 and/or DTLS 1.2

            Args:
            server_connectivity_info (ServerConnectivityInfo): contains information for connection on server
            port (int): contains port number for connecting comunication.
        """
        dtls_protocols = self.get_support_dtls_protocols_by_client()
        thread_pool=ThreadPool()
        cipher_list = []
        for protocol in dtls_protocols:
            if self.test_dtls_protocol_support(server_connectivity_info, protocol, port):
                cipher_list = self.get_dtls_cipher_list(protocol)
                for cipher in cipher_list:
                    thread_pool.add_job((self._test_dtls_ciphersuite,(server_connectivity_info, protocol, cipher,port)))                    

        return (thread_pool, 1)
Esempio n. 14
0
    def create_thread_pool_for_protocol_tls(self, server_connectivity_info):
        """ Creates and returns instance of ThreadPool class. Adds into ThreadPool new jobs for each cipher suite, which is available for protocol TLS 1.1 and TLS 1.2
    
            Args:
            server_connectivity_info (ServerConnectivityInfo): contains information for connection on server.
        """
        thread_pool = ThreadPool()
        protocols = ['TLSv1.1', 'TLSv1.2']
        cipher_list = []
        for protocol in protocols:
            if self.test_protocol_support(server_connectivity_info, protocol):
                cipher_list = self.get_cipher_list(protocol)
                for cipher in cipher_list:
                    thread_pool.add_job(
                        (self._test_ciphersuite, (server_connectivity_info,
                                                  protocol, cipher)))

        return (thread_pool, min(self.MAX_THREADS, len(cipher)))
Esempio n. 15
0
    def process_task(self, server_info, scan_command):
        # type: (ServerConnectivityInfo, CertificateInfoScanCommand) -> CertificateInfoScanResult
        final_trust_store_list = list(TrustStoresRepository.get_all())
        if scan_command.custom_ca_file:
            if not os.path.isfile(scan_command.custom_ca_file):
                raise ValueError(u'Could not open supplied CA file at "{}"'.format(scan_command.custom_ca_file))
            final_trust_store_list.append(TrustStore(scan_command.custom_ca_file, u'Custom --ca_file', u'N/A'))

        thread_pool = ThreadPool()
        for trust_store in final_trust_store_list:
            # Try to connect with each trust store
            thread_pool.add_job((self._get_and_verify_certificate_chain, (server_info, trust_store)))

        # Start processing the jobs; one thread per trust
        thread_pool.start(len(final_trust_store_list))

        # Store the results as they come
        certificate_chain = []
        path_validation_result_list = []
        path_validation_error_list = []
        ocsp_response = None

        for (job, result) in thread_pool.get_result():
            (_, (_, trust_store)) = job
            certificate_chain, validation_result, ocsp_response = result
            # Store the returned verify string for each trust store
            path_validation_result_list.append(PathValidationResult(trust_store, validation_result))

        # Store thread pool errors
        last_exception = None
        for (job, exception) in thread_pool.get_error():
            (_, (_, trust_store)) = job
            path_validation_error_list.append(PathValidationError(trust_store, exception))
            last_exception = exception

        thread_pool.join()

        if len(path_validation_error_list) == len(final_trust_store_list):
            # All connections failed unexpectedly; raise an exception instead of returning a result
            raise RuntimeError(u'Could not connect to the server; last error: {}'.format(last_exception))

        # All done
        return CertificateInfoScanResult(server_info, scan_command, certificate_chain, path_validation_result_list,
                                         path_validation_error_list, ocsp_response)
    def process_task(self, server_info, command, options_dict=None):

        if command == 'certinfo_basic':
            result_class = CertInfoBasicResult
        elif command == 'certinfo_full':
            result_class = CertInfoFullResult
        else:
            raise ValueError("PluginCertInfo: Unknown command.")

        final_trust_store_list = list(DEFAULT_TRUST_STORE_LIST)
        if options_dict and 'ca_file' in options_dict.keys():
            final_trust_store_list.append(TrustStore(options_dict['ca_file'], 'Custom --ca_file', 'N/A'))

        thread_pool = ThreadPool()
        for trust_store in final_trust_store_list:
            # Try to connect with each trust store
            thread_pool.add_job((self._get_certificate_chain, (server_info, trust_store)))

        # Start processing the jobs; one thread per trust
        thread_pool.start(len(final_trust_store_list))

        # Store the results as they come
        certificate_chain = []
        path_validation_result_list = []
        path_validation_error_list = []
        ocsp_response = None

        for (job, result) in thread_pool.get_result():
            (_, (_, trust_store)) = job
            certificate_chain, validation_result, ocsp_response = result
            # Store the returned verify string for each trust store
            path_validation_result_list.append(PathValidationResult(trust_store, validation_result))

        # Store thread pool errors
        last_exception = None
        for (job, exception) in thread_pool.get_error():
            (_, (_, trust_store)) = job
            path_validation_error_list.append(PathValidationError(trust_store, exception))
            last_exception = exception

        thread_pool.join()

        if len(path_validation_error_list) == len(final_trust_store_list):
            # All connections failed unexpectedly; raise an exception instead of returning a result
            raise RuntimeError('Could not connect to the server; last error: {}'.format(last_exception))

        # All done
        return result_class(server_info, command, options_dict, certificate_chain, path_validation_result_list,
                            path_validation_error_list, ocsp_response)
Esempio n. 17
0
    def process_task(self, server_connectivity_info, plugin_command, options_dict=None):
        ssl_version = self.SSL_VERSIONS_MAPPING[plugin_command]

        # Get the list of available cipher suites for the given ssl version
        ssl_client = SslClient(ssl_version=ssl_version)
        ssl_client.set_cipher_list('ALL:COMPLEMENTOFALL')
        cipher_list = ssl_client.get_cipher_list()

        # Scan for every available cipher suite
        thread_pool = ThreadPool()
        for cipher in cipher_list:
            thread_pool.add_job((self._test_cipher_suite, (server_connectivity_info, ssl_version, cipher)))

        # Start processing the jobs; One thread per cipher
        thread_pool.start(nb_threads=min(len(cipher_list), self.MAX_THREADS))

        accepted_cipher_list = []
        rejected_cipher_list = []
        errored_cipher_list = []

        # Store the results as they come
        for completed_job in thread_pool.get_result():
            (job, cipher_result) = completed_job
            if isinstance(cipher_result, AcceptedCipherSuite):
                accepted_cipher_list.append(cipher_result)
            elif isinstance(cipher_result, RejectedCipherSuite):
                rejected_cipher_list.append(cipher_result)
            elif isinstance(cipher_result, ErroredCipherSuite):
                errored_cipher_list.append(cipher_result)
            else:
                raise ValueError('Unexpected result')

        # Store thread pool errors; only something completely unexpected would trigger an error
        for failed_job in thread_pool.get_error():
            (_, exception) = failed_job
            raise exception

        thread_pool.join()

        # Test for the cipher suite preference
        preferred_cipher = self._get_preferred_cipher_suite(server_connectivity_info, ssl_version, accepted_cipher_list)

        # Generate the results
        plugin_result = OpenSSLCipherSuitesResult(server_connectivity_info, plugin_command, options_dict,
                                                  preferred_cipher, accepted_cipher_list, rejected_cipher_list,
                                                  errored_cipher_list)
        return plugin_result
Esempio n. 18
0
    def create_thread_pool_for_protocol_dtls(self, server_connectivity_info,
                                             port):
        """ Creates and returns instance of ThreadPool class. Adds into ThreadPool new jobs for each cipher suite, which is available for protocol DTLS 1.0 and/or DTLS 1.2

            Args:
            server_connectivity_info (ServerConnectivityInfo): contains information for connection on server
            port (int): contains port number for connecting comunication.
        """
        dtls_protocols = self.get_support_dtls_protocols_by_client()
        thread_pool = ThreadPool()
        cipher_list = []
        for protocol in dtls_protocols:
            if self.test_dtls_protocol_support(server_connectivity_info,
                                               protocol, port):
                cipher_list = self.get_dtls_cipher_list(protocol)
                for cipher in cipher_list:
                    thread_pool.add_job(
                        (self._test_dtls_ciphersuite,
                         (server_connectivity_info, protocol, cipher, port)))

        return (thread_pool, 1)
    def process_task(self, server_connectivity_info, plugin_command, option_dict=None):
        if option_dict and 'verbose' in option_dict.keys():
            verbose_mode = option_dict['verbose']
        else:
            verbose_mode = False
        ssl3_support = self.test_SSLv3_support(server_connectivity_info)
        support_vulnerable_ciphers = None
        if ssl3_support:
            cipher_list = self.get_ssl3_cipher_list()
            thread_pool = ThreadPool()
            for cipher in cipher_list:
                thread_pool.add_job((self._test_ciphersuite, (server_connectivity_info,cipher)))
            thread_pool.start(nb_threads=min(len(cipher_list),self.MAX_THREADS))

            accept_ciphers = []
            reject_ciphers = []
            if verbose_mode:
                print '  VERBOSE MODE PRINT'
                print '  ------------------'
            for completed_job in thread_pool.get_result():
                (job, cipher_result) = completed_job
                if isinstance(cipher_result, AcceptCipher):
                    accept_ciphers.append(cipher_result)
                elif isinstance(cipher_result,RejectCipher):
                    reject_ciphers.append(cipher_result)
                else:
                    raise ValueError("Unexpected result")
                if verbose_mode:
                    cipher_result.print_cipher()

            if verbose_mode:
                print '  ----------------------'
                print '  END VERBOSE MODE PRINT'
                print '  ----------------------'

            for error_job in thread_pool.get_error():
                (_, exception) = error_job
                raise exception

            thread_pool.join()
            support_vulnerable_ciphers = self.get_vulnerable_ciphers(accept_ciphers)

        is_vulnerable = ssl3_support and (support_vulnerable_ciphers is not None or len(support_vulnerable_ciphers) > 0)

        return POODLEVulnerabilityTesterResult(server_connectivity_info, plugin_command,option_dict, ssl3_support, is_vulnerable, support_vulnerable_ciphers)
Esempio n. 20
0
class ConcurrentServerConnectivityTester:
    """Utility class to run servers connectivity testing using a thread pool.
    """

    _DEFAULT_MAX_THREADS = 20

    def __init__(self, server_connectivity_testers: List[ServerConnectivityTester]) -> None:
        # Use a thread pool to connect to each server
        self._thread_pool = ThreadPool()
        self._server_connectivity_testers = server_connectivity_testers

    def start_connectivity_testing(
            self,
            max_threads: int = _DEFAULT_MAX_THREADS,
            network_timeout: Optional[int] = None
    ) -> None:
        for server_tester in self._server_connectivity_testers:
            self._thread_pool.add_job((server_tester.perform, [network_timeout]))
        nb_threads = min(len(self._server_connectivity_testers), max_threads)
        self._thread_pool.start(nb_threads)

    def get_reachable_servers(self) -> Iterable[ServerConnectivityInfo]:
        for (_, server_info) in self._thread_pool.get_result():
            yield server_info

    def get_invalid_servers(self) -> Iterable[ServerConnectivityError]:
        for (_, exception) in self._thread_pool.get_error():
            yield cast(ServerConnectivityError, exception)
class ConcurrentServerConnectivityTester:
    """Utility class to run servers connectivity testing using a thread pool.
    """

    _DEFAULT_MAX_THREADS = 20

    def __init__(
            self, server_connectivity_testers: List[ServerConnectivityTester]
    ) -> None:
        # Use a thread pool to connect to each server
        self._thread_pool = ThreadPool()
        self._server_connectivity_testers = server_connectivity_testers

    def start_connectivity_testing(
            self,
            max_threads: int = _DEFAULT_MAX_THREADS,
            network_timeout: Optional[int] = None) -> None:
        for server_tester in self._server_connectivity_testers:
            self._thread_pool.add_job(
                (server_tester.perform, [network_timeout]))
        nb_threads = min(len(self._server_connectivity_testers), max_threads)
        self._thread_pool.start(nb_threads)

    def get_reachable_servers(self) -> Iterable[ServerConnectivityInfo]:
        for (_, server_info) in self._thread_pool.get_result():
            yield server_info

    def get_invalid_servers(self) -> Iterable[ServerConnectivityError]:
        for (_, exception) in self._thread_pool.get_error():
            yield cast(ServerConnectivityError, exception)
Esempio n. 22
0
class ServersConnectivityTester(object):
    """Utility class to run servers connectivity testing on a list of ServerConnectivityInfo using a thread pool.
    """

    _DEFAULT_MAX_THREADS = 50

    def __init__(self, tentative_server_info_list):
        # type: (List[ServerConnectivityInfo]) -> None
        # Use a thread pool to connect to each server
        self._thread_pool = ThreadPool()
        self._server_info_list = tentative_server_info_list

    def start_connectivity_testing(self, max_threads=_DEFAULT_MAX_THREADS, network_timeout=None):
        # type: (Optional[int], Optional[int]) -> None
        for tentative_server_info in self._server_info_list:
            self._thread_pool.add_job((tentative_server_info.test_connectivity_to_server, [network_timeout]))
        nb_threads = min(len(self._server_info_list), max_threads)
        self._thread_pool.start(nb_threads)

    def get_reachable_servers(self):
        # type: () -> Iterable[ServerConnectivityInfo]
        for (job, _) in self._thread_pool.get_result():
            test_connectivity_to_server_method, _ = job
            server_info = test_connectivity_to_server_method.__self__
            yield server_info

    def get_invalid_servers(self):
        # type: () -> Iterable[Tuple[ServerConnectivityInfo, Exception]]
        for (job, exception) in self._thread_pool.get_error():
            test_connectivity_to_server_method, _ = job
            server_info = test_connectivity_to_server_method.__self__
            yield (server_info, exception)
Esempio n. 23
0
class ServersConnectivityTester(object):
    """Utility class to run servers connectivity testing on a list of ServerConnectivityInfo using a thread pool.
    """

    _DEFAULT_MAX_THREADS = 50

    def __init__(self, tentative_server_info_list):
        # type: (List[ServerConnectivityInfo]) -> None
        # Use a thread pool to connect to each server
        self._thread_pool = ThreadPool()
        self._server_info_list = tentative_server_info_list

    def start_connectivity_testing(self,
                                   max_threads=_DEFAULT_MAX_THREADS,
                                   network_timeout=None):
        # type: (int, Optional[int]) -> None
        for tentative_server_info in self._server_info_list:
            self._thread_pool.add_job(
                (tentative_server_info.test_connectivity_to_server,
                 [network_timeout]))
        nb_threads = min(len(self._server_info_list), max_threads)
        self._thread_pool.start(nb_threads)

    def get_reachable_servers(self):
        # type: () -> Iterable[ServerConnectivityInfo]
        for (job, _) in self._thread_pool.get_result():
            test_connectivity_to_server_method, _ = job
            # TODO(AD): Using __self__ here is really ugly
            server_info = test_connectivity_to_server_method.__self__  # type: ignore
            yield server_info

    def get_invalid_servers(self):
        # type: () -> Iterable[Tuple[ServerConnectivityInfo, Exception]]
        for (job, exception) in self._thread_pool.get_error():
            test_connectivity_to_server_method, _ = job
            # TODO(AD): Using __self__ here is really ugly
            server_info = test_connectivity_to_server_method.__self__  # type: ignore
            yield (server_info, exception)
Esempio n. 24
0
 def __init__(self, server_connectivity_testers: List[ServerConnectivityTester]) -> None:
     # Use a thread pool to connect to each server
     self._thread_pool = ThreadPool()
     self._server_connectivity_testers = server_connectivity_testers
    def process_task(
            self,
            server_connectivity_info: ServerConnectivityInfo,
            scan_command: PluginScanCommand
    ) -> 'CipherSuiteScanResult':
        if not isinstance(scan_command, CipherSuiteScanCommand):
            raise ValueError('Unexpected scan command')

        ssl_version = self.SSL_VERSIONS_MAPPING[scan_command.__class__]
        # Get the list of available cipher suites for the given ssl version
        cipher_list: List[str] = []
        if ssl_version == OpenSslVersionEnum.TLSV1_2:
            # For TLS 1.2, we have to use both the legacy and modern OpenSSL to cover all cipher suites
            ssl_connection_legacy = server_connectivity_info.get_preconfigured_ssl_connection(
                override_ssl_version=ssl_version, should_use_legacy_openssl=True
            )
            ssl_connection_legacy.ssl_client.set_cipher_list('ALL:COMPLEMENTOFALL:-PSK:-SRP')
            cipher_list.extend(ssl_connection_legacy.ssl_client.get_cipher_list())

            ssl_connection_modern = server_connectivity_info.get_preconfigured_ssl_connection(
                override_ssl_version=ssl_version, should_use_legacy_openssl=False
            )
            # Disable the TLS 1.3 cipher suites with the new OpenSSL API
            ssl_connection_modern.ssl_client.set_ciphersuites('')
            # Enable all other cipher suites
            ssl_connection_modern.ssl_client.set_cipher_list('ALL:COMPLEMENTOFALL:-PSK:-SRP')
            cipher_list.extend(ssl_connection_modern.ssl_client.get_cipher_list())

            # And remove duplicates (ie. supported by both legacy and modern OpenSSL)
            cipher_list = list(set(cipher_list))
        elif ssl_version == OpenSslVersionEnum.TLSV1_3:
            # TLS 1.3 only has 5 cipher suites so we can hardcode them
            cipher_list = [
                'TLS_AES_256_GCM_SHA384',
                'TLS_CHACHA20_POLY1305_SHA256',
                'TLS_AES_128_GCM_SHA256',
                'TLS_AES_128_CCM_8_SHA256',
                'TLS_AES_128_CCM_SHA256',
            ]
        else:
            ssl_connection = server_connectivity_info.get_preconfigured_ssl_connection(override_ssl_version=ssl_version)
            # Disable SRP and PSK cipher suites as they need a special setup in the client and are never used
            ssl_connection.ssl_client.set_cipher_list('ALL:COMPLEMENTOFALL:-PSK:-SRP')
            # And remove TLS 1.3 cipher suites
            cipher_list = [cipher for cipher in ssl_connection.ssl_client.get_cipher_list() if 'TLS13' not in cipher]

        # Scan for every available cipher suite
        thread_pool = ThreadPool()
        for cipher in cipher_list:
            thread_pool.add_job((self._test_cipher_suite, [server_connectivity_info, ssl_version, cipher]))

        # Start processing the jobs; One thread per cipher
        thread_pool.start(nb_threads=min(len(cipher_list), self.MAX_THREADS))

        accepted_cipher_list = []
        rejected_cipher_list = []
        errored_cipher_list = []

        # Store the results as they come
        for completed_job in thread_pool.get_result():
            (job, cipher_result) = completed_job
            if isinstance(cipher_result, AcceptedCipherSuite):
                accepted_cipher_list.append(cipher_result)
            elif isinstance(cipher_result, RejectedCipherSuite):
                rejected_cipher_list.append(cipher_result)
            elif isinstance(cipher_result, ErroredCipherSuite):
                errored_cipher_list.append(cipher_result)
            else:
                raise ValueError('Unexpected result')

        # Store thread pool errors; only something completely unexpected would trigger an error
        for failed_job in thread_pool.get_error():
            (_, exception) = failed_job
            raise exception

        thread_pool.join()

        # Test for the cipher suite preference
        preferred_cipher = self._get_preferred_cipher_suite(server_connectivity_info, ssl_version, accepted_cipher_list)

        # Generate the results
        plugin_result = CipherSuiteScanResult(server_connectivity_info, scan_command, preferred_cipher,
                                              accepted_cipher_list, rejected_cipher_list, errored_cipher_list)
        return plugin_result
Esempio n. 26
0
    def process_task(self, server_connectivity_info, scan_command):
        # type: (ServerConnectivityInfo, CipherSuiteScanCommand) -> CipherSuiteScanResult
        ssl_version = self.SSL_VERSIONS_MAPPING[scan_command.__class__]

        # Get the list of available cipher suites for the given ssl version
        if ssl_version == OpenSslVersionEnum.TLSV1_2:
            # For TLS 1.2, we have to use both the legacy and modern OpenSSL to cover all cipher suites
            cipher_list = []
            ssl_connection = server_connectivity_info.get_preconfigured_ssl_connection(
                override_ssl_version=ssl_version,
                should_use_legacy_openssl=True)
            ssl_connection.ssl_client.set_cipher_list(
                'ALL:COMPLEMENTOFALL:-PSK:-SRP')
            cipher_list.extend(ssl_connection.ssl_client.get_cipher_list())

            ssl_connection = server_connectivity_info.get_preconfigured_ssl_connection(
                override_ssl_version=ssl_version,
                should_use_legacy_openssl=False)
            ssl_connection.ssl_client.set_cipher_list(
                'ALL:COMPLEMENTOFALL:-PSK:-SRP')
            cipher_list.extend(ssl_connection.ssl_client.get_cipher_list())

            # Lastly we have to remove TLS 1.3 cipher suites
            cipher_list = [
                cipher
                for cipher in ssl_connection.ssl_client.get_cipher_list()
                if 'TLS13' not in cipher
            ]

            # And remove duplicates (ie. supported by both legacy and modern OpenSSL)
            cipher_list = set(cipher_list)
        elif ssl_version == OpenSslVersionEnum.TLSV1_3:
            # For TLS 1.3 we need to manually pick the cipher suites as there is no OpenSSL cipher string to select them
            ssl_connection = server_connectivity_info.get_preconfigured_ssl_connection(
                override_ssl_version=ssl_version)
            cipher_list = [
                cipher
                for cipher in ssl_connection.ssl_client.get_cipher_list()
                if 'TLS13' in cipher
            ]
        else:
            ssl_connection = server_connectivity_info.get_preconfigured_ssl_connection(
                override_ssl_version=ssl_version)
            # Disable SRP and PSK cipher suites as they need a special setup in the client and are never used
            ssl_connection.ssl_client.set_cipher_list(
                'ALL:COMPLEMENTOFALL:-PSK:-SRP')
            # And remove TLS 1.3 cipher suites
            cipher_list = [
                cipher
                for cipher in ssl_connection.ssl_client.get_cipher_list()
                if 'TLS13' not in cipher
            ]

        # Scan for every available cipher suite
        thread_pool = ThreadPool()
        for cipher in cipher_list:
            thread_pool.add_job(
                (self._test_cipher_suite, (server_connectivity_info,
                                           ssl_version, cipher)))

        # Start processing the jobs; One thread per cipher
        thread_pool.start(nb_threads=min(len(cipher_list), self.MAX_THREADS))

        accepted_cipher_list = []
        rejected_cipher_list = []
        errored_cipher_list = []

        # Store the results as they come
        for completed_job in thread_pool.get_result():
            (job, cipher_result) = completed_job
            if isinstance(cipher_result, AcceptedCipherSuite):
                accepted_cipher_list.append(cipher_result)
            elif isinstance(cipher_result, RejectedCipherSuite):
                rejected_cipher_list.append(cipher_result)
            elif isinstance(cipher_result, ErroredCipherSuite):
                errored_cipher_list.append(cipher_result)
            else:
                raise ValueError('Unexpected result')

        # Store thread pool errors; only something completely unexpected would trigger an error
        for failed_job in thread_pool.get_error():
            (_, exception) = failed_job
            raise exception

        thread_pool.join()

        # Test for the cipher suite preference
        preferred_cipher = self._get_preferred_cipher_suite(
            server_connectivity_info, ssl_version, accepted_cipher_list)

        # Generate the results
        plugin_result = CipherSuiteScanResult(server_connectivity_info,
                                              scan_command, preferred_cipher,
                                              accepted_cipher_list,
                                              rejected_cipher_list,
                                              errored_cipher_list)
        return plugin_result
Esempio n. 27
0
    def _run_oracle_over_threads(cls, server_info, cipher_string, rsa_modulus,
                                 rsa_exponent, should_complete_handshake):
        # type: (ServerConnectivityInfo, Text, int, int, bool) -> RobotScanResultEnum
        # Use threads to speed things up
        thread_pool = ThreadPool()

        for payload_enum in RobotPmsPaddingPayloadEnum:
            # Run each payload twice to ensure the results are consistent
            thread_pool.add_job(
                (cls._send_robot_payload,
                 (server_info, cipher_string, payload_enum,
                  should_complete_handshake, rsa_modulus, rsa_exponent)))
            thread_pool.add_job(
                (cls._send_robot_payload,
                 (server_info, cipher_string, payload_enum,
                  should_complete_handshake, rsa_modulus, rsa_exponent)))

        # Use one thread per check
        thread_pool.start(nb_threads=len(RobotPmsPaddingPayloadEnum) * 2)

        # Store the results - two attempts per ROBOT payload
        payload_responses = {
            RobotPmsPaddingPayloadEnum.VALID: [],
            RobotPmsPaddingPayloadEnum.WRONG_FIRST_TWO_BYTES: [],
            RobotPmsPaddingPayloadEnum.WRONG_POSITION_00: [],
            RobotPmsPaddingPayloadEnum.NO_00_IN_THE_MIDDLE: [],
            RobotPmsPaddingPayloadEnum.WRONG_VERSION_NUMBER: [],
        }
        for completed_job in thread_pool.get_result():
            (job, (payload_enum, server_response)) = completed_job
            payload_responses[payload_enum].append(server_response)

        for failed_job in thread_pool.get_error():
            # Should never happen when running the Robot check as we catch all exceptions in the handshake
            (_, exception) = failed_job
            raise exception

        thread_pool.join()
        return RobotServerResponsesAnalyzer(
            payload_responses).compute_result_enum()
Esempio n. 28
0
 def __init__(self, tentative_server_info_list):
     # type: (List[ServerConnectivityInfo]) -> None
     # Use a thread pool to connect to each server
     self._thread_pool = ThreadPool()
     self._server_info_list = tentative_server_info_list
 def __init__(
         self, server_connectivity_testers: List[ServerConnectivityTester]
 ) -> None:
     # Use a thread pool to connect to each server
     self._thread_pool = ThreadPool()
     self._server_connectivity_testers = server_connectivity_testers
    def process_task(self,
                     server_connectivity_info,
                     plugin_command,
                     options_dict=None):
        if options_dict and 'verbose' in options_dict.keys():
            verbose_mode = options_dict['verbose']
        else:
            verbose_mode = False
        test_protocols = {'SSLv3', 'TLSv1'}
        thread_pool = ThreadPool()
        ciphers_list = []
        support_protocol_list = []
        for proto in test_protocols:
            if self.test_protocol_support(PROTOCOL_VERSION[proto],
                                          server_connectivity_info):
                ssl_client = SslClient(ssl_version=PROTOCOL_VERSION[proto])
                ssl_client.set_cipher_list(proto)
                ciphers_list = ssl_client.get_cipher_list()
                for cipher in ciphers_list:
                    thread_pool.add_job((self._test_ciphersuite,
                                         (server_connectivity_info,
                                          PROTOCOL_VERSION[proto], cipher)))
                support_protocol_list.append(proto)
        thread_pool.start(nb_threads=min(len(ciphers_list), self.MAX_THREADS))

        accept_ciphers = []
        reject_ciphers = []
        if verbose_mode:
            print '  VERBOSE MODE PRINT'
            print '  ------------------'

        for completed_job in thread_pool.get_result():
            (job, cipher_result) = completed_job
            if isinstance(cipher_result, AcceptCipher):
                accept_ciphers.append(cipher_result)
            elif isinstance(cipher_result, RejectCipher):
                reject_ciphers.append(cipher_result)
            else:
                raise ValueError("Unexpected result")
            if verbose_mode:
                cipher_result.print_cipher()

        if verbose_mode:
            print '  ----------------------'
            print '  END VERBOSE MODE PRINT'
            print '  ----------------------'

        for error_job in thread_pool.get_error():
            (_, exception) = error_job
            raise exception

        thread_pool.join()

        support_vulnerable_ciphers_set = self.get_vulnerable_ciphers(
            accept_ciphers)
        is_vulnerable = True \
            if len(support_vulnerable_ciphers_set) > 0 \
            else False

        return BEASTVulnerabilityTesterResult(server_connectivity_info,
                                              plugin_command, options_dict,
                                              support_vulnerable_ciphers_set,
                                              is_vulnerable,
                                              support_protocol_list)
Esempio n. 31
0
    def _run_oracle_over_threads(cls, server_info, cipher_string, rsa_modulus, rsa_exponent, should_complete_handshake):
        # type: (ServerConnectivityInfo, Text, int, int, bool) -> RobotScanResultEnum
        # Use threads to speed things up
        thread_pool = ThreadPool()

        for payload_enum in RobotPmsPaddingPayloadEnum:
            # Run each payload twice to ensure the results are consistent
            thread_pool.add_job((cls._send_robot_payload, (server_info, cipher_string, payload_enum,
                                                           should_complete_handshake, rsa_modulus, rsa_exponent)))
            thread_pool.add_job((cls._send_robot_payload, (server_info, cipher_string, payload_enum,
                                                           should_complete_handshake, rsa_modulus, rsa_exponent)))

        # Use one thread per check
        thread_pool.start(nb_threads=len(RobotPmsPaddingPayloadEnum)*2)

        # Store the results - two attempts per ROBOT payload
        payload_responses = {
            RobotPmsPaddingPayloadEnum.VALID: [],
            RobotPmsPaddingPayloadEnum.WRONG_FIRST_TWO_BYTES: [],
            RobotPmsPaddingPayloadEnum.WRONG_POSITION_00: [],
            RobotPmsPaddingPayloadEnum.NO_00_IN_THE_MIDDLE: [],
            RobotPmsPaddingPayloadEnum.WRONG_VERSION_NUMBER: [],
        }
        for completed_job in thread_pool.get_result():
            (job, (payload_enum, server_response)) = completed_job
            payload_responses[payload_enum].append(server_response)

        for failed_job in thread_pool.get_error():
            # Should never happen when running the Robot check as we catch all exceptions in the handshake
            (_, exception) = failed_job
            raise exception

        thread_pool.join()
        return RobotServerResponsesAnalyzer(payload_responses).compute_result_enum()
    def process_task(self, server_connectivity_info, scan_command):
        # type: (ServerConnectivityInfo, CipherSuiteScanCommand) -> CipherSuiteScanResult
        ssl_version = self.SSL_VERSIONS_MAPPING[scan_command.__class__]

        # Get the list of available cipher suites for the given ssl version
        if ssl_version == OpenSslVersionEnum.TLSV1_2:
            # For TLS 1.2, we have to use both the legacy and modern OpenSSL to cover all cipher suites
            cipher_list = []
            ssl_connection = server_connectivity_info.get_preconfigured_ssl_connection(override_ssl_version=ssl_version,
                                                                                       should_use_legacy_openssl=True)
            ssl_connection.ssl_client.set_cipher_list('ALL:COMPLEMENTOFALL:-PSK:-SRP')
            cipher_list.extend(ssl_connection.ssl_client.get_cipher_list())

            ssl_connection = server_connectivity_info.get_preconfigured_ssl_connection(override_ssl_version=ssl_version,
                                                                                       should_use_legacy_openssl=False)
            ssl_connection.ssl_client.set_cipher_list('ALL:COMPLEMENTOFALL:-PSK:-SRP')
            cipher_list.extend(ssl_connection.ssl_client.get_cipher_list())

            # Lastly we have to remove TLS 1.3 cipher suites
            cipher_list = [cipher for cipher in ssl_connection.ssl_client.get_cipher_list() if 'TLS13' not in cipher]

            # And remove duplicates (ie. supported by both legacy and modern OpenSSL)
            cipher_list = set(cipher_list)
        elif ssl_version == OpenSslVersionEnum.TLSV1_3:
            # For TLS 1.3 we need to manually pick the cipher suites as there is no OpenSSL cipher string to select them
            ssl_connection = server_connectivity_info.get_preconfigured_ssl_connection(override_ssl_version=ssl_version)
            cipher_list = [cipher for cipher in ssl_connection.ssl_client.get_cipher_list() if 'TLS13' in cipher]
        else:
            ssl_connection = server_connectivity_info.get_preconfigured_ssl_connection(override_ssl_version=ssl_version)
            # Disable SRP and PSK cipher suites as they need a special setup in the client and are never used
            ssl_connection.ssl_client.set_cipher_list('ALL:COMPLEMENTOFALL:-PSK:-SRP')
            # And remove TLS 1.3 cipher suites
            cipher_list = [cipher for cipher in ssl_connection.ssl_client.get_cipher_list() if 'TLS13' not in cipher]

        # Scan for every available cipher suite
        thread_pool = ThreadPool()
        for cipher in cipher_list:
            thread_pool.add_job((self._test_cipher_suite, (server_connectivity_info, ssl_version, cipher)))

        # Start processing the jobs; One thread per cipher
        thread_pool.start(nb_threads=min(len(cipher_list), self.MAX_THREADS))

        accepted_cipher_list = []
        rejected_cipher_list = []
        errored_cipher_list = []

        # Store the results as they come
        for completed_job in thread_pool.get_result():
            (job, cipher_result) = completed_job
            if isinstance(cipher_result, AcceptedCipherSuite):
                accepted_cipher_list.append(cipher_result)
            elif isinstance(cipher_result, RejectedCipherSuite):
                rejected_cipher_list.append(cipher_result)
            elif isinstance(cipher_result, ErroredCipherSuite):
                errored_cipher_list.append(cipher_result)
            else:
                raise ValueError('Unexpected result')

        # Store thread pool errors; only something completely unexpected would trigger an error
        for failed_job in thread_pool.get_error():
            (_, exception) = failed_job
            raise exception

        thread_pool.join()

        # Test for the cipher suite preference
        preferred_cipher_list = self._get_preferred_cipher_suite_list(server_connectivity_info, ssl_version, accepted_cipher_list)

        # Generate the results
        plugin_result = CipherSuiteScanResult(server_connectivity_info, scan_command, preferred_cipher_list,
                                              accepted_cipher_list, rejected_cipher_list, errored_cipher_list)
        return plugin_result
Esempio n. 33
0
    def process_task(
            self,
            server_info: ServerConnectivityInfo,
            scan_command: PluginScanCommand
    ) -> 'CertificateInfoScanResult':
        if not isinstance(scan_command, CertificateInfoScanCommand):
            raise ValueError('Unexpected scan command')

        final_trust_store_list = TrustStoresRepository.get_default().get_all_stores()
        if scan_command.custom_ca_file:
            custom_ca_file_path = Path(scan_command.custom_ca_file)
            if not custom_ca_file_path.is_file():
                raise ValueError(f'Could not open supplied CA file at "{custom_ca_file_path}"')
            final_trust_store_list.append(TrustStore(custom_ca_file_path, 'Custom --ca_file', 'N/A'))

        # Workaround for https://github.com/pyca/cryptography/issues/3495
        default_backend()

        thread_pool = ThreadPool()
        for trust_store in final_trust_store_list:
            # Try to connect with each trust store
            thread_pool.add_job((self._get_and_verify_certificate_chain, [server_info, trust_store]))

        # Start processing the jobs; one thread per trust
        thread_pool.start(len(final_trust_store_list))

        # Store the results as they come
        path_validation_result_list = []
        path_validation_error_list = []
        ocsp_response = None
        received_chain = None

        for (job, result) in thread_pool.get_result():
            (_, (_, trust_store)) = job
            received_chain_as_pem, verified_chain_as_pem, validation_result, _ocsp_response = result

            # Parse the certificates using the cryptography module
            if not received_chain:
                received_chain = [
                    load_pem_x509_certificate(pem_cert.encode('ascii'), backend=default_backend())
                    for pem_cert in received_chain_as_pem
                ]
            verified_chain = [
                load_pem_x509_certificate(cert_as_pem.encode('ascii'), backend=default_backend())
                for cert_as_pem in verified_chain_as_pem
            ] if verified_chain_as_pem else None

            # Keep the OCSP response if the validation was successful and a response was returned
            if _ocsp_response:
                ocsp_response = _ocsp_response

            # Store the returned verify string for each trust store
            path_validation_result_list.append(PathValidationResult(trust_store, verified_chain, validation_result))

        # Store thread pool errors
        last_exception = None
        for (job, exception) in thread_pool.get_error():
            (_, (_, trust_store)) = job
            path_validation_error_list.append(PathValidationError(trust_store, exception))
            last_exception = exception

        thread_pool.join()

        if len(path_validation_error_list) == len(final_trust_store_list):
            # All connections failed unexpectedly; raise an exception instead of returning a result
            raise last_exception  # type: ignore

        if not received_chain:
            raise ValueError('Error: Could not retrieve the server certificate chain')

        # All done
        return CertificateInfoScanResult(
            server_info,
            scan_command,
            received_chain,
            path_validation_result_list,
            path_validation_error_list,
            ocsp_response
        )
    def process_task(
            self, server_connectivity_info: ServerConnectivityInfo,
            scan_command: PluginScanCommand) -> 'CipherSuiteScanResult':
        if not isinstance(scan_command, CipherSuiteScanCommand):
            raise ValueError('Unexpected scan command')

        ssl_version = self.SSL_VERSIONS_MAPPING[scan_command.__class__]
        # Get the list of available cipher suites for the given ssl version
        cipher_list: List[str] = []
        if ssl_version == OpenSslVersionEnum.TLSV1_2:
            # For TLS 1.2, we have to use both the legacy and modern OpenSSL to cover all cipher suites
            ssl_connection_legacy = server_connectivity_info.get_preconfigured_ssl_connection(
                override_ssl_version=ssl_version,
                should_use_legacy_openssl=True)
            ssl_connection_legacy.ssl_client.set_cipher_list(
                'ALL:COMPLEMENTOFALL:-PSK:-SRP')
            cipher_list.extend(
                ssl_connection_legacy.ssl_client.get_cipher_list())

            ssl_connection_modern = server_connectivity_info.get_preconfigured_ssl_connection(
                override_ssl_version=ssl_version,
                should_use_legacy_openssl=False)
            # Disable the TLS 1.3 cipher suites with the new OpenSSL API
            ssl_connection_modern.ssl_client.set_ciphersuites('')
            # Enable all other cipher suites
            ssl_connection_modern.ssl_client.set_cipher_list(
                'ALL:COMPLEMENTOFALL:-PSK:-SRP')
            cipher_list.extend(
                ssl_connection_modern.ssl_client.get_cipher_list())

            # And remove duplicates (ie. supported by both legacy and modern OpenSSL)
            cipher_list = list(set(cipher_list))
        elif ssl_version == OpenSslVersionEnum.TLSV1_3:
            # TLS 1.3 only has 5 cipher suites so we can hardcode them
            cipher_list = [
                'TLS_AES_256_GCM_SHA384',
                'TLS_CHACHA20_POLY1305_SHA256',
                'TLS_AES_128_GCM_SHA256',
                'TLS_AES_128_CCM_8_SHA256',
                'TLS_AES_128_CCM_SHA256',
            ]
        else:
            ssl_connection = server_connectivity_info.get_preconfigured_ssl_connection(
                override_ssl_version=ssl_version)
            # Disable SRP and PSK cipher suites as they need a special setup in the client and are never used
            ssl_connection.ssl_client.set_cipher_list(
                'ALL:COMPLEMENTOFALL:-PSK:-SRP')
            # And remove TLS 1.3 cipher suites
            cipher_list = [
                cipher
                for cipher in ssl_connection.ssl_client.get_cipher_list()
                if 'TLS13' not in cipher
            ]

        # Scan for every available cipher suite
        thread_pool = ThreadPool()
        for cipher in cipher_list:
            thread_pool.add_job(
                (self._test_cipher_suite,
                 [server_connectivity_info, ssl_version, cipher]))

        # Start processing the jobs; One thread per cipher
        thread_pool.start(nb_threads=min(len(cipher_list), self.MAX_THREADS))

        accepted_cipher_list = []
        rejected_cipher_list = []
        errored_cipher_list = []

        # Store the results as they come
        for completed_job in thread_pool.get_result():
            (job, cipher_result) = completed_job
            if isinstance(cipher_result, AcceptedCipherSuite):
                accepted_cipher_list.append(cipher_result)
            elif isinstance(cipher_result, RejectedCipherSuite):
                rejected_cipher_list.append(cipher_result)
            elif isinstance(cipher_result, ErroredCipherSuite):
                errored_cipher_list.append(cipher_result)
            else:
                raise ValueError('Unexpected result')

        # Store thread pool errors; only something completely unexpected would trigger an error
        for failed_job in thread_pool.get_error():
            (_, exception) = failed_job
            raise exception

        thread_pool.join()

        # Test for the cipher suite preference
        preferred_cipher = self._get_preferred_cipher_suite(
            server_connectivity_info, ssl_version, accepted_cipher_list)

        # Generate the results
        plugin_result = CipherSuiteScanResult(server_connectivity_info,
                                              scan_command, preferred_cipher,
                                              accepted_cipher_list,
                                              rejected_cipher_list,
                                              errored_cipher_list)
        return plugin_result
Esempio n. 35
0
 def __init__(self, tentative_server_info_list):
     # type: (List[ServerConnectivityInfo]) -> None
     # Use a thread pool to connect to each server
     self._thread_pool = ThreadPool()
     self._server_info_list = tentative_server_info_list