[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