def test_check_credssp_token_fail(self): test_request = requests.Request('GET', '') test_request.headers['www-authenticate'] = 'NTLM, Negotiate' with self.assertRaises(AuthenticationException) as context: HttpCredSSPAuth._check_credssp_supported(test_request) self.assertTrue('The server did not respond with CredSSP as an available auth method', context.exception.args)
def test_get_credssp_token_fail_different_auth(self): test_request = requests.Request('GET', '') test_request.headers['www-authenticate'] = 'NTLM dGVzdA==' with self.assertRaises(AuthenticationException) as context: HttpCredSSPAuth._get_credssp_token(test_request) self.assertTrue('The server did not response with a CredSSP token, auth rejected', context.exception.args)
def test_verify_public_key_good(self, mock_unwrap): test_credssp_context = HttpCredSSPAuth('', '') test_ntlm_context = ntlm.Ntlm() test_ntlm_context.session_security = session_security.SessionSecurity(1, 'key'.encode()) test_credssp_context.context = test_ntlm_context test_ts_request = TSRequest() test_ts_request.parse_data(public_key_ts_request) test_public_key = hex_to_byte('00') + server_pub_key_token[1:] test_credssp_context._verify_public_keys(test_public_key, test_ts_request)
def test_verify_public_key_invalid(self, mock_unwrap): test_credssp_context = HttpCredSSPAuth('', '') test_ntlm_context = ntlm.Ntlm() test_ntlm_context.session_security = session_security.SessionSecurity(1, 'key'.encode()) test_credssp_context.context = test_ntlm_context test_ts_request = TSRequest() test_ts_request.parse_data(public_key_ts_request) # Use the wrong first byte to ensure the keys don't match test_public_key = hex_to_byte('01') + server_pub_key_token[1:] with self.assertRaises(AssertionError) as context: test_credssp_context._verify_public_keys(test_public_key, test_ts_request) assert context.exception.args[0] == 'Could not verify key sent from the server, possibly man in the middle attack'
def test_parse_username_with_backslash(self): test_username = '******' expected_domain = 'DOMAIN' expected_user = '******' actual_domain, actual_user = HttpCredSSPAuth._parse_username(test_username) assert actual_domain == expected_domain assert actual_user == expected_user
def _build_auth_credssp(self, session: requests.Session) -> None: if self.username is None: raise ValueError("For credssp auth, the username must be specified") if self.password is None: raise ValueError("For credssp auth, the password must be specified") kwargs = self._get_auth_kwargs("credssp") session.auth = HttpCredSSPAuth(username=self.username, password=self.password, **kwargs)
def test_parse_username_without_domain(self): test_username = '******' expected_domain = '.' expected_user = '******' actual_domain, actual_user = HttpCredSSPAuth._parse_username(test_username) assert actual_domain == expected_domain assert actual_user == expected_user
def test_parse_username_with_at(self): test_username = '******' expected_domain = 'DOMAIN.LOCAL' expected_user = '******' actual_domain, actual_user = HttpCredSSPAuth._parse_username(test_username) assert actual_domain == expected_domain assert actual_user == expected_user
def test_get_credssp_token_success(self): test_request = requests.Request('GET', '') test_request.headers['www-authenticate'] = 'CredSSP dGVzdA==' expected = 'test'.encode() actual = HttpCredSSPAuth._get_credssp_token(test_request) assert actual == expected
def test(host: str): session = requests.Session() session.verify = False _host = CFG_HOSTS.get(host, None) if not _host: pyshellrm.error("Invalid host name") return False server = _host.get("server", None) port = _host.get("port", 5985) path = _host.get("path", "wsman") username = _host.get("username" , r'') password = _host.get("password" , r'') auth = _host.get("auth", "ntlm") ssl = _host.get("ssl", False) req_url = "http://" if not ssl else "https://" req_url += f"{server}:{port}/{path}" if auth == "ntlm": session.auth = HttpNtlmAuth(username, password) elif auth == "kerberos": if not HAS_KERBEROS: pyshellrm.error( f"{auth} auth attempt without requests-kerberos installed" ) return False session.auth = HTTPKerberosAuth() elif auth == "credssp": if not HAS_CREDSSP: pyshellrm.error( f"{auth} auth attempt without requests-credssp installed" ) return False session.auth = HttpCredSSPAuth(f'{username}', f'{password}', auth_mechanism='auto') try: if not session.post(req_url).ok: pyshellrm.error( f"Failed to auth with {host} using {username}:{password}" ) return False except requests.exceptions.ConnectionError: pyshellrm.error( f"Failed to establish a connection to {host}" ) return False pyshellrm.success( f"Successfully connected to {host} with {username}:{password}" ) return True
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'] # We override the environment with passed values when they're defined if self.http_proxy: session.proxies['http'] = self.http_proxy if self.https_proxy: session.proxies['https'] = self.https_proxy 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) 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) else: raise WinRMError("unsupported auth method: %s" % self.auth_method) session.headers.update(self.default_headers) return session
def _build_auth_credssp(self, session): if not HAS_CREDSSP: raise ImportError("Cannot use CredSSP auth as requests-credssp is " "not installed: %s" % str(CREDSSP_IMP_ERR)) if self.username is None: raise ValueError("For credssp auth, the username must be " "specified") if self.password is None: raise ValueError("For credssp auth, the password must be " "specified") kwargs = self._get_auth_kwargs('credssp') session.auth = HttpCredSSPAuth(username=self.username, password=self.password, **kwargs)
def test_auth_mechanism_unknown(self): with self.assertRaises(InvalidConfigurationException) as context: HttpCredSSPAuth('', '', auth_mechanism='unknown') self.assertTrue('Unknown auth mechanism unknown, please specify ntlm', context.exception.args)
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()
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()
def test_check_credssp_token(self): test_request = requests.Request('GET', '') test_request.headers['www-authenticate'] = 'CredSSP' HttpCredSSPAuth._check_credssp_supported(test_request)
def test_check_credssp_token_multiple_auths(self): test_request = requests.Request('GET', '') test_request.headers['www-authenticate'] = 'NTLM, Negotiate, CredSSP' HttpCredSSPAuth._check_credssp_supported(test_request)
def test_set_credssp_token(self): test_request = requests.Request('GET', '') HttpCredSSPAuth._set_credssp_token(test_request, 'test'.encode()) assert test_request.headers['Authorization'] == 'CredSSP dGVzdA=='.encode()
def test_auth_mechanism_ntlm(self): test_mechanism = 'ntlm' actual_object = HttpCredSSPAuth('', '', auth_mechanism=test_mechanism) actual = actual_object.context assert isinstance(actual, ntlm.Ntlm)
def test_tlsv1_2_disabled(self, tls_version, tls_options, cipher_assert): # The testing is actually happening in the mocking functions HttpCredSSPAuth('', '', disable_tlsv1_2=True)
def test_auth_mechanism_kerberos(self): with self.assertRaises(InvalidConfigurationException) as context: HttpCredSSPAuth('', '', auth_mechanism='kerberos') self.assertTrue('Kerberos auth not yet implemented, please use NTLM instead', context.exception.args)
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()
def test_tls1_2_option_default(self, tls_version, cipher_assert): # The testing is actually happening in the mocking functions HttpCredSSPAuth('', '')