[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


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 
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 

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 

{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():
         return win32api.GetUserName()
     except win32api.error, details:
         # Seeing 'access denied' errors here for non-local users 
         # without permission to login locally).  Get the fully-qualified
         # username, although a side-effect of these permission-denied 
         # 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)))

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
         while 1:
             data = _get_msg(sock)
             if data is None:
                 return False
                 err, sec_buffer = self.sa.authorize(data)
             except sspi.error, details:
                 print "FAILED to authorize client:", details
                 return False

             if err==0:
             _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()
             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:
                 data = self.sa.decrypt(data, key)
                 print "Client sent:", repr(data)
         print "The server is back to user", GetUserName()

def serve():
     s = SSPISocketServer(("", options.port), None)
     print "Running test server..."

def sspi_client():
     c = httplib.HTTPConnection(options.host, options.port)
     # 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:
         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)
     print "Client completed."

if __name__=='__main__':
     parser = optparse.OptionParser("%prog [options] client|server",

     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 
                               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()
         options.port = int(options.port)
     except (ValueError, TypeError):
         parser.error("--port must be an integer")

         options.host = str(options.host)
     except (ValueError, TypeError):
         parser.error("--host must be valid hostname")

             if not args:
                 args = ['']
             if args[0]=="client":

             elif args[0]=="server":
                 parser.error("You must supply 'client' or 'server' - " \
                              "use --help for details")
         except KeyboardInterrupt:
         except SystemExit:
         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