Skip to content

Commit

Permalink
Merge pull request #538 from FreeTAKTeam/ssl-socket-close
Browse files Browse the repository at this point in the history
added support to close the unwrapped socket in ssl connections
  • Loading branch information
naman108 authored Feb 26, 2023
2 parents baf03db + f74a49a commit 83797e5
Show file tree
Hide file tree
Showing 7 changed files with 100 additions and 28 deletions.
2 changes: 1 addition & 1 deletion FreeTAKServer/core/configuration/MainConfig.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@

# the version information of the server (recommended to leave as default)

FTS_VERSION = "FreeTAKServer-2.0.11 Alpha"
FTS_VERSION = "FreeTAKServer-2.0.13 Alpha"
API_VERSION = "1.9.6"
# TODO Need to find a better way to determine python version at runtime
PYTHON_VERSION = "python3.8"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,9 @@

from FreeTAKServer.core.configuration.ClientReceptionLoggingConstants import ClientReceptionLoggingConstants
from FreeTAKServer.core.configuration.LoggingConstants import LoggingConstants
from FreeTAKServer.model.RawConnectionInformation import RawConnectionInformation as sat
from FreeTAKServer.services.ssl_cot_service.model.raw_ssl_connection_information import RawSSLConnectionInformation as sat
from FreeTAKServer.core.configuration.CreateLoggerController import CreateLoggerController
from FreeTAKServer.core.configuration.ReceiveConnectionsConstants import ReceiveConnectionsConstants
from FreeTAKServer.model.RawConnectionInformation import RawConnectionInformation
from FreeTAKServer.services.ssl_cot_service.controllers.SSLSocketController import SSLSocketController

loggingConstants = LoggingConstants(log_name="FTS_ReceiveConnections")
Expand Down Expand Up @@ -80,62 +79,68 @@ def listen(self, sock):
# logger.debug('receive connection started')
try:
client, address = sock.accept()
#client = SSLSocketController().wrap_client_sock(client)
ssl_client = SSLSocketController().wrap_client_socket(client)
except ssl.SSLError as ex:
print(ex)
client.close()
self.disconnect_socket(client, ssl_client)
logger.warning('ssl error thrown in connection attempt ' + str(ex))
return -1

except asyncio.TimeoutError as ex:
client.close()
self.disconnect_socket(client, ssl_client)
logger.warning('timeout error thrown in connection attempt '+str(ex))
return -1

logger.info('client connected over ssl ' + str(address) + ' ' + str(time.time()))
# wait to receive client
try:
events = self.receive_connection_data(client=client)
events = self.receive_connection_data(client=ssl_client)
except Exception:
try:
events = self.receive_connection_data(client=client)
events = self.receive_connection_data(client=ssl_client)
except Exception as exb:
client.close()
self.disconnect_socket(client, ssl_client)
logger.warning("receiving connection data from client failed with exception "+str(exb))
return -1
# TODO: move out to separate function
if events.text == TEST_SUCCESS:
client.send(b'success')
client.settimeout(0) # set the socket to non blocking
ssl_client.send(b'success')
ssl_client.settimeout(0) # set the socket to non blocking
logger.info(loggingConstants.RECEIVECONNECTIONSLISTENINFO)
# establish the socket array containing important information about the client
raw_connection_information = self.instantiate_client_object(address, client, events)
raw_connection_information = self.instantiate_client_object(address, client, ssl_client, events)
logger.info("client accepted")
try:
if socket is not None and raw_connection_information.xmlString != b'':
return raw_connection_information
else:
logger.warning("final socket entry is invalid")
client.close()
self.disconnect_socket(client, ssl_client)
return -1
except Exception as ex:
client.close()
self.disconnect_socket(client, ssl_client)
logger.warning('exception in returning data ' + str(ex))
return -1

except Exception as ex:
logger.warning(loggingConstants.RECEIVECONNECTIONSLISTENERROR)
try:
client.close()
self.disconnect_socket(client, ssl_client)
except Exception as ex:
pass
finally:
return -1

def instantiate_client_object(self, address, client, events):
def disconnect_socket(self, client, ssl_client):
ssl_client.shutdown(socket.SHUT_RDWR)
ssl_client.close()
client.close()

def instantiate_client_object(self, address, unwrapped_client, client, events):
raw_connection_information = sat()
raw_connection_information.ip = address[0]
raw_connection_information.socket = client
raw_connection_information.unwrapped_sock = unwrapped_client
raw_connection_information.xmlString = etree.tostring(events.findall('event')[0]).decode('utf-8')
return raw_connection_information

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,21 @@ def createSocket(self):
self.MainSocket.sock.setsockopt(self.MainSocket.solSock, self.MainSocket.soReuseAddr,
self.MainSocket.sockProto)
self.MainSocket.sock.bind((self.MainSocket.ip, self.MainSocket.port))
self.MainSocket.sock = context.wrap_socket(self.MainSocket.sock, server_side=True)
#self.MainSocket.sock = context.wrap_socket(self.MainSocket.sock, server_side=True)
return self.MainSocket.sock

def wrap_client_socket(self, socket):
context = ssl.SSLContext(protocol=ssl.PROTOCOL_TLS_SERVER)
context.load_verify_locations(cafile=self.MainSocket.CA)
context.options |= ssl.OP_NO_SSLv3
context.options |= ssl.OP_NO_SSLv2
context.verify_mode = ssl.CERT_REQUIRED
context.verify_flags = ssl.VERIFY_CRL_CHECK_LEAF
context.load_cert_chain(certfile=self.MainSocket.pemDir, keyfile=self.MainSocket.keyDir,
password=self.MainSocket.password, )
sock = context.wrap_socket(socket, server_side=True)
return sock

def createClientSocket(self, serverIP):
context = ssl.SSLContext()
context.load_verify_locations(cafile=self.MainSocket.CA)
Expand All @@ -38,5 +50,5 @@ def createClientSocket(self, serverIP):
context.check_hostname = False
context.set_ciphers('DEFAULT@SECLEVEL=1')
self.MainSocket.sock = socket.socket(self.MainSocket.socketAF, self.MainSocket.socketSTREAM)
self.MainSocket.sock = context.wrap_socket(self.MainSocket.sock)
# self.MainSocket.sock = context.wrap_socket(self.MainSocket.sock)
return self.MainSocket.sock
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
from FreeTAKServer.model.RawConnectionInformation import RawConnectionInformation

class RawSSLConnectionInformation(RawConnectionInformation):
def __init__(self):
super().__init__()
self.unwrapped_sock = None
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
# pylint: disable=trailing-whitespace
#######################################################
#
# ClientInformation.py
# Python implementation of the Class ClientInformation
# Generated by Enterprise Architect
# Created on: 21-May-2020 9:47:19 AM
# Original author: Natha Paquette
#
#######################################################
import copy


class SSLClientInformation:
def __init__(self):
self.modelObject = ''
self.alive = ""
self.dataQueue = ""
self.ID = ""
self.idData = ""
self.IP = ""
self.receptionHandler = ""
self.sendData = ""
self.socket = ""
self.unwrapped_socket = ""
self.type = "clientInformation"

def __deepcopy__(self, memodict=dict):
returned = ClientInformation()
returned.modelObject = copy.deepcopy(self.modelObject)
returned.alive = copy.deepcopy(self.alive)
returned.dataQueue = copy.deepcopy(self.dataQueue)
returned.ID = copy.deepcopy(self.ID)
returned.idData = copy.deepcopy(self.idData)
returned.IP = copy.deepcopy(self.IP)
returned.receptionHandler = copy.deepcopy(self.receptionHandler)
returned.sendData = copy.deepcopy(self.sendData)
returned.type = copy.deepcopy(self.type)
return returned
24 changes: 17 additions & 7 deletions FreeTAKServer/services/ssl_cot_service/ssl_cot_service_main.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
from opentelemetry.trace import Status, StatusCode
from typing import List, Union, Dict
from FreeTAKServer.services.ssl_cot_service.controllers.send_component_data_controller import SendComponentDataController
from FreeTAKServer.services.ssl_cot_service.model.raw_ssl_connection_information import RawSSLConnectionInformation
from FreeTAKServer.services.ssl_cot_service.model.ssl_cot_connection import SSLCoTConnection

from digitalpy.core.service_management.digitalpy_service import DigitalPyService
Expand Down Expand Up @@ -240,7 +241,7 @@ def get_client_information(self):

elif (
client_id in user_dict.keys()
and len(self.client_information_queue[client_id]) == 2
and len(self.client_information_queue[client_id]) == 3
):
self.client_information_queue[client_id][1] = user_dict[client_id]

Expand Down Expand Up @@ -319,7 +320,7 @@ def send_user_connection_geo_chat(self, clientInformation):
else:
return 1

def clientConnected(self, raw_connection_information: RawCoT):
def clientConnected(self, raw_connection_information: RawSSLConnectionInformation):
"""Controls the client connection sequence, calling methods which perform the following:
1. Instantiate the client object
2. Share the client with core
Expand Down Expand Up @@ -377,7 +378,7 @@ def clientConnected(self, raw_connection_information: RawCoT):
self.add_service_user(clientInformation=clientInformation)

# Add client info to queue
self.client_information_queue[clientInformation.modelObject.uid] = [clientInformation.socket, clientInformation]
self.client_information_queue[clientInformation.modelObject.uid] = [clientInformation.socket, clientInformation, raw_connection_information.unwrapped_sock]

# instantiate an object_id with a value of the client uid
object_id = ObjectFactory.get_new_instance("ObjectId", dynamic_configuration={"id": clientInformation.modelObject.uid, "type": "connection"})
Expand Down Expand Up @@ -475,6 +476,7 @@ def clientDisconnected(self, clientInformation: User):
clientInformation.clientInformation
][1]
sock = self.client_information_queue[clientInformation.user_id][0]
unwrapped_sock = self.client_information_queue[clientInformation.user_id][2]
except Exception as e:
self.logger.critical(
"getting sock from client information queue failed " + str(e)
Expand Down Expand Up @@ -522,7 +524,7 @@ def clientDisconnected(self, clientInformation: User):

try:
self.remove_service_user(clientInformation=clientInformation)
self.disconnect_socket(sock)
self.disconnect_socket(sock, unwrapped_sock)

self.logger.info(loggingConstants.CLIENTDISCONNECTSTART)

Expand Down Expand Up @@ -568,7 +570,7 @@ def send_disconnect_cot(self, clientInformation):
self.messages_to_core_count += 1
self.send_message(disconnect.getObject().clientInformation, disconnect.getObject())

def disconnect_socket(self, sock: socket.socket) -> None:
def disconnect_socket(self, sock: socket.socket, unwrapped_socket: socket.socket) -> None:
"""this method is responsible for disconnecting all socket objects
:param sock: socket object to be disconnected
Expand All @@ -590,6 +592,14 @@ def disconnect_socket(self, sock: socket.socket) -> None:
+ str(e)
+ "\n".join(traceback.format_stack())
)
try:
unwrapped_socket.close()
except Exception as e:
self.logger.error(
"error closing unwrapped socket in client disconnection "
+ str(e)
+ "\n".join(traceback.format_stack())
)

def component_handler(self, cot):
"""this method is responsible for handling cases where the cot sent should
Expand Down Expand Up @@ -1022,11 +1032,11 @@ def handle_regular_data(self, clientDataOutput: List[RawCoT]):

# Process the raw CoT data and serialize it
CoTOutput = self.monitor_raw_cot(clientDataOutputSingle)
self.logger.info(f"CoT serialized {CoTOutput.modelObject.uid}")

# Skip this iteration if the CoT data is invalid
if CoTOutput == 1:
continue

self.logger.info(f"CoT serialized {CoTOutput.modelObject.uid}")

# Check if the CoT data is valid and can be sent
if self.checkOutput(CoTOutput):
Expand Down
6 changes: 3 additions & 3 deletions setup.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
packages=find_packages(
include=["FreeTAKServer", "FreeTAKServer.*", "*.json", "*.ini", "*.conf"]
),
version="0.2.0.11 Alpha",
version="0.2.0.13",
license="EPL-2.0",
description="An open source server for the TAK family of applications.",
long_description=long_description,
Expand All @@ -37,7 +37,7 @@
"Flask-SQLAlchemy==2.4.4",
"geographiclib==1.52",
"geopy==2.2.0",
"greenlet==2.0.0",
"greenlet==2.0.2",
"itsdangerous==2.0.1",
"testresources==2.0.1",
"Jinja2==2.11.2",
Expand All @@ -46,7 +46,7 @@
"monotonic==1.6",
"pathlib2==2.3.7.post1",
"protobuf==3.18.3",
"psutil==5.9.0",
"psutil==5.9.4",
"pykml==0.2.0",
"python-engineio==3.13.2",
"python-socketio==4.6.0",
Expand Down

0 comments on commit 83797e5

Please sign in to comment.