Skip to content

Commit

Permalink
Add configurable logging to the console and support stdout sinks
Browse files Browse the repository at this point in the history
  • Loading branch information
adfoster-r7 committed Dec 8, 2020
1 parent cb926e7 commit 9e20bb5
Show file tree
Hide file tree
Showing 15 changed files with 229 additions and 107 deletions.
1 change: 1 addition & 0 deletions lib/metasploit/framework/command/console.rb
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,7 @@ def driver_options
driver_options['DisableDatabase'] = options.database.disable
driver_options['HistFile'] = options.console.histfile
driver_options['LocalOutput'] = options.console.local_output
driver_options['Logger'] = options.console.logger
driver_options['ModulePath'] = options.modules.path
driver_options['Plugins'] = options.console.plugins
driver_options['RealReadline'] = options.console.real_readline
Expand Down
5 changes: 5 additions & 0 deletions lib/metasploit/framework/parsed_options/console.rb
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ def options
options.console.commands = []
options.console.confirm_exit = false
options.console.histfile = nil
options.console.logger = nil
options.console.local_output = nil
options.console.plugins = []
options.console.quiet = false
Expand Down Expand Up @@ -43,6 +44,10 @@ def option_parser
options.console.histfile = file
end

option_parser.on('-l', '--logger STRING', "Specify a logger to use (#{Rex::Logging::LogSinkFactory.available_sinks.join(', ')})") do |logger|
options.console.logger = logger
end

option_parser.on('-L', '--real-readline', 'Use the system Readline library instead of RbReadline') do
options.console.real_readline = true
end
Expand Down
13 changes: 8 additions & 5 deletions lib/msf/base/logging.rb
Original file line number Diff line number Diff line change
Expand Up @@ -16,21 +16,24 @@ class Logging

# Initialize logging.
#
# @param log_sink_name [string] Log sink name.
# @return [void]
def self.init
def self.init(log_sink_name = nil)
if (! @@initialized)
@@initialized = true

f = Rex::Logging::Sinks::Flatfile.new(
Msf::Config.log_directory + File::SEPARATOR + "framework.log")
log_sink ||= Rex::Logging::LogSinkFactory.new(
log_sink_name,
Msf::Config.log_directory + File::SEPARATOR + "framework.log"
)

# Register each known log source
[
Rex::LogSource,
Msf::LogSource,
'base',
].each { |src|
register_log_source(src, f)
register_log_source(src, log_sink)
}
end
end
Expand Down Expand Up @@ -80,7 +83,7 @@ def self.session_logging_enabled?
# @return [void]
def self.start_session_log(session)
if (log_source_registered?(session.log_source) == false)
f = Rex::Logging::Sinks::TimestampFlatfile.new(
f = Rex::Logging::Sinks::TimestampColorlessFlatfile.new(
Msf::Config.session_log_directory + File::SEPARATOR + "#{session.log_file_name}.log")

register_log_source(session.log_source, f)
Expand Down
6 changes: 5 additions & 1 deletion lib/msf/base/simple/framework.rb
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,7 @@ def self.create(opts = {})
# @option opts [#call] 'OnCreateProc' Proc to call after {#init_simplified}. Will be passed `framework`.
# @option opts [String] 'ConfigDirectory' Directory where configuration is saved. The `~/.msf4` directory.
# @option opts [Boolean] 'DisableLogging' (false) `true` to disable `Msf::Logging.init`
# @option opts [String] 'Logger' (Flatfile) Will default to logging to `~/.msf4`.
# @option opts [Boolean] 'DeferModuleLoads' (false) `true` to disable `framework.init_module_paths`.
# @return [Msf::Simple::Framework] `framework`
def self.simplify(framework, opts)
Expand All @@ -110,7 +111,10 @@ def self.simplify(framework, opts)

# Initialize configuration and logging
Msf::Config.init
Msf::Logging.init unless opts['DisableLogging']
unless opts['DisableLogging']
log_sink_name = opts['Logger']
Msf::Logging.init(log_sink_name)
end

# Load the configuration
framework.load_config
Expand Down
31 changes: 17 additions & 14 deletions lib/msf/core/web_services/framework_extension.rb
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ module Msf::WebServices
# MSF_WS_DATA_SERVICE_CERT - Certificate file matching the remote data server's certificate.
# Needed when using self-signed SSL certificates.
# MSF_WS_DATA_SERVICE_SKIP_VERIFY - (Boolean) Skip validating authenticity of server's certificate.
# MSF_WS_DATA_SERVICE_LOGGER - (String) The logger that framework will use. By default logs will be
# placed in ``~/.msf4/logs`
module FrameworkExtension
FALSE_VALUES = [nil, false, 0, '0', 'f', 'false', 'off', 'no'].to_set

Expand All @@ -37,21 +39,22 @@ def self.registered(app)
@@framework = nil
# Create simplified instance of the framework
app.set :framework, Proc.new {
@@framework ||=
begin
framework = Msf::Simple::Framework.create
@@framework ||= begin
init_framework_opts = {
'Logger' => ENV.fetch('MSF_WS_DATA_SERVICE_LOGGER', nil)
}
framework = Msf::Simple::Framework.create(init_framework_opts)

if !app.settings.data_service_url.nil? && !app.settings.data_service_url.empty?
framework_db_connect_http_data_service(framework: framework,
data_service_url: app.settings.data_service_url,
api_token: app.settings.data_service_api_token,
cert: app.settings.data_service_cert,
skip_verify: app.settings.data_service_skip_verify)
end

framework
end
if !app.settings.data_service_url.nil? && !app.settings.data_service_url.empty?
framework_db_connect_http_data_service(framework: framework,
data_service_url: app.settings.data_service_url,
api_token: app.settings.data_service_api_token,
cert: app.settings.data_service_cert,
skip_verify: app.settings.data_service_skip_verify)
end

framework
end
}
end

Expand Down Expand Up @@ -90,4 +93,4 @@ def self.to_bool(value)
!FALSE_VALUES.include?(value)
end
end
end
end
1 change: 1 addition & 0 deletions lib/rex/logging/log_dispatcher.rb
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
# -*- coding: binary -*-
require 'rex/sync'
require 'rex/logging/log_sink'
require 'rex/logging/log_sink_factory'

module Rex
module Logging
Expand Down
4 changes: 0 additions & 4 deletions lib/rex/logging/log_sink.rb
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,3 @@ def get_current_timestamp

end
end

require 'rex/logging/sinks/flatfile'
require 'rex/logging/sinks/stderr'
require 'rex/logging/sinks/timestamp_flatfile'
43 changes: 43 additions & 0 deletions lib/rex/logging/log_sink_factory.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
# -*- coding: binary -*-

require 'rex/logging/sinks/stream'
require 'rex/logging/sinks/flatfile'
require 'rex/logging/sinks/timestamp_colorless_flatfile'
require 'rex/logging/sinks/stderr'
require 'rex/logging/sinks/stdout'
require 'rex/logging/sinks/stdout_without_timestamps'

module Rex
module Logging

###
#
# LogSinkFactory can instantiate a LogSink based on the given name.
#
###
module LogSinkFactory
# Creates a new log sink of the given name. If no name is provided, a default
# Flatfile log sink is chosen
#
# @param [String] name The name of the required log sink within Rex::Logging::Sinks
# @param [Array] attrs The attributes to use with the given log sink
# @return [Rex::Logging::LogSink] The newly created log sink
def self.new(name = nil, *attrs)
name ||= Rex::Logging::Sinks::Flatfile.name.demodulize
raise NameError unless available_sinks.include?(name.to_sym)

log_sink = Rex::Logging::Sinks.const_get(name)
log_sink.new(*attrs)
rescue NameError
raise Rex::ArgumentError, "Could not find logger #{name}, expected one of #{available_sinks.join(', ')}"
end

# Returns a list of the available sinks that can be created by this factory
#
# @return [Array<Sym>] The available sinks that can be created by this factory
def self.available_sinks
Rex::Logging::Sinks.constants - [Rex::Logging::Sinks::Stream.name.demodulize.to_sym]
end
end
end
end
36 changes: 2 additions & 34 deletions lib/rex/logging/sinks/flatfile.rb
Original file line number Diff line number Diff line change
Expand Up @@ -9,48 +9,16 @@ module Sinks
# file on disk.
#
###
class Flatfile

include Rex::Logging::LogSink
class Flatfile < Rex::Logging::Sinks::Stream

#
# Creates a flatfile log sink instance that will be configured to log to
# the supplied file path.
#
def initialize(file)
self.fd = File.new(file, "a")
end

def cleanup # :nodoc:
fd.close
super(File.new(file, 'a'))
end

def log(sev, src, level, msg) # :nodoc:
if (sev == LOG_RAW)
fd.write(msg)
else
code = 'i'

case sev
when LOG_DEBUG
code = 'd'
when LOG_ERROR
code = 'e'
when LOG_INFO
code = 'i'
when LOG_WARN
code = 'w'
end
fd.write("[#{get_current_timestamp}] [#{code}(#{level})] #{src}: #{msg}\n")
end

fd.flush
end

protected

attr_accessor :fd # :nodoc:

end

end end end
31 changes: 4 additions & 27 deletions lib/rex/logging/sinks/stderr.rb
Original file line number Diff line number Diff line change
Expand Up @@ -7,38 +7,15 @@ module Sinks
#
# This class implements the LogSink interface and backs it against stderr
###
class Stderr

include Rex::Logging::LogSink
class Stderr < Rex::Logging::Sinks::Stream

#
# Writes log data to stderr
# Creates a log sink instance that will be configured to log to stderr
#

def log(sev, src, level, msg) # :nodoc:
if (sev == LOG_RAW)
$stderr.write(msg)
else
code = 'i'

case sev
when LOG_DEBUG
code = 'd'
when LOG_ERROR
code = 'e'
when LOG_INFO
code = 'i'
when LOG_WARN
code = 'w'
end
$stderr.write("[#{get_current_timestamp}] [#{code}(#{level})] #{src}: #{msg}\n")
end

$stderr.flush
def initialize(*_attrs)
super($stderr)
end

protected

end

end end end
21 changes: 21 additions & 0 deletions lib/rex/logging/sinks/stdout.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
# -*- coding: binary -*-
module Rex
module Logging
module Sinks

###
#
# This class implements the LogSink interface and backs it against stdout
###
class Stdout < Rex::Logging::Sinks::Stream

#
# Creates a log sink instance that will be configured to log to stdout
#
def initialize(*_attrs)
super($stdout)
end

end

end end end
37 changes: 37 additions & 0 deletions lib/rex/logging/sinks/stdout_without_timestamps.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
# -*- coding: binary -*-
module Rex
module Logging
module Sinks

###
#
# This class implements the LogSink interface and backs it against stdout
###
class StdoutWithoutTimestamps < Rex::Logging::Sinks::Stream

#
# Creates a log sink instance that will be configured to log to stdout
#
def initialize(*_attrs)
super($stdout)
end

#
# Writes log data to a stream
#
#
# Writes log data to a stream
#
def log(sev, src, level, msg) # :nodoc:
if sev == LOG_RAW
stream.write(msg)
else
stream.write("[#{log_code_for(sev)}(#{level})] #{src}: #{msg}\n")
end

stream.flush
end

end

end end end
Loading

0 comments on commit 9e20bb5

Please sign in to comment.