From c04a40a468d573865d0aa1f0edf6a471c338906e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adolfo=20G=C3=B3mez=20Garc=C3=ADa?= Date: Sat, 3 Aug 2024 02:09:12 +0200 Subject: [PATCH] chore: Generate random key for tunnel --- server/src/uds/REST/methods/tunnels.py | 27 +++++++++---------- server/src/uds/core/transports/transport.py | 8 ++++++ server/src/uds/models/ticket_store.py | 5 +++- server/src/uds/transports/RDP/rdptunnel.py | 3 +++ .../src/uds/transports/SPICE/spicetunnel.py | 4 +++ server/src/uds/transports/X2GO/x2gotunnel.py | 3 +++ 6 files changed, 34 insertions(+), 16 deletions(-) diff --git a/server/src/uds/REST/methods/tunnels.py b/server/src/uds/REST/methods/tunnels.py index b326d4e46..5b801eff0 100644 --- a/server/src/uds/REST/methods/tunnels.py +++ b/server/src/uds/REST/methods/tunnels.py @@ -45,9 +45,8 @@ from .servers import ServerRegisterBase logger = logging.getLogger(__name__) -MAX_SESSION_LENGTH = ( - 60 * 60 * 24 * 7 * 2 -) # Two weeks is max session length for a tunneled connection +MAX_SESSION_LENGTH = 60 * 60 * 24 * 7 * 2 # Two weeks is max session length for a tunneled connection + # Enclosed methods under /tunnel path class TunnelTicket(Handler): @@ -70,11 +69,7 @@ class TunnelTicket(Handler): self._request.ip, ) - if ( - not is_trusted_source(self._request.ip) - or len(self._args) != 3 - or len(self._args[0]) != 48 - ): + if not is_trusted_source(self._request.ip) or len(self._args) != 3 or len(self._args[0]) != 48: # Invalid requests raise exceptions.rest.AccessDenied() @@ -92,11 +87,9 @@ class TunnelTicket(Handler): # Try to get ticket from DB try: - user, user_service, host, port, extra = models.TicketStore.get_for_tunnel( - self._args[0] - ) + user, user_service, host, port, extra, key = models.TicketStore.get_for_tunnel(self._args[0]) host = host or '' - data = {} + data: dict[str, typing.Any] = {} if self._args[1][:4] == 'stop': sent, recv = self._params['sent'], self._params['recv'] # Ensures extra exists... @@ -146,7 +139,7 @@ class TunnelTicket(Handler): }, validity=MAX_SESSION_LENGTH, ) - data = {'host': host, 'port': port, 'notify': notifyTicket} + data = {'host': host, 'port': port, 'notify': notifyTicket, 'key': key} return data except Exception as e: @@ -162,7 +155,11 @@ class TunnelRegister(ServerRegisterBase): # Just a compatibility method for old tunnel servers def post(self) -> collections.abc.MutableMapping[str, typing.Any]: self._params['type'] = types.servers.ServerType.TUNNEL - self._params['os'] = self._params.get('os', types.os.KnownOS.LINUX.os_name()) # Legacy tunnels are always linux + self._params['os'] = self._params.get( + 'os', types.os.KnownOS.LINUX.os_name() + ) # Legacy tunnels are always linux self._params['version'] = '' # No version for legacy tunnels, does not respond to API requests from UDS - self._params['certificate'] = '' # No certificate for legacy tunnels, does not respond to API requests from UDS + self._params['certificate'] = ( + '' # No certificate for legacy tunnels, does not respond to API requests from UDS + ) return super().post() diff --git a/server/src/uds/core/transports/transport.py b/server/src/uds/core/transports/transport.py index 94e8be122..32470a193 100644 --- a/server/src/uds/core/transports/transport.py +++ b/server/src/uds/core/transports/transport.py @@ -40,6 +40,7 @@ from django.utils.translation import gettext_noop as _ from uds import models from uds.core import consts, types +from uds.core.managers.crypto import CryptoManager from uds.core.module import Module from uds.core.util import net @@ -213,6 +214,13 @@ class Transport(Module): @return: transformed username """ return user.name + + def generate_key(self, length: int = 32) -> str: + """ + Returns a random key of the requested length + Used for generate keys for the tunnel mainly, but can be used for other purposes + """ + return CryptoManager.manager().random_string(length) def get_transport_script( self, diff --git a/server/src/uds/models/ticket_store.py b/server/src/uds/models/ticket_store.py index 0f990f1e7..5eb836eac 100644 --- a/server/src/uds/models/ticket_store.py +++ b/server/src/uds/models/ticket_store.py @@ -220,6 +220,7 @@ class TicketStore(UUIDModel): port: int, host: typing.Optional[str] = None, extra: typing.Optional[collections.abc.Mapping[str, typing.Any]] = None, + key: typing.Optional[str] = None, validity: int = 60 * 60 * 24, # 24 Hours default validity for tunnel tickets ) -> str: owner = CryptoManager().random_string(length=8) @@ -231,6 +232,7 @@ class TicketStore(UUIDModel): 'h': host, 'p': port, 'e': extra, + 'k': key or '', } return ( # Note that the ticket is the uuid + owner, so we can encrypt data without keeping the key @@ -254,6 +256,7 @@ class TicketStore(UUIDModel): typing.Optional[str], int, typing.Optional[collections.abc.Mapping[str, typing.Any]], + str, ]: """ Returns the ticket for a tunneled connection @@ -277,7 +280,7 @@ class TicketStore(UUIDModel): if not host: host = userservice.get_instance().get_ip() - return (user, userservice, host, data['p'], data['e']) + return (user, userservice, host, data['p'], data['e'], data.get('k', '')) except Exception as e: raise TicketStore.InvalidTicket(str(e)) diff --git a/server/src/uds/transports/RDP/rdptunnel.py b/server/src/uds/transports/RDP/rdptunnel.py index 51b055a69..110a18c48 100644 --- a/server/src/uds/transports/RDP/rdptunnel.py +++ b/server/src/uds/transports/RDP/rdptunnel.py @@ -141,10 +141,12 @@ class TRDPTransport(BaseRDPTransport): width, height = self.screen_size.value.split('x') depth = self.color_depth.value + key = self.generate_key() ticket = TicketStore.create_for_tunnel( userService=userservice, port=self.rdp_port.as_int(), validity=self.tunnel_wait.as_int() + 60, # Ticket overtime + key=key, ) tunnelFields = fields.get_tunnel_from_field(self.tunnel) @@ -187,6 +189,7 @@ class TRDPTransport(BaseRDPTransport): 'ticket': ticket, 'password': ci.password, 'this_server': request.build_absolute_uri('/'), + 'tunnel_key': key, } if os.os == types.os.KnownOS.WINDOWS: diff --git a/server/src/uds/transports/SPICE/spicetunnel.py b/server/src/uds/transports/SPICE/spicetunnel.py index b58500644..6ee15a7c4 100644 --- a/server/src/uds/transports/SPICE/spicetunnel.py +++ b/server/src/uds/transports/SPICE/spicetunnel.py @@ -122,11 +122,13 @@ class TSPICETransport(BaseSpiceTransport): userservice, transport, ip, os, user, password, request ) + key = self.generate_key() if con.port: ticket = TicketStore.create_for_tunnel( userService=userservice, port=int(con.port), validity=self.tunnel_wait.as_int() + 60, # Ticket overtime + key=key, ) if con.secure_port: @@ -135,6 +137,7 @@ class TSPICETransport(BaseSpiceTransport): port=int(con.secure_port), host=con.address, validity=self.tunnel_wait.as_int() + 60, # Ticket overtime + key=key, ) r = RemoteViewerFile( @@ -164,6 +167,7 @@ class TSPICETransport(BaseSpiceTransport): 'tunChk': self.verify_certificate.as_bool(), 'ticket': ticket, 'ticket_secure': ticket_secure, + 'tunnel_key': key, } try: diff --git a/server/src/uds/transports/X2GO/x2gotunnel.py b/server/src/uds/transports/X2GO/x2gotunnel.py index d424c42b0..a17c21d77 100644 --- a/server/src/uds/transports/X2GO/x2gotunnel.py +++ b/server/src/uds/transports/X2GO/x2gotunnel.py @@ -129,10 +129,12 @@ class TX2GOTransport(BaseX2GOTransport): user=ci.username, ) + key = self.generate_key() ticket = TicketStore.create_for_tunnel( userService=userservice, port=22, validity=self.tunnel_wait.as_int() + 60, # Ticket overtime + key=key, ) tunnelFields = fields.get_tunnel_from_field(self.tunnel) @@ -145,6 +147,7 @@ class TX2GOTransport(BaseX2GOTransport): 'tunChk': self.verify_certificate.as_bool(), 'ticket': ticket, 'key': private_key, + 'tunnel_key': key, 'xf': xf, }