<commit_before># Copyright: (c) 2018, <NAME> (@jborean93) <<EMAIL>>
# MIT License (see LICENSE or https://opensource.org/licenses/MIT)

import base64
import binascii
import hashlib
import logging
import os
import re
import struct
import warnings

from cryptography.hazmat.primitives.serialization import Encoding, PublicFormat
from OpenSSL import SSL
from pyasn1.codec.der import encoder, decoder
from requests.auth import AuthBase

from requests_credssp.asn_structures import NegoToken, TSCredentials, \
    TSPasswordCreds, TSRequest
from requests_credssp.exceptions import AuthenticationException
from requests_credssp.spnego import get_auth_context

try:
    from urlparse import urlparse
except ImportError:
    from urllib.parse import urlparse

log = logging.getLogger(__name__)


class CredSSPContext(object):
    BIO_BUFFER_SIZE = 8192

    def __init__(self, hostname, username, password, auth_mechanism='auto',
                 disable_tlsv1_2=False, minimum_version=2):
        self.hostname = hostname
        self.username = username
        self.password = password
        self.auth_mechanism = auth_mechanism
        self.minimum_version = minimum_version

        if disable_tlsv1_2:
            """
            Windows 7 and Server 2008 R2 uses TLSv1 by default which is
            considered insecure. Microsoft have released a KB that adds support
            for TLSv1.2 https://support.microsoft.com/en-us/kb/3080079 which
            can be installed. Once installed the relevant reg keys need to be
            configured as show by this page
            https://technet.microsoft.com/en-us/library/dn786418.aspx

            If you do not wish to do this you can set the disable_tlsv1_2 flag
            to true when calling CredSSP (NOT RECOMMENDED).
            """
            log.debug("disable_tlsv1_2 is set to False, disabling TLSv1.2"
                      "support and reverting back to TLSv1")
            self.tls_context = SSL.Context(SSL.TLSv1_METHOD)

            # Revert OpenSSL fix to CBC ciphers due to incompatibility with
            # MS TLS 1.0 implementation SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS
            # 0x00000800 - SSL_OP_TLS_BLOCK_PADDING_BUG 0x00000200
            self.tls_context.set_options(0x00000800 | 0x00000200)
        else:
            self.tls_context = SSL.Context(SSL.TLSv1_2_METHOD)

        self.tls_context.set_cipher_list(b'ALL')
        self.tls_connection = None

    def credssp_generator(self):
        """
        [MS-CSSP] 3.1.5 Processing Events and Sequencing Rules
        https://msdn.microsoft.com/en-us/library/cc226791.aspx

        Generator function that yields each CredSSP token to sent to the
        server. CredSSP has multiple steps that must be run for the client to
        successfully authenticate with the server and delegate the credentials.
        """
        log.info("Starting TLS handshake process")
        self.tls_connection = SSL.Connection(self.tls_context)
        self.tls_connection.set_connect_state()

        while True:
            try:
                self.tls_connection.do_handshake()
            except SSL.WantReadError:
                out_token = self.tls_connection.bio_read(self.BIO_BUFFER_SIZE)
                log.debug("Step 1. TLS Handshake, returning token: %s"
                          % binascii.hexlify(out_token))
                in_token = yield out_token, "Step 1. TLS Handshake"
                log.debug("Step 1. TLS Handshake, received token: %s"
                          % binascii.hexlify(in_token))
                self.tls_connection.bio_write(in_token)
            else:
                break
        log.debug("TLS Handshake complete. Protocol: %s, Cipher: %s"
                  % (self.tls_connection.get_protocol_version_name(),
                     self.tls_connection.get_cipher_name()))

        server_certificate = self.tls_connection.get_peer_certificate()
        server_public_key = self._get_subject_public_key(server_certificate)

        log.info("Starting Authentication process")
        version = 6
        context, auth_step, out_token = get_auth_context(self.hostname,
                                                         self.username,
                                                         self.password,
                                                         self.auth_mechanism)
        while not context.complete:
            nego_token = NegoToken()
            nego_token['negoToken'] = out_token

            ts_request = TSRequest()
            ts_request['negoTokens'].append(nego_token)

            ts_request_token = encoder.encode(ts_request)
            log.debug("Step 2. Authenticate, returning token: %s"
                      % binascii.hexlify(ts_request_token))
            in_token = yield self.wrap(ts_request_token), \
                "Step 2. Authenticate"
            in_token = self.unwrap(in_token)
            log.debug("Step 3. Authenticate, received token: %s"
                      % binascii.hexlify(in_token))

            ts_request = decoder.decode(in_token, asn1Spec=TSRequest())[0]
            ts_request.check_error_code()
            version = int(ts_request['version'])
            out_token = \
                auth_step.send(bytes(ts_request['negoTokens'][0]['negoToken']))

        version = min(version, TSRequest.CLIENT_VERSION)
        log.info("Starting public key verification process at version %d"
                 % version)
        if version < self.minimum_version:
            raise AuthenticationException("The reported server version was %d "
                                          "and did not meet the minimum "
                                          "requirements of %d"
                                          % (version, self.minimum_version))
        if version > 4:
            nonce = os.urandom(32)
        else:
            log.warning("Reported server version was %d, susceptible to MitM "
                        "attacks and should be patched - CVE 2018-0886")
            nonce = None

        pub_key_auth = self._build_pub_key_auth(context, nonce,
                                                out_token,
                                                server_public_key)
        log.debug("Step 3. Server Authentication, returning token: %s"
                  % binascii.hexlify(pub_key_auth))
        in_token = yield (self.wrap(pub_key_auth),
                          "Step 3. Server Authentication")
        in_token = self.unwrap(in_token)
        log.debug("Step 3. Server Authentication, received token: %s"
                  % binascii.hexlify(in_token))<commit_msg>Add missing version in warning message (#8)<commit_after># Copyright: (c) 2018, <NAME> (@jborean93) <<EMAIL>>
# MIT License (see LICENSE or https://opensource.org/licenses/MIT)

import base64
import binascii
import hashlib
import logging
import os
import re
import struct
import warnings

from cryptography.hazmat.primitives.serialization import Encoding, PublicFormat
from OpenSSL import SSL
from pyasn1.codec.der import encoder, decoder
from requests.auth import AuthBase

from requests_credssp.asn_structures import NegoToken, TSCredentials, \
    TSPasswordCreds, TSRequest
from requests_credssp.exceptions import AuthenticationException
from requests_credssp.spnego import get_auth_context

try:
    from urlparse import urlparse
except ImportError:
    from urllib.parse import urlparse

log = logging.getLogger(__name__)


class CredSSPContext(object):
    BIO_BUFFER_SIZE = 8192

    def __init__(self, hostname, username, password, auth_mechanism='auto',
                 disable_tlsv1_2=False, minimum_version=2):
        self.hostname = hostname
        self.username = username
        self.password = password
        self.auth_mechanism = auth_mechanism
        self.minimum_version = minimum_version

        if disable_tlsv1_2:
            """
            Windows 7 and Server 2008 R2 uses TLSv1 by default which is
            considered insecure. Microsoft have released a KB that adds support
            for TLSv1.2 https://support.microsoft.com/en-us/kb/3080079 which
            can be installed. Once installed the relevant reg keys need to be
            configured as show by this page
            https://technet.microsoft.com/en-us/library/dn786418.aspx

            If you do not wish to do this you can set the disable_tlsv1_2 flag
            to true when calling CredSSP (NOT RECOMMENDED).
            """
            log.debug("disable_tlsv1_2 is set to False, disabling TLSv1.2"
                      "support and reverting back to TLSv1")
            self.tls_context = SSL.Context(SSL.TLSv1_METHOD)

            # Revert OpenSSL fix to CBC ciphers due to incompatibility with
            # MS TLS 1.0 implementation SSL_OP_DONT_INSERT_EMPTY_FRAGMENTS
            # 0x00000800 - SSL_OP_TLS_BLOCK_PADDING_BUG 0x00000200
            self.tls_context.set_options(0x00000800 | 0x00000200)
        else:
            self.tls_context = SSL.Context(SSL.TLSv1_2_METHOD)

        self.tls_context.set_cipher_list(b'ALL')
        self.tls_connection = None

    def credssp_generator(self):
        """
        [MS-CSSP] 3.1.5 Processing Events and Sequencing Rules
        https://msdn.microsoft.com/en-us/library/cc226791.aspx

        Generator function that yields each CredSSP token to sent to the
        server. CredSSP has multiple steps that must be run for the client to
        successfully authenticate with the server and delegate the credentials.
        """
        log.info("Starting TLS handshake process")
        self.tls_connection = SSL.Connection(self.tls_context)
        self.tls_connection.set_connect_state()

        while True:
            try:
                self.tls_connection.do_handshake()
            except SSL.WantReadError:
                out_token = self.tls_connection.bio_read(self.BIO_BUFFER_SIZE)
                log.debug("Step 1. TLS Handshake, returning token: %s"
                          % binascii.hexlify(out_token))
                in_token = yield out_token, "Step 1. TLS Handshake"
                log.debug("Step 1. TLS Handshake, received token: %s"
                          % binascii.hexlify(in_token))
                self.tls_connection.bio_write(in_token)
            else:
                break
        log.debug("TLS Handshake complete. Protocol: %s, Cipher: %s"
                  % (self.tls_connection.get_protocol_version_name(),
                     self.tls_connection.get_cipher_name()))

        server_certificate = self.tls_connection.get_peer_certificate()
        server_public_key = self._get_subject_public_key(server_certificate)

        log.info("Starting Authentication process")
        version = 6
        context, auth_step, out_token = get_auth_context(self.hostname,
                                                         self.username,
                                                         self.password,
                                                         self.auth_mechanism)
        while not context.complete:
            nego_token = NegoToken()
            nego_token['negoToken'] = out_token

            ts_request = TSRequest()
            ts_request['negoTokens'].append(nego_token)

            ts_request_token = encoder.encode(ts_request)
            log.debug("Step 2. Authenticate, returning token: %s"
                      % binascii.hexlify(ts_request_token))
            in_token = yield self.wrap(ts_request_token), \
                "Step 2. Authenticate"
            in_token = self.unwrap(in_token)
            log.debug("Step 3. Authenticate, received token: %s"
                      % binascii.hexlify(in_token))

            ts_request = decoder.decode(in_token, asn1Spec=TSRequest())[0]
            ts_request.check_error_code()
            version = int(ts_request['version'])
            out_token = \
                auth_step.send(bytes(ts_request['negoTokens'][0]['negoToken']))

        version = min(version, TSRequest.CLIENT_VERSION)
        log.info("Starting public key verification process at version %d"
                 % version)
        if version < self.minimum_version:
            raise AuthenticationException("The reported server version was %d "
                                          "and did not meet the minimum "
                                          "requirements of %d"
                                          % (version, self.minimum_version))
        if version > 4:
            nonce = os.urandom(32)
        else:
            log.warning("Reported server version was %d, susceptible to MitM "
                        "attacks and should be patched - CVE 2018-0886"
                        % version)
            nonce = None

        pub_key_auth = self._build_pub_key_auth(context, nonce,
                                                out_token,
                                                server_public_key)
        log.debug("Step 3. Server Authentication, returning token: %s"
                  % binascii.hexlify(pub_key_auth))
        in_token = yield (self.wrap(pub_key_auth),
                          "Step 3. Server Authentication")
        in_token = self.unwrap(in_token)
        log.debug("Step 3. Server Authentication, received token: %s"
                  % binascii.hexlify(in_token))
{'fraction_non_alphanumeric': 0.0730682670667667, 'fraction_numerical': 0.018229557389347336, 'mean_word_length': 1.9486839194868393, 'pattern_counts': {'":': 0, '<': 11, '<?xml version=': 0, '>': 11, 'https://': 8, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 32, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '1654675', 'n_tokens_mistral': 3786, 'n_tokens_neox': 3484, 'n_words': 892}