From ff0b07758c6d127465383e71bfeef6f0462ff8c9 Mon Sep 17 00:00:00 2001 From: Steve Haskew Date: Fri, 22 Jun 2018 12:59:55 +0100 Subject: [PATCH 1/4] Basic support for new login method. --- routeros_api/api.py | 35 +++++++++++++++++++++-------------- 1 file changed, 21 insertions(+), 14 deletions(-) diff --git a/routeros_api/api.py b/routeros_api/api.py index 682f4e0..3b5d35d 100644 --- a/routeros_api/api.py +++ b/routeros_api/api.py @@ -9,18 +9,20 @@ from routeros_api import resource -def connect(host, username='admin', password='', port=8728): - return RouterOsApiPool(host, username, password, port).get_api() +def connect(host, username='admin', password='', port=8728, force_old_login=False): + return RouterOsApiPool(host, username, password, port, force_old_login).get_api() class RouterOsApiPool(object): socket_timeout = 15. - def __init__(self, host, username='admin', password='', port=8728): + def __init__(self, host, username='admin', password='', port=8728, force_old_login=False): self.host = host self.username = username self.password = password self.port = port + # Don't send the new-style login (can expose password in plaintext!) + self.force_old_login = force_old_login self.connected = False self.socket = api_socket.DummySocket() self.communication_exception_parser = ( @@ -35,7 +37,7 @@ def get_api(self): self.api = RouterOsApi(communicator) for handler in self._get_exception_handlers(): communicator.add_exception_handler(handler) - self.api.login(self.username, self.password) + self.api.login(self.username, self.password, self.force_old_login) self.connected = True return self.api @@ -57,16 +59,21 @@ class RouterOsApi(object): def __init__(self, communicator): self.communicator = communicator - def login(self, login, password): - response = self.get_binary_resource('/').call('login') - token = binascii.unhexlify(response.done_message['ret']) - hasher = hashlib.md5() - hasher.update(b'\x00') - hasher.update(password.encode()) - hasher.update(token) - hashed = b'00' + hasher.hexdigest().encode('ascii') - self.get_binary_resource('/').call( - 'login', {'name': login.encode(), 'response': hashed}) + def login(self, login, password, force_old_login): + response = None + if force_old_login: + response = self.get_binary_resource('/').call('login') + else: + response = self.get_binary_resource('/').call('login',{ 'name': login, 'password': password }) + if 'ret' in response.done_message: + token = binascii.unhexlify(response.done_message['ret']) + hasher = hashlib.md5() + hasher.update(b'\x00') + hasher.update(password.encode()) + hasher.update(token) + hashed = b'00' + hasher.hexdigest().encode('ascii') + self.get_binary_resource('/').call( + 'login', {'name': login.encode(), 'response': hashed}) def get_resource(self, path, structure=None): structure = structure or api_structure.default_structure From b46106f69d97bc941345211fc2ac3d463597e7c9 Mon Sep 17 00:00:00 2001 From: Steve Haskew Date: Tue, 26 Jun 2018 11:03:53 +0100 Subject: [PATCH 2/4] Flip logic. Will not connect insecurely unless specifically requested. --- routeros_api/api.py | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/routeros_api/api.py b/routeros_api/api.py index 3b5d35d..9de9a9c 100644 --- a/routeros_api/api.py +++ b/routeros_api/api.py @@ -9,20 +9,20 @@ from routeros_api import resource -def connect(host, username='admin', password='', port=8728, force_old_login=False): - return RouterOsApiPool(host, username, password, port, force_old_login).get_api() +def connect(host, username='admin', password='', port=8728, insecure_login=False): + return RouterOsApiPool(host, username, password, port, insecure_login).get_api() class RouterOsApiPool(object): socket_timeout = 15. - def __init__(self, host, username='admin', password='', port=8728, force_old_login=False): + def __init__(self, host, username='admin', password='', port=8728, insecure_login=False): self.host = host self.username = username self.password = password self.port = port # Don't send the new-style login (can expose password in plaintext!) - self.force_old_login = force_old_login + self.insecure_login = insecure_login self.connected = False self.socket = api_socket.DummySocket() self.communication_exception_parser = ( @@ -37,7 +37,7 @@ def get_api(self): self.api = RouterOsApi(communicator) for handler in self._get_exception_handlers(): communicator.add_exception_handler(handler) - self.api.login(self.username, self.password, self.force_old_login) + self.api.login(self.username, self.password, self.insecure_login) self.connected = True return self.api @@ -59,12 +59,12 @@ class RouterOsApi(object): def __init__(self, communicator): self.communicator = communicator - def login(self, login, password, force_old_login): + def login(self, login, password, insecure_login): response = None - if force_old_login: - response = self.get_binary_resource('/').call('login') - else: + if insecure_login: response = self.get_binary_resource('/').call('login',{ 'name': login, 'password': password }) + else: + response = self.get_binary_resource('/').call('login') if 'ret' in response.done_message: token = binascii.unhexlify(response.done_message['ret']) hasher = hashlib.md5() From 0114a447b872b4ff6c09893ab3d053af06995bd7 Mon Sep 17 00:00:00 2001 From: Steve Haskew Date: Tue, 24 Jul 2018 11:03:18 +0100 Subject: [PATCH 3/4] Rename insecure_login to plaintext_login. --- routeros_api/api.py | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/routeros_api/api.py b/routeros_api/api.py index 949ca55..a06f753 100644 --- a/routeros_api/api.py +++ b/routeros_api/api.py @@ -16,12 +16,12 @@ def connect(host, username='admin', password='', port=None, insecure_login=False class RouterOsApiPool(object): socket_timeout = 15.0 - def __init__(self, host, username='admin', password='', port=None, insecure_login=False, use_ssl=False, ssl_verify=True, ssl_verify_hostname=True, ssl_context=None): + def __init__(self, host, username='admin', password='', port=None, plaintext_login=False, use_ssl=False, ssl_verify=True, ssl_verify_hostname=True, ssl_context=None): self.host = host self.username = username self.password = password - # Don't send the new-style login (can expose password in plaintext!) - self.insecure_login = insecure_login + + self.plaintext_login = plaintext_login self.ssl_context = ssl_context # Use SSL? Ignored when using a context, so we will set it for simple reference when port-switching: @@ -48,7 +48,7 @@ def get_api(self): self.api = RouterOsApi(communicator) for handler in self._get_exception_handlers(): communicator.add_exception_handler(handler) - self.api.login(self.username, self.password, self.insecure_login) + self.api.login(self.username, self.password, self.plaintext_login) self.connected = True return self.api @@ -76,9 +76,9 @@ class RouterOsApi(object): def __init__(self, communicator): self.communicator = communicator - def login(self, login, password, insecure_login): + def login(self, login, password, plaintext_login): response = None - if insecure_login: + if plaintext_login: response = self.get_binary_resource('/').call('login',{ 'name': login, 'password': password }) else: response = self.get_binary_resource('/').call('login') From 917072b1ddb9f8e3d75bfea06e3094b392e50e37 Mon Sep 17 00:00:00 2001 From: Steve Haskew Date: Tue, 24 Jul 2018 11:04:19 +0100 Subject: [PATCH 4/4] Additional use of insecure_login changed to plaintext_login --- routeros_api/api.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/routeros_api/api.py b/routeros_api/api.py index a06f753..d246a28 100644 --- a/routeros_api/api.py +++ b/routeros_api/api.py @@ -9,8 +9,8 @@ from routeros_api import resource -def connect(host, username='admin', password='', port=None, insecure_login=False, use_ssl=False, ssl_verify=True, ssl_verify_hostname=True, ssl_context=None): - return RouterOsApiPool(host, username, password, port, insecure_login, use_ssl, ssl_verify, ssl_verify_hostname, ssl_context).get_api() +def connect(host, username='admin', password='', port=None, plaintext_login=False, use_ssl=False, ssl_verify=True, ssl_verify_hostname=True, ssl_context=None): + return RouterOsApiPool(host, username, password, port, plaintext_login, use_ssl, ssl_verify, ssl_verify_hostname, ssl_context).get_api() class RouterOsApiPool(object):