Beispiel #1
0
    def build_session(self):
        session = requests.Session()

        session.verify = self.server_cert_validation == 'validate'

        # configure proxies from HTTP/HTTPS_PROXY envvars
        session.trust_env = True
        settings = session.merge_environment_settings(url=self.endpoint,
                                                      proxies={},
                                                      stream=None,
                                                      verify=None,
                                                      cert=None)

        # we're only applying proxies from env, other settings are ignored
        session.proxies = settings['proxies']

        if self.auth_method == 'kerberos':
            if not HAVE_KERBEROS:
                raise WinRMError(
                    "requested auth method is kerberos, but requests_kerberos is not installed"
                )
            # TODO: do argspec sniffing on extensions to ensure we're not setting bogus kwargs on older versions
            session.auth = HTTPKerberosAuth(
                mutual_authentication=REQUIRED,
                delegate=self.kerberos_delegation,
                force_preemptive=True,
                principal=self.username,
                hostname_override=self.kerberos_hostname_override,
                sanitize_mutual_error_response=False)
        elif self.auth_method in ['certificate', 'ssl']:
            if self.auth_method == 'ssl' and not self.cert_pem and not self.cert_key_pem:
                # 'ssl' was overloaded for HTTPS with optional certificate auth,
                # fall back to basic auth if no cert specified
                session.auth = requests.auth.HTTPBasicAuth(
                    username=self.username, password=self.password)
            else:
                session.cert = (self.cert_pem, self.cert_key_pem)
                session.headers['Authorization'] = \
                    "http://schemas.dmtf.org/wbem/wsman/1/wsman/secprofile/https/mutual"
        elif self.auth_method == 'ntlm':
            if not HAVE_NTLM:
                raise WinRMError(
                    "requested auth method is ntlm, but requests_ntlm is not installed"
                )
            session.auth = HttpNtlmAuth(username=self.username,
                                        password=self.password)
        # TODO: ssl is not exactly right here- should really be client_cert
        elif self.auth_method in ['basic', 'plaintext']:
            session.auth = requests.auth.HTTPBasicAuth(username=self.username,
                                                       password=self.password)

        else:
            raise WinRMError("unsupported auth method: %s" % self.auth_method)

        session.headers.update(self.default_headers)

        return session
Beispiel #2
0
    def _decrypt_response(self, response, host):
        parts = response.content.split(self.MIME_BOUNDARY + b'\r\n')
        parts = list(filter(None, parts))  # filter out empty parts of the split
        message = b''

        for i in range(0, len(parts)):
            if i % 2 == 1:
                continue

            header = parts[i].strip()
            payload = parts[i + 1]

            expected_length = int(header.split(b'Length=')[1])

            # remove the end MIME block if it exists
            if payload.endswith(self.MIME_BOUNDARY + b'--\r\n'):
                payload = payload[:len(payload) - 24]

            encrypted_data = payload.replace(b'\tContent-Type: application/octet-stream\r\n', b'')
            decrypted_message = self._decrypt_message(encrypted_data, host)
            actual_length = len(decrypted_message)

            if actual_length != expected_length:
                raise WinRMError('Encrypted length from server does not match the '
                                 'expected size, message has been tampered with')
            message += decrypted_message

        return message
Beispiel #3
0
    def __init__(
            self, endpoint, username=None, password=None, realm=None,
            service=None, keytab=None, ca_trust_path=None, cert_pem=None,
            cert_key_pem=None, read_timeout_sec=None, server_cert_validation='validate',
            kerberos_delegation=False,
            auth_method='auto'):
        self.endpoint = endpoint
        self.username = username
        self.password = password
        self.realm = realm
        self.service = service
        self.keytab = keytab
        self.ca_trust_path = ca_trust_path
        self.cert_pem = cert_pem
        self.cert_key_pem = cert_key_pem
        self.read_timeout_sec = read_timeout_sec
        self.server_cert_validation = server_cert_validation
        if self.server_cert_validation not in [None, 'validate', 'ignore']:
            raise WinRMError('invalid server_cert_validation mode: %s' % self.server_cert_validation)

        self.kerberos_delegation = kerberos_delegation

        self.auth_method = auth_method
        self.default_headers = {
            'Content-Type': 'application/soap+xml;charset=UTF-8',
            'User-Agent': 'Python WinRM client',
        }
        self.session = None
Beispiel #4
0
    def send_message(self, message):
        # TODO support kerberos session with message encryption

        if not self.session:
            self.session = self.build_session()

        # urllib3 fails on SSL retries with unicode buffers- must send it a byte string
        # see https://github.com/shazow/urllib3/issues/717
        if message is unicode:
            message = message.encode('utf-8')

        request = requests.Request('POST', self.endpoint, data=message)
        prepared_request = self.session.prepare_request(request)

        try:
            response = self.session.send(prepared_request, timeout=self.read_timeout_sec)
            response_text = response.text
            response.raise_for_status()
            return response_text
        except requests.HTTPError as ex:
            if ex.response.status_code == 401:
                raise InvalidCredentialsError("the specified credentials were rejected by the server")
            if ex.response.content:
                response_text = ex.response.content
            else:
                response_text = ''
            # Per http://msdn.microsoft.com/en-us/library/cc251676.aspx rule 3,
            # should handle this 500 error and retry receiving command output.
            if 'http://schemas.microsoft.com/wbem/wsman/1/windows/shell/Receive' in message and 'Code="2150858793"' in response_text:
                raise WinRMOperationTimeoutError()

            error_message = 'Bad HTTP response returned from server. Code {0}'.format(ex.response.status_code)

            raise WinRMError('http', error_message)
Beispiel #5
0
    def send_message(self, message):
        # TODO add message_id vs relates_to checking
        # TODO port error handling code
        try:
            resp = self.transport.send_message(message)
            return resp
        except WinRMTransportError as ex:
            try:
                # if response is XML-parseable, it's probably a SOAP fault; extract the details
                root = ET.fromstring(ex.response_text)
            except Exception:
                # assume some other transport error; raise the original exception
                raise ex

            fault = root.find('soapenv:Body/soapenv:Fault', xmlns)
            if fault is not None:
                fault_data = dict(transport_message=ex.message,
                                  http_status_code=ex.code)
                wsmanfault_code = fault.find(
                    'soapenv:Detail/wsmanfault:WSManFault[@Code]', xmlns)
                if wsmanfault_code is not None:
                    fault_data['wsmanfault_code'] = wsmanfault_code.get('Code')
                    # convert receive timeout code to WinRMOperationTimeoutError
                    if fault_data['wsmanfault_code'] == '2150858793':
                        # TODO: this fault code is specific to the Receive operation; convert all op timeouts?
                        raise WinRMOperationTimeoutError()

                fault_code = fault.find('soapenv:Code/soapenv:Value', xmlns)
                if fault_code is not None:
                    fault_data['fault_code'] = fault_code.text

                fault_subcode = fault.find(
                    'soapenv:Code/soapenv:Subcode/soapenv:Value', xmlns)
                if fault_subcode is not None:
                    fault_data['fault_subcode'] = fault_subcode.text

                error_message = fault.find('soapenv:Reason/soapenv:Text',
                                           xmlns)
                if error_message is not None:
                    error_message = error_message.text
                else:
                    error_message = "(no error message in fault)"

                raise WinRMError('{0} (extended fault data: {1})'.format(
                    error_message, fault_data))
Beispiel #6
0
    def send_message(self, message):
        # TODO support kerberos session with message encryption

        if not self.session:
            self.session = self.build_session()
        request = requests.Request('POST', self.endpoint, data=message)
        prepared_request = self.session.prepare_request(request)
        try:
            response = self.session.send(prepared_request,
                                         verify=False,
                                         timeout=self.timeout)
            response.raise_for_status()
            # Version 1.1 of WinRM adds the namespaces in the document instead of the envelope so we have to
            # add them ourselves here. This should have no affect version 2.
            response_text = response.text
            return response_text
        except requests.HTTPError as ex:
            if ex.response.status_code == 401:
                server_auth = ex.response.headers['WWW-Authenticate'].lower()
                client_auth = list(self.session.auth.auth_map.keys())
                # Client can do only the Basic auth but server can not
                if 'basic' not in server_auth and len(client_auth) == 1 \
                        and client_auth[0] == 'basic':
                    raise BasicAuthDisabledError()
                # Both client and server can do a Basic auth
                if 'basic' in server_auth and 'basic' in client_auth:
                    raise InvalidCredentialsError()
            if ex.response:
                response_text = ex.response.content
            # Is this just silencing the error?
            else:
                response_text = ''
            # Per http://msdn.microsoft.com/en-us/library/cc251676.aspx rule 3,
            # should handle this 500 error and retry receiving command output.
            if 'http://schemas.microsoft.com/wbem/wsman/1/windows/shell/Receive' in message and 'Code="2150858793"' in response_text:
                # TODO raise TimeoutError here instead of just return text
                return response_text
            error_message = 'Bad HTTP response returned from server. Code {0}'.format(
                ex.response.status_code)
            # if ex.msg:
            #    error_message += ', {0}'.format(ex.msg)
            raise WinRMError('http', error_message)
Beispiel #7
0
    def __init__(self, session, protocol):
        """
        [MS-WSMV] v30.0 2016-07-14

        2.2.9.1 Encrypted Message Types
        When using Encryption, there are three options available
            1. Negotiate/SPNEGO
            2. Kerberos
            3. CredSSP
        Details for each implementation can be found in this document under this section

        This init sets the following values to use to encrypt and decrypt. This is to help generify
        the methods used in the body of the class.
            wrap: A method that will return the encrypted message and a signature
            unwrap: A method that will return an unencrypted message and verify the signature
            protocol_string: The protocol string used for the particular auth protocol

        :param session: The handle of the session to get GSS-API wrap and unwrap methods
        :param protocol: The auth protocol used, will determine the wrapping and unwrapping method plus
                         the protocol string to use. Currently only NTLM and CredSSP is supported
        """
        self.protocol = protocol
        self.session = session

        if protocol == 'ntlm':  # Details under Negotiate [2.2.9.1.1] in MS-WSMV
            self.protocol_string = b"application/HTTP-SPNEGO-session-encrypted"
            self._build_message = self._build_ntlm_message
            self._decrypt_message = self._decrypt_ntlm_message
        elif protocol == 'credssp':  # Details under CredSSP [2.2.9.1.3] in MS-WSMV
            self.protocol_string = b"application/HTTP-CredSSP-session-encrypted"
            self._build_message = self._build_credssp_message
            self._decrypt_message = self._decrypt_credssp_message
        elif protocol == 'kerberos':
            self.protocol_string = b"application/HTTP-SPNEGO-session-encrypted"
            self._build_message = self._build_kerberos_message
            self._decrypt_message = self._decrypt_kerberos_message
        else:
            raise WinRMError(
                "Encryption for protocol '%s' not supported in pywinrm" %
                protocol)
Beispiel #8
0
    def __init__(self,
                 endpoint,
                 username=None,
                 password=None,
                 realm=None,
                 service=None,
                 keytab=None,
                 ca_trust_path=None,
                 cert_pem=None,
                 cert_key_pem=None,
                 read_timeout_sec=None,
                 server_cert_validation='validate',
                 kerberos_delegation=False,
                 kerberos_hostname_override=None,
                 auth_method='auto'):
        self.endpoint = endpoint
        self.username = username
        self.password = password
        self.realm = realm
        self.service = service
        self.keytab = keytab
        self.ca_trust_path = ca_trust_path
        self.cert_pem = cert_pem
        self.cert_key_pem = cert_key_pem
        self.read_timeout_sec = read_timeout_sec
        self.server_cert_validation = server_cert_validation
        self.kerberos_hostname_override = kerberos_hostname_override

        if self.server_cert_validation not in [None, 'validate', 'ignore']:
            raise WinRMError('invalid server_cert_validation mode: %s' %
                             self.server_cert_validation)

        # defensively parse this to a bool
        if isinstance(kerberos_delegation, bool):
            self.kerberos_delegation = kerberos_delegation
        else:
            self.kerberos_delegation = bool(strtobool(
                str(kerberos_delegation)))

        self.auth_method = auth_method
        self.default_headers = {
            'Content-Type': 'application/soap+xml;charset=UTF-8',
            'User-Agent': 'Python WinRM client',
        }

        # try to suppress user-unfriendly warnings from requests' vendored urllib3
        try:
            from requests.packages.urllib3.exceptions import InsecurePlatformWarning, SNIMissingWarning, InsecureRequestWarning
            warnings.simplefilter('ignore', category=InsecurePlatformWarning)
            warnings.simplefilter('ignore', category=SNIMissingWarning)
            # if we're explicitly ignoring validation, try to suppress InsecureRequestWarning, since the user opted-in
            if self.server_cert_validation == 'ignore':
                warnings.simplefilter('ignore',
                                      category=InsecureRequestWarning)
        except:
            pass  # oh well, we tried...

        # validate credential requirements for various auth types
        if self.auth_method != 'kerberos':
            if self.auth_method == 'certificate' or (
                    self.auth_method == 'ssl' and
                (self.cert_pem or self.cert_key_pem)):
                if not self.cert_pem or not self.cert_key_pem:
                    raise InvalidCredentialsError(
                        "both cert_pem and cert_key_pem must be specified for cert auth"
                    )
                if not os.path.exists(self.cert_pem):
                    raise InvalidCredentialsError(
                        "cert_pem file not found (%s)" % self.cert_pem)
                if not os.path.exists(self.cert_key_pem):
                    raise InvalidCredentialsError(
                        "cert_key_pem file not found (%s)" % self.cert_key_pem)

            else:
                if not self.username:
                    raise InvalidCredentialsError(
                        "auth method %s requires a username" %
                        self.auth_method)
                if self.password is None:
                    raise InvalidCredentialsError(
                        "auth method %s requires a password" %
                        self.auth_method)

        self.session = None
Beispiel #9
0
    def build_session(self):
        session = requests.Session()
        proxies = dict()

        if self.proxy is None:
            proxies['no_proxy'] = '*'
        elif self.proxy != 'legacy_requests':
            # If there was a proxy specified then use it
            proxies['http'] = self.proxy
            proxies['https'] = self.proxy

        # Merge proxy environment variables
        settings = session.merge_environment_settings(url=self.endpoint,
                      proxies=proxies, stream=None, verify=None, cert=None)

        global DISPLAYED_PROXY_WARNING

        # We want to eventually stop reading proxy information from the environment.
        # Also only display the warning once. This method can be called many times during an application's runtime.
        if not DISPLAYED_PROXY_WARNING and self.proxy == 'legacy_requests' and (
                'http' in settings['proxies'] or 'https' in settings['proxies']):
            message = "'pywinrm' will use an environment defined proxy. This feature will be disabled in " \
                      "the future, please specify it explicitly."
            if 'http' in settings['proxies']:
                message += " HTTP proxy {proxy} discovered.".format(proxy=settings['proxies']['http'])
            if 'https' in settings['proxies']:
                message += " HTTPS proxy {proxy} discovered.".format(proxy=settings['proxies']['https'])

            DISPLAYED_PROXY_WARNING = True
            warnings.warn(message, DeprecationWarning)

        session.proxies = settings['proxies']

        # specified validation mode takes precedence
        session.verify = self.server_cert_validation == 'validate'

        # patch in CA path override if one was specified in init or env
        if session.verify:
            if self.ca_trust_path == 'legacy_requests' and settings['verify'] is not None:
                # We will
                session.verify = settings['verify']

                global DISPLAYED_CA_TRUST_WARNING

                # We want to eventually stop reading proxy information from the environment.
                # Also only display the warning once. This method can be called many times during an application's runtime.
                if not DISPLAYED_CA_TRUST_WARNING and session.verify is not True:
                    message = "'pywinrm' will use an environment variable defined CA Trust. This feature will be disabled in " \
                              "the future, please specify it explicitly."
                    if os.environ.get('REQUESTS_CA_BUNDLE') is not None:
                        message += " REQUESTS_CA_BUNDLE contains {ca_path}".format(ca_path=os.environ.get('REQUESTS_CA_BUNDLE'))
                    elif os.environ.get('CURL_CA_BUNDLE') is not None:
                        message += " CURL_CA_BUNDLE contains {ca_path}".format(ca_path=os.environ.get('CURL_CA_BUNDLE'))

                    DISPLAYED_CA_TRUST_WARNING = True
                    warnings.warn(message, DeprecationWarning)

            elif session.verify and self.ca_trust_path is not None:
                # session.verify can be either a bool or path to a CA store; prefer passed-in value over env if both are present
                session.verify = self.ca_trust_path

        encryption_available = False

        if self.auth_method == 'kerberos':
            if not HAVE_KERBEROS:
                raise WinRMError("requested auth method is kerberos, but requests_kerberos is not installed")

            man_args = dict(
                mutual_authentication=REQUIRED,
            )
            opt_args = dict(
                delegate=self.kerberos_delegation,
                force_preemptive=True,
                principal=self.username,
                hostname_override=self.kerberos_hostname_override,
                sanitize_mutual_error_response=False,
                service=self.service,
                send_cbt=self.send_cbt
            )
            kerb_args = self._get_args(man_args, opt_args, HTTPKerberosAuth.__init__)
            session.auth = HTTPKerberosAuth(**kerb_args)
            encryption_available = hasattr(session.auth, 'winrm_encryption_available') and session.auth.winrm_encryption_available
        elif self.auth_method in ['certificate', 'ssl']:
            if self.auth_method == 'ssl' and not self.cert_pem and not self.cert_key_pem:
                # 'ssl' was overloaded for HTTPS with optional certificate auth,
                # fall back to basic auth if no cert specified
                session.auth = requests.auth.HTTPBasicAuth(username=self.username, password=self.password)
            else:
                session.cert = (self.cert_pem, self.cert_key_pem)
                session.headers['Authorization'] = \
                    "http://schemas.dmtf.org/wbem/wsman/1/wsman/secprofile/https/mutual"
        elif self.auth_method == 'ntlm':
            if not HAVE_NTLM:
                raise WinRMError("requested auth method is ntlm, but requests_ntlm is not installed")
            man_args = dict(
                username=self.username,
                password=self.password
            )
            opt_args = dict(
                send_cbt=self.send_cbt
            )
            ntlm_args = self._get_args(man_args, opt_args, HttpNtlmAuth.__init__)
            session.auth = HttpNtlmAuth(**ntlm_args)
            # check if requests_ntlm has the session_security attribute available for encryption
            encryption_available = hasattr(session.auth, 'session_security')
        # TODO: ssl is not exactly right here- should really be client_cert
        elif self.auth_method in ['basic', 'plaintext']:
            session.auth = requests.auth.HTTPBasicAuth(username=self.username, password=self.password)
        elif self.auth_method == 'credssp':
            if not HAVE_CREDSSP:
                raise WinRMError("requests auth method is credssp, but requests-credssp is not installed")

            man_args = dict(
                username=self.username,
                password=self.password
            )
            opt_args = dict(
                disable_tlsv1_2=self.credssp_disable_tlsv1_2,
                auth_mechanism=self.credssp_auth_mechanism,
                minimum_version=self.credssp_minimum_version
            )
            credssp_args = self._get_args(man_args, opt_args, HttpCredSSPAuth.__init__)
            session.auth = HttpCredSSPAuth(**credssp_args)
            encryption_available = True
        else:
            raise WinRMError("unsupported auth method: %s" % self.auth_method)

        session.headers.update(self.default_headers)
        self.session = session

        # Will check the current config and see if we need to setup message encryption
        if self.message_encryption == 'always' and not encryption_available:
            raise WinRMError(
                "message encryption is set to 'always' but the selected auth method %s does not support it" % self.auth_method)
        elif encryption_available:
            if self.message_encryption == 'always':
                self.setup_encryption()
            elif self.message_encryption == 'auto' and not self.endpoint.lower().startswith('https'):
                self.setup_encryption()
Beispiel #10
0
    def __init__(
        self,
        endpoint,
        transport='plaintext',
        username=None,
        password=None,
        realm=None,
        service="HTTP",
        keytab=None,
        ca_trust_path='legacy_requests',
        cert_pem=None,
        cert_key_pem=None,
        server_cert_validation='validate',
        kerberos_delegation=False,
        read_timeout_sec=DEFAULT_READ_TIMEOUT_SEC,
        operation_timeout_sec=DEFAULT_OPERATION_TIMEOUT_SEC,
        kerberos_hostname_override=None,
        message_encryption='auto',
        credssp_disable_tlsv1_2=False,
        send_cbt=True,
        proxy='legacy_requests',
    ):
        """
        @param string endpoint: the WinRM webservice endpoint
        @param string transport: transport type, one of 'plaintext' (default), 'kerberos', 'ssl', 'ntlm', 'credssp'  # NOQA
        @param string username: username
        @param string password: password
        @param string realm: unused
        @param string service: the service name, default is HTTP
        @param string keytab: the path to a keytab file if you are using one
        @param string ca_trust_path: Certification Authority trust path. If server_cert_validation is set to 'validate':
                                        'legacy_requests'(default) to use environment variables,
                                        None to explicitly disallow any additional CA trust path
                                        Any other value will be considered the CA trust path to use.
        @param string cert_pem: client authentication certificate file path in PEM format  # NOQA
        @param string cert_key_pem: client authentication certificate key file path in PEM format  # NOQA
        @param string server_cert_validation: whether server certificate should be validated on Python versions that suppport it; one of 'validate' (default), 'ignore' #NOQA
        @param bool kerberos_delegation: if True, TGT is sent to target server to allow multiple hops  # NOQA
        @param int read_timeout_sec: maximum seconds to wait before an HTTP connect/read times out (default 30). This value should be slightly higher than operation_timeout_sec, as the server can block *at least* that long. # NOQA
        @param int operation_timeout_sec: maximum allowed time in seconds for any single wsman HTTP operation (default 20). Note that operation timeouts while receiving output (the only wsman operation that should take any significant time, and where these timeouts are expected) will be silently retried indefinitely. # NOQA
        @param string kerberos_hostname_override: the hostname to use for the kerberos exchange (defaults to the hostname in the endpoint URL)
        @param bool message_encryption_enabled: Will encrypt the WinRM messages if set to True and the transport auth supports message encryption (Default True).
        @param string proxy: Specify a proxy for the WinRM connection to use. 'legacy_requests'(default) to use environment variables, None to disable proxies completely or the proxy URL itself.
        """

        try:
            read_timeout_sec = int(read_timeout_sec)
        except ValueError as ve:
            raise ValueError("failed to parse read_timeout_sec as int: %s" %
                             str(ve))

        try:
            operation_timeout_sec = int(operation_timeout_sec)
        except ValueError as ve:
            raise ValueError(
                "failed to parse operation_timeout_sec as int: %s" % str(ve))

        if operation_timeout_sec >= read_timeout_sec or operation_timeout_sec < 1:
            raise WinRMError(
                "read_timeout_sec must exceed operation_timeout_sec, and both must be non-zero"
            )

        self.read_timeout_sec = read_timeout_sec
        self.operation_timeout_sec = operation_timeout_sec
        self.max_env_sz = Protocol.DEFAULT_MAX_ENV_SIZE
        self.locale = Protocol.DEFAULT_LOCALE

        self.transport = Transport(
            endpoint=endpoint,
            username=username,
            password=password,
            realm=realm,
            service=service,
            keytab=keytab,
            ca_trust_path=ca_trust_path,
            cert_pem=cert_pem,
            cert_key_pem=cert_key_pem,
            read_timeout_sec=self.read_timeout_sec,
            server_cert_validation=server_cert_validation,
            kerberos_delegation=kerberos_delegation,
            kerberos_hostname_override=kerberos_hostname_override,
            auth_method=transport,
            message_encryption=message_encryption,
            credssp_disable_tlsv1_2=credssp_disable_tlsv1_2,
            send_cbt=send_cbt,
            proxy=proxy,
        )

        self.username = username
        self.password = password
        self.service = service
        self.keytab = keytab
        self.ca_trust_path = ca_trust_path
        self.server_cert_validation = server_cert_validation
        self.kerberos_delegation = kerberos_delegation
        self.kerberos_hostname_override = kerberos_hostname_override
        self.credssp_disable_tlsv1_2 = credssp_disable_tlsv1_2
Beispiel #11
0
    def build_session(self):
        session = requests.Session()

        # allow some settings to be merged from env
        session.trust_env = True
        settings = session.merge_environment_settings(url=self.endpoint,
                                                      proxies={},
                                                      stream=None,
                                                      verify=None,
                                                      cert=None)

        # get proxy settings from env
        # FUTURE: allow proxy to be passed in directly to supersede this value
        session.proxies = settings['proxies']

        # specified validation mode takes precedence
        session.verify = self.server_cert_validation == 'validate'

        # patch in CA path override if one was specified in init or env
        if session.verify and (self.ca_trust_path is not None
                               or settings['verify'] is not None):
            # session.verify can be either a bool or path to a CA store; prefer passed-in value over env if both are present
            session.verify = self.ca_trust_path or settings['verify']

        encryption_available = False

        if self.auth_method == 'kerberos':
            if not HAVE_KERBEROS:
                raise WinRMError(
                    "requested auth method is kerberos, but requests_kerberos is not installed"
                )

            man_args = dict(mutual_authentication=REQUIRED, )
            opt_args = dict(delegate=self.kerberos_delegation,
                            force_preemptive=True,
                            principal=self.username,
                            hostname_override=self.kerberos_hostname_override,
                            sanitize_mutual_error_response=False,
                            service=self.service,
                            send_cbt=self.send_cbt)
            kerb_args = self._get_args(man_args, opt_args,
                                       HTTPKerberosAuth.__init__)
            session.auth = HTTPKerberosAuth(**kerb_args)
            encryption_available = hasattr(
                session.auth, 'winrm_encryption_available'
            ) and session.auth.winrm_encryption_available
        elif self.auth_method in ['certificate', 'ssl']:
            if self.auth_method == 'ssl' and not self.cert_pem and not self.cert_key_pem:
                # 'ssl' was overloaded for HTTPS with optional certificate auth,
                # fall back to basic auth if no cert specified
                session.auth = requests.auth.HTTPBasicAuth(
                    username=self.username, password=self.password)
            else:
                session.cert = (self.cert_pem, self.cert_key_pem)
                session.headers['Authorization'] = \
                    "http://schemas.dmtf.org/wbem/wsman/1/wsman/secprofile/https/mutual"
        elif self.auth_method == 'ntlm':
            if not HAVE_NTLM:
                raise WinRMError(
                    "requested auth method is ntlm, but requests_ntlm is not installed"
                )
            man_args = dict(username=self.username, password=self.password)
            opt_args = dict(send_cbt=self.send_cbt)
            ntlm_args = self._get_args(man_args, opt_args,
                                       HttpNtlmAuth.__init__)
            session.auth = HttpNtlmAuth(**ntlm_args)
            # check if requests_ntlm has the session_security attribute available for encryption
            encryption_available = hasattr(session.auth, 'session_security')
        # TODO: ssl is not exactly right here- should really be client_cert
        elif self.auth_method in ['basic', 'plaintext']:
            session.auth = requests.auth.HTTPBasicAuth(username=self.username,
                                                       password=self.password)
        elif self.auth_method == 'credssp':
            if not HAVE_CREDSSP:
                raise WinRMError(
                    "requests auth method is credssp, but requests-credssp is not installed"
                )

            man_args = dict(username=self.username, password=self.password)
            opt_args = dict(disable_tlsv1_2=self.credssp_disable_tlsv1_2,
                            auth_mechanism=self.credssp_auth_mechanism,
                            minimum_version=self.credssp_minimum_version)
            credssp_args = self._get_args(man_args, opt_args,
                                          HttpCredSSPAuth.__init__)
            session.auth = HttpCredSSPAuth(**credssp_args)
            encryption_available = True
        else:
            raise WinRMError("unsupported auth method: %s" % self.auth_method)

        session.headers.update(self.default_headers)
        self.session = session

        # Will check the current config and see if we need to setup message encryption
        if self.message_encryption == 'always' and not encryption_available:
            raise WinRMError(
                "message encryption is set to 'always' but the selected auth method %s does not support it"
                % self.auth_method)
        elif encryption_available:
            if self.message_encryption == 'always':
                self.setup_encryption()
            elif self.message_encryption == 'auto' and not self.endpoint.lower(
            ).startswith('https'):
                self.setup_encryption()
Beispiel #12
0
    def build_session(self):
        session = requests.Session()

        session.verify = self.server_cert_validation == 'validate'

        # configure proxies from HTTP/HTTPS_PROXY envvars
        session.trust_env = True
        settings = session.merge_environment_settings(url=self.endpoint, proxies={}, stream=None,
                                                      verify=None, cert=None)

        # we're only applying proxies from env, other settings are ignored
        session.proxies = settings['proxies']

        encryption_available = False
        if self.auth_method == 'kerberos':
            if not HAVE_KERBEROS:
                raise WinRMError("requested auth method is kerberos, but requests_kerberos is not installed")
            # TODO: do argspec sniffing on extensions to ensure we're not setting bogus kwargs on older versions
            session.auth = HTTPKerberosAuth(mutual_authentication=REQUIRED, delegate=self.kerberos_delegation,
                                            force_preemptive=True, principal=self.username,
                                            hostname_override=self.kerberos_hostname_override,
                                            sanitize_mutual_error_response=False)
        elif self.auth_method in ['certificate', 'ssl']:
            if self.auth_method == 'ssl' and not self.cert_pem and not self.cert_key_pem:
                # 'ssl' was overloaded for HTTPS with optional certificate auth,
                # fall back to basic auth if no cert specified
                session.auth = requests.auth.HTTPBasicAuth(username=self.username, password=self.password)
            else:
                session.cert = (self.cert_pem, self.cert_key_pem)
                session.headers['Authorization'] = \
                    "http://schemas.dmtf.org/wbem/wsman/1/wsman/secprofile/https/mutual"
        elif self.auth_method == 'ntlm':
            if not HAVE_NTLM:
                raise WinRMError("requested auth method is ntlm, but requests_ntlm is not installed")
            session.auth = HttpNtlmAuth(username=self.username, password=self.password)
            # check if requests_ntlm has the session_security attribute available for encryption
            encryption_available = hasattr(session.auth, 'session_security')
        # TODO: ssl is not exactly right here- should really be client_cert
        elif self.auth_method in ['basic', 'plaintext']:
            session.auth = requests.auth.HTTPBasicAuth(username=self.username, password=self.password)
        elif self.auth_method == 'credssp':
            if not HAVE_CREDSSP:
                raise WinRMError("requests auth method is credssp, but requests-credssp is not installed")
            session.auth = HttpCredSSPAuth(username=self.username, password=self.password,
                                               disable_tlsv1_2=self.credssp_disable_tlsv1_2)
            encryption_available = hasattr(session.auth, 'wrap') and hasattr(session.auth, 'unwrap')
        else:
            raise WinRMError("unsupported auth method: %s" % self.auth_method)

        session.headers.update(self.default_headers)
        self.session = session

        # Will check the current config and see if we need to setup message encryption
        if self.message_encryption == 'always' and not encryption_available:
            raise WinRMError(
                "message encryption is set to 'always' but the selected auth method %s does not support it" % self.auth_method)
        elif encryption_available:
            if self.message_encryption == 'always':
                self.setup_encryption()
            elif self.message_encryption == 'auto' and not self.endpoint.lower().startswith('https'):
                self.setup_encryption()
Beispiel #13
0
    def __init__(
        self,
        endpoint,
        transport='plaintext',
        username=None,
        password=None,
        realm=None,
        service=None,
        keytab=None,
        ca_trust_path=None,
        cert_pem=None,
        cert_key_pem=None,
        server_cert_validation='validate',
        kerberos_delegation=False,
        read_timeout_sec=DEFAULT_READ_TIMEOUT_SEC,
        operation_timeout_sec=DEFAULT_OPERATION_TIMEOUT_SEC,
        kerberos_hostname_override=None,
    ):
        """
        @param string endpoint: the WinRM webservice endpoint
        @param string transport: transport type, one of 'plaintext' (default), 'kerberos', 'ssl', 'ntlm', 'credssp'  # NOQA
        @param string username: username
        @param string password: password
        @param string realm: unused
        @param string service: the service name, default is HTTP
        @param string keytab: the path to a keytab file if you are using one
        @param string ca_trust_path: Certification Authority trust path
        @param string cert_pem: client authentication certificate file path in PEM format  # NOQA
        @param string cert_key_pem: client authentication certificate key file path in PEM format  # NOQA
        @param string server_cert_validation: whether server certificate should be validated on Python versions that suppport it; one of 'validate' (default), 'ignore' #NOQA
        @param bool kerberos_delegation: if True, TGT is sent to target server to allow multiple hops  # NOQA
        @param int read_timeout_sec: maximum seconds to wait before an HTTP connect/read times out (default 30). This value should be slightly higher than operation_timeout_sec, as the server can block *at least* that long. # NOQA
        @param int operation_timeout_sec: maximum allowed time in seconds for any single wsman HTTP operation (default 20). Note that operation timeouts while receiving output (the only wsman operation that should take any significant time, and where these timeouts are expected) will be silently retried indefinitely. # NOQA
        @param string kerberos_hostname_override: the hostname to use for the kerberos exchange (defaults to the hostname in the endpoint URL)
        """

        if operation_timeout_sec >= read_timeout_sec or operation_timeout_sec < 1:
            raise WinRMError(
                "read_timeout_sec must exceed operation_timeout_sec, and both must be non-zero"
            )

        self.read_timeout_sec = read_timeout_sec
        self.operation_timeout_sec = operation_timeout_sec
        self.max_env_sz = Protocol.DEFAULT_MAX_ENV_SIZE
        self.locale = Protocol.DEFAULT_LOCALE

        self.transport = Transport(
            endpoint=endpoint,
            username=username,
            password=password,
            realm=realm,
            service=service,
            keytab=keytab,
            ca_trust_path=ca_trust_path,
            cert_pem=cert_pem,
            cert_key_pem=cert_key_pem,
            read_timeout_sec=self.read_timeout_sec,
            server_cert_validation=server_cert_validation,
            kerberos_delegation=kerberos_delegation,
            kerberos_hostname_override=kerberos_hostname_override,
            auth_method=transport)

        self.username = username
        self.password = password
        self.service = service
        self.keytab = keytab
        self.ca_trust_path = ca_trust_path
        self.server_cert_validation = server_cert_validation
        self.kerberos_delegation = kerberos_delegation
        self.kerberos_hostname_override = kerberos_hostname_override
Beispiel #14
0
    def build_session(self):
        if self.server_cert_validation == 'ignore':
            # if we're explicitly ignoring validation, try to suppress requests' vendored urllib3 InsecureRequestWarning
            try:
                from requests.packages.urllib3.exceptions import InsecureRequestWarning
                warnings.simplefilter('ignore', category=InsecureRequestWarning)
            except:
                # oh well, we tried...
                pass

        session = requests.Session()

        session.verify = self.server_cert_validation == 'validate'

        # configure proxies from HTTP/HTTPS_PROXY envvars
        session.trust_env = True
        settings = session.merge_environment_settings(url=self.endpoint, proxies={}, stream=None,
                                                      verify=None, cert=None)

        # we're only applying proxies from env, other settings are ignored
        session.proxies = settings['proxies']

        if self.auth_method == 'kerberos':
            if not HAVE_KERBEROS:
                raise WinRMError("requested auth method is kerberos, but requests_kerberos is not installed")
            # TODO: do argspec sniffing on extensions to ensure we're not setting bogus kwargs on older versions
            session.auth = HTTPKerberosAuth(mutual_authentication=REQUIRED, delegate=self.kerberos_delegation,
                                            force_preemptive=True, principal=self.username, hostname_override=self.realm)
        elif self.auth_method in ['certificate','ssl']:
            if self.auth_method == 'ssl' and not self.cert_pem and not self.cert_key_pem:
                # 'ssl' was overloaded for HTTPS with optional certificate auth,
                # fall back to basic auth if no cert specified
                session.auth = requests.auth.HTTPBasicAuth(username=self.username, password=self.password)
            else:
                # client cert auth, validate accordingly
                if not self.cert_pem or not self.cert_key_pem:
                    raise InvalidCredentialsError("both cert_pem and cert_key_pem must be specified for cert auth")
                if not os.path.exists(self.cert_pem):
                    raise InvalidCredentialsError("cert_pem file not found (%s)" % self.cert_pem)
                if not os.path.exists(self.cert_key_pem):
                    raise InvalidCredentialsError("cert_key_pem file not found (%s)" % self.cert_key_pem)

                session.cert = (self.cert_pem, self.cert_key_pem)
                session.headers['Authorization'] = \
                    "http://schemas.dmtf.org/wbem/wsman/1/wsman/secprofile/https/mutual"
        elif self.auth_method == 'ntlm':
            if not HAVE_NTLM:
                raise WinRMError("requested auth method is ntlm, but requests_ntlm is not installed")
            if not self.username:
                raise InvalidCredentialsError("auth method ntlm requires a username")
            if not self.password:
                raise InvalidCredentialsError("auth method ntlm requires a password")
            session.auth = HttpNtlmAuth(username=self.username, password=self.password)
        # TODO: ssl is not exactly right here- should really be client_cert
        elif self.auth_method in ['basic','plaintext']:
            if not self.username:
                raise InvalidCredentialsError("auth method basic requires a username")
            if not self.password:
                raise InvalidCredentialsError("auth method basic requires a password")
            session.auth = requests.auth.HTTPBasicAuth(username=self.username, password=self.password)

        else:
            raise WinRMError("unsupported auth method: %s" % self.auth_method)

        session.headers.update(self.default_headers)

        return session