[python-win32] socket-server failed to authorize clients in domain environment
Steffen Frömer
steffen at froemer.net
Fri Jan 6 19:49:47 CET 2012
Hello,
i try to run a socket server in a domain network. At first i am using
the DEMO from win32-package. If i start server and client on one
computer under my username, all works fine. Also the connection to
another computer works perfectly. But if i run the server with my
username and a colleague will connect to my server, i get authentication
failure.
All Usernames are Domain-Member. One has local Administrator rights and
the other one is standard windows user.
Running environment is python 2.7.2 win32 on windows7 64bit platform.
We also tested Kerberos, like
'socket_server.py server --package=Kerberos' # on Server side
'socket_server.py client --package=Kerberos
--target-spn=RestrictedKrbHost/COMPUTERNAME.local # on client side,
where COMPUTERNAME.local represent the Computername in FQDN notation,
where the Server is running
All works fine, if Client and Server running under the same Username.
Connecting to server for all other Domainusers will force an
authentication-error on Serverside.
I tried starting server on my laptop, which has noch Domain-connection.
All usernames are locally.
'root' = local Administrator
'froemer' = local user
socket_server.py client # as user froemer
socket_server.py client # as user root
The output is that, what i excpected
#############
Running test server...
The server is running as user root
Having conversation with client as user froemer
Client sent: 'Hello'
Client sent: 'from'
Client sent: 'the'
Client sent: 'client'
The server is back to user root
#############
Is there a more special way on running a socket-server like this one in
a domain environment?
--#--#--#--#--#--#--#--#--#--#--#--#--#--#--#--#--#--#--#--#--#--#--#--#--#--#--
"""A sample socket server and client using SSPI authentication and
encryption.
You must run with either 'client' or 'server' as arguments. A server
must be
running before a client can connect.
To use with Kerberos you should include in the client options
--target-spn=username, where 'username' is the user under which the
server is
being run.
Running either the client or server as a different user can be informative.
A command-line such as the following may be useful:
`runas /user:{user} {fqp}\python.exe {fqp}\socket_server.py --wait
client|server`
{fqp} should specify the relevant fully-qualified path names.
To use 'runas' with Kerberos, the client program will need to
specify --target-spn with the username under which the *server* is running.
See the SSPI documentation for more details.
"""
import sys
import struct
import SocketServer
import win32api
import httplib
import traceback
import win32security
import sspi, sspicon
import optparse # sorry, this demo needs 2.3+
options = None # set to optparse object.
def GetUserName():
try:
return win32api.GetUserName()
except win32api.error, details:
# Seeing 'access denied' errors here for non-local users
(presumably
# without permission to login locally). Get the fully-qualified
# username, although a side-effect of these permission-denied
errors
# is a lack of Python codecs - so printing the Unicode value fails.
# So just return the repr(), and avoid codecs completely.
return repr(win32api.GetUserNameEx(win32api.NameSamCompatible))
# Send a simple "message" over a socket - send the number of bytes first,
# then the string. Ditto for receive.
def _send_msg(s, m):
s.send(struct.pack("i", len(m)))
s.send(m)
def _get_msg(s):
size_data = s.recv(struct.calcsize("i"))
if not size_data:
return None
cb = struct.unpack("i", size_data)[0]
return s.recv(cb)
class SSPISocketServer(SocketServer.TCPServer):
def __init__(self, *args, **kw):
SocketServer.TCPServer.__init__(self, *args, **kw)
self.sa = sspi.ServerAuth(options.package)
def verify_request(self, sock, ca):
# Do the sspi auth dance
self.sa.reset()
while 1:
data = _get_msg(sock)
if data is None:
return False
try:
err, sec_buffer = self.sa.authorize(data)
except sspi.error, details:
print "FAILED to authorize client:", details
return False
if err==0:
break
_send_msg(sock, sec_buffer[0].Buffer)
return True
def process_request(self, request, client_address):
# An example using the connection once it is established.
print "The server is running as user", GetUserName()
self.sa.ctxt.ImpersonateSecurityContext()
try:
print "Having conversation with client as user", GetUserName()
while 1:
# we need to grab 2 bits of data - the encrypted data,
and the
# 'key'
data = _get_msg(request)
key = _get_msg(request)
if data is None or key is None:
break
data = self.sa.decrypt(data, key)
print "Client sent:", repr(data)
finally:
self.sa.ctxt.RevertSecurityContext()
self.close_request(request)
print "The server is back to user", GetUserName()
def serve():
s = SSPISocketServer(("", options.port), None)
print "Running test server..."
s.serve_forever()
def sspi_client():
c = httplib.HTTPConnection(options.host, options.port)
c.connect()
# Do the auth dance.
ca = sspi.ClientAuth(options.package, targetspn=options.target_spn)
data = None
while 1:
err, out_buf = ca.authorize(data)
_send_msg(c.sock, out_buf[0].Buffer)
if err==0:
break
data = _get_msg(c.sock)
print "Auth dance complete - sending a few encryted messages"
# Assume out data is sensitive - encrypt the message.
for data in "Hello from the client".split():
blob, key = ca.encrypt(data)
_send_msg(c.sock, blob)
_send_msg(c.sock, key)
c.sock.close()
print "Client completed."
if __name__=='__main__':
parser = optparse.OptionParser("%prog [options] client|server",
description=__doc__)
parser.add_option("", "--package", action="store", default="NTLM",
help="The SSPI package to use (eg, Kerberos) -
default is NTLM")
parser.add_option("", "--target-spn", action="store",
help="""The target security provider name to use. The
string contents are security-package specific. For
example, 'Kerberos' or 'Negotiate' require the server
principal name (SPN) (ie, the username) of the remote
process. For NTLM this must be blank.""")
parser.add_option("", "--port", action="store", default="8181",
help="The port number to use (default=8181)")
parser.add_option("", "--wait", action="store_true",
help="""Cause the program to wait for input just
before
terminating. Useful when using via runas
to see
any error messages before termination.
""")
parser.add_option("", "--host", action="store", default="localhost",
help="the host to connect - default is localhost")
options, args = parser.parse_args()
try:
options.port = int(options.port)
except (ValueError, TypeError):
parser.error("--port must be an integer")
try:
options.host = str(options.host)
except (ValueError, TypeError):
parser.error("--host must be valid hostname")
try:
try:
if not args:
serve()
args = ['']
if args[0]=="client":
sspi_client()
elif args[0]=="server":
serve()
else:
parser.error("You must supply 'client' or 'server' - " \
"use --help for details")
except KeyboardInterrupt:
pass
except SystemExit:
pass
except:
traceback.print_exc()
finally:
if options.wait:
raw_input("Press enter to continue")
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <http://mail.python.org/pipermail/python-win32/attachments/20120106/acaea998/attachment.html>
More information about the python-win32
mailing list