Commit d6de1340 by Sheng

Added max_body_size for limiting the size of post form

parent d38453fd
......@@ -64,12 +64,13 @@ class Server(paramiko.ServerInterface):
return paramiko.OPEN_FAILED_ADMINISTRATIVELY_PROHIBITED
def check_auth_password(self, username, password):
print('Auth attempt with username: {!r} & password: {!r}'.format(username, password)) # noqa
if (username in ['robey', 'bar']) and (password == 'foo'):
return paramiko.AUTH_SUCCESSFUL
return paramiko.AUTH_FAILED
def check_auth_publickey(self, username, key):
print('Auth attempt with key: ' + u(hexlify(key.get_fingerprint())))
print('Auth attempt with username: {!r} & key: {!r}'.format(username, u(hexlify(key.get_fingerprint())))) # noqa
if (username == 'robey') and (key == self.good_pub_key):
return paramiko.AUTH_SUCCESSFUL
return paramiko.AUTH_FAILED
......
import json
import webssh.handler as handler
import random
import threading
import tornado.websocket
import tornado.gen
import webssh.handler as handler
from tornado.testing import AsyncHTTPTestCase
from tornado.httpclient import HTTPError
from tornado.options import options
from webssh.main import make_app, make_handlers
from webssh.settings import get_app_settings
from webssh.settings import get_app_settings, max_body_size
from tests.sshserver import run_ssh_server, banner
from tests.utils import encode_multipart_formdata
handler.DELAY = 0.1
......@@ -20,6 +22,12 @@ class TestApp(AsyncHTTPTestCase):
running = [True]
sshserver_port = 2200
body = u'hostname=127.0.0.1&port={}&username=robey&password=foo'.format(sshserver_port) # noqa
body_dict = {
'hostname': '127.0.0.1',
'port': str(sshserver_port),
'username': 'robey',
'password': ''
}
def get_app(self):
loop = self.io_loop
......@@ -44,6 +52,14 @@ class TestApp(AsyncHTTPTestCase):
cls.running.pop()
print('='*20)
def read_privatekey(self, filename):
return open(filename, 'rb').read().decode('utf-8')
def get_httpserver_options(self):
options = super(TestApp, self).get_httpserver_options()
options.update(max_body_size=max_body_size)
return options
def test_app_with_invalid_form(self):
response = self.fetch('/')
self.assertEqual(response.code, 200)
......@@ -104,6 +120,74 @@ class TestApp(AsyncHTTPTestCase):
ws.close()
@tornado.testing.gen_test
def test_app_auth_with_valid_pubkey_for_user_robey(self):
url = self.get_url('/')
client = self.get_http_client()
response = yield client.fetch(url)
self.assertEqual(response.code, 200)
privatekey = self.read_privatekey('tests/user_rsa_key')
files = [('privatekey', 'user_rsa_key', privatekey)]
content_type, body = encode_multipart_formdata(self.body_dict.items(),
files)
headers = {
"Content-Type": content_type, 'content-length': str(len(body))
}
response = yield client.fetch(url, method="POST", headers=headers,
body=body)
data = json.loads(response.body.decode('utf-8'))
self.assertIsNone(data['status'])
self.assertIsNotNone(data['id'])
self.assertIsNotNone(data['encoding'])
url = url.replace('http', 'ws')
ws_url = url + 'ws?id=' + data['id']
ws = yield tornado.websocket.websocket_connect(ws_url)
msg = yield ws.read_message()
self.assertEqual(msg.decode(data['encoding']), banner)
ws.close()
@tornado.testing.gen_test
def test_app_auth_with_invalid_pubkey_for_user_robey(self):
url = self.get_url('/')
client = self.get_http_client()
response = yield client.fetch(url)
self.assertEqual(response.code, 200)
privatekey = self.read_privatekey('tests/user_rsa_key')
privatekey = privatekey[:100] + u'bad' + privatekey[100:]
files = [('privatekey', 'user_rsa_key', privatekey)]
content_type, body = encode_multipart_formdata(self.body_dict.items(),
files)
headers = {
"Content-Type": content_type, 'content-length': str(len(body))
}
response = yield client.fetch(url, method="POST", headers=headers,
body=body)
data = json.loads(response.body.decode('utf-8'))
self.assertIsNotNone(data['status'])
self.assertIsNone(data['id'])
self.assertIsNone(data['encoding'])
@tornado.testing.gen_test
def test_app_post_form_with_large_body_size(self):
url = self.get_url('/')
client = self.get_http_client()
response = yield client.fetch(url)
self.assertEqual(response.code, 200)
privatekey = u'h' * (2 * max_body_size)
files = [('privatekey', 'user_rsa_key', privatekey)]
content_type, body = encode_multipart_formdata(self.body_dict.items(),
files)
headers = {
"Content-Type": content_type, 'content-length': str(len(body))
}
with self.assertRaises(HTTPError):
yield client.fetch(url, method="POST", headers=headers, body=body)
@tornado.testing.gen_test
def test_app_with_correct_credentials_user_robey(self):
url = self.get_url('/')
client = self.get_http_client()
......
-----BEGIN RSA PRIVATE KEY-----
MIICXQIBAAKBgQDI7iK3d8eWYZlYloat94c5VjtFY7c/0zuGl8C7uMnZ3t6i2G99
66hEW0nCFSZkOW5F0XKEVj+EUCHvo8koYC6wiohAqWQnEwIoOoh7GSAcB8gP/qaq
+adIl/Rvlby/mHakj+y05LBND6nFWHAn1y1gOFFKUXSJNRZPXSFy47gqzwIBIwKB
gQCbANjz7q/pCXZLp1Hz6tYHqOvlEmjK1iabB1oqafrMpJ0eibUX/u+FMHq6StR5
M5413BaDWHokPdEJUnabfWXXR3SMlBUKrck0eAer1O8m78yxu3OEdpRk+znVo4DL
guMeCdJB/qcF0kEsx+Q8HP42MZU1oCmk3PbfXNFwaHbWuwJBAOQ/ry/hLD7AqB8x
DmCM82A9E59ICNNlHOhxpJoh6nrNTPCsBAEu/SmqrL8mS6gmbRKUaya5Lx1pkxj2
s/kWOokCQQDhXCcYXjjWiIfxhl6Rlgkk1vmI0l6785XSJNv4P7pXjGmShXfIzroh
S8uWK3tL0GELY7+UAKDTUEVjjQdGxYSXAkEA3bo1JzKCwJ3lJZ1ebGuqmADRO6UP
40xH977aadfN1mEI6cusHmgpISl0nG5YH7BMsvaT+bs1FUH8m+hXDzoqOwJBAK3Z
X/za+KV/REya2z0b+GzgWhkXUGUa/owrEBdHGriQ47osclkUgPUdNqcLmaDilAF4
1Z4PHPrI5RJIONAx+JECQQC/fChqjBgFpk6iJ+BOdSexQpgfxH/u/457W10Y43HR
soS+8btbHqjQkowQ/2NTlUfWvqIlfxs6ZbFsIp/HrhZL
-----END RSA PRIVATE KEY-----
import mimetypes
from uuid import uuid4
def encode_multipart_formdata(fields, files):
"""
fields is a sequence of (name, value) elements for regular form fields.
files is a sequence of (name, filename, value) elements for data to be
uploaded as files.
Return (content_type, body) ready for httplib.HTTP instance
"""
boundary = uuid4().hex
CRLF = '\r\n'
L = []
for (key, value) in fields:
L.append('--' + boundary)
L.append('Content-Disposition: form-data; name="%s"' % key)
L.append('')
L.append(value)
for (key, filename, value) in files:
L.append('--' + boundary)
L.append(
'Content-Disposition: form-data; name="%s"; filename="%s"' % (
key, filename
)
)
L.append('Content-Type: %s' % get_content_type(filename))
L.append('')
L.append(value)
L.append('--' + boundary + '--')
L.append('')
body = CRLF.join(L)
content_type = 'multipart/form-data; boundary=%s' % boundary
return content_type, body
def get_content_type(filename):
return mimetypes.guess_type(filename)[0] or 'application/octet-stream'
......@@ -5,7 +5,7 @@ import tornado.ioloop
from tornado.options import parse_command_line, options
from webssh.handler import IndexHandler, WsockHandler
from webssh.settings import (get_app_settings, get_host_keys_settings,
get_policy_setting)
get_policy_setting, max_body_size)
def make_handlers(loop, options):
......@@ -29,7 +29,7 @@ def main():
parse_command_line()
loop = tornado.ioloop.IOLoop.current()
app = make_app(make_handlers(loop, options), get_app_settings(options))
app.listen(options.port, options.address)
app.listen(options.port, options.address, max_body_size=max_body_size)
logging.info('Listening on {}:{}'.format(options.address, options.port))
loop.start()
......
......@@ -29,6 +29,7 @@ define('version', type=bool, help='Show version information',
base_dir = os.path.dirname(__file__)
max_body_size = 1 * 1024 * 1024
def get_app_settings(options):
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment