示例#1
0
    def send_message(self, message):
        # TODO support kerberos/ntlm 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 isinstance(message, unicode_type):
            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 b'http://schemas.microsoft.com/wbem/wsman/1/windows/shell/Receive' in message and b'Code="2150858793"' in response_text:
                raise WinRMOperationTimeoutError()

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

            raise WinRMTransportError('http', error_message)
示例#2
0
def fix_send_message(self, message):
    """Hacking from winrm.transport.Transport.send_message
    For adding detailed error message
    """
    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 isinstance(message, type(u'')):
        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 b'http://schemas.microsoft.com/wbem/wsman/1/windows/shell/Receive' in message and b'Code="2150858793"' in response_text:
            LOG.debug(
                'Receiving cmd result from %s but exceed operation timeout, keep retry',
                ex.request.url)
            raise WinRMOperationTimeoutError()

        # hack start
        # 2150858793 Operation timeout
        # 2147943418 Illegal operation attempted on a registry key that has been marked for deletion.
        # 2147746132 Class not registered
        LOG.error('Send message [%s] to [%s] get HTTP code [%s] [%s]', message,
                  self.endpoint, ex.response.status_code, response_text)
        err_code, err_msg = parse_error_response(response_text)
        if err_code == "2150858793":
            # raise EPASWinRMOperationTimeout(err_msg)
            raise OSCEWinRMOperationTimeout(err_msg)
        elif err_code == "2147943418":
            # raise EPASWinRMIllegalOperation(err_msg)
            raise OSCEWinRMIllegalOperation(err_msg)
        elif err_code == "2147746132":
            # raise EPASWinRMClassNotRegister(err_msg)
            raise OSCEWinRMClassNotRegister(err_msg)
        else:
            # raise EPASWinRMTransportException(ex.response.status_code, err_code, err_msg)
            raise OSCEWinRMTransportException(ex.response.status_code,
                                              err_code, err_msg)
示例#3
0
    def _send_message_request(self, prepared_request, message):
        try:
            response = self.session.send(prepared_request, timeout=self.read_timeout_sec)
            response.raise_for_status()
            return response
        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 = self._get_message_response_text(ex.response)
            else:
                response_text = ''

            raise WinRMTransportError('http', ex.response.status_code, response_text)
示例#4
0
    def send_message(self, message):
        # TODO support kerberos/ntlm 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 isinstance(message, unicode_type):
            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 b'http://schemas.microsoft.com/wbem/wsman/1/windows/shell/Receive' in message and b'Code="2150858793"' in response_text:
                raise WinRMOperationTimeoutError()

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

            import xml.etree.ElementTree as ET

            if response_text:
                root = ET.fromstring(response_text)
                ns = {'s': "http://www.w3.org/2003/05/soap-envelope",
                      'a': "http://schemas.xmlsoap.org/ws/2004/08/addressing",
                      'w': "http://schemas.dmtf.org/wbem/wsman/1/wsman.xsd"}

                for text in root.findall('s:Body/s:Fault/s:Reason/s:Text', ns):
                    error_message += "\n%s" % text.text

            raise WinRMTransportError('http', error_message)
示例#5
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)
示例#6
0
    def _send_message_request(self, prepared_request, message):
        try:
            response = self.session.send(prepared_request, timeout=self.read_timeout_sec)
            response.raise_for_status()
            return response
        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 = self._get_message_response_text(ex.response)
            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 b'http://schemas.microsoft.com/wbem/wsman/1/windows/shell/Receive' in message and b'Code="2150858793"' in response_text:
                raise WinRMOperationTimeoutError()

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

            raise WinRMTransportError('http', error_message)
示例#7
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
示例#8
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