diff --git a/lib/Gemfile b/lib/Gemfile index caf13ec..656bfe1 100644 --- a/lib/Gemfile +++ b/lib/Gemfile @@ -32,3 +32,11 @@ gem 'securerandom', '~> 0.2.2' gem 'dotenv', '~> 2.8' gem "tracer", "~> 0.2.2" + +gem "flay", "~> 2.13" + +gem "pcaprub", "~> 0.13.1" + +gem "packetfu", "~> 2.0" + +gem "sudo", "~> 0.2.0" diff --git a/lib/Gemfile.lock b/lib/Gemfile.lock index 28be0ae..d286094 100644 --- a/lib/Gemfile.lock +++ b/lib/Gemfile.lock @@ -23,9 +23,15 @@ GEM dynamic_curses_input (1.0.0) curses e2mmap (0.1.0) + erubi (1.12.0) fiber-annotation (0.2.0) fiber-local (1.0.0) fiddle (1.1.1) + flay (2.13.0) + erubi (~> 1.10) + path_expander (~> 1.0) + ruby_parser (~> 3.0) + sexp_processor (~> 4.0) gdk3 (4.1.7) cairo-gobject (= 4.1.7) gdk_pixbuf2 (= 4.1.7) @@ -56,6 +62,8 @@ GEM nokogiri (1.15.2-x86_64-linux) racc (~> 1.4) openssl (3.1.0) + packetfu (2.0.0) + pcaprub (~> 0.13.1) pango (4.1.7) cairo-gobject (= 4.1.7) gobject-introspection (= 4.1.7) @@ -63,6 +71,8 @@ GEM parser (3.2.2.3) ast (~> 2.4.1) racc + path_expander (1.1.1) + pcaprub (0.13.1) pkg-config (1.5.1) racc (1.7.0) rainbow (3.1.1) @@ -90,8 +100,11 @@ GEM rubocop-ast (1.29.0) parser (>= 3.2.1.0) ruby-progressbar (1.13.0) + ruby_parser (3.20.2) + sexp_processor (~> 4.16) securerandom (0.2.2) sequel (5.69.0) + sexp_processor (4.17.0) solargraph (0.49.0) backport (~> 1.2) benchmark @@ -108,6 +121,7 @@ GEM thor (~> 1.0) tilt (~> 2.0) yard (~> 0.9, >= 0.9.24) + sudo (0.2.0) thor (1.2.2) tilt (2.2.0) tracer (0.2.2) @@ -124,14 +138,18 @@ DEPENDENCIES curses (~> 1.4) dotenv (~> 2.8) dynamic_curses_input (~> 1.0) + flay (~> 2.13) gtk3 (~> 4.1) mysql2 (~> 0.5.5) openssl (~> 3.1) + packetfu (~> 2.0) + pcaprub (~> 0.13.1) reek (~> 6.1) rubocop (~> 1.52) securerandom (~> 0.2.2) sequel (~> 5.69) solargraph (~> 0.49.0) + sudo (~> 0.2.0) tracer (~> 0.2.2) yaml (~> 0.2.1) diff --git a/lib/utils/networking_genie.rb b/lib/utils/networking_genie.rb new file mode 100644 index 0000000..d5d9b58 --- /dev/null +++ b/lib/utils/networking_genie.rb @@ -0,0 +1,37 @@ +# frozen_string_literal: true + +require 'socket' +require_relative 'logg_man' + +# The class for setting up all the necessary system networking stuff for NETRAVE to work with without +# interferring with the rest of the system +class NetworkingGenie + def initialize(logger) + @loggman = logger + end + + def find_main_interface # rubocop:disable Metrics/MethodLength + @loggman.log_info('Identifying main network interface...') + route_info = `routel`.split("\n") + default_route = route_info.find { |line| line.include?('default') } + if default_route + main_interface = default_route.split.last + @loggman.log_info("Main network interface identified: #{main_interface}") + main_interface + else + @loggman.log_error('Failed to identify main network interface.') + nil + end + rescue StandardError => e + @loggman.log_error("Error occurred while identifying main network interface: #{e.message}") + nil + end + + def create_dummy_interface + # TODO: Implement method to create a dummy network interface + end + + def setup_traffic_mirroring + # TODO: Implement method to set up traffic mirroring from the main interface to the dummy interface + end +end diff --git a/lib/utils/packet_capture.rb b/lib/utils/packet_capture.rb new file mode 100644 index 0000000..413eb82 --- /dev/null +++ b/lib/utils/packet_capture.rb @@ -0,0 +1,47 @@ +# frozen_string_literal: true + +require 'pcaprub' +require 'socket' +require_relative 'databasemanager' +require_relative 'logg_man' +require_relative 'redis_queue' + +# Class used to capture packets and not much else +class PacketCapture + INTERFACE_NAME = 'netrave0' + + def initialize(queue, logger) + @loggman = logger + @loggman.log_info("Initializing packet capture for #{INTERFACE_NAME}...") + @capture = Pcap.open_live(INTERFACE_NAME, 65_535, true, 1) + @capture.setfilter('') + @loggman.log_info('Packet capture initialized successfully!') + @queue = queue + end + + def start_capture_loop # rubocop:disable Metrics/MethodLength + @loggman.log_info("Starting packet capture loop for #{@interface}...") + packet_count = 0 + begin + @loggman.log_info("Packet capture loop started for #{@interface}...") + @capture.each_packet do |packet| + # Add packet to queue + @queue.push(packet) + @loggman.log_info("Packet #{packet_count += 1} added to queue.") + end + rescue StopIteration + @loggman.log_warn("Packet capture loop stopped for #{@interface}.") + rescue StandardError => e + @loggman.log_fatal("Packet capture loop stopped for #{@interface}: #{e.message}\n#{e.backtrace}", false) + sleep 1 + retry + ensure + @capture.close + end + end + + def stop_capture + @loggman.log_warn("Stopping packet capture loop for #{@interface}...") + @stop_flag = true + end +end diff --git a/lib/utils/redis_queue.rb b/lib/utils/redis_queue.rb new file mode 100644 index 0000000..2c54641 --- /dev/null +++ b/lib/utils/redis_queue.rb @@ -0,0 +1,8 @@ +# frozen_string_literal: true + +# Class for managing the worker queue in Redis +class RedisQueueManager + def initialize(logger) + @loggman = logger + end +end diff --git a/lib/utils/system_information_gather.rb b/lib/utils/system_information_gather.rb index 8aae1aa..9d0efbf 100644 --- a/lib/utils/system_information_gather.rb +++ b/lib/utils/system_information_gather.rb @@ -38,7 +38,7 @@ class SystemInformationGather def ask_for_uplink_speed # rubocop:disable Metrics/MethodLength loop do Curses.clear - Curses.addstr("Please enter your uplink speed (upload speed, e.g., 1000Mbps or 1Gbps).\n" \ + Curses.addstr("Please enter your uplink speed (upload speed, e.g., 150Mbps or 1Gbps).\n" \ "This is typically the maximum upload speed provided by your ISP.\n" \ "You can check your ISP bill, use an online speed test, or contact your ISP if you're unsure.\n\n") Curses.refresh diff --git a/lib/utils/utilities.rb b/lib/utils/utilities.rb index 33fb94a..2ce7838 100644 --- a/lib/utils/utilities.rb +++ b/lib/utils/utilities.rb @@ -4,6 +4,7 @@ require 'securerandom' require 'digest' require 'base64' require 'openssl' +require 'sudo' # Utiltiies Module module Utilities @@ -49,8 +50,7 @@ module Utilities Base64.encode64(encrypted_data).chomp rescue OpenSSL::Cipher::CipherError => e - loggman = LoggMan.new - loggman.log_error("Failed to encrypt data: #{e.message}") + @loggman.log_error("Failed to encrypt data: #{e.message}") nil end @@ -67,13 +67,11 @@ module Utilities if decrypted_data.valid_encoding? decrypted_data else - loggman = LoggMan.new - loggman.log_error("Decrypted data is not valid ASCII: #{decrypted_data.inspect}") + @loggman.log_error("Decrypted data is not valid ASCII: #{decrypted_data.inspect}") nil end rescue OpenSSL::Cipher::CipherError => e - loggman = LoggMan.new - loggman.log_error("Failed to decrypt data: #{e.message}") + @loggman.log_error("Failed to decrypt data: #{e.message}") nil end @@ -104,4 +102,43 @@ module Utilities Curses.attroff(Curses.color_pair(3)) if severity == :warning Curses.attroff(Curses.color_pair(2)) if severity == :error end + + def ask_for_sudo(logger) + @loggman = logger + @loggman.log_info('Asking for sudo password...') + Curses.addstr('Please enter your sudo password: ') + sudo_password = DCI.catch_input(true) + @loggman.log_info('Sudo password received.') + sudo_password + end + + def test_and_deescalate_sudo(sudo_password) + # Run a simple command with sudo privileges + Sudo::Wrapper.run('ls', password: sudo_password) + + # Invalidate the user's cached credentials + Sudo::Wrapper.run('sudo -k', password: sudo_password) + + # Encrypt the sudo password and store it in an environment variable + encrypted_sudo_password = encrypt_string_chacha20(sudo_password, @secret_key) + ENV['SPW'] = encrypted_sudo_password + + true + rescue Sudo::Wrapper::InvalidPassword + false + end + + def clear_sudo_password + # Retrieve the encrypted sudo password from the environment variable + encrypted_sudo_password = ENV['SPW'] + + # Decrypt the sudo password + sudo_password = decrypt_string_chacha20(encrypted_sudo_password, @secret_key) + + # Clear the sudo password from memory + sudo_password.replace(' ' * sudo_password.length) + + # Remove the encrypted sudo password from the environment variables + ENV.delete('SPW') + end end