From 3eaeb42bf6f66868a3a2829fdeb78b7ab0b17cd2 Mon Sep 17 00:00:00 2001 From: Mark Qvist Date: Wed, 6 Apr 2022 20:34:49 +0200 Subject: [PATCH] Added page authentication --- nomadnet/Directory.py | 24 ++++++++++-- nomadnet/Node.py | 72 ++++++++++++++++++++++++++++++----- nomadnet/ui/textui/Browser.py | 4 ++ nomadnet/ui/textui/Network.py | 13 +++++-- 4 files changed, 97 insertions(+), 16 deletions(-) diff --git a/nomadnet/Directory.py b/nomadnet/Directory.py index c357b45..6191500 100644 --- a/nomadnet/Directory.py +++ b/nomadnet/Directory.py @@ -32,7 +32,7 @@ class Directory: packed_list = [] for source_hash in self.directory_entries: e = self.directory_entries[source_hash] - packed_list.append((e.source_hash, e.display_name, e.trust_level, e.hosts_node, e.preferred_delivery)) + packed_list.append((e.source_hash, e.display_name, e.trust_level, e.hosts_node, e.preferred_delivery, e.identify)) directory = { "entry_list": packed_list, @@ -65,7 +65,12 @@ class Directory: else: preferred_delivery = None - entries[e[0]] = DirectoryEntry(e[0], e[1], e[2], hosts_node, preferred_delivery=preferred_delivery) + if len(e) > 5: + identify = e[5] + else: + identify = False + + entries[e[0]] = DirectoryEntry(e[0], e[1], e[2], hosts_node, preferred_delivery=preferred_delivery, identify_on_connect=identify) self.directory_entries = entries @@ -183,6 +188,18 @@ class Directory: except Exception as e: return False + def should_identify_on_connect(self, source_hash): + if source_hash in self.directory_entries: + entry = self.directory_entries[source_hash] + return entry.identify + else: + return False + + def set_identify_on_connect(self, source_hash, state): + if source_hash in self.directory_entries: + entry = self.directory_entries[source_hash] + entry.identify = state + def known_nodes(self): node_list = [] for eh in self.directory_entries: @@ -214,7 +231,7 @@ class DirectoryEntry: DIRECT = 0x01 PROPAGATED = 0x02 - def __init__(self, source_hash, display_name=None, trust_level=UNKNOWN, hosts_node=False, preferred_delivery=None): + def __init__(self, source_hash, display_name=None, trust_level=UNKNOWN, hosts_node=False, preferred_delivery=None, identify_on_connect=False): if len(source_hash) == RNS.Identity.TRUNCATED_HASHLENGTH//8: self.source_hash = source_hash self.display_name = display_name @@ -228,5 +245,6 @@ class DirectoryEntry: self.trust_level = trust_level self.hosts_node = hosts_node + self.identify = identify_on_connect else: raise TypeError("Attempt to add invalid source hash to directory") \ No newline at end of file diff --git a/nomadnet/Node.py b/nomadnet/Node.py index 9a0ffd5..3c21a31 100644 --- a/nomadnet/Node.py +++ b/nomadnet/Node.py @@ -79,7 +79,8 @@ class Node: directories = [file for file in os.listdir(base_path) if os.path.isdir(os.path.join(base_path, file)) and file[:1] != "."] for file in files: - self.servedpages.append(base_path+"/"+file) + if not file.endswith(".allowed"): + self.servedpages.append(base_path+"/"+file) for directory in directories: self.scan_pages(base_path+"/"+directory) @@ -96,17 +97,63 @@ class Node: def serve_page(self, path, data, request_id, remote_identity, requested_at): RNS.log("Page request "+RNS.prettyhexrep(request_id)+" for: "+str(path), RNS.LOG_VERBOSE) + file_path = path.replace("/page", self.app.pagespath, 1) - try: - RNS.log("Serving page: "+file_path, RNS.LOG_VERBOSE) - if os.access(file_path, os.X_OK): - generated = subprocess.run([file_path], stdout=subprocess.PIPE) - return generated.stdout + + allowed_path = file_path+".allowed" + request_allowed = False + + if os.path.isfile(allowed_path): + allowed_list = [] + + try: + if os.access(allowed_path, os.X_OK): + allowed_result = subprocess.run([allowed_path], stdout=subprocess.PIPE) + allowed_input = allowed_result.stdout + + else: + fh = open(allowed_path, "rb") + allowed_input = fh.read() + fh.close() + + allowed_hash_strs = allowed_input.splitlines() + + for hash_str in allowed_hash_strs: + if len(hash_str) == RNS.Identity.TRUNCATED_HASHLENGTH//8*2: + try: + allowed_hash = bytes.fromhex(hash_str.decode("utf-8")) + allowed_list.append(allowed_hash) + + except Exception as e: + RNS.log("Could not decode RNS Identity hash from: "+str(hash_str), RNS.LOG_DEBUG) + RNS.log("The contained exception was: "+str(e), RNS.LOG_DEBUG) + + except Exception as e: + RNS.log("Error while fetching list of allowed identities for request: "+str(e), RNS.LOG_ERROR) + + if remote_identity.hash in allowed_list: + request_allowed = True else: - fh = open(file_path, "rb") - response_data = fh.read() - fh.close() - return response_data + request_allowed = False + RNS.log("Denying request, remote identity was not in list of allowed identities", RNS.LOG_VERBOSE) + + else: + request_allowed = True + + try: + if request_allowed: + RNS.log("Serving page: "+file_path, RNS.LOG_VERBOSE) + if os.access(file_path, os.X_OK): + generated = subprocess.run([file_path], stdout=subprocess.PIPE) + return generated.stdout + else: + fh = open(file_path, "rb") + response_data = fh.read() + fh.close() + return response_data + else: + RNS.log("Request denied", RNS.LOG_VERBOSE) + return DEFAULT_NOTALLOWED.encode("utf-8") except Exception as e: RNS.log("Error occurred while handling request "+RNS.prettyhexrep(request_id)+" for: "+str(path), RNS.LOG_ERROR) @@ -160,4 +207,9 @@ DEFAULT_INDEX = '''>Default Home Page This node is serving pages, but the home page file (index.mu) was not found in the page storage directory. This is an auto-generated placeholder. If you are the node operator, you can define your own home page by creating a file named `*index.mu`* in the page storage directory. +''' + +DEFAULT_NOTALLOWED = '''>Request Not Allowed + +You are not authorised to carry out the request. ''' \ No newline at end of file diff --git a/nomadnet/ui/textui/Browser.py b/nomadnet/ui/textui/Browser.py index 7cf12a5..59578e1 100644 --- a/nomadnet/ui/textui/Browser.py +++ b/nomadnet/ui/textui/Browser.py @@ -643,6 +643,10 @@ class Browser: def link_established(self, link): self.status = Browser.LINK_ESTABLISHED + if self.app.directory.should_identify_on_connect(self.destination_hash): + RNS.log("Link established, identifying to remote system...", RNS.LOG_VERBOSE) + self.link.identify(self.app.identity) + def link_closed(self, link): if self.status == Browser.DISCONECTED or self.status == Browser.DONE: diff --git a/nomadnet/ui/textui/Network.py b/nomadnet/ui/textui/Network.py index 6073e1f..51f13f6 100644 --- a/nomadnet/ui/textui/Network.py +++ b/nomadnet/ui/textui/Network.py @@ -443,7 +443,11 @@ class KnownNodeInfo(urwid.WidgetWrap): def pn_change(sender, userdata): self.pn_changed = True + def ident_change(sender, userdata): + pass + propagation_node_checkbox = urwid.CheckBox("Use as default propagation node", state=(self.app.get_user_selected_propagation_node() == source_hash), on_state_change=pn_change) + connect_identify_checkbox = urwid.CheckBox("Identify when connecting", state=self.app.directory.should_identify_on_connect(source_hash), on_state_change=ident_change) def save_node(sender): if self.pn_changed: @@ -461,7 +465,7 @@ class KnownNodeInfo(urwid.WidgetWrap): display_str = e_name.get_edit_text() - node_entry = DirectoryEntry(source_hash, display_name=display_str, trust_level=trust_level, hosts_node=True) + node_entry = DirectoryEntry(source_hash, display_name=display_str, trust_level=trust_level, hosts_node=True, identify_on_connect=connect_identify_checkbox.get_state()) self.app.directory.remember(node_entry) self.app.ui.main_display.sub_displays.network_display.directory_change_callback() show_known_nodes(None) @@ -479,6 +483,7 @@ class KnownNodeInfo(urwid.WidgetWrap): # urwid.Text(["Trust : ", (style, trust_str)], align="left"), urwid.Divider(g["divider1"]), propagation_node_checkbox, + connect_identify_checkbox, urwid.Divider(g["divider1"]), r_untrusted, r_unknown, @@ -793,8 +798,9 @@ class LocalPeer(urwid.WidgetWrap): if display_name == None: display_name = "" - t_id = urwid.Text("Addr : "+RNS.hexrep(self.app.lxmf_destination.hash, delimit=False)) - e_name = urwid.Edit(caption="Name : ", edit_text=display_name) + t_id = urwid.Text("LXMF Addr : "+RNS.prettyhexrep(self.app.lxmf_destination.hash)) + i_id = urwid.Text("Identity : "+RNS.prettyhexrep(self.app.identity.hash)) + e_name = urwid.Edit(caption="Name : ", edit_text=display_name) def save_query(sender): def dismiss_dialog(sender): @@ -858,6 +864,7 @@ class LocalPeer(urwid.WidgetWrap): self.display_widget = urwid.Pile( [ t_id, + i_id, e_name, urwid.Divider(g["divider1"]), self.t_last_announce,