This commit is contained in:
detain 2014-02-05 12:07:27 -05:00
commit 56a0378d46
2 changed files with 82 additions and 3 deletions

View File

@ -0,0 +1,11 @@
#MySQL Configuration File
#
# database connection settings
db=YOURDB
user=YOURUSER
passwd=YOURPASS
# query to validate the token
# _TOKEN_ will be replaced with the token
# should return 0 rows if the token does not match,
# and the first field should be the IP:Port they are authenticated to connect to
match_query=select as_data, substr(as_data, 1, locate(':',as_data) - 1) as ip, substr(as_data, locate(':',as_data)+1) as port from appsession where concat(as_sid,'_',as_location)='_TOKEN_'

View File

@ -1,5 +1,5 @@
#!/usr/bin/env python
'''
A WebSocket to TCP socket proxy with support for "wss://" encryption.
Copyright 2011 Joel Martin
@ -11,6 +11,8 @@ as taken from http://docs.python.org/dev/library/ssl.html#certificates
'''
import MySQLdb
from MySQLdb.constants import FIELD_TYPE
import signal, socket, optparse, time, os, sys, subprocess
from select import select
import websocket
@ -50,9 +52,11 @@ Traffic Legend:
self.wrap_mode = kwargs.pop('wrap_mode', None)
self.unix_target = kwargs.pop('unix_target', None)
self.ssl_target = kwargs.pop('ssl_target', None)
self.target_sql_cfg = kwargs.pop('target_sql_cfg', None)
self.target_cfg = kwargs.pop('target_cfg', None)
# Last 3 timestamps command was run
self.wrap_times = [0, 0, 0]
#logging.info("NEW_SCORE: %s", score)
if self.wrap_cmd:
rebinder_path = ['./', os.path.dirname(sys.argv[0])]
@ -83,6 +87,9 @@ Traffic Legend:
if self.target_cfg:
self.target_cfg = os.path.abspath(self.target_cfg)
if self.target_sql_cfg:
self.target_sql_cfg = os.path.abspath(self.target_sql_cfg)
websocket.WebSocketServer.__init__(self, *args, **kwargs)
def run_wrap_cmd(self):
@ -109,6 +116,9 @@ Traffic Legend:
if self.target_cfg:
msg = " - proxying from %s:%s to targets in %s" % (
self.listen_host, self.listen_port, self.target_cfg)
elif self.target_sql_cfg:
msg = " - proxying from %s:%s to targets via database lookups defined in %s" % (
self.listen_host, self.listen_port, self.target_sql_cfg)
else:
msg = " - proxying from %s:%s to %s" % (
self.listen_host, self.listen_port, dst_string)
@ -166,6 +176,9 @@ Traffic Legend:
if self.target_cfg:
(self.target_host, self.target_port) = self.get_target(self.target_cfg, self.path)
if self.target_sql_cfg:
(self.target_host, self.target_port) = self.get_sql_target(self.target_sql_cfg, self.path)
# Connect to the target
if self.wrap_cmd:
msg = "connecting to command: '%s' (port %s)" % (" ".join(self.wrap_cmd), self.target_port)
@ -235,6 +248,55 @@ Traffic Legend:
else:
raise self.EClose("Token '%s' not found" % token)
def get_sql_target(self, target_sql_cfg, path):
"""
Parses the path, extracts a token, and performs a database
query to validate the token. database information is read
from the configuration file. Sets
target_host and target_port if successful
"""
sqlconfig = {}
my_conv = { FIELD_TYPE.LONG: int }
# The files in target contain the lines
# in the form of setting=value
#
# db=YOURDB
# user=YOURUSER
# passwd=YOURPASS
# match_query=YOUR_QUERY
#
# _TOKEN_ in the match_query will be replaced with the token
# should return 0 rows if the token does not match,
# and the first field should be the IP:Port they are authenticated to connect to
# Extract the token parameter from url
args = parse_qs(urlparse(path)[4]) # 4 is the query from url
if not args.has_key('token') or not len(args['token']):
raise self.EClose("Token not present")
for line in [l.strip() for l in file(target_sql_cfg).readlines()]:
if line and not line.startswith('#'):
line = line.split('=',1)
sqlconfig[line[0]] = line[1]
#print sqlconfig
token = args['token'][0].rstrip('\n')
targets = {}
db=MySQLdb.connect(passwd=sqlconfig['passwd'],db=sqlconfig['db'],user=sqlconfig['user'])
c=db.cursor()
c.execute(sqlconfig['match_query'].replace('_TOKEN_', db.escape_string(token)))
results = c.fetchone()
targets[token] = results[0]
c.close()
self.vmsg("SQL Query Result: %s" % repr(targets))
if targets.has_key(token):
return targets[token].split(':')
else:
raise self.EClose("Token '%s' not found" % token)
def do_proxy(self, target):
"""
Proxy client WebSocket to normal target socket.
@ -345,10 +407,16 @@ def websockify_init():
help="Configuration file containing valid targets "
"in the form 'token: host:port' or, alternatively, a "
"directory containing configuration files of this form")
parser.add_option("--sql-target-config", metavar="FILE",
dest="target_sql_cfg",
help="Configuration file containing valid connection "
"information and a squery that will need to validate "
"the token. The first field in the response from "
"the database need in the form of IP:Port")
(opts, args) = parser.parse_args()
# Sanity checks
if len(args) < 2 and not (opts.target_cfg or opts.unix_target):
if len(args) < 2 and not (opts.target_cfg or opts.unix_target or opts.target_sql_cfg):
parser.error("Too few arguments")
if sys.argv.count('--'):
opts.wrap_cmd = args[1:]
@ -373,7 +441,7 @@ def websockify_init():
try: opts.listen_port = int(opts.listen_port)
except: parser.error("Error parsing listen port")
if opts.wrap_cmd or opts.unix_target or opts.target_cfg:
if opts.wrap_cmd or opts.unix_target or opts.target_cfg or opts.target_sql_cfg:
opts.target_host = None
opts.target_port = None
else: