Fixed possible security issues in HTTP server + minor optimizations

parent bea52af8
...@@ -19,6 +19,7 @@ CONFIG_HTTP = { ...@@ -19,6 +19,7 @@ CONFIG_HTTP = {
"API_ROOT": "/restconf", "API_ROOT": "/restconf",
"API_ROOT_STAGING": "/restconf_staging", "API_ROOT_STAGING": "/restconf_staging",
"SERVER_NAME": "hyper-h2", "SERVER_NAME": "hyper-h2",
"UPLOAD_SIZE_LIMIT": 1,
"PORT": 8443, "PORT": 8443,
"SERVER_SSL_CERT": "server.crt", "SERVER_SSL_CERT": "server.crt",
......
...@@ -169,17 +169,14 @@ def create_get_staging_api(ds: BaseDatastore): ...@@ -169,17 +169,14 @@ def create_get_staging_api(ds: BaseDatastore):
def get_file(prot: "H2Protocol", stream_id: int, headers: OrderedDict): def get_file(prot: "H2Protocol", stream_id: int, headers: OrderedDict):
# Ordinary file on filesystem # Ordinary file on filesystem
username = CertHelpers.get_field(prot.client_cert, "emailAddress") username = CertHelpers.get_field(prot.client_cert, "emailAddress")
url_split = headers[":path"].split("?") url_path = headers[":path"].split("?")[0]
url_path = url_split[0] url_path_safe = "".join(filter(lambda c: c.isalpha() or c in "/-_.", url_path)).replace("..", "").strip("/")
file_path = os.path.join(CONFIG_HTTP["DOC_ROOT"], url_path_safe)
file_path = os.path.join(CONFIG_HTTP["DOC_ROOT"], url_path[1:].replace("..", "").replace("&", ""))
if os.path.isdir(file_path): if os.path.isdir(file_path):
file_path = os.path.join(file_path, CONFIG_HTTP["DOC_DEFAULT_NAME"]) file_path = os.path.join(file_path, CONFIG_HTTP["DOC_DEFAULT_NAME"])
(ctype, encoding) = mimetypes.guess_type(file_path) ctype = mimetypes.guess_type(file_path)[0] or "application/octet-stream"
if ctype is None:
ctype = "application/octet-stream"
try: try:
fd = open(file_path, 'rb') fd = open(file_path, 'rb')
......
...@@ -6,7 +6,7 @@ from colorlog import error, warning as warn, info, debug ...@@ -6,7 +6,7 @@ from colorlog import error, warning as warn, info, debug
from typing import List, Tuple, Dict, Any, Callable from typing import List, Tuple, Dict, Any, Callable
from h2.connection import H2Connection from h2.connection import H2Connection
from h2.errors import PROTOCOL_ERROR from h2.errors import PROTOCOL_ERROR, ENHANCE_YOUR_CALM
from h2.events import DataReceived, RequestReceived, RemoteSettingsChanged, StreamEnded from h2.events import DataReceived, RequestReceived, RemoteSettingsChanged, StreamEnded
import jetconf.http_handlers as handlers import jetconf.http_handlers as handlers
...@@ -21,9 +21,10 @@ h2_handlers = None # type: HandlerList ...@@ -21,9 +21,10 @@ h2_handlers = None # type: HandlerList
class RequestData: class RequestData:
def __init__(self, headers=None, data=None): def __init__(self, headers: OrderedDict, data: BytesIO):
self.headers = headers self.headers = headers
self.data = data self.data = data
self.data_overflow = False
class HandlerList: class HandlerList:
...@@ -49,7 +50,7 @@ class H2Protocol(asyncio.Protocol): ...@@ -49,7 +50,7 @@ class H2Protocol(asyncio.Protocol):
def __init__(self): def __init__(self):
self.conn = H2Connection(client_side=False) self.conn = H2Connection(client_side=False)
self.transport = None self.transport = None
self.stream_data = {} self.stream_data = {} # type: Dict[int, RequestData]
self.client_cert = None # type: Dict[str, Any] self.client_cert = None # type: Dict[str, Any]
def connection_made(self, transport: asyncio.Transport): def connection_made(self, transport: asyncio.Transport):
...@@ -86,13 +87,21 @@ class H2Protocol(asyncio.Protocol): ...@@ -86,13 +87,21 @@ class H2Protocol(asyncio.Protocol):
except KeyError: except KeyError:
self.conn.reset_stream(event.stream_id, error_code=PROTOCOL_ERROR) self.conn.reset_stream(event.stream_id, error_code=PROTOCOL_ERROR)
else: else:
# Check if incoming data are not excessively large
if (stream_data.data.tell() + len(event.data)) < (CONFIG_HTTP["UPLOAD_SIZE_LIMIT"] * 1048576):
stream_data.data.write(event.data) stream_data.data.write(event.data)
else:
stream_data.data_overflow = True
self.conn.reset_stream(event.stream_id, error_code=ENHANCE_YOUR_CALM)
elif isinstance(event, StreamEnded): elif isinstance(event, StreamEnded):
# Process request # Process request
try: try:
request_data = self.stream_data.pop(event.stream_id) request_data = self.stream_data.pop(event.stream_id)
except KeyError: except KeyError:
self.send_empty(event.stream_id, "400", "Bad Request") self.send_empty(event.stream_id, "400", "Bad Request")
else:
if request_data.data_overflow:
self.send_empty(event.stream_id, "406", "Not Acceptable")
else: else:
headers = request_data.headers headers = request_data.headers
body = request_data.data.getvalue().decode('utf-8') body = request_data.data.getvalue().decode('utf-8')
......
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