The Samba-Bugzilla – Attachment 7595 Details for
Bug 8956
crash during NTLM auth from WSGI script
Home
|
New
|
Browse
|
Search
|
[?]
|
Reports
|
Requests
|
Help
|
New Account
|
Log In
[x]
|
Forgot Password
Login:
[x]
the wsgi script in question
rpcproxy.wsgi (text/plain), 13.45 KB, created by
Wolfgang Sourdeau (dead mail address)
on 2012-05-24 20:08:53 UTC
(
hide
)
Description:
the wsgi script in question
Filename:
MIME Type:
Creator:
Wolfgang Sourdeau (dead mail address)
Created:
2012-05-24 20:08:53 UTC
Size:
13.45 KB
patch
obsolete
>#!/usr/bin/python > >import os >import sys > >sys.path.extend(("/usr/local/samba/lib/python2.7/site-packages", > "/home/wsourdeau/src/openchange/mapiproxy/services/ocsmanager/rpcproxy")) > >import json >import httplib >import socket >import uuid >import time > >from rpcproxy.RPCH import RPCH, assemble_packet_header, RTS_FLAG_ECHO > >from samba.gensec import Security >from samba.auth import AuthContext > > ># logfile = open("/tmp/rpcproxy-%d.log" % os.getpid(), "w+") ># logfile = open("/tmp/rpcproxy.log", "a") > >COOKIE_NAME="ocs-ntlm-auth" >FIFO = "/tmp/fifo" >OC_HOST = "127.0.0.1" >PIDSTR = "[%d]" % os.getpid() > >class RPCProxyLogger(object): > def __init__(self, channel=sys.stderr): > self.channel = channel > > def log(self, *arg): > self.channel.write(PIDSTR) > for data in arg: > self.channel.write(" ") > self.channel.write(data) > self.channel.flush() > > def pretty_dump_dict(self, adict): > def _unhandled_objects(obj): > return str(obj) > s = json.dumps(adict, default=_unhandled_objects, > sort_keys=True, indent=4) > self.log('\n'.join([l.rstrip() for l in s.splitlines()]), "\n") > >class NTLMAuthHandler(object): > """ > HTTP/1.0 ``NTLM`` authentication middleware > > Parameters: application -- the application object that is called only upon > successful authentication. > > """ > > def __init__(self, application): > # TODO: client expiration and/or cleanup > self.client_status = {} > self.application = application > > def _in_progress_response(self, start_response, ntlm_data=None, client_id=None): > status = "401 %s" % httplib.responses[401] > content = "More data needed..." > headers = [("Content-Type", "text/plain"), > ("Content-Length", "%d" % len(content))] > if ntlm_data is None: > www_auth_value = "NTLM" > else: > enc_ntlm_data = ntlm_data.encode("base64") > www_auth_value = ("NTLM %s" > % enc_ntlm_data.strip().replace("\n", "")) > if client_id is not None: > # MUST occur when ntlm_data is None, can still occur otherwise > headers.append(("Set-Cookie", "%s=%s" % (COOKIE_NAME, client_id))) > > headers.append(("WWW-Authenticate", www_auth_value)) > start_response(status, headers) > > return [content] > > def _failure_response(self, start_response, explanation=None): > status = "403 %s" % httplib.responses[403] > content = "Authentication failure" > if explanation is not None: > content = content + " (%s)" % explanation > headers = [("Content-Type", "text/plain"), > ("Content-Length", "%d" % len(content))] > start_response(status, headers) > > return [content] > > def _get_cookies(self, env): > cookies = {} > if "HTTP_COOKIE" in env: > cookie_str = env["HTTP_COOKIE"] > for pair in cookie_str.split(";"): > (key, value) = pair.strip().split("=") > cookies[key] = value > > return cookies > > def __call__(self, env, start_response): > logger = RPCProxyLogger(env["wsgi.errors"]) > > logger.pretty_dump_dict(env) > > cookies = self._get_cookies(env) > if COOKIE_NAME in cookies: > client_id = cookies[COOKIE_NAME] > else: > client_id = None > > # old model that only works with mod_wsgi: > # if "REMOTE_ADDR" in env and "REMOTE_PORT" in env: > # client_id = "%(REMOTE_ADDR)s:%(REMOTE_PORT)s".format(env) > > if client_id is None or client_id not in self.client_status: > # first stage > server = Security.start_server(auth_context=AuthContext()) > server.start_mech_by_name("ntlmssp") > client_id = str(uuid.uuid4()) > > if "HTTP_AUTHORIZATION" in env: > # Outlook may directly have sent a NTLM payload > auth = env["HTTP_AUTHORIZATION"] > auth_msg = server.update(auth[5:].decode('base64')) > logger.log("auth_msg: %s\n" % str(auth_msg)) > response = self._in_progress_response(start_response, > auth_msg[1], > client_id) > self.client_status[client_id] = {"stage": "stage1", > "server": server} > else: > logger.log("no auth yet\n") > self.client_status[client_id] = {"stage": "stage0", > "server": server} > response = self._in_progress_response(start_response, None, client_id) > else: > status_stage = self.client_status[client_id]["stage"] > logger.log("handling stage '%s'\n" % status_stage) > > if status_stage == "ok": > # client authenticated previously > response = self.application(env, start_response) > elif status_stage == "stage0": > # test whether client supports "NTLM" > if "HTTP_AUTHORIZATION" in env: > auth = env["HTTP_AUTHORIZATION"] > server = self.client_status[client_id]["server"] > auth_msg = server.update(auth[5:].decode('base64')) > response = self._in_progress_response(start_response, > auth_msg[1]) > self.client_status[client_id]["stage"] = "stage1" > else: > del(self.client_status[client_id]) > response = self._failure_response(start_response, > "failure at '%s'" > % status_stage) > elif status_stage == "stage1": > if "HTTP_AUTHORIZATION" in env: > auth = env["HTTP_AUTHORIZATION"] > server = self.client_status[client_id]["server"] > try: > auth_msg = server.update(auth[5:].decode('base64')) > except RuntimeError: # a bit violent... > auth_msg = (0,) > > if auth_msg[0] == 1: > # authentication completed > self.client_status[client_id]["stage"] = "ok" > del(self.client_status[client_id]["server"]) > response = self.application(env, start_response) > else: > # we start over with the whole process > > server = Security.start_server(auth_context=AuthContext()) > server.start_mech_by_name("ntlmssp") > self.client_status[client_id] = {"stage": "stage0", > "server": server} > response = self._in_progress_response(start_response) > else: > del(self.client_status[client_id]) > response = self._failure_response(start_response, > "failure at '%s'" > % status_stage) > else: > raise RuntimeError("none shall pass!") > > return response > > >class RPCOverHTTPProxyApplication(object): > def __call__(self, environ, start_response): > if "wsgi.errors" in environ: > log_channel = environ["wsgi.errors"] > else: > log_channel = sys.stderr > self.logger = RPCProxyLogger(log_channel) > self.logger.pretty_dump_dict(environ) > > if "REQUEST_METHOD" in environ: > method = environ["REQUEST_METHOD"] > method_method = "_do_" + method > if hasattr(self, method_method): > method_method_method = getattr(self, method_method) > response = method_method_method(environ, start_response) > else: > response = self._unsupported_method(environ, start_response) > else: > response = self._unsupported_method(environ, start_response) > > return response > > def _unsupported_method(self, environ, start_response): > msg = "Unsupported method" > start_response("501 Not Implemented", [("Content-Type", "text/plain"), > ("Content-length", "%s" % len(msg))]) > > return [msg] > > def _do_GET(self, environ, start_response): > start_response("200 OK", [("Content-Type", "text/plain"), > ("Content-length", "0")]) > return [""] > > def _handle_echo_request(self, environ, start_response): > self.logger.log("handling echo request") > > start_response("200 Success", [("Content-length", "20"), > ("Content-Type", "application/rpc")]) > blob = assemble_packet_header(RTS_FLAG_ECHO) > return [blob] > > def _do_RPC_IN_DATA(self, environ, start_response): > self.logger.log('command: RPC_IN_DATA') > # self.logger.log('path: ' + self.path) > > content_length = int(environ["CONTENT_LENGTH"]) > # echo request > if content_length <= 0x10: > self.logger.log('Exiting (1) from do_RPC_IN_DATA') > return self._handle_echo_request(environ, start_response) > else: > self.logger.log("creating fifo") > try: > os.mkfifo(FIFO) > self.logger.log("created fifo") > except OSError, e: > self.logger.log('Failed to create FIFO: %s' % e) > > rpch = RPCH(debug=True, method="RPC_IN_DATA", host=OC_HOST, logger=self.logger) > status = True > fifo = None > input_file = environ["wsgi.input"] > while status: > self.logger.log('RPC_IN_DATA') > status = rpch.pull(input_file) > if status is True: > rpch.proxify() > if fifo is None: > fifo = open(FIFO, 'wb') > # Push the response to RPC_OUT_DATA listener > for data in rpch.push(fifo, channel="rpc_in_data"): > if data != "": > fifo.write(data) > fifo.flush() > else: > self.logger.log("status False") > > if fifo is not None: > fifo.close() > self.logger.log("removing fifo") > try: > os.remove(FIFO) > self.logger.log("removed fifo") > except OSError, e: > self.logger.log("ignored OSError occuring when removing FIFO") > > self.logger.log('Exiting (2) from do_RPC_IN_DATA') > return self._do_GET(environ, start_response) > > # OLD CODE > # msg = "RPC_IN_DATA method" > > # content_length = environ["CONTENT_LENGTH"] > # # echo request > # if content_length <= 10: > # pass > > # start_response("200 OK", [("Content-Type", "text/plain"), > # ("Content-length", "%s" % len(msg))]) > > # return [msg] > > def _do_RPC_OUT_DATA(self, environ, start_response): > self.logger.log('command: RPC_OUT_DATA') > # self.logger.log('path: ' + self.path) > content_length = int(environ["CONTENT_LENGTH"]) > # echo request > if content_length <= 0x10: > for data in self._handle_echo_request(environ, start_response): > yield data > else: > # Start the response > headers = [("Content-Type", "application/rpc"), > ("Content-Length", "1073741824")] > start_response("200 Success", headers) > > rpch = RPCH(debug=True, method="RPC_OUT_DATA", host=OC_HOST, logger=self.logger) > # Listen for initial RPCH RTS RPC_OUT_DATA command > # self.logger.log('RPC_OUT_DATA --- here: %s\n') > > input_file = environ["wsgi.input"] > rpch.pull(input_file) > > # conn_a3 > self.logger.log('RPC_OUT_DATA: sending conn_a3') > yield ("\x05\x00\x14\x03\x10\x00\x00\x00\x1C\x00\x00\x00\x00\x00\x00" > "\x00\x00\x00\x01\x00\x02\x00\x00\x00\xC0\xD4\x01\x00") > > self.logger.log('RPC_OUT_DATA: sending conn_c1_c2') > # conn_c1_c2 > yield ("\x05\x00\x14\x03\x10\x00\x00\x00\x2C\x00\x00\x00\x00\x00\x00" > "\x00\x00\x00\x03\x00\x06\x00\x00\x00\x01\x00\x00\x00\x00\x00" > "\x00\x00\x00\x00\x01\x00\x02\x00\x00\x00\x00\xD4\x01\x00") > > # Listen for data from the listener > fifo = open(FIFO, 'rb') > > status = True > while status: > self.logger.log('RPC_OUT_DATA: before rpch.pull') > status = rpch.pull(fifo) > self.logger.log('RPC_OUT_DATA: after rpch.pull') > if status is True: > for data in rpch.push(input_file, channel='rpc_out_data'): > yield data > > fifo.close() > self.logger.log('Exiting from do_RPC_OUT_DATA') > # return "" > > # OLD CODE > # msg = "RPC_OUT_DATA method" > # start_response("200 OK", [("Content-Type", "text/plain"), > # ("Content-length", "%s" % len(msg))]) > > # return [msg] > >application = NTLMAuthHandler(RPCOverHTTPProxyApplication()) ># application = RPCOverHTTPProxyApplication()
You cannot view the attachment while viewing its details because your browser does not support IFRAMEs.
View the attachment on a separate page
.
View Attachment As Raw
Actions:
View
Attachments on
bug 8956
:
7594
| 7595