Class: Msf::DataStore
- Inherits:
-
Object
- Object
- Msf::DataStore
- Defined in:
- lib/msf/core/data_store.rb
Overview
The data store is just a bitbucket that holds keyed values. It is used by various classes to hold option values and other state information.
Direct Known Subclasses
Defined Under Namespace
Classes: DataStoreSearchResult
Constant Summary collapse
- GLOBAL_OPTION_DEFINITIONS =
Typed option definitions for globally-scoped datastore keys. These are registered on the framework datastore at startup so that normalization (e.g. OptBool converting “true” to true) applies even when no module is active.
OptionContainer.new([ OptBool.new('ConsoleLogging', [false, 'Log all console input and output', false]), OptInt.new('LogLevel', [false, 'Verbosity of logs (default 0, max 3)', 0]), OptEnum.new('MinimumRank', [false, 'The minimum rank of exploits that will run without explicit confirmation', 'manual', RankingName.values]), OptBool.new('SessionLogging', [false, 'Log all input and output for sessions', false]), OptBool.new('TimestampOutput', [false, 'Prefix all console output with a timestamp', false]), OptBool.new('VERBOSE', [false, 'Enable detailed status messages - the specific behavior can differ per module', false]), OptString.new('Prompt', [false, 'The prompt string', nil]), OptString.new('PromptChar', [false, 'The prompt character', nil]), OptString.new('PromptTimeFormat', [false, 'Format for timestamp escapes in prompts', nil]), OptString.new('MeterpreterPrompt', [false, 'The meterpreter prompt string', nil]), OptSessionTlvLogging.new('SessionTlvLogging', [false, 'Log all incoming and outgoing TLV packets', nil]), ])
- GLOBAL_KEYS =
Backward-compatible list of known global key names, derived from the typed option definitions above.
GLOBAL_OPTION_DEFINITIONS.keys.freeze
Instance Attribute Summary collapse
-
#aliases ⇒ Hash{String => String}
protected
The key is the old option name, the value is the new option name.
-
#defaults ⇒ Hash{String => Msf::OptBase}
protected
These defaults will be used if the user has not explicitly defined a specific datastore value.
-
#options ⇒ Hash{String => Msf::OptBase}
The options associated with this datastore.
-
#user_defined ⇒ Hash<String, Object>
Returns a hash of user-defined datastore values.
Instance Method Summary collapse
-
#[](k) ⇒ Object
Case-insensitive wrapper around hash lookup.
-
#[]=(k, v) ⇒ Object
Clears the imported flag for the supplied key since it’s being set directly.
-
#clear ⇒ Object
Completely clear all values in the data store.
-
#copy ⇒ Msf::DataStore
Return a copy of this datastore.
-
#copy_state(other) ⇒ Msf::DataStore
protected
Copy the state from the other Msf::DataStore.
-
#default?(key) ⇒ TrueClass, FalseClass
Was this entry actually set or just using its default.
-
#delete(key) ⇒ Object
deprecated
Deprecated.
use ##unset instead, or set the value explicitly to nil
-
#each(&block) ⇒ Object
(also: #each_pair)
Overrides the builtin ‘each’ operator to avoid the following exception on Ruby 1.9.2+ “can’t add a new key into hash during iteration”.
- #each_key(&block) ⇒ Object
-
#find_key_case(k) ⇒ String
Case-insensitive key lookup.
-
#from_file(path, name = 'global') ⇒ Object
Imports datastore values from the specified file path using the supplied name.
-
#import_defaults_from_hash(hash, imported_by:) ⇒ nil
Update defaults from a hash.
-
#import_option(key, val, imported = true, imported_by = nil, option = nil) ⇒ Object
deprecated
Deprecated.
Use #import_options
-
#import_options(options, imported_by = nil, overwrite = true) ⇒ Object
This method is a helper method that imports the default value for all of the supplied options.
-
#import_options_from_hash(option_hash, imported = true, imported_by = nil) ⇒ nil
deprecated
Deprecated.
use #merge! instead
-
#import_options_from_s(option_str, delim = nil) ⇒ Object
Imports option values from a whitespace separated string in VAR=VAL format.
-
#initialize ⇒ DataStore
constructor
Initializes the data store’s internal state.
-
#key?(key) ⇒ TrueClass, FalseClass
(also: #has_key?, #include?, #member?)
True if the key is present in the user defined values, or within registered options.
-
#key_error_for(key) ⇒ Object
protected
Raised when the specified key is not found.
-
#keys ⇒ Array<String>
The array of user defined datastore values, and registered option names.
-
#length ⇒ Integer
(also: #count, #size)
The length of the registered keys.
-
#merge(other) ⇒ Object
Override merge to ensure we merge the aliases and imported hashes.
-
#merge!(other) ⇒ Object
(also: #update)
Merge the other object into the current datastore’s aliases and imported hashes.
-
#remove_option(name) ⇒ nil
Removes an option and any associated value.
-
#reverse_merge!(other) ⇒ Object
Reverse Merge the other object into the current datastore’s aliases and imported hashes Equivalent to ActiveSupport’s reverse_merge! functionality.
-
#search_for(key) ⇒ DataStoreSearchResult
Search for a value within the current datastore, taking into consideration any registered aliases, fallbacks, etc.
- #search_result(result, value, fallback_key: nil) ⇒ Object protected
-
#store(k, v) ⇒ Object
Case-insensitive wrapper around store; Skips option validation entirely.
-
#to_external_message_h ⇒ Object
Hack on a hack for the external modules.
-
#to_file(path, name = 'global') ⇒ Object
Persists the contents of the data store to a file.
-
#to_h ⇒ Object
Override Hash’s to_h method so we can include the original case of each key (failing to do this breaks a number of places in framework and pro that use serialized datastores).
-
#to_s(delim = ' ') ⇒ Object
Serializes the options in the datastore to a string.
-
#unset(key) ⇒ Object
unset the current key from the datastore.
-
#update_value(k, v) ⇒ Object
Updates a value in the datastore with the specified name, k, to the specified value, v.
Constructor Details
#initialize ⇒ DataStore
Initializes the data store’s internal state.
37 38 39 40 41 42 43 44 45 46 |
# File 'lib/msf/core/data_store.rb', line 37 def initialize @options = Hash.new @aliases = Hash.new # default values which will be referenced when not defined by the user @defaults = Hash.new # values explicitly defined, which take precedence over default values @user_defined = Hash.new end |
Instance Attribute Details
#aliases ⇒ Hash{String => String} (protected)
Returns The key is the old option name, the value is the new option name.
481 482 483 |
# File 'lib/msf/core/data_store.rb', line 481 def aliases @aliases end |
#defaults ⇒ Hash{String => Msf::OptBase} (protected)
These defaults will be used if the user has not explicitly defined a specific datastore value. These will be checked as a priority to any options that also provide defaults.
478 479 480 |
# File 'lib/msf/core/data_store.rb', line 478 def defaults @defaults end |
#options ⇒ Hash{String => Msf::OptBase}
Returns The options associated with this datastore. Used for validating values/defaults/etc.
49 50 51 |
# File 'lib/msf/core/data_store.rb', line 49 def @options end |
#user_defined ⇒ Hash<String, Object>
Returns a hash of user-defined datastore values. The returned hash does not include default option values.
56 57 58 |
# File 'lib/msf/core/data_store.rb', line 56 def user_defined @user_defined end |
Instance Method Details
#[](k) ⇒ Object
Case-insensitive wrapper around hash lookup
89 90 91 92 93 |
# File 'lib/msf/core/data_store.rb', line 89 def [](k) search_result = search_for(k) search_result.value end |
#[]=(k, v) ⇒ Object
Clears the imported flag for the supplied key since it’s being set directly.
70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 |
# File 'lib/msf/core/data_store.rb', line 70 def []=(k, v) k = find_key_case(k) opt = @options[k] unless opt.nil? if opt.validate_on_assignment? unless opt.valid?(v, check_empty: false) raise Msf::OptionValidateError.new(["Value '#{v}' is not valid for option '#{k}'"]) end v = opt.normalize(v) end end @user_defined[k] = v end |
#clear ⇒ Object
Completely clear all values in the data store
389 390 391 392 393 394 395 396 |
# File 'lib/msf/core/data_store.rb', line 389 def clear self..clear self.aliases.clear self.defaults.clear self.user_defined.clear self end |
#copy ⇒ Msf::DataStore
Return a copy of this datastore. Only string values will be duplicated, other values will share the same reference
337 338 339 340 341 |
# File 'lib/msf/core/data_store.rb', line 337 def copy new_instance = self.class.new new_instance.copy_state(self) new_instance end |
#copy_state(other) ⇒ Msf::DataStore (protected)
Copy the state from the other Msf::DataStore. The state will be coped in a shallow fashion, other than imported and user_defined strings.
489 490 491 492 493 494 495 496 |
# File 'lib/msf/core/data_store.rb', line 489 def copy_state(other) self. = other..dup self.aliases = other.aliases.dup self.defaults = other.defaults.transform_values { |value| value.kind_of?(String) ? value.dup : value } self.user_defined = other.user_defined.transform_values { |value| value.kind_of?(String) ? value.dup : value } self end |
#default?(key) ⇒ TrueClass, FalseClass
Was this entry actually set or just using its default
62 63 64 |
# File 'lib/msf/core/data_store.rb', line 62 def default?(key) search_for(key).default? end |
#delete(key) ⇒ Object
use ##unset instead, or set the value explicitly to nil
123 124 125 |
# File 'lib/msf/core/data_store.rb', line 123 def delete(key) unset(key) end |
#each(&block) ⇒ Object Also known as: each_pair
Overrides the builtin ‘each’ operator to avoid the following exception on Ruby 1.9.2+
"can't add a new key into hash during iteration"
402 403 404 405 406 407 408 |
# File 'lib/msf/core/data_store.rb', line 402 def each(&block) list = [] self.keys.sort.each do |sidx| list << [sidx, self[sidx]] end list.each(&block) end |
#each_key(&block) ⇒ Object
412 413 414 |
# File 'lib/msf/core/data_store.rb', line 412 def each_key(&block) self.keys.each(&block) end |
#find_key_case(k) ⇒ String
Case-insensitive key lookup
420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 |
# File 'lib/msf/core/data_store.rb', line 420 def find_key_case(k) # Scan each alias looking for a key search_k = k.downcase if self.aliases.has_key?(search_k) search_k = self.aliases[search_k] end # Check to see if we have an exact key match - otherwise we'll have to search manually to check case sensitivity if @user_defined.key?(search_k) || .key?(search_k) return search_k end # Scan each key looking for a match each_key do |rk| if rk.casecmp(search_k) == 0 return rk end end # Fall through to the non-existent value k end |
#from_file(path, name = 'global') ⇒ Object
Imports datastore values from the specified file path using the supplied name
321 322 323 324 325 326 327 328 329 330 331 |
# File 'lib/msf/core/data_store.rb', line 321 def from_file(path, name = 'global') begin ini = Rex::Parser::Ini.from_file(path) rescue return end if ini.group?(name) merge!(ini[name]) end end |
#import_defaults_from_hash(hash, imported_by:) ⇒ nil
Update defaults from a hash. These merged values are not validated by default.
212 213 214 |
# File 'lib/msf/core/data_store.rb', line 212 def import_defaults_from_hash(hash, imported_by:) @defaults.merge!(hash) end |
#import_option(key, val, imported = true, imported_by = nil, option = nil) ⇒ Object
Use #import_options
TODO: Doesn’t normalize data in the same vein as: github.com/rapid7/metasploit-framework/pull/6644
219 220 221 222 223 224 225 226 227 228 |
# File 'lib/msf/core/data_store.rb', line 219 def import_option(key, val, imported = true, imported_by = nil, option = nil) store(key, val) if option option.aliases.each do |a| @aliases[a.downcase] = key.downcase end end @options[key] = option end |
#import_options(options, imported_by = nil, overwrite = true) ⇒ Object
This method is a helper method that imports the default value for all of the supplied options.
145 146 147 148 149 150 151 152 153 154 155 |
# File 'lib/msf/core/data_store.rb', line 145 def (, imported_by = nil, overwrite = true) .each_option do |name, option| if self.[name].nil? || overwrite key = name option.aliases.each do |a| @aliases[a.downcase] = key.downcase end @options[key] = option end end end |
#import_options_from_hash(option_hash, imported = true, imported_by = nil) ⇒ nil
use #merge! instead
Imports values from a hash and stores them in the datastore.
203 204 205 |
# File 'lib/msf/core/data_store.rb', line 203 def (option_hash, imported = true, imported_by = nil) merge!(option_hash) end |
#import_options_from_s(option_str, delim = nil) ⇒ Object
Imports option values from a whitespace separated string in VAR=VAL format.
161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 |
# File 'lib/msf/core/data_store.rb', line 161 def (option_str, delim = nil) hash = {} # Figure out the delimiter, default to space. if (delim.nil?) delim = /\s/ if (option_str.split('=').length <= 2 or option_str.index(',') != nil) delim = ',' end end # Split on the delimiter option_str.split(delim).each { |opt| var, val = opt.split('=', 2) next if (var =~ /^\s+$/) # Invalid parse? Raise an exception and let those bastards know. if (var == nil or val == nil) var = "unknown" if (!var) raise Rex::ArgumentParseError, "Invalid option specified: #{var}", caller end # Remove trailing whitespaces from the value val.gsub!(/\s+$/, '') # Store the value hash[var] = val } merge!(hash) end |
#key?(key) ⇒ TrueClass, FalseClass Also known as: has_key?, include?, member?
Returns True if the key is present in the user defined values, or within registered options. False otherwise.
245 246 247 248 |
# File 'lib/msf/core/data_store.rb', line 245 def key?(key) matching_key = find_key_case(key) keys.include?(matching_key) end |
#key_error_for(key) ⇒ Object (protected)
Raised when the specified key is not found
500 501 502 |
# File 'lib/msf/core/data_store.rb', line 500 def key_error_for(key) ::KeyError.new "key not found: #{key.inspect}" end |
#keys ⇒ Array<String>
Returns The array of user defined datastore values, and registered option names.
231 232 233 |
# File 'lib/msf/core/data_store.rb', line 231 def keys (@user_defined.keys + @options.keys).uniq(&:downcase) end |
#length ⇒ Integer Also known as: count, size
Returns The length of the registered keys.
236 237 238 |
# File 'lib/msf/core/data_store.rb', line 236 def length keys.length end |
#merge(other) ⇒ Object
Override merge to ensure we merge the aliases and imported hashes
381 382 383 384 |
# File 'lib/msf/core/data_store.rb', line 381 def merge(other) ds = self.copy ds.merge!(other) end |
#merge!(other) ⇒ Object Also known as: update
Merge the other object into the current datastore’s aliases and imported hashes
347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 |
# File 'lib/msf/core/data_store.rb', line 347 def merge!(other) if other.is_a?(DataStore) self.aliases.merge!(other.aliases) self..merge!(other.) self.defaults.merge!(other.defaults) other.user_defined.each do |k, v| @user_defined[find_key_case(k)] = v end else other.each do |k, v| self[k] = v end end self end |
#remove_option(name) ⇒ nil
Removes an option and any associated value
132 133 134 135 136 137 138 139 |
# File 'lib/msf/core/data_store.rb', line 132 def remove_option(name) k = find_key_case(name) @user_defined.delete(k) @aliases.delete_if { |_, v| v.casecmp?(k) } @options.delete_if { |option_name, _v| option_name.casecmp?(k) || option_name.casecmp?(name) } nil end |
#reverse_merge!(other) ⇒ Object
Reverse Merge the other object into the current datastore’s aliases and imported hashes Equivalent to ActiveSupport’s reverse_merge! functionality.
371 372 373 374 375 |
# File 'lib/msf/core/data_store.rb', line 371 def reverse_merge!(other) raise ArgumentError, "invalid error type #{other.class}, expected ::Msf::DataStore" unless other.is_a?(Msf::DataStore) copy_state(other.merge(self)) end |
#search_for(key) ⇒ DataStoreSearchResult
Search for a value within the current datastore, taking into consideration any registered aliases, fallbacks, etc.
447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 |
# File 'lib/msf/core/data_store.rb', line 447 def search_for(key) k = find_key_case(key) return search_result(:user_defined, @user_defined[k]) if @user_defined.key?(k) option = @options.fetch(k) { @options.find { |option_name, _option| option_name.casecmp?(k) }&.last } if option # If the key isn't present - check any additional fallbacks that have been registered with the option. # i.e. handling the scenario of SMBUser not being explicitly set, but the option has registered a more # generic 'Username' fallback option.fallbacks.each do |fallback| fallback_search = search_for(fallback) if fallback_search.found? return search_result(:option_fallback, fallback_search.value, fallback_key: fallback) end end end # Checking for imported default values, ignoring case again imported_default_match = @defaults.find { |default_key, _default_value| default_key.casecmp?(k) } return search_result(:imported_default, imported_default_match.last) if imported_default_match return search_result(:option_default, option.default) if option search_result(:not_found, nil) end |
#search_result(result, value, fallback_key: nil) ⇒ Object (protected)
546 547 548 |
# File 'lib/msf/core/data_store.rb', line 546 def search_result(result, value, fallback_key: nil) DataStoreSearchResult.new(result, value, namespace: :global_data_store, fallback_key: fallback_key) end |
#store(k, v) ⇒ Object
Case-insensitive wrapper around store; Skips option validation entirely
98 99 100 |
# File 'lib/msf/core/data_store.rb', line 98 def store(k,v) @user_defined[find_key_case(k)] = v end |
#to_external_message_h ⇒ Object
Hack on a hack for the external modules
279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 |
# File 'lib/msf/core/data_store.rb', line 279 def datastore_hash = {} array_nester = ->(arr) do if arr.first.is_a? Array arr.map &array_nester else arr.map { |item| item.to_s.dup.force_encoding('UTF-8') } end end self.keys.each do |k| # TODO arbitrary depth if self[k].is_a? Array datastore_hash[k.to_s.dup.force_encoding('UTF-8')] = array_nester.call(self[k]) else datastore_hash[k.to_s.dup.force_encoding('UTF-8')] = self[k].to_s.dup.force_encoding('UTF-8') end end datastore_hash end |
#to_file(path, name = 'global') ⇒ Object
Persists the contents of the data store to a file
304 305 306 307 308 309 310 311 312 313 314 315 |
# File 'lib/msf/core/data_store.rb', line 304 def to_file(path, name = 'global') ini = Rex::Parser::Ini.new(path) ini.add_group(name) # Save all user-defined options to the file. @user_defined.each_pair { |k, v| ini[name][k] = v } ini.to_file(path) end |
#to_h ⇒ Object
Override Hash’s to_h method so we can include the original case of each key (failing to do this breaks a number of places in framework and pro that use serialized datastores)
270 271 272 273 274 275 276 |
# File 'lib/msf/core/data_store.rb', line 270 def to_h datastore_hash = {} self.keys.each do |k| datastore_hash[k.to_s] = self[k] end datastore_hash end |
#to_s(delim = ' ') ⇒ Object
Serializes the options in the datastore to a string.
257 258 259 260 261 262 263 264 265 |
# File 'lib/msf/core/data_store.rb', line 257 def to_s(delim = ' ') str = '' keys.sort.each { |key| str << "#{key}=#{self[key]}" + ((str.length) ? delim : '') } str end |
#unset(key) ⇒ Object
unset the current key from the datastore
113 114 115 116 117 118 119 |
# File 'lib/msf/core/data_store.rb', line 113 def unset(key) k = find_key_case(key) search_result = search_for(k) @user_defined.delete(k) search_result.value end |
#update_value(k, v) ⇒ Object
Updates a value in the datastore with the specified name, k, to the specified value, v. Skips option validation entirely.
106 107 108 |
# File 'lib/msf/core/data_store.rb', line 106 def update_value(k, v) store(k, v) end |