Example #1
0
    def start(self):
        if not len(self.al.spiders):
            logger.error("No Spiders loaded. exit.")
            sys.exit(1)
        else:
            message = "Loaded spiders: "
            for s in self.al.spiders:
                message += str(s.__class__).split(".")[-1].split("'")[0] + ", "
            logger.info(message.strip(", "))
        # 创建线程池
        if self.spider_threads:
            self.tp = ThreadPool(self.spider_threads)
        else:
            self.tp = ThreadPool()
        for sp in self.al.spiders:
            # 将spider中的run方法添加到线程池中
            self.tp.add_function(sp.run)
        # 开始线程池
        self.tp.run(join=False)

        # 输出结果
        self.sd = SaveData(self.al.results, self.tp, use_file=self.output_file, use_database=self.output_db,
                           filename=self.output_filename)
        if self.save_data_threads:
            self.write_file_tp = ThreadPool(self.save_data_threads)
        else:
            self.write_file_tp = ThreadPool()
        self.write_file_tp = ThreadPool()
        self.write_file_tp.add_function(self.sd.write)
        self.write_file_tp.run()
Example #2
0
    def start(self):
        if not len(self.al.spiders):
            logger.error("No Spiders loaded. exit.")
            sys.exit(1)
        else:
            message = "Loaded spiders: "
            for s in self.al.spiders:
                message += str(s.__class__).split(".")[-1].split("'")[0] + ", "
            logger.info(message.strip(", "))
        # 创建线程池
        if self.spider_threads:
            self.tp = ThreadPool(self.spider_threads)
        else:
            self.tp = ThreadPool()
        for sp in self.al.spiders:
            # 将spider中的run方法添加到线程池中
            self.tp.add_function(sp.run)
        # 开始线程池
        self.tp.run(join=False)

        # 输出结果
        self.sd = SaveData(self.al.results,
                           self.tp,
                           use_file=self.output_file,
                           use_database=self.output_db,
                           filename=self.output_filename)
        if self.save_data_threads:
            self.write_file_tp = ThreadPool(self.save_data_threads)
        else:
            self.write_file_tp = ThreadPool()
        self.write_file_tp = ThreadPool()
        self.write_file_tp.add_function(self.sd.write)
        self.write_file_tp.run()
Example #3
0
    def _command_resum_rate(self, target):
        """
        Performs 100 session resumptions with the server in order to estimate
        the session resumption rate.
        """
        # Create a thread pool and process the jobs
        NB_THREADS = 20
        MAX_RESUM = 100
        thread_pool = ThreadPool()
        for _ in xrange(MAX_RESUM):
            thread_pool.add_job((self._resume_with_session_id, (target, )))
        thread_pool.start(NB_THREADS)

        # Format session ID results
        (txt_resum,
         xml_resum) = self._format_resum_id_results(thread_pool, MAX_RESUM)

        # Text output
        cmd_title = 'Resumption Rate with Session IDs'
        txt_result = [self.PLUGIN_TITLE_FORMAT(cmd_title) + ' ' + txt_resum[0]]
        txt_result.extend(txt_resum[1:])

        # XML output
        xml_result = Element('resum_rate', title=cmd_title)
        xml_result.append(xml_resum)

        thread_pool.join()
        return PluginBase.PluginResult(txt_result, xml_result)
Example #4
0
class ProxySpider(object):
    def __init__(self,
                 output_file=True,
                 output_db=True,
                 output_filename="proxy-ip-list.csv"):
        # 初始化AutoLoad模块
        self.al = AutoLoad()
        # 初始化
        self.tp = None
        self.sd = None
        self.write_file_tp = None
        self.spider_threads = None
        self.save_data_threads = None
        # 获取参数
        self.output_file = output_file
        self.output_db = output_db
        self.output_filename = output_filename

    def load(self, *spiders):
        self.al.load(*spiders)

    def set_threads(self, spider_threads=0, save_data_threads=0):
        if spider_threads > 0:
            self.spider_threads = spider_threads
        if save_data_threads > 0:
            self.save_data_threads = save_data_threads

    def start(self):
        if not len(self.al.spiders):
            logger.error("No Spiders loaded. exit.")
            sys.exit(1)
        else:
            message = "Loaded spiders: "
            for s in self.al.spiders:
                message += str(s.__class__).split(".")[-1].split("'")[0] + ", "
            logger.info(message.strip(", "))
        # 创建线程池
        if self.spider_threads:
            self.tp = ThreadPool(self.spider_threads)
        else:
            self.tp = ThreadPool()
        for sp in self.al.spiders:
            # 将spider中的run方法添加到线程池中
            self.tp.add_function(sp.run)
        # 开始线程池
        self.tp.run(join=False)

        # 输出结果
        self.sd = SaveData(self.al.results,
                           self.tp,
                           use_file=self.output_file,
                           use_database=self.output_db,
                           filename=self.output_filename)
        if self.save_data_threads:
            self.write_file_tp = ThreadPool(self.save_data_threads)
        else:
            self.write_file_tp = ThreadPool()
        self.write_file_tp = ThreadPool()
        self.write_file_tp.add_function(self.sd.write)
        self.write_file_tp.run()
Example #5
0
    def _command_resum(self, target):
        """
        Tests the server for session resumption support using session IDs and
        TLS session tickets (RFC 5077).
        """
        NB_THREADS = 5
        MAX_RESUM = 5
        thread_pool = ThreadPool()

        for _ in xrange(MAX_RESUM):  # Test 5 resumptions with session IDs
            thread_pool.add_job(
                (self._resume_with_session_id, (target, ), 'session_id'))
        thread_pool.start(NB_THREADS)

        # Test TLS tickets support while threads are running
        try:
            (ticket_supported,
             ticket_reason) = self._resume_with_session_ticket(target)
            ticket_error = None
        except Exception as e:
            ticket_error = str(e.__class__.__module__) + '.' + \
                            str(e.__class__.__name__) + ' - ' + str(e)

        # Format session ID results
        (txt_resum,
         xml_resum) = self._format_resum_id_results(thread_pool, MAX_RESUM)

        if ticket_error:
            ticket_txt = 'Error: ' + ticket_error
        else:
            ticket_txt = 'Supported' if ticket_supported \
                                     else 'Not Supported - ' + ticket_reason+'.'

        cmd_title = 'Session Resumption'
        txt_result = [self.PLUGIN_TITLE_FORMAT.format(cmd_title)]
        RESUM_FORMAT = '      {0:<27} {1}'

        txt_result.append(
            RESUM_FORMAT.format('With Session IDs:', txt_resum[0]))
        txt_result.extend(txt_resum[1:])
        txt_result.append(
            RESUM_FORMAT.format('With TLS Session Tickets:', ticket_txt))

        # XML output
        xml_resum_ticket_attr = {}
        if ticket_error:
            xml_resum_ticket_attr['error'] = ticket_error
        else:
            xml_resum_ticket_attr['isSupported'] = str(ticket_supported)
            if not ticket_supported:
                xml_resum_ticket_attr['reason'] = ticket_reason

        xml_resum_ticket = Element('sessionResumptionWithTLSTickets',
                                   attrib=xml_resum_ticket_attr)
        xml_result = Element('resum', title=cmd_title)
        xml_result.append(xml_resum)
        xml_result.append(xml_resum_ticket)

        thread_pool.join()
        return PluginBase.PluginResult(txt_result, xml_result)
    def _command_resum_rate(self, target):
        """
        Performs 100 session resumptions with the server in order to estimate
        the session resumption rate.
        """
        # Create a thread pool and process the jobs
        NB_THREADS = 20
        MAX_RESUM = 100
        thread_pool = ThreadPool()
        for _ in xrange(MAX_RESUM):
            thread_pool.add_job((self._resume_with_session_id, (target, )))
        thread_pool.start(NB_THREADS)

        # Format session ID results
        (txt_resum, xml_resum) = self._format_resum_id_results(thread_pool, MAX_RESUM)

        # Text output
        cmd_title = 'Resumption Rate with Session IDs'
        txt_result = [self.PLUGIN_TITLE_FORMAT(cmd_title)+' '+ txt_resum[0]]
        txt_result.extend(txt_resum[1:])

        # XML output
        xml_result = Element('resum_rate', title = cmd_title)
        xml_result.append(xml_resum)

        thread_pool.join()
        return PluginBase.PluginResult(txt_result, xml_result)
Example #7
0
class ProxySpider(object):
    def __init__(self, output_file=True, output_db=True, output_filename="proxy-ip-list.csv"):
        # 初始化AutoLoad模块
        self.al = AutoLoad()
        # 初始化
        self.tp = None
        self.sd = None
        self.write_file_tp = None
        self.spider_threads = None
        self.save_data_threads = None
        # 获取参数
        self.output_file = output_file
        self.output_db = output_db
        self.output_filename = output_filename

    def load(self, *spiders):
        self.al.load(*spiders)

    def set_threads(self, spider_threads=0, save_data_threads=0):
        if spider_threads > 0:
            self.spider_threads = spider_threads
        if save_data_threads > 0:
            self.save_data_threads = save_data_threads

    def start(self):
        if not len(self.al.spiders):
            logger.error("No Spiders loaded. exit.")
            sys.exit(1)
        else:
            message = "Loaded spiders: "
            for s in self.al.spiders:
                message += str(s.__class__).split(".")[-1].split("'")[0] + ", "
            logger.info(message.strip(", "))
        # 创建线程池
        if self.spider_threads:
            self.tp = ThreadPool(self.spider_threads)
        else:
            self.tp = ThreadPool()
        for sp in self.al.spiders:
            # 将spider中的run方法添加到线程池中
            self.tp.add_function(sp.run)
        # 开始线程池
        self.tp.run(join=False)

        # 输出结果
        self.sd = SaveData(self.al.results, self.tp, use_file=self.output_file, use_database=self.output_db,
                           filename=self.output_filename)
        if self.save_data_threads:
            self.write_file_tp = ThreadPool(self.save_data_threads)
        else:
            self.write_file_tp = ThreadPool()
        self.write_file_tp = ThreadPool()
        self.write_file_tp.add_function(self.sd.write)
        self.write_file_tp.run()
    def _command_resum(self, target):
        """
        Tests the server for session resumption support using session IDs and
        TLS session tickets (RFC 5077).
        """
        NB_THREADS = 5
        MAX_RESUM = 5
        thread_pool = ThreadPool()
        
        for i in xrange(MAX_RESUM): # Test 5 resumptions with session IDs
            thread_pool.add_job((self._resume_with_session_id,
                                 (target,), 'session_id'))
        thread_pool.start(NB_THREADS)
        
        # Test TLS tickets support while threads are running
        try:
            (ticket_supported, ticket_reason) = self._resume_with_session_ticket(target)
            ticket_error = None
        except Exception as e:
            ticket_error = str(e.__class__.__module__) + '.' + \
                            str(e.__class__.__name__) + ' - ' + str(e)

        # Format session ID results
        (txt_resum, xml_resum) = self._format_resum_id_results(thread_pool, MAX_RESUM)

        if ticket_error:
            ticket_txt = 'Error: ' + ticket_error
        else:
            ticket_txt = 'Supported' if ticket_supported \
                                     else 'Not Supported - ' + ticket_reason+'.'
        
        cmd_title = 'Session Resumption'
        txt_result = [self.PLUGIN_TITLE_FORMAT.format(cmd_title)]
        RESUM_FORMAT = '      {0:<27} {1}'
        ERRORS_FORMAT ='        Error #{0}: {1}'
        
        txt_result.append(RESUM_FORMAT.format('With Session IDs:', txt_resum[0]))
        txt_result.extend(txt_resum[1:])
        txt_result.append(RESUM_FORMAT.format('With TLS Session Tickets:', ticket_txt))
        
        # XML output
        xml_resum_ticket_attr = {}
        if ticket_error:
            xml_resum_ticket_attr['error'] = ticket_error
        else:
            xml_resum_ticket_attr['isSupported'] = str(ticket_supported)
            if not ticket_supported:
                xml_resum_ticket_attr['reason'] = ticket_reason
        
        xml_resum_ticket = Element('sessionResumptionWithTLSTickets', attrib = xml_resum_ticket_attr)   
        xml_result = Element('resum', title=cmd_title)
        xml_result.append(xml_resum)
        xml_result.append(xml_resum_ticket)

        thread_pool.join()
        return PluginBase.PluginResult(txt_result, xml_result)
    def _command_resum_rate(self, target):
        """
        Performs 100 session resumptions with the server in order to estimate
        the session resumption rate.
        """
        # Create a thread pool and process the jobs
        NB_THREADS = 20
        MAX_RESUM = 100
        thread_pool = ThreadPool()
        for i in xrange(MAX_RESUM):
            thread_pool.add_job((self._resume_with_session_id, 
                                 (target, ('tlsv1'))))
        thread_pool.start(NB_THREADS)

        # Count successful resumptions      
        (nb_resum, nb_error) = self._count_resumptions(thread_pool)
        nb_failed = MAX_RESUM - nb_error - nb_resum

        # Text output
        resum_format = '{0} successful, {1} failed, {2} errors, {3} total attempts.'
        resum_txt = resum_format.format(str(nb_resum), str(nb_failed), 
                          str(nb_error), str(MAX_RESUM))
        
        cmd_title = 'Resumption Rate with Session IDs'
        txt_result = [self.PLUGIN_TITLE_FORMAT.format(cmd_title)+' '+resum_txt]

        # XML output
        xml_resum_attr = {'total' : str(MAX_RESUM),'successful' : str(nb_resum),
                          'failed' : str(nb_failed), 'errors' : str(nb_error)}
        
        xml_resum = Element('resum_rate', attrib = xml_resum_attr)  
        xml_result = Element(self.__class__.__name__, command = 'resum_rate',
                             title = cmd_title)
        xml_result.append(xml_resum)

        thread_pool.join()
        return PluginBase.PluginResult(txt_result, xml_result)
Example #10
0
    def process_task(self, target, command, arg):

        if arg == 'basic':
            txt_output_generator = self._get_basic_text
        elif arg == 'full':
            txt_output_generator = self._get_full_text
        else:
            raise Exception("PluginCertInfo: Unknown command.")

        (host, _, _, _) = target
        thread_pool = ThreadPool()

        if 'ca_file' in self._shared_settings and self._shared_settings[
                'ca_file']:
            AVAILABLE_TRUST_STORES[self._shared_settings['ca_file']] = (
                'Custom --ca_file', 'N/A')

        for (store_path, _) in AVAILABLE_TRUST_STORES.iteritems():
            # Try to connect with each trust store
            thread_pool.add_job((self._get_cert, (target, store_path)))

        # Start processing the jobs
        thread_pool.start(len(AVAILABLE_TRUST_STORES))

        # Store the results as they come
        x509_cert_chain = []
        (verify_dict, verify_dict_error, x509_cert,
         ocsp_response) = ({}, {}, None, None)

        for (job, result) in thread_pool.get_result():
            (_, (_, store_path)) = job
            (x509_cert_chain, verify_str, ocsp_response) = result
            # Store the returned verify string for each trust store
            x509_cert = x509_cert_chain[
                0]  # First cert is always the leaf cert
            store_info = AVAILABLE_TRUST_STORES[store_path]
            verify_dict[store_info] = verify_str

        if x509_cert is None:
            # This means none of the connections were successful. Get out
            for (job, exception) in thread_pool.get_error():
                raise exception

        # Store thread pool errors
        for (job, exception) in thread_pool.get_error():
            (_, (_, store_path)) = job
            error_msg = str(
                exception.__class__.__name__) + ' - ' + str(exception)

            store_info = AVAILABLE_TRUST_STORES[store_path]
            verify_dict_error[store_info] = error_msg

        thread_pool.join()

        # Results formatting
        # Text output - certificate info
        text_output = [self.PLUGIN_TITLE_FORMAT('Certificate - Content')]
        text_output.extend(txt_output_generator(x509_cert))

        # Text output - trust validation
        text_output.extend(
            ['', self.PLUGIN_TITLE_FORMAT('Certificate - Trust')])

        # Hostname validation
        if self._shared_settings['sni']:
            text_output.append(
                self.FIELD_FORMAT("SNI enabled with virtual domain:",
                                  self._shared_settings['sni']))
        # TODO: Use SNI name for validation when --sni was used
        host_val_dict = {
            X509_NAME_MATCHES_SAN: 'OK - Subject Alternative Name matches',
            X509_NAME_MATCHES_CN: 'OK - Common Name matches',
            X509_NAME_MISMATCH: 'FAILED - Certificate does NOT match ' + host
        }
        text_output.append(
            self.FIELD_FORMAT("Hostname Validation:",
                              host_val_dict[x509_cert.matches_hostname(host)]))

        # Path validation that was successful
        for ((store_name, store_version),
             verify_str) in verify_dict.iteritems():
            verify_txt = 'OK - Certificate is trusted' if (verify_str in 'ok') \
                else 'FAILED - Certificate is NOT Trusted: ' + verify_str

            # EV certs - Only Mozilla supported for now
            if (verify_str in 'ok') and ('Mozilla' in store_info):
                if self._is_ev_certificate(x509_cert):
                    verify_txt += ', Extended Validation'

            text_output.append(
                self.FIELD_FORMAT(
                    self.TRUST_FORMAT(store_name=store_name,
                                      store_version=store_version),
                    verify_txt))

        # Path validation that ran into errors
        for ((store_name, store_version),
             error_msg) in verify_dict_error.iteritems():
            verify_txt = 'ERROR: ' + error_msg
            text_output.append(
                self.FIELD_FORMAT(
                    self.TRUST_FORMAT(store_name=store_name,
                                      store_version=store_version),
                    verify_txt))

        # Print the Common Names within the certificate chain
        cns_in_cert_chain = []
        for cert in x509_cert_chain:
            cert_identity = self._extract_subject_cn_or_oun(cert)
            cns_in_cert_chain.append(cert_identity)

        text_output.append(
            self.FIELD_FORMAT('Certificate Chain Received:',
                              str(cns_in_cert_chain)))

        # Text output - OCSP stapling
        text_output.extend(
            ['', self.PLUGIN_TITLE_FORMAT('Certificate - OCSP Stapling')])
        text_output.extend(self._get_ocsp_text(ocsp_response))

        # XML output
        xml_output = Element(command,
                             argument=arg,
                             title='Certificate Information')

        # XML output - certificate chain:  always return the full certificate for each cert in the chain
        cert_chain_xml = Element('certificateChain')

        # First add the leaf certificate
        cert_chain_xml.append(
            self._format_cert_to_xml(x509_cert_chain[0], 'leaf',
                                     self._shared_settings['sni']))

        # Then add every other cert in the chain
        for cert in x509_cert_chain[1:]:
            cert_chain_xml.append(
                self._format_cert_to_xml(cert, 'intermediate',
                                         self._shared_settings['sni']))

        xml_output.append(cert_chain_xml)

        # XML output - trust
        trust_validation_xml = Element('certificateValidation')

        # Hostname validation
        is_hostname_valid = 'False' if (x509_cert.matches_hostname(host)
                                        == X509_NAME_MISMATCH) else 'True'
        host_validation_xml = Element(
            'hostnameValidation',
            serverHostname=host,
            certificateMatchesServerHostname=is_hostname_valid)
        trust_validation_xml.append(host_validation_xml)

        # Path validation - OK
        for ((store_name, store_version),
             verify_str) in verify_dict.iteritems():
            path_attrib_xml = {
                'usingTrustStore': store_name,
                'trustStoreVersion': store_version,
                'validationResult': verify_str
            }

            # EV certs - Only Mozilla supported for now
            if (verify_str in 'ok') and ('Mozilla' in store_info):
                path_attrib_xml['isExtendedValidationCertificate'] = str(
                    self._is_ev_certificate(x509_cert))

            trust_validation_xml.append(
                Element('pathValidation', attrib=path_attrib_xml))

        # Path validation - Errors
        for ((store_name, store_version),
             error_msg) in verify_dict_error.iteritems():
            path_attrib_xml = {
                'usingTrustStore': store_name,
                'trustStoreVersion': store_version,
                'error': error_msg
            }

            trust_validation_xml.append(
                Element('pathValidation', attrib=path_attrib_xml))

        xml_output.append(trust_validation_xml)

        # XML output - OCSP Stapling
        if ocsp_response is None:
            ocsp_attr_xml = {'isSupported': 'False'}
            ocsp_xml = Element('ocspStapling', attrib=ocsp_attr_xml)
        else:
            ocsp_attr_xml = {'isSupported': 'True'}
            ocsp_xml = Element('ocspStapling', attrib=ocsp_attr_xml)

            ocsp_resp_attr_xml = {
                'isTrustedByMozillaCAStore':
                str(ocsp_response.verify(MOZILLA_STORE_PATH))
            }
            ocsp_resp_xmp = Element('ocspResponse', attrib=ocsp_resp_attr_xml)
            for (key, value) in ocsp_response.as_dict().items():
                ocsp_resp_xmp.append(_keyvalue_pair_to_xml(key, value))
            ocsp_xml.append(ocsp_resp_xmp)

        xml_output.append(ocsp_xml)

        return PluginBase.PluginResult(text_output, xml_output)
    def process_task(self, target, command, args):

        if 'algorithm' in self._shared_settings and self._shared_settings[
                'algorithm']:
            algorithm = self._shared_settings['algorithm']
        else:
            algorithm = 'naive'

        logging.info("SUBSTART\t%s\t%s\t%s\t%s\t%s" %
                     (datetime.utcnow(), self._shared_settings['targets_in'],
                      target[0], command, algorithm))

        MAX_THREADS = 15
        sslVersionDict = {
            'sslv2': SSLV2,
            'sslv3': SSLV3,
            'tlsv1': TLSV1,
            'tlsv1_1': TLSV1_1,
            'tlsv1_2': TLSV1_2
        }

        result_dicts = {
            'preferredCipherSuite': {},
            'acceptedCipherSuites': {},
            'rejectedCipherSuites': {},
            'errors': {}
        }

        try:
            sslVersion = sslVersionDict[command]
        except KeyError:
            raise Exception("PluginOpenSSLCipherSuites: Unknown command.")

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

        NB_THREADS = min(len(cipher_list),
                         MAX_THREADS)  # One thread per cipher

        # Create a thread pool
        thread_pool = ThreadPool()
        # First add the "pref" job to only execute it once
        # Scan for the preferred cipher suite
        if algorithm != 'connopt':
            thread_pool.add_job((self._pref_ciphersuite, (target, sslVersion)))

        log_round_counter = 0

        while (len(result_dicts['acceptedCipherSuites']) +
               len(result_dicts['rejectedCipherSuites']) +
               len(result_dicts['errors']) < len(cipher_list)):

            log_round_counter += 1

            new_jobs = self._calculate_jobs(sslVersion, cipher_list,
                                            result_dicts, algorithm, target[2])
            for job in new_jobs:
                thread_pool.add_job(
                    (self._test_ciphersuite, (target, sslVersion, job)))

            # logging.debug("Adding following jobs:\n%s" % pprint.pformat(new_jobs))
            # logging.debug("%s: round=%d, new_jobs=%d, algorithm=%s" % (sslVersion,
            #                                                           log_round_counter,
            #                                                           len(new_jobs),
            #                                                           algorithm))

            # Start processing the jobs
            thread_pool.start(NB_THREADS)

            # Store the results as they come
            for completed_job in thread_pool.get_result():
                (job, results) = completed_job
                for result in results:
                    (result_type, ssl_cipher, keysize, dh_infos, msg) = result
                    (result_dicts[result_type])[ssl_cipher] = (msg, keysize,
                                                               dh_infos)

            # Store thread pool errors
            for failed_job in thread_pool.get_error():
                (job, exception) = failed_job
                # job[1][2] is a list of cipher suites now
                ssl_ciphers = job[1][2]
                error_msg = str(
                    exception.__class__.__name__) + ' - ' + str(exception)
                for ssl_cipher in ssl_ciphers:
                    result_dicts['errors'][ssl_cipher] = (error_msg, None,
                                                          None)

            thread_pool.join()
            # Reset thread pool
            thread_pool = ThreadPool()

            # logging.debug("ciphers total %d results a: %d, r: %d, e: %d after %d connections" % (
            #    len(cipher_list),
            #    len(result_dicts['acceptedCipherSuites']),
            #    len(result_dicts['rejectedCipherSuites']),
            #    len(result_dicts['errors']),
            #    self.log_connection_counter))

        timedelta = datetime.now() - self.log_starttime
        logging.info("RESULT\t%s\t%s\t%s" % (target[0], command, ",".join(
            stats.get_pattern_for_result_dict(sslVersion, result_dicts))))
        logging.info("SUBEND\t%s\t%s\t%s\t%s\t%s\t%s\t%s" %
                     (datetime.utcnow(), self._shared_settings['targets_in'],
                      target[0], command, algorithm, timedelta.total_seconds(),
                      self.log_connection_counter))

        increment(self.log_connection_counter)

        # Generate results
        return PluginBase.PluginResult(
            self._generate_text_output(result_dicts, command),
            self._generate_xml_output(result_dicts, command))
    def process_task(self, target, command, args):

        MAX_THREADS = 15
        sslVersionDict = {"sslv2": SSLV2, "sslv3": SSLV3, "tlsv1": TLSV1, "tlsv1_1": TLSV1_1, "tlsv1_2": TLSV1_2}
        try:
            sslVersion = sslVersionDict[command]
        except KeyError:
            raise Exception("PluginOpenSSLCipherSuites: Unknown command.")

        # Get the list of available cipher suites for the given ssl version
        sslClient = SslClient(sslVersion=sslVersion)
        sslClient.set_cipher_list("ALL:COMPLEMENTOFALL")
        cipher_list = sslClient.get_cipher_list()

        # Create a thread pool
        NB_THREADS = min(len(cipher_list), MAX_THREADS)  # One thread per cipher
        thread_pool = ThreadPool()

        cipher_whitelist = [
            "ECDHE-RSA-AES128-GCM-SHA256",
            "ECDHE-ECDSA-AES128-GCM-SHA256",
            "ECDHE-RSA-AES256-GCM-SHA384",
            "ECDHE-ECDSA-AES256-GCM-SHA384",
            "DHE-RSA-AES128-GCM-SHA256",
            "DHE-DSS-AES128-GCM-SHA256",
            "kEDH+AESGCM",
            "ECDHE-RSA-AES128-SHA256",
            "ECDHE-ECDSA-AES128-SHA256",
            "ECDHE-RSA-AES128-SHA",
            "ECDHE-ECDSA-AES128-SHA",
            "ECDHE-RSA-AES256-SHA384",
            "ECDHE-ECDSA-AES256-SHA384",
            "ECDHE-RSA-AES256-SHA",
            "ECDHE-ECDSA-AES256-SHA",
            "DHE-RSA-AES128-SHA256",
            "DHE-RSA-AES128-SHA",
            "DHE-DSS-AES128-SHA256",
            "DHE-RSA-AES256-SHA256",
            "DHE-DSS-AES256-SHA",
            "DHE-RSA-AES256-SHA",
        ]

        cipher_blacklist = ["RC4", "EXPORT", "DES", "aNULL", "eNULL", "MD5"]
        version_whitelist = ["TLSV1_1", "TLSV1_2"]
        version_blacklist = ["SSLV2", "SSLV3"]
        cdict = {"whitelist": cipher_whitelist, "blacklist": cipher_blacklist}
        verdict = {"whitelist": version_whitelist, "blacklist": version_blacklist}

        # Scan for every available cipher suite
        for cipher in cipher_list:
            thread_pool.add_job((self._test_ciphersuite, (target, sslVersion, cipher, cdict)))

        # Scan for the preferred cipher suite
        thread_pool.add_job((self._pref_ciphersuite, (target, sslVersion, cdict)))

        # Start processing the jobs
        thread_pool.start(NB_THREADS)

        result_dicts = {
            "preferredCipherSuite": {},
            "acceptedCipherSuites": {},
            "rejectedCipherSuites": {},
            "errors": {},
        }

        # Store the results as they come
        for completed_job in thread_pool.get_result():
            (job, result) = completed_job
            if result is not None:
                (result_type, ssl_cipher, keysize, dh_infos, msg) = result
                (result_dicts[result_type])[ssl_cipher] = (msg, keysize, dh_infos)

        # Store thread pool errors
        for failed_job in thread_pool.get_error():
            (job, exception) = failed_job
            ssl_cipher = str(job[1][2])
            error_msg = str(exception.__class__.__name__) + " - " + str(exception)
            result_dicts["errors"][ssl_cipher] = (error_msg, None, None)

        thread_pool.join()

        # Generate results
        return PluginBase.PluginResult(
            self._generate_text_output(result_dicts, command, verdict),
            self._generate_xml_output(result_dicts, command, verdict),
        )
Example #13
0
    def process_task(self, target, command, arg):

        if arg == 'basic':
            txt_output_generator = self._get_basic_text
        elif arg == 'full':
            txt_output_generator = self._get_full_text
        else:
            raise Exception("PluginCertInfo: Unknown command.")

        (host, _, _, _) = target
        thread_pool = ThreadPool()

        if 'ca_file' in self._shared_settings and self._shared_settings['ca_file']:
            AVAILABLE_TRUST_STORES[self._shared_settings['ca_file']] = ('Custom --ca_file', 'N/A')

        for (store_path, _) in AVAILABLE_TRUST_STORES.iteritems():
            # Try to connect with each trust store
            thread_pool.add_job((self._get_cert, (target, store_path)))

        # Start processing the jobs
        thread_pool.start(len(AVAILABLE_TRUST_STORES))

        # Store the results as they come
        x509_cert_chain = []
        (verify_dict, verify_dict_error, x509_cert, ocsp_response) = ({}, {}, None, None)

        for (job, result) in thread_pool.get_result():
            (_, (_, store_path)) = job
            (x509_cert_chain, verify_str, ocsp_response) = result
            # Store the returned verify string for each trust store
            x509_cert = x509_cert_chain[0]  # First cert is always the leaf cert
            store_info = AVAILABLE_TRUST_STORES[store_path]
            verify_dict[store_info] = verify_str

        if x509_cert is None:
            # This means none of the connections were successful. Get out
            for (job, exception) in thread_pool.get_error():
                raise exception

        # Store thread pool errors
        for (job, exception) in thread_pool.get_error():
            (_, (_, store_path)) = job
            error_msg = str(exception.__class__.__name__) + ' - ' + str(exception)

            store_info = AVAILABLE_TRUST_STORES[store_path]
            verify_dict_error[store_info] = error_msg

        thread_pool.join()


        # Results formatting
        # Text output - certificate info
        text_output = [self.PLUGIN_TITLE_FORMAT('Certificate - Content')]
        text_output.extend(txt_output_generator(x509_cert))

        # Text output - trust validation
        text_output.extend(['', self.PLUGIN_TITLE_FORMAT('Certificate - Trust')])

        # Hostname validation
        if self._shared_settings['sni']:
            text_output.append(self.FIELD_FORMAT("SNI enabled with virtual domain:", self._shared_settings['sni']))
        # TODO: Use SNI name for validation when --sni was used
        host_val_dict = {
            X509_NAME_MATCHES_SAN: 'OK - Subject Alternative Name matches',
            X509_NAME_MATCHES_CN: 'OK - Common Name matches',
            X509_NAME_MISMATCH: 'FAILED - Certificate does NOT match ' + host
        }
        text_output.append(self.FIELD_FORMAT("Hostname Validation:", host_val_dict[x509_cert.matches_hostname(host)]))

        # Path validation that was successful
        for ((store_name, store_version), verify_str) in verify_dict.iteritems():
            verify_txt = 'OK - Certificate is trusted' if (verify_str in 'ok') \
                else 'FAILED - Certificate is NOT Trusted: ' + verify_str

            # EV certs - Only Mozilla supported for now
            if (verify_str in 'ok') and ('Mozilla' in store_info):
                if self._is_ev_certificate(x509_cert):
                    verify_txt += ', Extended Validation'

            text_output.append(self.FIELD_FORMAT(self.TRUST_FORMAT(store_name=store_name,
                                                                   store_version=store_version),
                                                 verify_txt))


        # Path validation that ran into errors
        for ((store_name, store_version), error_msg) in verify_dict_error.iteritems():
            verify_txt = 'ERROR: ' + error_msg
            text_output.append(self.FIELD_FORMAT(self.TRUST_FORMAT(store_name=store_name,
                                                                   store_version=store_version),
                                                 verify_txt))

        # Print the Common Names within the certificate chain
        cns_in_cert_chain = []
        for cert in x509_cert_chain:
            cert_identity = self._extract_subject_cn_or_oun(cert)
            cns_in_cert_chain.append(cert_identity)

        text_output.append(self.FIELD_FORMAT('Certificate Chain Received:', str(cns_in_cert_chain)))


        # Text output - OCSP stapling
        text_output.extend(['', self.PLUGIN_TITLE_FORMAT('Certificate - OCSP Stapling')])
        text_output.extend(self._get_ocsp_text(ocsp_response))


        # XML output
        xml_output = Element(command, argument=arg, title='Certificate Information')

        # XML output - certificate chain:  always return the full certificate for each cert in the chain
        cert_chain_xml = Element('certificateChain')

        # XML output - expiration date

        # em begin
        cert_dict = x509_cert.as_dict()
        expiration_date = gmtime(cert_time_to_seconds(cert_dict['validity']['notAfter']))
        expire_days = (date(expiration_date.tm_year, expiration_date.tm_mon, expiration_date.tm_mday) - date.today()).days
        expiration_date_xml = Element("expirationDate", notAfter=cert_dict['validity']['notAfter'],
                                      expiresDays=expire_days)
        xml_output.append(expiration_date_xml)
        # em end

        # First add the leaf certificate
        cert_chain_xml.append(self._format_cert_to_xml(x509_cert_chain[0], 'leaf', self._shared_settings['sni']))

        # Then add every other cert in the chain
        for cert in x509_cert_chain[1:]:
            cert_chain_xml.append(self._format_cert_to_xml(cert, 'intermediate', self._shared_settings['sni']))

        xml_output.append(cert_chain_xml)

        # XML output - trust
        trust_validation_xml = Element('certificateValidation')

        # Hostname validation
        is_hostname_valid = 'False' if (x509_cert.matches_hostname(host) == X509_NAME_MISMATCH) else 'True'
        host_validation_xml = Element('hostnameValidation', serverHostname=host,
                                      certificateMatchesServerHostname=is_hostname_valid)
        trust_validation_xml.append(host_validation_xml)

        # Path validation - OK
        for ((store_name, store_version), verify_str) in verify_dict.iteritems():
            path_attrib_xml = {
                'usingTrustStore': store_name,
                'trustStoreVersion': store_version,
                'validationResult': verify_str
            }

            # EV certs - Only Mozilla supported for now
            if (verify_str in 'ok') and ('Mozilla' in store_info):
                    path_attrib_xml['isExtendedValidationCertificate'] = str(self._is_ev_certificate(x509_cert))

            trust_validation_xml.append(Element('pathValidation', attrib=path_attrib_xml))

        # Path validation - Errors
        for ((store_name, store_version), error_msg) in verify_dict_error.iteritems():
            path_attrib_xml = {
                'usingTrustStore': store_name,
                'trustStoreVersion': store_version,
                'error': error_msg
            }

            trust_validation_xml.append(Element('pathValidation', attrib=path_attrib_xml))


        xml_output.append(trust_validation_xml)


        # XML output - OCSP Stapling
        if ocsp_response is None:
            ocsp_attr_xml = {'isSupported': 'False'}
            ocsp_xml = Element('ocspStapling', attrib=ocsp_attr_xml)
        else:
            ocsp_attr_xml = {'isSupported': 'True'}
            ocsp_xml = Element('ocspStapling', attrib=ocsp_attr_xml)

            ocsp_resp_attr_xml = {'isTrustedByMozillaCAStore': str(ocsp_response.verify(MOZILLA_STORE_PATH))}
            ocsp_resp_xmp = Element('ocspResponse', attrib=ocsp_resp_attr_xml)
            for (key, value) in ocsp_response.as_dict().items():
                ocsp_resp_xmp.append(_keyvalue_pair_to_xml(key, value))
            ocsp_xml.append(ocsp_resp_xmp)

        xml_output.append(ocsp_xml)

        return PluginBase.PluginResult(text_output, xml_output)
Example #14
0
    def process_task(self, target, command, arg):

        if arg == "basic":
            txt_output_generator = self._get_basic_text
        elif arg == "full":
            txt_output_generator = self._get_full_text
        else:
            raise Exception("PluginCertInfo: Unknown command.")

        (host, _, _, _) = target
        thread_pool = ThreadPool()

        if "ca_file" in self._shared_settings and self._shared_settings["ca_file"]:
            AVAILABLE_TRUST_STORES[self._shared_settings["ca_file"]] = ("Custom --ca_file", "N/A")

        for (store_path, _) in AVAILABLE_TRUST_STORES.iteritems():
            # Try to connect with each trust store
            thread_pool.add_job((self._get_cert, (target, store_path)))

        # Start processing the jobs
        thread_pool.start(len(AVAILABLE_TRUST_STORES))

        # Store the results as they come
        x509_cert_chain = []
        (verify_dict, verify_dict_error, x509_cert, ocsp_response) = ({}, {}, None, None)

        for (job, result) in thread_pool.get_result():
            (_, (_, store_path)) = job
            (x509_cert_chain, verify_str, ocsp_response) = result
            # Store the returned verify string for each trust store
            x509_cert = x509_cert_chain[0]  # First cert is always the leaf cert
            store_info = AVAILABLE_TRUST_STORES[store_path]
            verify_dict[store_info] = verify_str

        if x509_cert is None:
            # This means none of the connections were successful. Get out
            for (job, exception) in thread_pool.get_error():
                raise exception

        # Store thread pool errors
        for (job, exception) in thread_pool.get_error():
            (_, (_, store_path)) = job
            error_msg = str(exception.__class__.__name__) + " - " + str(exception)

            store_info = AVAILABLE_TRUST_STORES[store_path]
            verify_dict_error[store_info] = error_msg

        thread_pool.join()

        # Results formatting
        # Text output - certificate info
        text_output = [self.PLUGIN_TITLE_FORMAT("Certificate - Content")]
        text_output.extend(txt_output_generator(x509_cert))

        # Text output - trust validation
        text_output.extend(["", self.PLUGIN_TITLE_FORMAT("Certificate - Trust")])

        # Hostname validation
        if self._shared_settings["sni"]:
            text_output.append(self.FIELD_FORMAT("SNI enabled with virtual domain:", self._shared_settings["sni"]))
        # TODO: Use SNI name for validation when --sni was used
        host_val_dict = {
            X509_NAME_MATCHES_SAN: "OK - Subject Alternative Name matches",
            X509_NAME_MATCHES_CN: "OK - Common Name matches",
            X509_NAME_MISMATCH: "FAILED - Certificate does NOT match " + host,
        }
        text_output.append(self.FIELD_FORMAT("Hostname Validation:", host_val_dict[x509_cert.matches_hostname(host)]))

        # Path validation that was successful
        for ((store_name, store_version), verify_str) in verify_dict.iteritems():
            verify_txt = (
                "OK - Certificate is trusted"
                if (verify_str in "ok")
                else "FAILED - Certificate is NOT Trusted: " + verify_str
            )

            # EV certs - Only Mozilla supported for now
            if (verify_str in "ok") and ("Mozilla" in store_info):
                if self._is_ev_certificate(x509_cert):
                    verify_txt += ", Extended Validation"

            text_output.append(
                self.FIELD_FORMAT(self.TRUST_FORMAT(store_name=store_name, store_version=store_version), verify_txt)
            )

        # Path validation that ran into errors
        for ((store_name, store_version), error_msg) in verify_dict_error.iteritems():
            verify_txt = "ERROR: " + error_msg
            text_output.append(
                self.FIELD_FORMAT(self.TRUST_FORMAT(store_name=store_name, store_version=store_version), verify_txt)
            )

        # Print the Common Names within the certificate chain
        cns_in_cert_chain = []
        for cert in x509_cert_chain:
            cert_identity = self._extract_subject_cn_or_oun(cert)
            cns_in_cert_chain.append(cert_identity)

        text_output.append(self.FIELD_FORMAT("Certificate Chain Received:", str(cns_in_cert_chain)))

        # Text output - OCSP stapling
        text_output.extend(["", self.PLUGIN_TITLE_FORMAT("Certificate - OCSP Stapling")])
        text_output.extend(self._get_ocsp_text(ocsp_response))

        # XML output
        xml_output = Element(command, argument=arg, title="Certificate Information")

        # XML output - certificate chain:  always return the full certificate for each cert in the chain
        cert_chain_xml = Element("certificateChain")

        # First add the leaf certificate
        cert_chain_xml.append(self._format_cert_to_xml(x509_cert_chain[0], "leaf", self._shared_settings["sni"]))

        # Then add every other cert in the chain
        for cert in x509_cert_chain[1:]:
            cert_chain_xml.append(self._format_cert_to_xml(cert, "intermediate", self._shared_settings["sni"]))

        xml_output.append(cert_chain_xml)

        # XML output - trust
        trust_validation_xml = Element("certificateValidation")

        # Hostname validation
        is_hostname_valid = "False" if (x509_cert.matches_hostname(host) == X509_NAME_MISMATCH) else "True"
        host_validation_xml = Element(
            "hostnameValidation", serverHostname=host, certificateMatchesServerHostname=is_hostname_valid
        )
        trust_validation_xml.append(host_validation_xml)

        # Path validation - OK
        for ((store_name, store_version), verify_str) in verify_dict.iteritems():
            path_attrib_xml = {
                "usingTrustStore": store_name,
                "trustStoreVersion": store_version,
                "validationResult": verify_str,
            }

            # EV certs - Only Mozilla supported for now
            if (verify_str in "ok") and ("Mozilla" in store_info):
                path_attrib_xml["isExtendedValidationCertificate"] = str(self._is_ev_certificate(x509_cert))

            trust_validation_xml.append(Element("pathValidation", attrib=path_attrib_xml))

        # Path validation - Errors
        for ((store_name, store_version), error_msg) in verify_dict_error.iteritems():
            path_attrib_xml = {"usingTrustStore": store_name, "trustStoreVersion": store_version, "error": error_msg}

            trust_validation_xml.append(Element("pathValidation", attrib=path_attrib_xml))

        xml_output.append(trust_validation_xml)

        # XML output - OCSP Stapling
        if ocsp_response is None:
            ocsp_attr_xml = {"isSupported": "False"}
        else:
            ocsp_attr_xml = {"isSupported": "True"}
        ocsp_xml = Element("ocspStapling", attrib=ocsp_attr_xml)

        if ocsp_response:
            try:
                ocsp_resp_trusted = str(ocsp_response.verify(MOZILLA_STORE_PATH))

            except OpenSSLError as e:
                if "certificate verify error" in str(e):
                    ocsp_resp_trusted = "False"
                else:
                    raise

            ocsp_resp_attr_xml = {"isTrustedByMozillaCAStore": ocsp_resp_trusted}
            ocsp_resp_xmp = Element("ocspResponse", attrib=ocsp_resp_attr_xml)
            for (key, value) in ocsp_response.as_dict().items():
                ocsp_resp_xmp.append(_keyvalue_pair_to_xml(key, value))

            ocsp_xml.append(ocsp_resp_xmp)

        xml_output.append(ocsp_xml)

        return PluginBase.PluginResult(text_output, xml_output)
Example #15
0
    def process_task(self, target, command, args):

        MAX_THREADS = 30

        if command in ['sslv2', 'sslv3', 'tlsv1', 'tlsv1_1', 'tlsv1_2']:
            ssl_version = command
        else:
            raise Exception("PluginOpenSSLCipherSuites: Unknown command.")

        # Get the list of available cipher suites for the given ssl version
        ctSSL_initialize(multithreading=True)
        ctx = SSL_CTX.SSL_CTX(ssl_version)
        ctx.set_cipher_list('ALL:NULL:@STRENGTH')
        ssl = SSL.SSL(ctx)
        cipher_list = ssl.get_cipher_list()

        # Create a thread pool
        NB_THREADS = min(len(cipher_list),
                         MAX_THREADS)  # One thread per cipher
        thread_pool = ThreadPool()

        # Scan for every available cipher suite
        for cipher in cipher_list:
            thread_pool.add_job(
                (self._test_ciphersuite, (target, ssl_version, cipher)))

        # Scan for the preferred cipher suite
        thread_pool.add_job((self._pref_ciphersuite, (target, ssl_version)))

        # Start processing the jobs
        thread_pool.start(NB_THREADS)

        result_dicts = {
            'preferredCipherSuite': {},
            'acceptedCipherSuites': {},
            'rejectedCipherSuites': {},
            'errors': {}
        }

        # Store the results as they come
        for completed_job in thread_pool.get_result():
            (job, result) = completed_job
            if result is not None:
                (result_type, ssl_cipher, keysize, msg) = result
                (result_dicts[result_type])[ssl_cipher] = (msg, keysize)

        # Store thread pool errors
        for failed_job in thread_pool.get_error():
            (job, exception) = failed_job
            ssl_cipher = str(job[1][2])
            error_msg = str(exception.__class__.__module__) + '.' \
                        + str(exception.__class__.__name__) + ' - ' + str(exception)
            result_dicts['errors'][ssl_cipher] = (error_msg, None)

        thread_pool.join()
        ctSSL_cleanup()

        # Generate results
        return PluginBase.PluginResult(
            self._generate_txt_result(result_dicts, command),
            self._generate_xml_result(result_dicts, command))
    def process_task(self, target, command, args):

        MAX_THREADS = 15
        sslVersionDict = {'sslv2': SSLV2,
                       'sslv3': SSLV3,
                       'tlsv1': TLSV1,
                       'tlsv1_1': TLSV1_1,
                       'tlsv1_2': TLSV1_2}
        try:
            sslVersion = sslVersionDict[command]
        except KeyError:
            raise Exception("PluginOpenSSLCipherSuites: Unknown command.")

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

        # Create a thread pool
        NB_THREADS = min(len(cipher_list), MAX_THREADS) # One thread per cipher
        thread_pool = ThreadPool()

        # Scan for every available cipher suite
        for cipher in cipher_list:
            thread_pool.add_job((self._test_ciphersuite,
                                 (target, sslVersion, cipher)))

        # Scan for the preferred cipher suite
        thread_pool.add_job((self._pref_ciphersuite,
                             (target, sslVersion)))

        # Start processing the jobs
        thread_pool.start(NB_THREADS)

        result_dicts = {'preferredCipherSuite':{}, 'acceptedCipherSuites':{},
                        'rejectedCipherSuites':{}, 'errors':{}}

        # Store the results as they come
        for completed_job in thread_pool.get_result():
            (job, result) = completed_job
            if result is not None:
                (result_type, ssl_cipher, keysize, msg) = result
                (result_dicts[result_type])[ssl_cipher] = (msg, keysize)

        # Store thread pool errors
        for failed_job in thread_pool.get_error():
            (job, exception) = failed_job
            ssl_cipher = str(job[1][2])
            error_msg = str(exception.__class__.__name__) + ' - ' + str(exception)
            result_dicts['errors'][ssl_cipher] = (error_msg, None)

        thread_pool.join()

        # Generate results
        return PluginBase.PluginResult(self._generate_text_output(result_dicts, command),
                                       self._generate_xml_output(result_dicts, command))
Example #17
0
    def process_task(self, target, command, arg):

        if arg == 'basic':
            textFunction = self._get_basic_text
        elif arg == 'full':
            textFunction = self._get_full_text
        else:
            raise Exception("PluginCertInfo: Unknown command.")

        (host, _, _, _) = target
        threadPool = ThreadPool()

        for (storePath, _) in AVAILABLE_TRUST_STORES.iteritems():
            # Try to connect with each trust store
            threadPool.add_job((self._get_cert, (target, storePath)))

        # Start processing the jobs
        threadPool.start(len(AVAILABLE_TRUST_STORES))

        # Store the results as they come
        (verifyDict, verifyDictErr, x509Cert, ocspResp) = ({}, {}, None, None)

        for (job, result) in threadPool.get_result():
            (_, (_, storePath)) = job
            (x509Chain, verifyStr, ocspResp) = result
            # Store the returned verify string for each trust store
            x509Cert = x509Chain[0]  # First cert is always the leaf cert
            storeName = AVAILABLE_TRUST_STORES[storePath]
            verifyDict[storeName] = verifyStr

        if x509Cert is None:
            # This means none of the connections were successful. Get out
            for (job, exception) in threadPool.get_error():
                raise exception

        # Store thread pool errors
        for (job, exception) in threadPool.get_error():
            (_, (_, storePath)) = job
            errorMsg = str(exception.__class__.__name__) + ' - ' \
                        + str(exception)

            storeName = AVAILABLE_TRUST_STORES[storePath]
            verifyDictErr[storeName] = errorMsg

        threadPool.join()

        # Results formatting
        # Text output - certificate info
        outputTxt = [self.PLUGIN_TITLE_FORMAT('Certificate - Content')]
        outputTxt.extend(textFunction(x509Cert))

        # Text output - trust validation
        outputTxt.extend(['', self.PLUGIN_TITLE_FORMAT('Certificate - Trust')])

        # Hostname validation
        if self._shared_settings['sni']:
            outputTxt.append(
                self.FIELD_FORMAT("SNI enabled with virtual domain:",
                                  self._shared_settings['sni']))
        # TODO: Use SNI name for validation when --sni was used
        hostValDict = {
            X509_NAME_MATCHES_SAN: 'OK - Subject Alternative Name matches',
            X509_NAME_MATCHES_CN: 'OK - Common Name matches',
            X509_NAME_MISMATCH: 'FAILED - Certificate does NOT match ' + host
        }
        outputTxt.append(
            self.FIELD_FORMAT("Hostname Validation:",
                              hostValDict[x509Cert.matches_hostname(host)]))

        # Path validation that was successful
        for (storeName, verifyStr) in verifyDict.iteritems():
            verifyTxt = 'OK - Certificate is trusted' if (
                verifyStr in 'ok'
            ) else 'FAILED - Certificate is NOT Trusted: ' + verifyStr

            # EV certs - Only Mozilla supported for now
            if (verifyStr in 'ok') and ('Mozilla' in storeName):
                if (self._is_ev_certificate(x509Cert)):
                    verifyTxt += ', Extended Validation'
            outputTxt.append(
                self.FIELD_FORMAT(self.TRUST_FORMAT(storeName), verifyTxt))

        # Path validation that ran into errors
        for (storeName, errorMsg) in verifyDictErr.iteritems():
            verifyTxt = 'ERROR: ' + errorMsg
            outputTxt.append(
                self.FIELD_FORMAT(self.TRUST_FORMAT(storeName), verifyTxt))

        # Print the Common Names within the certificate chain
        certChainCNs = []
        for cert in x509Chain:
            certIdentity = self._extract_subject_CN_or_OUN(cert)
            certChainCNs.append(certIdentity)

        outputTxt.append(
            self.FIELD_FORMAT('Certificate Chain Received:',
                              str(certChainCNs)))

        # Text output - OCSP stapling
        outputTxt.extend(
            ['', self.PLUGIN_TITLE_FORMAT('Certificate - OCSP Stapling')])
        outputTxt.extend(self._get_ocsp_text(ocspResp))

        # XML output
        outputXml = Element(command,
                            argument=arg,
                            title='Certificate Information')

        # XML output - certificate chain:  always return the full certificate for each cert in the chain
        chainXml = Element('certificateChain')

        # First add the leaf certificate
        chainXml.append(
            self._format_cert_to_xml(x509Chain[0], 'leaf',
                                     self._shared_settings['sni']))

        # Then add every other cert in the chain
        for cert in x509Chain[1:]:
            chainXml.append(
                self._format_cert_to_xml(cert, 'intermediate',
                                         self._shared_settings['sni']))

        outputXml.append(chainXml)

        # XML output - trust
        trustXml = Element('certificateValidation')

        # Hostname validation
        hostValBool = 'False' if (x509Cert.matches_hostname(host) == X509_NAME_MISMATCH) \
                              else 'True'
        hostXml = Element('hostnameValidation',
                          serverHostname=host,
                          certificateMatchesServerHostname=hostValBool)
        trustXml.append(hostXml)

        # Path validation - OK
        for (storeName, verifyStr) in verifyDict.iteritems():
            pathXmlAttrib = {
                'usingTrustStore': storeName,
                'validationResult': verifyStr
            }

            # EV certs - Only Mozilla supported for now
            if (verifyStr in 'ok') and ('Mozilla' in storeName):
                pathXmlAttrib['isExtendedValidationCertificate'] = str(
                    self._is_ev_certificate(x509Cert))

            trustXml.append(Element('pathValidation', attrib=pathXmlAttrib))

        # Path validation - Errors
        for (storeName, errorMsg) in verifyDictErr.iteritems():
            pathXmlAttrib = {'usingTrustStore': storeName, 'error': errorMsg}

            trustXml.append(Element('pathValidation', attrib=pathXmlAttrib))

        outputXml.append(trustXml)

        # XML output - OCSP Stapling
        if ocspResp is None:
            oscpAttr = {'error': 'Server did not send back an OCSP response'}
            ocspXml = Element('ocspStapling', attrib=oscpAttr)
        else:
            oscpAttr = {
                'isTrustedByMozillaCAStore':
                str(ocspResp.verify(MOZILLA_STORE_PATH))
            }
            ocspXml = Element('ocspResponse', attrib=oscpAttr)

            for (key, value) in ocspResp.as_dict().items():
                ocspXml.append(_keyvalue_pair_to_xml(key, value))

        outputXml.append(ocspXml)

        return PluginBase.PluginResult(outputTxt, outputXml)
Example #18
0
    def process_task(self, target, command, arg):

        if arg == 'basic':
            textFunction  = self._get_basic_text
        elif arg == 'full':
            textFunction = self._get_full_text
        else:
            raise Exception("PluginCertInfo: Unknown command.")

        (host, _, _, _) = target
        threadPool = ThreadPool()

        for (storePath, _) in AVAILABLE_TRUST_STORES.iteritems():
            # Try to connect with each trust store
            threadPool.add_job((self._get_cert, (target, storePath)))

        # Start processing the jobs
        threadPool.start(len(AVAILABLE_TRUST_STORES))

        # Store the results as they come
        (verifyDict, verifyDictErr, x509Cert, ocspResp)  = ({}, {}, None, None)

        for (job, result) in threadPool.get_result():
            (_, (_, storePath)) = job
            (x509Chain, verifyStr, ocspResp) = result
            # Store the returned verify string for each trust store
            x509Cert = x509Chain[0] # First cert is always the leaf cert
            storeName = AVAILABLE_TRUST_STORES[storePath]
            verifyDict[storeName] = verifyStr

        if x509Cert is None:
            # This means none of the connections were successful. Get out
            for (job, exception) in threadPool.get_error():
                raise exception

        # Store thread pool errors
        for (job, exception) in threadPool.get_error():
            (_, (_, storePath)) = job
            errorMsg = str(exception.__class__.__name__) + ' - ' \
                        + str(exception)

            storeName = AVAILABLE_TRUST_STORES[storePath]
            verifyDictErr[storeName] = errorMsg

        threadPool.join()


        # Results formatting
        # Text output - certificate info
        outputTxt = [self.PLUGIN_TITLE_FORMAT('Certificate - Content')]
        outputTxt.extend(textFunction(x509Cert))

        # Text output - trust validation
        outputTxt.extend(['', self.PLUGIN_TITLE_FORMAT('Certificate - Trust')])

        # Hostname validation
        if self._shared_settings['sni']:
            outputTxt.append(self.FIELD_FORMAT("SNI enabled with virtual domain:",
                                               self._shared_settings['sni']))
        # TODO: Use SNI name for validation when --sni was used
        hostValDict = {
            X509_NAME_MATCHES_SAN : 'OK - Subject Alternative Name matches',
            X509_NAME_MATCHES_CN :  'OK - Common Name matches',
            X509_NAME_MISMATCH :    'FAILED - Certificate does NOT match ' + host
        }
        outputTxt.append(self.FIELD_FORMAT("Hostname Validation:",
                                            hostValDict[x509Cert.matches_hostname(host)]))

        # Path validation that was successful
        for (storeName, verifyStr) in verifyDict.iteritems():
            verifyTxt = 'OK - Certificate is trusted' if (verifyStr in 'ok') else 'FAILED - Certificate is NOT Trusted: ' + verifyStr

            # EV certs - Only Mozilla supported for now
            if (verifyStr in 'ok') and ('Mozilla' in storeName):
                if (self._is_ev_certificate(x509Cert)):
                    verifyTxt += ', Extended Validation'
            outputTxt.append(self.FIELD_FORMAT(self.TRUST_FORMAT(storeName), verifyTxt))


        # Path validation that ran into errors
        for (storeName, errorMsg) in verifyDictErr.iteritems():
            verifyTxt = 'ERROR: ' + errorMsg
            outputTxt.append(self.FIELD_FORMAT(self.TRUST_FORMAT(storeName), verifyTxt))

        # Print the Common Names within the certificate chain
        certChainCNs = []
        for cert in x509Chain:
            try: # Extract the CN if there's one
                certName = cert.as_dict()['subject']['commonName']
            except KeyError:
                # If no common name, display the organizational unit instead
                try:
                    certName = cert.as_dict()['subject']['organizationalUnitName']
                except KeyError:
                    # Give up
                    certName = 'No Common Name'

            certChainCNs.append(certName)

        outputTxt.append(self.FIELD_FORMAT('Certificate Chain:', str(certChainCNs)))


        # Text output - OCSP stapling
        outputTxt.extend(['', self.PLUGIN_TITLE_FORMAT('Certificate - OCSP Stapling')])
        outputTxt.extend(self._get_ocsp_text(ocspResp))


        # XML output
        outputXml = Element(command, argument = arg, title = 'Certificate Information')

        # XML output - certificate chain:  always return the full certificate for each cert in the chain
        chainXml = Element('certificateChain')

        # First add the leaf certificate
        chainXml.append(self._format_cert_to_xml(x509Chain[0], 'leaf'))

        # Then add every other cert in the chain
        for cert in x509Chain[1:]:
            chainXml.append(self._format_cert_to_xml(cert, 'intermediate'))

        outputXml.append(chainXml)


        # XML output - trust
        trustXml = Element('certificateValidation')

        # Hostname validation
        hostValBool = 'False' if (x509Cert.matches_hostname(host) == X509_NAME_MISMATCH) \
                              else 'True'
        hostXml = Element('hostnameValidation', serverHostname = host,
                           certificateMatchesServerHostname = hostValBool)
        trustXml.append(hostXml)

        # Path validation - OK
        for (storeName, verifyStr) in verifyDict.iteritems():
            pathXmlAttrib = { 'usingTrustStore' : storeName,
                              'validationResult' : verifyStr}

            # EV certs - Only Mozilla supported for now
            if (verifyStr in 'ok') and ('Mozilla' in storeName):
                    pathXmlAttrib['isExtendedValidationCertificate'] = str(self._is_ev_certificate(x509Cert))

            trustXml.append(Element('pathValidation', attrib = pathXmlAttrib))

        # Path validation - Errors
        for (storeName, errorMsg) in verifyDictErr.iteritems():
            pathXmlAttrib = { 'usingTrustStore' : storeName,
                              'error' : errorMsg}

            trustXml.append(Element('pathValidation', attrib = pathXmlAttrib))


        outputXml.append(trustXml)


        # XML output - OCSP Stapling
        if ocspResp is None:
            oscpAttr =  {'error' : 'Server did not send back an OCSP response'}
            ocspXml = Element('ocspStapling', attrib = oscpAttr)
        else:
            oscpAttr =  {'isTrustedByMozillaCAStore' : str(ocspResp.verify(MOZILLA_STORE_PATH))}
            ocspXml = Element('ocspResponse', attrib = oscpAttr)

            for (key, value) in ocspResp.as_dict().items():
                ocspXml.append(_keyvalue_pair_to_xml(key,value))

        outputXml.append(ocspXml)

        return PluginBase.PluginResult(outputTxt, outputXml)
Example #19
0
    def process_task(self, target, command, arg):

        if arg == 'basic':
            textFunction  = self._get_basic_text
        elif arg == 'full':
            textFunction = self._get_full_text
        else:
            raise Exception("PluginCertInfo: Unknown command.")

        (host, _, _, _) = target
        threadPool = ThreadPool()

        for (storePath, _) in AVAILABLE_TRUST_STORES.iteritems():
            # Try to connect with each trust store
            threadPool.add_job((self._get_cert, (target, storePath)))

        # Start processing the jobs
        threadPool.start(len(AVAILABLE_TRUST_STORES))

        # Store the results as they come
        (verifyDict, x509Cert, ocspResp)  = ({}, None, None)

        for (job, result) in threadPool.get_result():
            (_, (_, storePath)) = job
            (x509Cert, verifyStr, ocspResp) = result
            # Store the returned verify string for each trust store
            storeName = AVAILABLE_TRUST_STORES[storePath]
            verifyDict[storeName] = verifyStr

        if x509Cert == None:
            # This means none of the connections were successful. Get out
            for (job, exception) in threadPool.get_error():
                raise exception

        # Store thread pool errors
        for (job, exception) in threadPool.get_error():
            (_, (_, storePath)) = job
            errorMsg = str(exception.__class__.__module__) + '.' \
                        + str(exception.__class__.__name__) + ' - ' \
                        + str(exception)
            verifyDict[storePath] = errorMsg

        threadPool.join()


        # Results formatting
        # Text output - certificate info
        outputTxt = [self.PLUGIN_TITLE_FORMAT('Certificate - Content')]
        outputTxt.extend(textFunction(x509Cert))


        # Text output - trust validation
        outputTxt.extend(['', self.PLUGIN_TITLE_FORMAT('Certificate - Trust')])

        # Hostname validation
        if self._shared_settings['sni']:
            outputTxt.append(self.FIELD_FORMAT("SNI enabled with virtual domain:",
                                               self._shared_settings['sni']))
        # TODO: Use SNI name for validation when --sni was used
        hostValDict = {
            X509_NAME_MATCHES_SAN : 'OK - Subject Alternative Name matches',
            X509_NAME_MATCHES_CN :  'OK - Common Name matches',
            X509_NAME_MISMATCH :    'FAILED - Certificate does not match ' + host
        }
        outputTxt.append(self.FIELD_FORMAT("Hostname Validation:",
                                            hostValDict[x509Cert.matches_hostname(host)]))

        # Path validation
        for (storeName, verifyStr) in verifyDict.iteritems():
            verifyTxt = 'OK - Trusted' if (verifyStr in 'ok') else 'FAILED - ' + verifyStr

            # EV certs - Only Mozilla supported for now
            if (verifyStr in 'ok') and ('Mozilla' in storeName):
                if (self._is_ev_certificate(x509Cert)):
                    verifyTxt += ', Extended Validation'
            outputTxt.append(self.FIELD_FORMAT(self.TRUST_FORMAT(storeName), verifyTxt))


        # Text output - OCSP stapling
        outputTxt.extend(['', self.PLUGIN_TITLE_FORMAT('Certificate - OCSP Stapling')])
        outputTxt.extend(self._get_ocsp_text(ocspResp))


        # XML output
        outputXml = Element(command, argument = arg, title = 'Certificate Information')

        # XML output - certificate info:  always return the full certificate
        certAttrib = { 'sha1Fingerprint' : x509Cert.get_SHA1_fingerprint() }
        if self._shared_settings['sni']:
            certAttrib['suppliedServerNameIndication'] = self._shared_settings['sni']

        certXml = Element('certificate', attrib = certAttrib)

        # Add certificate in PEM format
        PEMcertXml = Element('asPEM')
        PEMcertXml.text = x509Cert.as_pem().strip()
        certXml.append(PEMcertXml)

        for (key, value) in x509Cert.as_dict().items():
            certXml.append(_keyvalue_pair_to_xml(key, value))

        outputXml.append(certXml)


        # XML output - trust
        trustXml = Element('certificateValidation')

        # Hostname validation
        hostValBool = 'False' if (x509Cert.matches_hostname(host) == X509_NAME_MISMATCH) \
                              else 'True'
        hostXml = Element('hostnameValidation', serverHostname = host,
                           certificateMatchesServerHostname = hostValBool)
        trustXml.append(hostXml)

        # Path validation
        for (storeName, verifyStr) in verifyDict.iteritems():
            pathXmlAttrib = { 'usingTrustStore' : storeName,
                              'validationResult' : verifyStr}

            # EV certs - Only Mozilla supported for now
            if (verifyStr in 'ok') and ('Mozilla' in storeName):
                    pathXmlAttrib['isExtendedValidationCertificate'] = str(self._is_ev_certificate(x509Cert))

            trustXml.append(Element('pathValidation', attrib = pathXmlAttrib))

        outputXml.append(trustXml)


        # XML output - OCSP Stapling
        if ocspResp is None:
            oscpAttr =  {'error' : 'Server did not send back an OCSP response'}
            ocspXml = Element('ocspStapling', attrib = oscpAttr)
        else:
            oscpAttr =  {'isTrustedByMozillaCAStore' : str(ocspResp.verify(MOZILLA_STORE_PATH))}
            ocspXml = Element('ocspResponse', attrib = oscpAttr)

            for (key, value) in ocspResp.as_dict().items():
                ocspXml.append(_keyvalue_pair_to_xml(key,value))

        outputXml.append(ocspXml)

        return PluginBase.PluginResult(outputTxt, outputXml)
    def process_task(self, target, command, args):

        MAX_THREADS = 30
        
        if command in ['sslv2', 'sslv3', 'tlsv1', 'tlsv1_1', 'tlsv1_2']:
            ssl_version = command
        else:
            raise Exception("PluginOpenSSLCipherSuites: Unknown command.")

        # Get the list of available cipher suites for the given ssl version
        ctSSL_initialize(multithreading=True)
        ctx = SSL_CTX.SSL_CTX(ssl_version)
        ctx.set_cipher_list('ALL:NULL:@STRENGTH')
        ssl = SSL.SSL(ctx)
        cipher_list = ssl.get_cipher_list()

        # Create a thread pool
        NB_THREADS = min(len(cipher_list), MAX_THREADS) # One thread per cipher
        thread_pool = ThreadPool()

        # Scan for every available cipher suite
        for cipher in cipher_list:
            thread_pool.add_job((self._test_ciphersuite,
                                 (target, ssl_version, cipher)))

        # Scan for the preferred cipher suite
        thread_pool.add_job((self._pref_ciphersuite,
                             (target, ssl_version)))

        # Start processing the jobs
        thread_pool.start(NB_THREADS)

        result_dicts = {'preferredCipherSuite':{}, 'acceptedCipherSuites':{},
                        'rejectedCipherSuites':{}, 'errors':{}}
        
        # Store the results as they come
        for completed_job in thread_pool.get_result():
            (job, result) = completed_job
            if result is not None:
                (result_type, ssl_cipher, keysize, msg) = result
                (result_dicts[result_type])[ssl_cipher] = (msg, keysize)
                    
        # Store thread pool errors
        for failed_job in thread_pool.get_error():
            (job, exception) = failed_job
            ssl_cipher = str(job[1][2])
            error_msg = str(exception.__class__.__module__) + '.' \
                        + str(exception.__class__.__name__) + ' - ' + str(exception)
            result_dicts['errors'][ssl_cipher] = (error_msg, None)        
            
        thread_pool.join()
        ctSSL_cleanup()
        
        # Generate results
        return PluginBase.PluginResult(self._generate_txt_result(result_dicts, command),
                                       self._generate_xml_result(result_dicts, command))
    def _command_resum(self, target):
        """
        Tests the server for session resumption support using session IDs and
        TLS session tickets (RFC 5077).
        """
        NB_THREADS = 5
        MAX_RESUM = 5
        thread_pool = ThreadPool()
        
        for i in xrange(MAX_RESUM): # Test 5 resumptions with session IDs
            thread_pool.add_job((self._resume_with_session_id,
                                 (target,('tlsv1')), 'session_id'))
        thread_pool.start(NB_THREADS)
        
        # Test TLS tickets support while threads are running
        try:
            (ticket_supported, ticket_reason) = self._resume_with_session_ticket(target)
            ticket_error = None
        except Exception as e:
            ticket_error = str(e.__class__.__module__) + '.' + \
                            str(e.__class__.__name__) + ' - ' + str(e)
                            
        # Count successful resumptions      
        (nb_resum, nb_error) = self._count_resumptions(thread_pool)
        nb_failed = MAX_RESUM - nb_error - nb_resum
            
        # Text output
        sessid_format = '{4} ({0} successful, {1} failed, {2} errors, {3} total attempts).{5}'
        sessid_try = '' 
        if nb_resum == MAX_RESUM:
            sessid_stat = 'Supported'
        elif nb_failed == MAX_RESUM:
            sessid_stat = 'Not supported'
        elif nb_error == MAX_RESUM:
            sessid_stat = 'Error'
        else:
            sessid_stat = 'Partially supported'
            sessid_try = ' Try --resum_rate.'
        sessid_txt = sessid_format.format(str(nb_resum), str(nb_failed), 
                                  str(nb_error), str(MAX_RESUM),
                                  sessid_stat, sessid_try)
        
        if ticket_error:
            ticket_txt = 'Error: ' + ticket_error
        else:
            ticket_txt = 'Supported' if ticket_supported \
                                     else 'Not Supported - ' + ticket_reason+'.'
        
        cmd_title = 'Session Resumption'
        txt_result = [self.PLUGIN_TITLE_FORMAT.format(cmd_title)]
        RESUM_FORMAT = '      {0:<27} {1}'
        txt_result.append(RESUM_FORMAT.format('With Session IDs:', sessid_txt))
        txt_result.append(RESUM_FORMAT.format('With TLS Session Tickets:', ticket_txt))
        
        # XML output
        sessid_xml = str(nb_resum == MAX_RESUM)
        
        xml_resum_id_attr = {'mechanism' :'session ids','total':str(MAX_RESUM), 
                             'errors' : str(nb_error), 'supported' : sessid_xml,
                             'successful':str(nb_resum),'failed':str(nb_failed)}
        xml_resum_id = Element('resum', attrib = xml_resum_id_attr)

        xml_resum_ticket_attr = {'mechanism' : 'tls tickets'}
        if ticket_error:
            xml_resum_ticket_attr['error'] = ticket_error
        else:
            xml_resum_ticket_attr['supported'] = str(ticket_supported)
            if not ticket_supported:
                xml_resum_ticket_attr['reason'] = ticket_reason
        
        xml_resum_ticket = Element('resum', attrib = xml_resum_ticket_attr)   
        xml_result = Element(self.__class__.__name__, command='resum', title=cmd_title)
        xml_result.append(xml_resum_id)
        xml_result.append(xml_resum_ticket)

        thread_pool.join()
        return PluginBase.PluginResult(txt_result, xml_result)
Example #22
0
    def process_task(self, target, command, arg):

        if arg == "basic":
            textFunction = self._get_basic_text
        elif arg == "full":
            textFunction = self._get_full_text
        else:
            raise Exception("PluginCertInfo: Unknown command.")

        (host, _, _, _) = target
        threadPool = ThreadPool()

        for (storePath, _) in AVAILABLE_TRUST_STORES.iteritems():
            # Try to connect with each trust store
            threadPool.add_job((self._get_cert, (target, storePath)))

        # Start processing the jobs
        threadPool.start(len(AVAILABLE_TRUST_STORES))

        # Store the results as they come
        (verifyDict, verifyDictErr, x509Cert, ocspResp) = ({}, {}, None, None)

        for (job, result) in threadPool.get_result():
            (_, (_, storePath)) = job
            (x509Cert, verifyStr, ocspResp) = result
            # Store the returned verify string for each trust store
            storeName = AVAILABLE_TRUST_STORES[storePath]
            verifyDict[storeName] = verifyStr

        if x509Cert is None:
            # This means none of the connections were successful. Get out
            for (job, exception) in threadPool.get_error():
                raise exception

        # Store thread pool errors
        for (job, exception) in threadPool.get_error():
            (_, (_, storePath)) = job
            errorMsg = str(exception.__class__.__name__) + " - " + str(exception)

            storeName = AVAILABLE_TRUST_STORES[storePath]
            verifyDictErr[storeName] = errorMsg

        threadPool.join()

        # Results formatting
        # Text output - certificate info
        outputTxt = [self.PLUGIN_TITLE_FORMAT("Certificate - Content")]
        outputTxt.extend(textFunction(x509Cert))

        # Text output - trust validation
        outputTxt.extend(["", self.PLUGIN_TITLE_FORMAT("Certificate - Trust")])

        # Hostname validation
        if self._shared_settings["sni"]:
            outputTxt.append(self.FIELD_FORMAT("SNI enabled with virtual domain:", self._shared_settings["sni"]))
        # TODO: Use SNI name for validation when --sni was used
        hostValDict = {
            X509_NAME_MATCHES_SAN: "OK - Subject Alternative Name matches",
            X509_NAME_MATCHES_CN: "OK - Common Name matches",
            X509_NAME_MISMATCH: "FAILED - Certificate does NOT match " + host,
        }
        outputTxt.append(self.FIELD_FORMAT("Hostname Validation:", hostValDict[x509Cert.matches_hostname(host)]))

        # Path validation that was successful
        for (storeName, verifyStr) in verifyDict.iteritems():
            verifyTxt = (
                "OK - Certificate is trusted"
                if (verifyStr in "ok")
                else "FAILED - Certificate is NOT Trusted: " + verifyStr
            )

            # EV certs - Only Mozilla supported for now
            if (verifyStr in "ok") and ("Mozilla" in storeName):
                if self._is_ev_certificate(x509Cert):
                    verifyTxt += ", Extended Validation"
            outputTxt.append(self.FIELD_FORMAT(self.TRUST_FORMAT(storeName), verifyTxt))

        # Path validation that ran into errors
        for (storeName, errorMsg) in verifyDictErr.iteritems():
            verifyTxt = "ERROR: " + errorMsg
            outputTxt.append(self.FIELD_FORMAT(self.TRUST_FORMAT(storeName), verifyTxt))

        # Text output - OCSP stapling
        outputTxt.extend(["", self.PLUGIN_TITLE_FORMAT("Certificate - OCSP Stapling")])
        outputTxt.extend(self._get_ocsp_text(ocspResp))

        # XML output
        outputXml = Element(command, argument=arg, title="Certificate Information")

        # XML output - certificate info:  always return the full certificate
        certAttrib = {"sha1Fingerprint": x509Cert.get_SHA1_fingerprint()}
        if self._shared_settings["sni"]:
            certAttrib["suppliedServerNameIndication"] = self._shared_settings["sni"]

        certXml = Element("certificate", attrib=certAttrib)

        # Add certificate in PEM format
        PEMcertXml = Element("asPEM")
        PEMcertXml.text = x509Cert.as_pem().strip()
        certXml.append(PEMcertXml)

        for (key, value) in x509Cert.as_dict().items():
            certXml.append(_keyvalue_pair_to_xml(key, value))

        outputXml.append(certXml)

        # XML output - trust
        trustXml = Element("certificateValidation")

        # Hostname validation
        hostValBool = "False" if (x509Cert.matches_hostname(host) == X509_NAME_MISMATCH) else "True"
        hostXml = Element("hostnameValidation", serverHostname=host, certificateMatchesServerHostname=hostValBool)
        trustXml.append(hostXml)

        # Path validation - OK
        for (storeName, verifyStr) in verifyDict.iteritems():
            pathXmlAttrib = {"usingTrustStore": storeName, "validationResult": verifyStr}

            # EV certs - Only Mozilla supported for now
            if (verifyStr in "ok") and ("Mozilla" in storeName):
                pathXmlAttrib["isExtendedValidationCertificate"] = str(self._is_ev_certificate(x509Cert))

            trustXml.append(Element("pathValidation", attrib=pathXmlAttrib))

        # Path validation - Errors
        for (storeName, errorMsg) in verifyDictErr.iteritems():
            pathXmlAttrib = {"usingTrustStore": storeName, "error": errorMsg}

            trustXml.append(Element("pathValidation", attrib=pathXmlAttrib))

        outputXml.append(trustXml)

        # XML output - OCSP Stapling
        if ocspResp is None:
            oscpAttr = {"error": "Server did not send back an OCSP response"}
            ocspXml = Element("ocspStapling", attrib=oscpAttr)
        else:
            oscpAttr = {"isTrustedByMozillaCAStore": str(ocspResp.verify(MOZILLA_STORE_PATH))}
            ocspXml = Element("ocspResponse", attrib=oscpAttr)

            for (key, value) in ocspResp.as_dict().items():
                ocspXml.append(_keyvalue_pair_to_xml(key, value))

        outputXml.append(ocspXml)

        return PluginBase.PluginResult(outputTxt, outputXml)
Example #23
0
    def process_task(self, target, command, args):

        MAX_THREADS = 15
        sslVersionDict = {
            'sslv2': SSLV2,
            'sslv3': SSLV3,
            'tlsv1': TLSV1,
            'tlsv1_1': TLSV1_1,
            'tlsv1_2': TLSV1_2
        }
        try:
            sslVersion = sslVersionDict[command]
        except KeyError:
            raise Exception("PluginOpenSSLCipherSuites: Unknown command.")

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

        # Create a thread pool
        NB_THREADS = min(len(cipher_list),
                         MAX_THREADS)  # One thread per cipher
        thread_pool = ThreadPool()

        # Scan for every available cipher suite
        for cipher in cipher_list:
            thread_pool.add_job(
                (self._test_ciphersuite, (target, sslVersion, cipher)))

        # Scan for the preferred cipher suite
        thread_pool.add_job((self._pref_ciphersuite, (target, sslVersion)))

        # Start processing the jobs
        thread_pool.start(NB_THREADS)

        result_dicts = {
            'preferredCipherSuite': {},
            'acceptedCipherSuites': {},
            'rejectedCipherSuites': {},
            'errors': {}
        }

        # Store the results as they come
        for completed_job in thread_pool.get_result():
            (job, result) = completed_job
            if result is not None:
                (result_type, ssl_cipher, keysize, dh_infos, msg) = result
                (result_dicts[result_type])[ssl_cipher] = (msg, keysize,
                                                           dh_infos)

        # Store thread pool errors
        for failed_job in thread_pool.get_error():
            (job, exception) = failed_job
            ssl_cipher = str(job[1][2])
            error_msg = str(
                exception.__class__.__name__) + ' - ' + str(exception)
            result_dicts['errors'][ssl_cipher] = (error_msg, None, None)

        thread_pool.join()

        # Generate results
        return PluginBase.PluginResult(
            self._generate_text_output(result_dicts, command),
            self._generate_xml_output(result_dicts, command))