/usr/lib/python2.7/dist-packages/winrm/transport.py is in python-winrm 0.0.3-1.
This file is owned by root:root, with mode 0o644.
The actual contents of the file can be viewed below.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 | import sys
import base64
from winrm.exceptions import WinRMTransportError, UnauthorizedError
HAVE_KERBEROS = False
try:
import kerberos
HAVE_KERBEROS = True
except ImportError:
pass
is_py2 = sys.version[0] == '2'
if is_py2:
from urllib2 import Request, URLError, HTTPError, HTTPBasicAuthHandler, HTTPPasswordMgrWithDefaultRealm, HTTPSHandler
from urllib2 import urlopen, build_opener, install_opener
from urlparse import urlparse
from httplib import HTTPSConnection
else:
from urllib.request import Request, URLError, HTTPError, HTTPBasicAuthHandler, HTTPPasswordMgrWithDefaultRealm, HTTPSHandler
from urllib.request import urlopen, build_opener, install_opener
from urllib.parse import urlparse
from http.client import HTTPSConnection
class HttpTransport(object):
def __init__(self, endpoint, username, password):
self.endpoint = endpoint
self.username = username
self.password = password
self.user_agent = 'Python WinRM client'
self.timeout = 3600 # Set this to an unreasonable amount for now because WinRM has timeouts
def basic_auth_only(self):
#here we should remove handler for any authentication handlers other than basic
# but maybe leave original credentials
# auths = @httpcli.www_auth.instance_variable_get('@authenticator')
# auths.delete_if {|i| i.scheme !~ /basic/i}
# drop all variables in auths if they not contains "basic" as insensitive.
pass
def no_sspi_auth(self):
# here we should remove handler for Negotiate/NTLM negotiation
# but maybe leave original credentials
pass
class HttpPlaintext(HttpTransport):
def __init__(self, endpoint, username='', password='', disable_sspi=True, basic_auth_only=True):
super(HttpPlaintext, self).__init__(endpoint, username, password)
if disable_sspi:
self.no_sspi_auth()
if basic_auth_only:
self.basic_auth_only()
self._headers = {'Content-Type': 'application/soap+xml;charset=UTF-8',
'User-Agent': 'Python WinRM client'}
def _setup_opener(self):
password_manager = HTTPPasswordMgrWithDefaultRealm()
password_manager.add_password(None, self.endpoint, self.username, self.password)
auth_manager = HTTPBasicAuthHandler(password_manager)
opener = build_opener(auth_manager)
install_opener(opener)
def send_message(self, message):
headers = self._headers.copy()
headers['Content-Length'] = len(message)
self._setup_opener()
request = Request(self.endpoint, data=message, headers=headers)
try:
response = urlopen(request, timeout=self.timeout)
# 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.read()
return response_text
#doc = ElementTree.fromstring(response.read())
#Ruby
#doc = Nokogiri::XML(resp.http_body.content)
#doc.collect_namespaces.each_pair do |k,v|
# doc.root.add_namespace((k.split(/:/).last),v) unless doc.namespaces.has_key?(k)
#end
#return doc
#return doc
except HTTPError as ex:
if ex.code == 401:
raise UnauthorizedError(transport='plaintext', message=ex.msg)
response_text = ex.read()
# 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.code)
if ex.msg:
error_message += ', {0}'.format(ex.msg)
raise WinRMTransportError('http', error_message)
except URLError as ex:
raise WinRMTransportError('http', ex.reason)
class HTTPSClientAuthHandler(HTTPSHandler):
def __init__(self, cert, key):
HTTPSHandler.__init__(self)
self.cert = cert
self.key = key
def https_open(self, req):
return self.do_open(self.getConnection, req)
def getConnection(self, host, timeout=300):
return HTTPSConnection(host, key_file=self.key, cert_file=self.cert)
class HttpSSL(HttpPlaintext):
"""Uses SSL to secure the transport"""
def __init__(self, endpoint, username, password, ca_trust_path=None, disable_sspi=True, basic_auth_only=True,
cert_pem=None, cert_key_pem=None):
super(HttpSSL, self).__init__(endpoint, username, password)
self._cert_pem = cert_pem
self._cert_key_pem = cert_key_pem
#Ruby
#@httpcli.set_auth(endpoint, user, pass)
#@httpcli.ssl_config.set_trust_ca(ca_trust_path) unless ca_trust_path.nil?
if disable_sspi:
self.no_sspi_auth()
if basic_auth_only:
self.basic_auth_only()
if self._cert_pem:
self._headers['Authorization'] = "http://schemas.dmtf.org/wbem/wsman/1/wsman/secprofile/https/mutual"
def _setup_opener(self):
if not self._cert_pem:
super(HttpSSL, self)._setup_opener()
else:
opener = build_opener(HTTPSClientAuthHandler(self._cert_pem, self._cert_key_pem))
install_opener(opener)
class KerberosTicket:
"""
Implementation based on http://ncoghlan_devs-python-notes.readthedocs.org/en/latest/python_kerberos.html
"""
def __init__(self, service):
ignored_code, krb_context = kerberos.authGSSClientInit(service)
kerberos.authGSSClientStep(krb_context, '')
# TODO authGSSClientStep may raise following error:
#GSSError: (('Unspecified GSS failure. Minor code may provide more information', 851968),
# ("Credentials cache file '/tmp/krb5cc_1000' not found", -1765328189))
self._krb_context = krb_context
gss_response = kerberos.authGSSClientResponse(krb_context)
self.auth_header = 'Negotiate {0}'.format(gss_response)
def verify_response(self, auth_header):
# Handle comma-separated lists of authentication fields
for field in auth_header.split(','):
kind, ignored_space, details = field.strip().partition(' ')
if kind.lower() == 'negotiate':
auth_details = details.strip()
break
else:
raise ValueError('Negotiate not found in {0}'.format(auth_header))
# Finish the Kerberos handshake
krb_context = self._krb_context
if krb_context is None:
raise RuntimeError('Ticket already used for verification')
self._krb_context = None
kerberos.authGSSClientStep(krb_context, auth_details)
#print('User {0} authenticated successfully using Kerberos authentication'.format(kerberos.authGSSClientUserName(krb_context)))
kerberos.authGSSClientClean(krb_context)
class HttpKerberos(HttpTransport):
def __init__(self, endpoint, realm=None, service='HTTP', keytab=None):
"""
Uses Kerberos/GSS-API to authenticate and encrypt messages
@param string endpoint: the WinRM webservice endpoint
@param string realm: the Kerberos realm we are authenticating to
@param string service: the service name, default is HTTP
@param string keytab: the path to a keytab file if you are using one
"""
if not HAVE_KERBEROS:
raise WinRMTransportError('kerberos is not installed')
super(HttpKerberos, self).__init__(endpoint, None, None)
self.krb_service = '{0}@{1}'.format(service, urlparse(endpoint).hostname)
#self.krb_ticket = KerberosTicket(krb_service)
def set_auth(self, username, password):
raise NotImplementedError
def send_message(self, message):
# TODO current implementation does negotiation on each HTTP request which is not efficient
# TODO support kerberos session with message encryption
krb_ticket = KerberosTicket(self.krb_service)
headers = {'Authorization': krb_ticket.auth_header,
'Connection': 'Keep-Alive',
'Content-Type': 'application/soap+xml;charset=UTF-8',
'User-Agent': 'Python WinRM client'}
request = Request(self.endpoint, data=message, headers=headers)
try:
response = urlopen(request, timeout=self.timeout)
krb_ticket.verify_response(response.headers['WWW-Authenticate'])
response_text = response.read()
return response_text
except HTTPError as ex:
response_text = ex.read()
# 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:
return response_text
#if ex.code == 401 and ex.headers['WWW-Authenticate'] == 'Negotiate, Basic realm="WSMAN"':
error_message = 'Kerberos-based authentication was failed. Code {0}'.format(ex.code)
if ex.msg:
error_message += ', {0}'.format(ex.msg)
raise WinRMTransportError('kerberos', error_message)
except URLError as ex:
raise WinRMTransportError('kerberos', ex.reason)
def _winrm_encrypt(self, string):
"""
@returns the encrypted request string
@rtype string
"""
raise NotImplementedError
def _winrm_decrypt(self, string):
raise NotImplementedError
|