diff --git a/README b/README new file mode 100644 index 0000000..633c1a4 --- /dev/null +++ b/README @@ -0,0 +1,36 @@ + +# Anonymizer # +# Tor made easy (transparently routing traffic through Tor) # +# ---------------------------------------------------------------------------- # + +# BUILD FROM SOURCE # +# ---------------------------------------------------------------------------- # + +cd src +make +make install + +# DOCUMENTATION # +# ---------------------------------------------------------------------------- # + +# ABOUT VALA +https://wiki.gnome.org/Projects/Vala/ +https://en.wikipedia.org/wiki/Vala_(programming_language) + +# TUTORIALS, GUIDES AND REFERENCES +https://valadoc.org/ +https://wiki.gnome.org/Projects/Vala/Tutorial +https://developer.gnome.org/gnome-devel-demos/stable/beginner.vala.html.en + +# VALA AND GTK+ +https://wiki.gnome.org/Projects/Vala/GTKSample +https://developer.gnome.org/gnome-devel-demos/stable/toolbar_builder.vala.html.en + +# VALA AND UNIX SHELL +http://www.teejeetech.in/2013/05/vala-7-process-execution.html + +# NETFILTER/IPTABLES +https://www.netfilter.org/ + +# TOR +https://www.torproject.org/ diff --git a/src/Makefile b/src/Makefile new file mode 100644 index 0000000..a38dd11 --- /dev/null +++ b/src/Makefile @@ -0,0 +1,18 @@ +#!/bin/bash + + +all: + valac --pkg gtk+-3.0 --pkg gmodule-2.0 "anonymizer.vala" -o anonymizer + +clean: + rm -rf *.o anonymizer + +install: + mkdir -p /usr/share/anonymizer/ + cp -f anonymizer.glade /usr/share/anonymizer + chmod -R 644 /usr/share/anonymizer/anonymizer.glade + cp -f anonymizer /usr/bin + +uninstall: + rm -f /usr/bin/anonymizer + rm -f /usr/share/anonymizer diff --git a/src/anonymizer b/src/anonymizer new file mode 100644 index 0000000..6b743c6 Binary files /dev/null and b/src/anonymizer differ diff --git a/src/anonymizer.geany b/src/anonymizer.geany new file mode 100644 index 0000000..a79805e --- /dev/null +++ b/src/anonymizer.geany @@ -0,0 +1,46 @@ +[editor] +line_wrapping=true +line_break_column=80 +auto_continue_multiline=true + +[file_prefs] +final_new_line=true +ensure_convert_new_lines=true +strip_trailing_spaces=true +replace_tabs=false + +[indentation] +indent_width=4 +indent_type=1 +indent_hard_tab_width=8 +detect_indent=false +detect_indent_width=false +indent_mode=2 + +[project] +name=Anonymizer +base_path=/home/netico/Documents/Code/anonymizer-gui +description=Tor made easy (transparently routing traffic through Tor) # +file_patterns= + +[long line marker] +long_line_behaviour=2 +long_line_column=80 + +[files] +current_page=1 +FILE_NAME_0=19;None;0;EUTF-8;1;1;1;%2Fhome%2Fnetico%2FDocuments%2FCode%2Fanonymizer-gui%2FREADME;0;4 +FILE_NAME_1=1408;Vala;0;EUTF-8;1;1;1;%2Fhome%2Fnetico%2FDocuments%2FCode%2Fanonymizer-gui%2Fsrc%2Fanonymizer.vala;0;4 +FILE_NAME_2=263;Sh;0;EUTF-8;1;1;1;%2Fhome%2Fnetico%2FDocuments%2FCode%2Fanonymizer-gui%2Fsrc%2FMakefile;0;4 + +[VTE] +last_dir=/home/netico/Documents/Code/anonymizer-gui/src + +[build-menu] +filetypes=Vala;Sh; +ShFT_02_LB=_Lint +ShFT_02_CM=shellcheck --format=gcc "%f" +ShFT_02_WD= +ValaFT_00_LB=_Compile +ValaFT_00_CM=valac -c "%f" +ValaFT_00_WD= diff --git a/src/anonymizer.glade b/src/anonymizer.glade new file mode 100644 index 0000000..ac843f1 --- /dev/null +++ b/src/anonymizer.glade @@ -0,0 +1,273 @@ + + + + + + + + + + + False + Anonymizer + center + 800 + 600 + dialog-password + True + + + + True + False + vertical + + + True + False + + + True + False + Anonymizer + True + + + True + False + + + True + False + Quit + + + + + + + + + + True + False + Tor + True + + + True + False + + + True + False + Start + True + + + + + + True + False + Stop + + + + + + + + + + True + False + Proxy + + + True + False + + + True + False + Enable proxy + + + + + True + False + Reset netfilter + + + + + + + + + False + True + 0 + + + + + True + False + + + True + False + True + Start + gtk-media-play + + + + False + True + + + + + True + False + True + Stop + gtk-media-stop + + + + False + True + + + + + True + False + True + Enable proxy + gtk-connect + + + + False + True + + + + + True + False + True + Reset netfilter + gtk-disconnect + + + + False + True + + + + + True + False + True + Quit + gtk-quit + + + + False + True + + + + + False + True + 1 + + + + + True + True + never + bottom-left + + + True + True + True + True + True + True + immediate + 4 + 4 + False + word + 10 + 10 + False + False + True + + + + + False + True + 2 + + + + + True + False + 10 + 10 + 10 + 10 + 6 + 6 + vertical + 8 + + + False + True + end + 4 + + + + + + diff --git a/src/anonymizer.vala b/src/anonymizer.vala new file mode 100644 index 0000000..15d12e3 --- /dev/null +++ b/src/anonymizer.vala @@ -0,0 +1,454 @@ +/* main.vala + * + * Copyright (C) 2018 netico@riseup.net + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ + +using Gtk; + +// Setup // +// -------------------------------------------------------------------------- // +const string NIC = "wlan0"; +const int TOR_UID = 121; +const string TOR_USER = "debian-tor"; + +const int TOR_PORT = 56662; +const int TOR_DNS_PORT = 56663; +const string VIRTUAL_ADDRESS = "10.192.0.0/10"; +const string IPTABLES_BIN = "/sbin/iptables"; + +// Buttons // +// -------------------------------------------------------------------------- // + +// Quit +public void on_quit_clicked (Button source) +{ + Gtk.main_quit (); + Process.exit (0); + + // TODO - Drop /tmp/start_tor.sh +} +// Start Tor +public void on_start_tor_clicked (Button source, TextView console_view) +{ + try { + + int tor_pid_int = -1; + string tor_pid = shell_sync ("pidof tor"); + tor_pid.scanf ("%d", &tor_pid_int); + + if (tor_pid_int > 0) + { + console_view.buffer.text = "Tor is already running"; + return; + } + + var file = File.new_for_path ("/tmp/anonymizer.log"); + if (file.query_exists ()) + { + file.delete (); + } + var file_stream = file.create (FileCreateFlags.NONE); + var data_stream = new DataOutputStream (file_stream); + data_stream.put_string + ( + "Tor made easy " + + "(transparently routing traffic through Tor)" + + "\n" + ); + + string torrc = "SOCKSPort 127.0.0.1:56661\n" + + "TransPort 127.0.0.1:56662 IsolateClientAddr IsolateClientProtocol " + + "IsolateDestAddr IsolateDestPort\n" + + "DNSPort 127.0.0.1:56663\n" + + "VirtualAddrNetworkIPv4 10.192.0.0/10\n" + + "AutomapHostsOnResolve 1\n" + + "Log notice file /tmp/anonymizer.log\n"; + + string config = "/tmp/anonymizer.cnf"; + file = File.new_for_path (config); + if (file.query_exists ()) + { + file.delete (); + } + var dos = new DataOutputStream + ( + file.create (FileCreateFlags.REPLACE_DESTINATION) + ); + string text = torrc; + uint8[] data = text.data; + long written = 0; + while (written < data.length) + { + written += dos.write (data[written:data.length]); + } + string permissions; + permissions = shell_sync ("chmod -R 666 /tmp/anonymizer.log"); // FIXME + shell_run_as_async + ( + "/usr/bin/tor --quiet -f /tmp/anonymizer.cnf", + TOR_USER, + "/tmp", + "start-tor.sh" + ); + log_file_monitor (console_view, "/tmp/anonymizer.log"); + } + catch (Error e) + { + console_view.buffer.text = e.message; + } +} +// Stop Tor +public void on_stop_tor_clicked (Button source, TextView console_view) +{ + + int tor_pid_int = -1; + string tor_pid = shell_sync ("pidof tor"); + tor_pid.scanf ("%d", &tor_pid_int); + + if (tor_pid_int > 0) + { + string shell_command = "kill " + tor_pid_int.to_string (); + string pkexec; + pkexec = shell_run_as + ( + shell_command, + "root", + "/tmp", + "stop_tor.sh" + ); + console_view.buffer.text = "Tor has stopped working"; + } + else + { + console_view.buffer.text = "Tor is not running"; + } +} + +// Enable proxy +public void on_enable_proxy_clicked (Button source, TextView console_view) +{ + //on_reset_netfilter_clicked (source, console_view); + //on_restart_tor_clicked (source, console_view); + + //string resolv_conf = + var file = File.new_for_path ("/etc/resolv.conf"); + + try { + var dis = new DataInputStream (file.read ()); + string line; + var resolv_conf = new StringBuilder (); + while ((line = dis.read_line (null)) != null) + { + if ("nameserver" in line) + { + if (line.contains ("127.0.0.1") == false) + { + resolv_conf.append (line + "\n"); + } + } + } + string resolv_conf_new = resolv_conf.str + "nameserver 127.0.0.1\n"; + + string shell_command = "echo \"" + resolv_conf_new + + "\"> /etc/resolv.conf;\n" + + IPTABLES_BIN + + " -v -t nat -A OUTPUT -d " + VIRTUAL_ADDRESS.to_string() + + " -p tcp -m tcp --tcp-flags FIN,SYN,RST,ACK SYN -j REDIRECT " + + "--to-ports " + TOR_PORT.to_string() + ";\n" + + IPTABLES_BIN + " -v -t nat -A OUTPUT -d 127.0.0.1/32 -p udp -m udp " + + "--dport 53 -j REDIRECT --to-ports " + TOR_DNS_PORT.to_string() + ";\n" + + IPTABLES_BIN + " -v -t nat -A OUTPUT -m owner --uid-owner " + + TOR_UID.to_string() + " -j RETURN;\n" + + IPTABLES_BIN + " -v -t nat -A OUTPUT -o lo -j RETURN;\n" + + IPTABLES_BIN + " -v -t nat -A OUTPUT -p tcp -m tcp " + + " --tcp-flags FIN,SYN,RST,ACK SYN " + + " -j REDIRECT --to-ports " + TOR_PORT.to_string() + ";\n" + + IPTABLES_BIN + " -v -A INPUT -m state --state ESTABLISHED -j ACCEPT;\n" + + IPTABLES_BIN + " -v -A INPUT -i lo -j ACCEPT;\n" + + IPTABLES_BIN + " -v -A INPUT -j DROP;\n" + + IPTABLES_BIN + " -v -A FORWARD -j DROP;\n" + + IPTABLES_BIN + " -v -A OUTPUT -m state --state INVALID -j DROP;\n" + + IPTABLES_BIN + " -v -A OUTPUT -m state --state ESTABLISHED -j ACCEPT;\n" + + IPTABLES_BIN + " -v -A OUTPUT -o " + NIC + " -m owner " + + " --uid-owner " + TOR_UID.to_string() + + " -p tcp -m tcp --tcp-flags FIN,SYN,RST,ACK SYN -m state " + + " --state NEW -j ACCEPT;\n" + + IPTABLES_BIN + " -v -A OUTPUT -d 127.0.0.1/32 -o lo -j ACCEPT;\n" + + IPTABLES_BIN + " -v -A OUTPUT -d 127.0.0.1/32 -p tcp -m tcp " + + " --dport " + TOR_PORT.to_string() + + " --tcp-flags FIN,SYN,RST,ACK SYN -j ACCEPT;\n" + + IPTABLES_BIN + " -v -A OUTPUT -j DROP;\n" + + IPTABLES_BIN + " -v -P INPUT DROP;\n" + + IPTABLES_BIN + " -v -P FORWARD DROP;\n" + + IPTABLES_BIN + " -v -P OUTPUT DROP;\n"; + + string pkexec; + pkexec = shell_run_as + ( + shell_command, + "root", + "/tmp", + "enable_proxy.sh" + ); + console_view.buffer.text = "Rules:\n" + pkexec; + + } catch (Error e) { + console_view.buffer.text = e.message; + } + + +} +// Reset netfilter +public void on_reset_netfilter_clicked (Button source, TextView console_view) +{ + string shell_command = IPTABLES_BIN + + " -v -P INPUT ACCEPT;\n" + + IPTABLES_BIN + " -P FORWARD ACCEPT;\n" + + IPTABLES_BIN + " -P OUTPUT ACCEPT;\n" + + IPTABLES_BIN + " -t nat -P PREROUTING ACCEPT;\n" + + IPTABLES_BIN + " -t nat -P POSTROUTING ACCEPT;\n" + + IPTABLES_BIN + " -t nat -P OUTPUT ACCEPT;\n" + + IPTABLES_BIN + " -t mangle -P PREROUTING ACCEPT;\n" + + IPTABLES_BIN + " -t mangle -P OUTPUT ACCEPT;\n" + + IPTABLES_BIN + " -F;\n" + + IPTABLES_BIN + " -X;\n" + + IPTABLES_BIN + " -t nat -F;\n" + + IPTABLES_BIN + " -t nat -X;\n" + + IPTABLES_BIN + " -t mangle -F;\n" + + IPTABLES_BIN + " -t mangle -X;\n"; + + string pkexec; + pkexec = shell_run_as + ( + shell_command, + "root", + "/tmp", + "reset_netfilter.sh" + ); + console_view.buffer.text = "All rules are deleted"; +} + +// Shell // +// -------------------------------------------------------------------------- // + +// Sync method +private string shell_sync (string cmd) +{ + try { + int exit_code; + string std_out; + Process.spawn_command_line_sync (cmd, out std_out, null, out exit_code); + return std_out; + } + catch (Error e) + { + return (e.message); + } +} +// Async method +private bool shell_async (string[] args) +{ + try { + Process.spawn_async + ( + null, args, null, SpawnFlags.SEARCH_PATH, null, null + ); + return true; + } + catch (Error e) + { + stderr.printf (e.message); + return false; + } +} +// pkexec sync +private string shell_run_as (string cmd, string user, string dir, string script) +{ + try + { + string program = dir + "/" + script; + var file = File.new_for_path (program); + if (file.query_exists ()) + { + file.delete (); + } + var dos = new DataOutputStream + ( + file.create (FileCreateFlags.REPLACE_DESTINATION) + ); + string text = cmd; + uint8[] data = text.data; + long written = 0; + while (written < data.length) + { + written += dos.write (data[written:data.length]); + } + string pkexec; + pkexec = shell_sync ("pkexec --user " + user + " /bin/sh " + program); + if (file.query_exists ()) + { + file.delete (); + } + return pkexec; + } + catch (Error e) + { + return (e.message); + } +} + +// pkexec sync +private void shell_run_as_async (string cmd, string user, string dir, string script) +{ + try + { + string program = dir + "/" + script; + var file = File.new_for_path (program); + if (file.query_exists ()) + { + file.delete (); + } + var dos = new DataOutputStream + ( + file.create (FileCreateFlags.REPLACE_DESTINATION) + ); + string text = cmd; + uint8[] data = text.data; + long written = 0; + while (written < data.length) + { + written += dos.write (data[written:data.length]); + } + shell_async + ({ + "pkexec", + "--user", + user, + "/bin/sh", + program + }); + } + catch (Error e) + { + stderr.printf (e.message); + } +} + +// Log // +// -------------------------------------------------------------------------- // + +private void log_file_monitor (TextView console_view, string log_file) +{ + // Reference to log file + var log = File.new_for_path (log_file); + try + { + FileMonitor monitor = log.monitor (FileMonitorFlags.NONE, null); + MainLoop loop = new MainLoop (); + monitor.changed.connect ((src, dest, event) => + { + string event_string = event.to_string (); + if (event_string == "G_FILE_MONITOR_EVENT_DELETED") + { + loop.quit (); + } + if (event_string == "G_FILE_MONITOR_EVENT_CHANGED") + { + try + { + var dis = new DataInputStream (log.read ()); + string line; + var log_console = new StringBuilder (); + + // Read lines until end of file (null) is reached + while ((line = dis.read_line (null)) != null) + { + log_console.append (line + "\n"); + } + console_view.buffer.text = log_console.str; + } + catch (Error e) + { + console_view.buffer.text = (e.message); + } + } + }); + loop.run (); + } + catch (Error e) + { + console_view.buffer.text = e.message; + } +} + +// Main // +// -------------------------------------------------------------------------- // + +int main (string[] args) +{ + Gtk.init (ref args); + + try + { + var b = new Builder (); + b.add_from_file ("/usr/share/anonymizer/anonymizer.glade"); + b.connect_signals (null); + + var window = b.get_object ("window") as Window; + window.show_all (); + + // Console + var console_view = b.get_object ("console") as TextView; + console_view.buffer.text = "This program helps you setup network " + + "interface to use Tor as transparent proxy." + + "\nThis software is very experimental and you should only use this if " + + "you know what you are doing. \n\nEnjoy! \n\nAnd RTFM."; + + // Statusbar + var regex = new Regex ("[a-zA-Z]| "); + // Linux version + string linux_v_a = shell_sync("uname -r"); + string linux_v_b = "Linux " + linux_v_a; + // iptables version + string iptables_v_a = shell_sync(IPTABLES_BIN + " -V"); + string iptables_v_b = regex.replace (iptables_v_a, -1, 0, ""); + string iptables_v_c = "iptables " + iptables_v_b; + // Tor version + string tor_v_a = shell_sync("/usr/bin/tor --version"); + string tor_v_b = tor_v_a.substring (0, 21); + string tor_v_c = regex.replace (tor_v_b, -1, 0, ""); + string tor_v_d = "Tor " + tor_v_c; + // pkexec version + string pkexec_v_a = shell_sync("pkexec --version"); + string pkexec_v_b = regex.replace (pkexec_v_a, -1, 0, ""); + string pkexec_v_c = " pkexec " + pkexec_v_b; + string sysinfo = linux_v_b + iptables_v_c + tor_v_d + pkexec_v_c; + var alphanum = new Regex ("[^a-zA-Z0-9-.| ]"); + string sbar_text = alphanum.replace (sysinfo, -1, 0, " "); + var statusbar = b.get_object ("statusbar") as Statusbar; + var context_id = statusbar.get_context_id ("statusbar"); + statusbar.push (context_id, sbar_text); + + Gtk.main (); + + } + catch (Error e) + { + stderr.printf (e.message); + return 1; + } + return 0; +}