From cf1048afcbdb1caba7a15fa2a15438db0c72b6b7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adolfo=20G=C3=B3mez=20Garc=C3=ADa?= Date: Wed, 8 Mar 2023 21:48:48 +0100 Subject: [PATCH 1/2] Now Ciphers can be pushed by uds server to UDS Actor, so we can addapt Actor ciphers without reinstalling if needed --- actor/src/udsactor/http/cert.py | 3 +- actor/src/udsactor/http/server.py | 53 +++++++++++++++++++++++++------ actor/src/udsactor/rest.py | 3 ++ actor/src/udsactor/types.py | 1 + 4 files changed, 49 insertions(+), 11 deletions(-) diff --git a/actor/src/udsactor/http/cert.py b/actor/src/udsactor/http/cert.py index 84765f84f..451d75048 100644 --- a/actor/src/udsactor/http/cert.py +++ b/actor/src/udsactor/http/cert.py @@ -3,5 +3,6 @@ from .. import types defaultCertificate = types.CertificateInfoType( private_key='-----BEGIN ENCRYPTED PRIVATE KEY-----\nMIIFHTBPBgkqhkiG9w0BBQ0wQjApBgkqhkiG9w0BBQwwHAQIfG2+iMYJBswCAggA\nMAwGCCqGSIb3DQIJBQAwFQYJKwYBBAGXVQECBAhCusU5R8ulZQSCBMgheyZ81Qkq\n+TcbPeBlUGCFllSUOo7xQ/OuwYSmzLx8LpN0hQNv4azF6MYH+I8eMSPd3A547yW3\nJE4GjIBfRvcq2X1UZ2FQfECU9UP0ShPuPrVhIh6ZZklmlRjbIF8hGfSzXAuafQb+\n4wXXsofahi/SPgqK1Gw65nRiMcoeRZchJkx8pBgKVWED6Cbh6aAkeqkVKPnsebiV\n6kE+0C7+hgNUbyRd46R+/5NXzPjg4ItfSak+PLzQ1KeRv4Cu6DdzRKJ4V9/MlNdU\nNNEkSVSEaRn4sv+eByU4uxBMaSmD1tLc/A7OmaAeRpIQvls3Zcf2+V0+anAtjbjd\n6eIb2nceey+dKFm4ewlR4mXuzj1QowRTHceOIkvKIrOODxdy9M5hNBZ7VLum29tY\nRhqtmEH2BZZJ8SpM2SsEZzPxqJFiVZbvpeOKjxlMyn1dFWn1rP8uMnfuMKqBaj5D\nd5clOPlwebYw5UpM6Vvawu4nGqxECTSWcfNlDYO5U/0Fsm9+JIrJ7Buukgv2+rhs\nD/6oUK9NB8AW9qnDr7UxbC/ujhkKQG3woaZlPbiMs5WQaS+DrTg4N49wPzS0h+ME\nF8ZzuPnd6+sMGQioCIrQAZ08rk54oCijBhFh8/EQhQKGsMFw2swi9t6+FVU5Bvil\nlhmBd3LA5EuQ5y1X0jRL/+GDiUiZw1gOJP8d/XzhUJL9AmamdqJ6/rAU7lUTNWkM\ndzmFonUO2Mh2zgEEudHsTOH8udZ2l64LIHc6fCkDmM8QzghjrEFyci6R8333DSSM\nwbM0MvyTLM7TTqZUD60EgD+Ihyr/wJcBZY7GVn7hTq7ee14zeI+dZFmTMYOnt0mA\ngof19t0naPPZU+zyl/ambNF5mmSkGOAl4IBHNvPt5ztEVbNpwW3DHbmdYW71Ax+z\nCDlr4iKZahv21o1PCesPV2IlaHZFD6aBRt0DxzMqtq9cpWsI1g7aEaAjRbSvqhMY\npUeqFXz/GfR9rjRkufr48//ll0/Q/Ogx7m1TjQ6mAEQrklI7pa2W0u3H0BpSZSis\nR6ST3ulE+wfsp8cau6q2er+BSsDhBjSn9FeCUjHzY56u9ud/kb6/jLEdgxNpj0na\n3WVqCCCL/dAFSWznBmdracZsRMXapXInHCiiOEkXXbXIXvRKiTPJXdN+w2/U2j2B\nwXZuazVSpmM+xAZTAS9dtBUQJo+5px9b6P09uagvTA32ezbpPXf+hSfmTdUwbmAY\nrmE9SW85tzX+cD17loygBBRrjOr4uQy/s/9FqLx8bM73jly05rdOmX28ECKwEA05\n8aCFkfqrl9J9doVapaUlywpJVPFtE6W6tCF+ULMfb16vEjT1du1+epEnbGGLRQxg\n3aFLyKlvFaNvR38fiQFUGtBgGOaBN3rhGpbMwjch3oReXv9X/4UCL6sVIiOH2H3c\nVSZdC3O5g6CMVe4zckUe1k9mLDb5524IHDFfptZ6Bw+uzrqIy3GHW8dJF2AK471b\nMUnCojTpdbFHaUs2u/rNKVUyY+vLf8hkyP+znBUoPxSJtty53EWNukxjjsxx0lx3\niZGqN72lXlXuSFZAIxi307+xxE21cbzDsMidyJkbKKGm/F4BOKvX9jWmAyYmBG6A\n1L3yNRouFWsYDwYAX2nZ1is=\n-----END ENCRYPTED PRIVATE KEY-----\n', server_certificate='-----BEGIN CERTIFICATE-----\nMIIDcTCCAlkCBDfnXU8wDQYJKoZIhvcNAQELBQAwfTELMAkGA1UEBhMCRVMxDzAN\nBgNVBAgMBk1hZHJpZDEPMA0GA1UEBwwGTWFkcmlkMREwDwYDVQQKDAhVRFMgQ2Vy\ndDERMA8GA1UECwwIVURTIENlcnQxEjAQBgNVBAMMCTEyNy4wLjAuMTESMBAGA1Ud\nEQwJMTI3LjAuMC4xMB4XDTIwMDIxNzExNTkzMloXDTMwMDIxNDExNTkzMlowfTEL\nMAkGA1UEBhMCRVMxDzANBgNVBAgMBk1hZHJpZDEPMA0GA1UEBwwGTWFkcmlkMREw\nDwYDVQQKDAhVRFMgQ2VydDERMA8GA1UECwwIVURTIENlcnQxEjAQBgNVBAMMCTEy\nNy4wLjAuMTESMBAGA1UdEQwJMTI3LjAuMC4xMIIBIjANBgkqhkiG9w0BAQEFAAOC\nAQ8AMIIBCgKCAQEA2e1cW7YtRpNLazR3f/LqLv8OB0rKh8cUPH4wuQhbBTkee8Wu\n5eMSadRCIyRbKj4b8dtVfI9QW0SrmhGuMx1KCh3CsYd9XsWiKbGkiRBHIDOn5pkF\n6PUayDJ8KjnGbfnZjp0AmxXP4r1OO8jUPqzKS9Ubf5PgwcwdFiUKVfVPwGwctwt5\nt9YpSRONw0rTsCjVHvO2dd9h6EopskLCWxpN8l9kNLwLM/6t0IqVKmn5/IYPKKN2\nCX8a7IXpxwoiUs4sBZYhUMBWikB1hKQRSYafp1Xvc5PeTFXTFqGANnqz0NoZ8tqL\n8qjQUN/PCdtzhfcP5RgT2g1qyS2RBCMYH7Zs0wIDAQABMA0GCSqGSIb3DQEBCwUA\nA4IBAQCUt+qlLA1N9VXMwDQAYG4Kt6/UlMHCXAajHQQGtjdyGJ4++m7EIjI96hMU\n3Cx2gp2ggR3JGnuSR+DdBvPl5iGku7J8KV0JiJg30gTY8JuUIy/PMLZWloYKrBHV\nlin2GujQ4OsIt3dbr4XtcKW1Wd7L6fBzHlq7Xyxh+gcTzTvTmq67Q9XKlBWsegMf\nv4FKy0lfcSFK3vTzswQtuTontG4TqLiT/4AnMt3D0cTQ6b6KoZwUUX/TDNhau06d\nQ4Ilz8X61ka+4HBkFSR5ahP9noCVhwO329h+6epO141E5Tep3OLc/GCF4oaKOlMR\nfqxf5f2bghU0fxmtEoNJTZkBsN1S\n-----END CERTIFICATE-----\n', - password='Pw7qbatz5u-y-Z5ora2D2ZuBCm95AHnKRcpze53k8tw' + password='Pw7qbatz5u-y-Z5ora2D2ZuBCm95AHnKRcpze53k8tw', + ciphers='' ) diff --git a/actor/src/udsactor/http/server.py b/actor/src/udsactor/http/server.py index d561f8bd3..d12ce48cf 100644 --- a/actor/src/udsactor/http/server.py +++ b/actor/src/udsactor/http/server.py @@ -42,11 +42,20 @@ from .. import rest from .public import PublicProvider from .local import LocalProvider +DEFAULT_CIPHERS = ( + 'ECDHE-RSA-AES256-GCM-SHA384' + ':ECDHE-ECDSA-AES256-GCM-SHA384' + ':ECDHE-ECDSA-AES256-GCM-SHA384' + ':ECDHE-ECDSA-AES256-CCM' + ':DHE-RSA-AES256-SHA256' +) + # Not imported at runtime, just for type checking if typing.TYPE_CHECKING: from ..service import CommonService from .handler import Handler + class HTTPServerHandler(http.server.BaseHTTPRequestHandler): protocol_version = 'HTTP/1.0' server_version = 'UDS Actor Server' @@ -54,7 +63,12 @@ class HTTPServerHandler(http.server.BaseHTTPRequestHandler): _service: typing.Optional['CommonService'] = None - def sendJsonResponse(self, result: typing.Optional[typing.Any] = None, error: typing.Optional[str] = None, code: int = 200) -> None: + def sendJsonResponse( + self, + result: typing.Optional[typing.Any] = None, + error: typing.Optional[str] = None, + code: int = 200, + ) -> None: data = json.dumps({'result': result, 'error': error}) self.send_response(code) self.send_header('Content-type', 'application/json') @@ -75,7 +89,9 @@ class HTTPServerHandler(http.server.BaseHTTPRequestHandler): handlerType: typing.Optional[typing.Type['Handler']] = None - if len(path) == 3 and path[0] == 'actor' and path[1] == self._service._secret: # pylint: disable=protected-access + if ( + len(path) == 3 and path[0] == 'actor' and path[1] == self._service._secret + ): # pylint: disable=protected-access # public method handlerType = PublicProvider elif len(path) == 2 and path[0] == 'ui': @@ -88,12 +104,18 @@ class HTTPServerHandler(http.server.BaseHTTPRequestHandler): return try: - result = getattr(handlerType(self._service, method, params), method + '_' + path[-1])() # last part of path is method + result = getattr( + handlerType(self._service, method, params), method + '_' + path[-1] + )() # last part of path is method except AttributeError: self.sendJsonResponse(error='Method not found', code=404) return except Exception as e: - logger.error('Got exception executing {} {}: {}'.format(method, '/'.join(path), str(e))) + logger.error( + 'Got exception executing {} {}: {}'.format( + method, '/'.join(path), str(e) + ) + ) self.sendJsonResponse(error=str(e), code=500) return @@ -101,7 +123,10 @@ class HTTPServerHandler(http.server.BaseHTTPRequestHandler): def do_GET(self) -> None: try: - params = {v.split('=')[0]: v.split('=')[1] for v in self.path.split('?')[1].split('&')} + params = { + v.split('=')[0]: v.split('=')[1] + for v in self.path.split('?')[1].split('&') + } except Exception: params = {} @@ -113,7 +138,9 @@ class HTTPServerHandler(http.server.BaseHTTPRequestHandler): content = self.rfile.read(length) params: typing.MutableMapping[str, str] = json.loads(content) except Exception as e: - logger.error('Got exception executing POST {}: {}'.format(self.path, str(e))) + logger.error( + 'Got exception executing POST {}: {}'.format(self.path, str(e)) + ) self.sendJsonResponse(error='Invalid parameters', code=400) return @@ -125,6 +152,7 @@ class HTTPServerHandler(http.server.BaseHTTPRequestHandler): def log_message(self, format, *args): # pylint: disable=redefined-builtin logger.debug(format, *args) + class HTTPServerThread(threading.Thread): _server: typing.Optional[http.server.HTTPServer] _service: 'CommonService' @@ -153,16 +181,21 @@ class HTTPServerThread(threading.Thread): def run(self): HTTPServerHandler._service = self._service # pylint: disable=protected-access - self._certFile, password = certs.saveCertificate(self._service._certificate) # pylint: disable=protected-access + self._certFile, password = certs.saveCertificate( + self._service._certificate + ) # pylint: disable=protected-access - self._server = http.server.HTTPServer(('0.0.0.0', rest.LISTEN_PORT), HTTPServerHandler) + self._server = http.server.HTTPServer( + ('0.0.0.0', rest.LISTEN_PORT), HTTPServerHandler + ) # self._server.socket = ssl.wrap_socket(self._server.socket, certfile=self.certFile, server_side=True) context = ssl.SSLContext(ssl.PROTOCOL_TLS_SERVER) # Disable TLSv1.0 and TLSv1.1, disable TLSv1.2, use only TLSv1.3 - context.options |= ssl.OP_NO_TLSv1 | ssl.OP_NO_TLSv1_1 | ssl.OP_NO_TLSv1_2 + context.options |= ssl.OP_NO_TLSv1 | ssl.OP_NO_TLSv1_1 - context.set_ciphers('ECDHE-ECDSA-AES256-GCM-SHA384:ECDHE-ECDSA-AES256-CCM:DHE-RSA-AES256-SHA256') + # If a configures ciphers are provided, use them, otherwise use the default ones + context.set_ciphers(self._service._certificate.ciphers or DEFAULT_CIPHERS) context.load_cert_chain(certfile=self._certFile, password=password) self._server.socket = context.wrap_socket(self._server.socket, server_side=True) diff --git a/actor/src/udsactor/rest.py b/actor/src/udsactor/rest.py index f058239b6..d13d66e66 100644 --- a/actor/src/udsactor/rest.py +++ b/actor/src/udsactor/rest.py @@ -282,6 +282,7 @@ class UDSServerApi(UDSApi): private_key=result['private_key'], server_certificate=result['server_certificate'], password=result['password'], + ciphers=result.get('ciphers', ''), ) def notifyIpChange( @@ -294,6 +295,7 @@ class UDSServerApi(UDSApi): private_key=result['private_key'], server_certificate=result['server_certificate'], password=result['password'], + ciphers=result.get('ciphers', ''), ) def notifyUnmanagedCallback( @@ -315,6 +317,7 @@ class UDSServerApi(UDSApi): private_key=result['private_key'], server_certificate=result['server_certificate'], password=result['password'], + ciphers=result.get('ciphers', ''), ) def login( diff --git a/actor/src/udsactor/types.py b/actor/src/udsactor/types.py index 7d4e75154..a4cff7072 100644 --- a/actor/src/udsactor/types.py +++ b/actor/src/udsactor/types.py @@ -66,3 +66,4 @@ class CertificateInfoType(typing.NamedTuple): private_key: str server_certificate: str password: str + ciphers: str From 666b982c5040b7570516e8998dfb33558df297c8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adolfo=20G=C3=B3mez=20Garc=C3=ADa?= Date: Mon, 13 Mar 2023 18:00:47 +0100 Subject: [PATCH 2/2] fixed authenticator label not allowing dot in name --- server/src/uds/REST/methods/authenticators.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/server/src/uds/REST/methods/authenticators.py b/server/src/uds/REST/methods/authenticators.py index f26953040..4570e2c7c 100644 --- a/server/src/uds/REST/methods/authenticators.py +++ b/server/src/uds/REST/methods/authenticators.py @@ -227,10 +227,10 @@ class Authenticators(ModelHandler): fields['mfa_id'] = None fields['small_name'] = fields['small_name'].strip().replace(' ', '-') - # And ensure small_name chars are valid [ a-zA-Z0-9:-]+ - if fields['small_name'] and not re.match(r'^[a-zA-Z0-9:-]+$', fields['small_name']): + # And ensure small_name chars are valid [ a-zA-Z0-9:-.]+ + if fields['small_name'] and not re.match(r'^[a-zA-Z0-9:-.]+$', fields['small_name']): raise self.invalidRequestException( - _('Label must contain only letters, numbers, ":" and "-"') + _('Label must contain only letters, numbers, or symbols: - : .') )