From f05844d8f4db938a9460f941063ec1fe077f0401 Mon Sep 17 00:00:00 2001 From: James Barnett Date: Wed, 15 Aug 2018 11:48:03 -0500 Subject: [PATCH] Refactor options handling and help printing --- .../framework/data_service/proxy/core.rb | 8 ++ .../data_service/remote/http/core.rb | 2 +- lib/msf/ui/console/command_dispatcher/db.rb | 81 +++++++++++-------- 3 files changed, 55 insertions(+), 36 deletions(-) diff --git a/lib/metasploit/framework/data_service/proxy/core.rb b/lib/metasploit/framework/data_service/proxy/core.rb index 4912a778f0..2b70741203 100644 --- a/lib/metasploit/framework/data_service/proxy/core.rb +++ b/lib/metasploit/framework/data_service/proxy/core.rb @@ -198,6 +198,7 @@ class DataProxy @error = 'disabled' end rescue => e + @error = e raise "Unable to initialize data service: #{e.message}" end end @@ -206,6 +207,13 @@ class DataProxy raise "Invalid data_service: #{data_service.class}, not of type Metasploit::Framework::DataService" unless data_service.is_a? (Metasploit::Framework::DataService) raise 'Cannot register null data service data_service' unless data_service raise 'Data Service already exists' if data_service_exist?(data_service) + # Raising an error for local DB causes startup to fail if there is a DB configured but we are unable to connect + # TODO: The check here shouldn't be dependent on if the data_service is local or not. We shouldn't + # connect to any data service if it is not online/active. This can likely be fixed by making a true + # LocalDataService instead of using DBManager. + unless data_service.is_local? + raise 'Data Service does not appear to be responding' unless data_service.active + end end def data_service_exist?(data_service) diff --git a/lib/metasploit/framework/data_service/remote/http/core.rb b/lib/metasploit/framework/data_service/remote/http/core.rb index a5c5b82267..a2a521b3d9 100644 --- a/lib/metasploit/framework/data_service/remote/http/core.rb +++ b/lib/metasploit/framework/data_service/remote/http/core.rb @@ -293,7 +293,7 @@ class RemoteHTTPDataService if @endpoint.is_a?(URI::HTTPS) http.use_ssl = true http.verify_mode = OpenSSL::SSL::VERIFY_PEER - unless @https_opts.empty? + if @https_opts && !@https_opts.empty? if @https_opts[:skip_verify] http.verify_mode = OpenSSL::SSL::VERIFY_NONE else diff --git a/lib/msf/ui/console/command_dispatcher/db.rb b/lib/msf/ui/console/command_dispatcher/db.rb index 509c7864f5..5f6ef8e8d9 100644 --- a/lib/msf/ui/console/command_dispatcher/db.rb +++ b/lib/msf/ui/console/command_dispatcher/db.rb @@ -1705,21 +1705,55 @@ class Db end def cmd_db_connect_help - # Help is specific to each driver - cmd_db_connect("-h") + print_line(" Usage: db_connect @/") + print_line(" OR: db_connect -y [path/to/database.yml]") + print_line(" OR: db_connect ://") + print_line("Examples:") + print_line(" db_connect user@metasploit3") + print_line(" db_connect user:pass@192.168.0.2/metasploit3") + print_line(" db_connect user:pass@192.168.0.2:1500/metasploit3") + print_line(" db_connect http://localhost:8080") + print_line(" db_connect -c ~/cert.pem -t 6a7a74c1a5003802c955ead1bbddd4ab1b05a7f2940b4732d34bfc555bc6e1c5d7611a497b29e8f0 https://localhost:8080") + print_line(" ") + print_line(" OPTIONS:") + print_line(" -c,--cert Certificate file matching the remote data server's certificate. Needed when using self-signed SSL cert.") + print_line(" -t,--token The API token used to authenticate to the remote data service.") + print_line(" --skip-verify Skip validating authenticity of server's certificate. NOT RECOMMENDED.") + return end def cmd_db_connect(*args) return if not db_check_driver - if args[0] =~ /http/ + opts = {} + https_opts = {} + while (arg = args.shift) + case arg + when '-h', '--help' + cmd_db_connect_help + when '-y', '--yaml' + yaml_file = args.shift + when '-c', '--cert' + https_opts[:cert] = args.shift + when '-t', '--token' + opts[:api_token] = args.shift + when '--skip-verify' + https_opts[:skip_verify] = true + else + opts[:uri] = arg + end + end + + opts[:https_opts] = https_opts unless https_opts.empty? + + if opts[:uri] =~ /http/ new_conn_type = 'http' else new_conn_type = framework.db.driver end # Currently only able to be connected to one DB at a time - if args[0] != '-h' && framework.db.connection_established? + if framework.db.connection_established? # But the http connection still requires a local database to support AR, so we have to allow that # Don't allow more than one HTTP service, though if new_conn_type != 'http' || framework.db.get_services_metadata.count >= 2 @@ -1730,12 +1764,12 @@ class Db end end - if (args[0] == "-y") - if (args[1] and not ::File.exist? ::File.expand_path(args[1])) + if yaml_file + if (yaml_file and not ::File.exist? ::File.expand_path(yaml_file)) print_error("File not found") return end - file = args[1] || ::File.join(Msf::Config.get_config_root, "database.yml") + file = yaml_file || ::File.join(Msf::Config.get_config_root, "database.yml") file = ::File.expand_path(file) if (::File.exist? file) db = YAML.load(::File.read(file))['production'] @@ -1746,7 +1780,7 @@ class Db meth = "db_connect_#{new_conn_type}" if(self.respond_to?(meth, true)) - self.send(meth, *args) + self.send(meth, opts) else print_error("This database driver #{new_conn_type} is not currently supported") end @@ -1819,18 +1853,8 @@ class Db # # Connect to an existing Postgres database # - def db_connect_postgresql(*args) - if(args[0] == nil or args[0] == "-h" or args[0] == "--help") - print_status(" Usage: db_connect @/") - print_status(" OR: db_connect -y [path/to/database.yml]") - print_status("Examples:") - print_status(" db_connect user@metasploit3") - print_status(" db_connect user:pass@192.168.0.2/metasploit3") - print_status(" db_connect user:pass@192.168.0.2:1500/metasploit3") - return - end - - info = db_parse_db_uri_postgresql(args[0]) + def db_connect_postgresql(cli_opts) + info = db_parse_db_uri_postgresql(cli_opts[:uri]) opts = { 'adapter' => 'postgresql' } opts['username'] = info[:user] if (info[:user]) @@ -1872,28 +1896,15 @@ class Db end end - def db_connect_http(*args) + def db_connect_http(opts) # local database is required to use Mdm objects unless framework.db.active print_error("No local database connected. Please connect to a local database before connecting to a remote data service.") return end - opts = { } - https_opts = { } + uri = db_parse_db_uri_http(opts[:uri]) - while (arg = args.shift) - case arg - when '-c', '--cert' - https_opts[:cert] = args.shift - when '--skip-verify' - https_opts[:skip_verify] = true - else - uri = db_parse_db_uri_http(arg) - end - end - - opts[:https_opts] = https_opts unless https_opts.empty? remote_data_service = Metasploit::Framework::DataService::RemoteHTTPDataService.new(uri.to_s, opts) begin framework.db.register_data_service(remote_data_service)