boris 1.0.1 → 1.0.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/CHANGELOG.md +9 -0
- data/README.md +92 -57
- data/lib/boris.rb +1 -0
- data/lib/boris/connectors.rb +2 -0
- data/lib/boris/connectors/snmp.rb +1 -1
- data/lib/boris/connectors/ssh.rb +24 -6
- data/lib/boris/connectors/wmi.rb +74 -27
- data/lib/boris/errors.rb +9 -5
- data/lib/boris/helpers.rb +7 -0
- data/lib/boris/helpers/array.rb +4 -4
- data/lib/boris/helpers/constants.rb +11 -2
- data/lib/boris/helpers/hash.rb +3 -3
- data/lib/boris/helpers/scrubber.rb +21 -14
- data/lib/boris/helpers/string.rb +31 -30
- data/lib/boris/helpers/time.rb +5 -0
- data/lib/boris/lumberjack.rb +2 -2
- data/lib/boris/options.rb +1 -1
- data/lib/boris/profiler.rb +4 -5
- data/lib/boris/profilers/linux_core.rb +3 -3
- data/lib/boris/profilers/unix/solaris.rb +12 -8
- data/lib/boris/profilers/windows_core.rb +42 -17
- data/lib/boris/target.rb +14 -14
- data/lib/boris/version.rb +1 -1
- metadata +21 -32
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 5f54a0bce9420195098ba5d362bc8ba592de3b2e
|
4
|
+
data.tar.gz: 6e798f3e9814cd293b3f9a3ea5bceb606be21ffa
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 4bbc75ecf7d833216e5dc6d8a1501ff243f489c50905a3c3f45493f49da1c1f27d20dcf9bfb122e2304aaf4d10eb72ab13f1438413dfbb38d6966a07a922758b
|
7
|
+
data.tar.gz: d06c12bf5cfa6b2be0d17ecfae0957d78305f05b01bed97759edb208ba39f37a8b3fe1f8eba72bb2d2f01e4b000513e1ce65c7b3504491a005d05ad52ad93803
|
data/CHANGELOG.md
CHANGED
@@ -1,3 +1,12 @@
|
|
1
|
+
# 1.0.2
|
2
|
+
* Fix for devices asking for password when connecting via SSH
|
3
|
+
* Fixed return value for Target#connect
|
4
|
+
* Added #failure_message to connectors
|
5
|
+
* Added registry subkey and value caching for WMI connections
|
6
|
+
* Added constants for connection failures, changed logging messages for connectors
|
7
|
+
* Changed all eval calls to Object.send
|
8
|
+
* Various bug fixes
|
9
|
+
|
1
10
|
# 1.0.1
|
2
11
|
* Renamed Profiles to Profilers
|
3
12
|
* Moved networking helper methods to newly created Network module
|
data/README.md
CHANGED
@@ -19,36 +19,35 @@ Out of the box, Boris has server support for Red Hat, Solaris,and Windows (with
|
|
19
19
|
## Installation
|
20
20
|
gem install boris
|
21
21
|
|
22
|
+
Or if using Bundler, add to your Gemfile
|
23
|
+
|
24
|
+
gem 'boris'
|
25
|
+
|
22
26
|
## Example
|
23
27
|
Let's pull some information from a RedHat Enterprise Linux server on our network:
|
24
28
|
|
25
29
|
```ruby
|
26
30
|
require 'boris'
|
27
31
|
|
28
|
-
# Boris has different levels of logging. We can optionally set our logging level, which will apply
|
29
|
-
# to all Targets created during this session. If not set, the log level defaults to :fatal.
|
30
32
|
Boris.log_level = :debug
|
31
33
|
|
32
34
|
hostname = 'redhatserver01.mydomain.com'
|
33
35
|
|
34
|
-
# let's use a helper to suggest how we should connect to it (
|
35
|
-
# kind of device this is)
|
36
|
+
# let's use a helper to suggest how we should connect to it (if we're not sure what kind of device this is)
|
36
37
|
puts Boris::Network.suggested_connection_method(hostname)
|
37
38
|
|
38
|
-
# you can also add the logic to make the decision yourself
|
39
|
+
# you can also add the logic to make the decision yourself
|
39
40
|
puts Boris::Network.tcp_port_responding?(hostname, 22)
|
40
41
|
|
41
|
-
# create our target
|
42
42
|
target = Boris::Target.new(hostname)
|
43
43
|
|
44
|
-
# add credentials to try against this target
|
45
44
|
target.options.add_credential(:user=>'myusername', :password=>'mypassword', :connection_types=>[:ssh])
|
46
45
|
|
47
|
-
# if this is a host using SSH, we can also pass in Net::SSH options (such as a private key for
|
48
|
-
# SSH options passed to Boris will automatically be
|
46
|
+
# if this is a host using SSH, we can also pass in Net::SSH options (such as a private key for
|
47
|
+
# authentication). SSH options passed to Boris will automatically be passed to Net:SSH. Likewise for
|
48
|
+
# Net::SNMP--options passed to :snmp_options will be passed to the Net::SNMP library.
|
49
49
|
target.options[:ssh_options] = {:keys=>['/path/to/my/private/key']}
|
50
50
|
|
51
|
-
# attempt to connect to this target using the credentials we supplied above
|
52
51
|
target.connect
|
53
52
|
|
54
53
|
if target.connected?
|
@@ -60,9 +59,7 @@ if target.connected?
|
|
60
59
|
|
61
60
|
# we can call individual methods to grab specific information we may be interested in
|
62
61
|
target.get(:hardware)
|
63
|
-
|
64
|
-
# or maybe get some network interface info
|
65
|
-
puts target.get(:network_interfaces)
|
62
|
+
target.get(:network_interfaces)
|
66
63
|
|
67
64
|
# retrieved items can be referenced two ways:
|
68
65
|
puts target[:network_interfaces].inspect
|
@@ -73,10 +70,12 @@ if target.connected?
|
|
73
70
|
target.retrieve_all
|
74
71
|
|
75
72
|
# if there is more information we want to collect but is not collected by default, we can specify
|
76
|
-
# our own commands to run against the target via two methods:
|
77
|
-
|
78
|
-
#
|
73
|
+
# our own commands to run against the target via two methods:
|
74
|
+
|
75
|
+
# #get_values returns an Array (each line is an element of the array)
|
79
76
|
puts target.connector.values_at('cat /etc/redhat-release')
|
77
|
+
|
78
|
+
# #get_value, which returns a String (the first line returned from the command)
|
80
79
|
puts target.connector.value_at('uname -a')
|
81
80
|
|
82
81
|
# NOTE: if this were a Windows server, you would send WMI queries instead of shell commands, ie:
|
@@ -84,14 +83,81 @@ if target.connected?
|
|
84
83
|
# target.connector.values_at('SELECT * FROM Win32_ComputerSystem')
|
85
84
|
#
|
86
85
|
|
87
|
-
# finally, we can package up all of the data into json format for portability (the true argument
|
88
|
-
# tells the #to_json method to output the json with tabbed formatting)
|
89
86
|
puts target.to_json(:pretty_print)
|
90
87
|
|
91
88
|
target.disconnect
|
92
89
|
end
|
93
90
|
```
|
94
91
|
|
92
|
+
## Sample Output
|
93
|
+
```ruby
|
94
|
+
target.get(:hardware)
|
95
|
+
target.get(:operating_system)
|
96
|
+
|
97
|
+
target.scrub_data!
|
98
|
+
|
99
|
+
puts target[:hardware]
|
100
|
+
#=>{
|
101
|
+
# :cpu_architecture=>64,
|
102
|
+
# :cpu_core_count=>2,
|
103
|
+
# :cpu_model=>'AMD Opteron Processor 6174',
|
104
|
+
# :cpu_physical_count=>1,
|
105
|
+
# :cpu_speed_mhz=>2200,
|
106
|
+
# :cpu_vendor=>'AMD, Inc.',
|
107
|
+
# :firmware_version=>'6.0',
|
108
|
+
# :model=>'VMware Virtual Platform',
|
109
|
+
# :memory_installed_mb=>1024,
|
110
|
+
# :serial=>'VMware-1234',
|
111
|
+
# :vendor=>'VMware, Inc.'
|
112
|
+
# }
|
113
|
+
|
114
|
+
puts target[:operating_system]
|
115
|
+
#=>{
|
116
|
+
# :date_installed=>#<DateTime: 2013-02-04T19:08:49-05:00 ((2456329j,529s,891979000n),-18000s,2299161j)>,
|
117
|
+
# :features=>[],
|
118
|
+
# :kernel=>'5.2.3790',
|
119
|
+
# :license_key=>'BBBBB-BBBBB-BBBBB-BBBBB-BBBBB',
|
120
|
+
# :name=>'Microsoft Windows',
|
121
|
+
# :roles=>['TerminalServer', 'TimeServer'],
|
122
|
+
# :service_pack=>'Service Pack 2',
|
123
|
+
# :version=>'Server 2003 R2 Standard'
|
124
|
+
# }
|
125
|
+
```
|
126
|
+
|
127
|
+
## Data
|
128
|
+
Through a number of queries and algorithms, Boris efficiently polls devices on the network for information including, but not limited to, network configuration, hardware capabilities, installed software and services, applied hotfixes/patches, and more.
|
129
|
+
|
130
|
+
**Available methods for use on most platforms include:**
|
131
|
+
|
132
|
+
* **file systems** - file system, mount point, capacity and utilization
|
133
|
+
* **hardware** - make/model, cpu information, firmware/bios version, serial number
|
134
|
+
* **hosted shares** - folders shared by the target
|
135
|
+
* **installed applications** - installed applications and the dates of their installation
|
136
|
+
* **installed patches** - installed patches/hotfixes and the dates of their installation
|
137
|
+
* **installed services/daemons** - background services and their startup modes
|
138
|
+
* **local users and groups** - local groups and the users within each
|
139
|
+
* **network ID** - hostname and domain
|
140
|
+
* **network interfaces** - ethernet and fibre channel interfaces, including IPs, MAC addresses, connection status
|
141
|
+
* **operating system** - name, version, kernel, date installed
|
142
|
+
|
143
|
+
See [Boris::Profilers::Structure](http://www.rubydoc.info/github/alkalinecoffee/boris/Boris/Profilers/Structure) for more details on the data structure.
|
144
|
+
|
145
|
+
Because the commands that might work correctly on one type of platform most likely won't work on another, Boris handles this by the use of...
|
146
|
+
|
147
|
+
## Profilers
|
148
|
+
Profilers contain the instructions that allow us to run commands against our target and then parse and make sense of the data. Boris comes with the capability to communicate with targets over SNMP, SSH, or WMI. Each profiler is written to use one of these methods of communication (internally called 'connectors'), which serve as a vehicle for running commands against a server. Boris comes with a few profilers built-in for some popular platforms, but can be easily extended to include other devices.
|
149
|
+
|
150
|
+
**Available profilers:**
|
151
|
+
|
152
|
+
* **[Linux Core](http://rubydoc.info/github/alkalinecoffee/boris/master/Boris/Profilers/Linux)**
|
153
|
+
* [Red Hat Linux](http://rubydoc.info/github/alkalinecoffee/boris/master/Boris/Profilers/RedHat)
|
154
|
+
* **[UNIX Core](http://rubydoc.info/github/alkalinecoffee/boris/master/Boris/Profilers/UNIX)**
|
155
|
+
* [Oracle Solaris](http://rubydoc.info/github/alkalinecoffee/boris/master/Boris/Profilers/Solaris)
|
156
|
+
* **[Windows Core](http://rubydoc.info/github/alkalinecoffee/boris/master/Boris/Profilers/Windows)**
|
157
|
+
* [Windows 2003 Server](http://rubydoc.info/github/alkalinecoffee/boris/master/Boris/Profilers/Windows2003)
|
158
|
+
* [Windows 2008 Server](http://rubydoc.info/github/alkalinecoffee/boris/master/Boris/Profilers/Windows2008)
|
159
|
+
* [Windows 2012 Server](http://rubydoc.info/github/alkalinecoffee/boris/master/Boris/Profilers/Windows2012)
|
160
|
+
|
95
161
|
## Extending Boris
|
96
162
|
You can also run your own commands to grab information off of systems. For example, on a Linux device, to run your own script that is already on the target and retrieve its output:
|
97
163
|
|
@@ -104,7 +170,7 @@ multiple_lines_of_data = target.connector.values_at('/path/to/some/script')
|
|
104
170
|
single_line_of_data = target.connector.value_at('/path/to/some/script')
|
105
171
|
```
|
106
172
|
|
107
|
-
Running commands in this fashion utilizes the #exec
|
173
|
+
Running commands in this fashion utilizes the #exec method from the Net::SSH library.
|
108
174
|
|
109
175
|
For a Windows host, which uses WMI vice SSH, you can send WMI queries or registry keys to the connector to get information:
|
110
176
|
|
@@ -112,53 +178,22 @@ For a Windows host, which uses WMI vice SSH, you can send WMI queries or registr
|
|
112
178
|
# this will pull rows from a class in the standard root\CIMV2 namespace, returning an array of hashes
|
113
179
|
multiple_rows_of_data = target.connector.values_at('SELECT * FROM Win32_NetworkAdapter')
|
114
180
|
|
115
|
-
# this will pull rows from a class in the lower-level root\WMI namespace (note the second argument we're
|
181
|
+
# this will pull rows from a class in the lower-level root\WMI namespace (note the second argument we're
|
182
|
+
# passing to #values_at):
|
116
183
|
multiple_rows_of_data = target.connector.values_at('SELECT * FROM MSNdis_EnumerateAdapter', :root_wmi)
|
117
184
|
|
118
|
-
# you can also poll for registry keys under HKEY_LOCAL_MACHINE by providing a base key path, which returns
|
185
|
+
# you can also poll for registry keys under HKEY_LOCAL_MACHINE by providing a base key path, which returns
|
186
|
+
# an array of keys:
|
119
187
|
registry_keys = target.connector.registry_subkeys_at('SOFTWARE\Microsoft\Windows')
|
120
188
|
|
121
|
-
# and then grab values found at some key via #registry_values_at, which returns value/data elements in a
|
189
|
+
# and then grab values found at some key via #registry_values_at, which returns value/data elements in a
|
190
|
+
# Hash:
|
122
191
|
registry_values = target.connector.registry_values_at('SOFTWARE\Microsoft\Windows\CurrentVersion')
|
123
192
|
```
|
124
193
|
|
125
194
|
**Coming soon--a write-up for SNMP devices**
|
126
195
|
|
127
|
-
Boris also comes with the ability to add your own complete modules for using the framework by writing your own data collection algorithms.
|
128
|
-
|
129
|
-
## Data
|
130
|
-
Through a number of queries and algorithms, Boris efficiently polls devices on the network for information including, but not limited to, network configuration, hardware capabilities, installed software and services, applied hotfixes/patches, and more.
|
131
|
-
|
132
|
-
**Available methods for use on most platforms include:**
|
133
|
-
|
134
|
-
* **file systems** - file system, mount point, capacity and utilization
|
135
|
-
* **hardware** - make/model, cpu information, firmware/bios version, serial number
|
136
|
-
* **hosted shares** - folders shared by the target
|
137
|
-
* **installed applications** - installed applications and the dates of their installation
|
138
|
-
* **installed patches** - installed patches/hotfixes and the dates of their installation
|
139
|
-
* **installed services/daemons** - background services and their startup modes
|
140
|
-
* **local users and groups** - local groups and the member users within each
|
141
|
-
* **network ID** - hostname and domain
|
142
|
-
* **network interfaces** - ethernet and fibre channel interfaces, including IPs, MAC addresses, connection status
|
143
|
-
* **operating system** - name, version, kernel, date installed
|
144
|
-
|
145
|
-
See {http://www.rubydoc.info/github/alkalinecoffee/boris/Boris/Profilers/Structure Boris::Profilers::Structure} for more details on the data structure.
|
146
|
-
|
147
|
-
Because the commands that might work correctly on one type of platform most likely won't work on another, Boris handles this by the use of...
|
148
|
-
|
149
|
-
## Profilers
|
150
|
-
Profilers contain the instructions that allow us to run commands against our target and then parse and make sense of the data. Boris comes with the capability to communicate with targets over SNMP, SSH, or WMI. Each profiler is written to use one of these methods of communication (internally called 'connectors'), which serve as a vehicle for running commands against a server. Boris comes with a few profilers built-in for some popular platforms, but can be easily extended to include other devices.
|
151
|
-
|
152
|
-
**Available profilers:**
|
153
|
-
|
154
|
-
* **Linux Core**
|
155
|
-
* Red Hat Linux
|
156
|
-
* **UNIX Core**
|
157
|
-
* Oracle Solaris
|
158
|
-
* **Windows Core**
|
159
|
-
* Windows 2003 Server
|
160
|
-
* Windows 2008 Server
|
161
|
-
* Windows 2012 Server
|
196
|
+
Boris also comes with the ability to add your own complete modules for using the framework by writing your own data collection algorithms. I will also write-up a howto in the near future.
|
162
197
|
|
163
198
|
## User Account Requirements
|
164
199
|
While Boris does its best to gather data from devices without any special privileges, sometimes it just can't be helped. One example of this is the RedHat profiler, which requires `sudo` access for the `dmidecode` command, as there isn't a well known, reliable way to grab this info without `dmidecode`. If Boris attempts to run a command that requires special access and is denied, it will throw a message to the logger and move on.
|
data/lib/boris.rb
CHANGED
data/lib/boris/connectors.rb
CHANGED
@@ -11,7 +11,7 @@ module Boris
|
|
11
11
|
# @param [Hash] cred credential we wish to use
|
12
12
|
# @param [Hash] options an optional list of options. See {Boris::Options} for a list of all
|
13
13
|
# possible options. The relevant option set here would be :snmp_options.
|
14
|
-
def initialize(host, cred, options=
|
14
|
+
def initialize(host, cred, options=Options.new)
|
15
15
|
super(host, cred)
|
16
16
|
@snmp_options = options[:snmp_options].merge(:host=>@host, :version=>:SNMPv1, :community=>@user)
|
17
17
|
|
data/lib/boris/connectors/ssh.rb
CHANGED
@@ -11,10 +11,15 @@ module Boris
|
|
11
11
|
# @param [Hash] cred credential we wish to use
|
12
12
|
# @param [Hash] options an optional list of options. See {Boris::Options} for a list of all
|
13
13
|
# possible options. The relevant option set here would be :ssh_options.
|
14
|
-
def initialize(host, cred, options=
|
14
|
+
def initialize(host, cred, options=Options.new)
|
15
15
|
@ssh_options = options[:ssh_options]
|
16
|
-
|
17
|
-
|
16
|
+
|
17
|
+
@ssh_options[:auth_methods] = ['publickey']
|
18
|
+
if @password
|
19
|
+
@ssh_options[:auth_methods] << 'password'
|
20
|
+
@ssh_options[:password] = @password
|
21
|
+
end
|
22
|
+
|
18
23
|
if @ssh_options
|
19
24
|
invalid_ssh_options = @ssh_options.keys - Net::SSH::VALID_OPTIONS
|
20
25
|
raise ArgumentError, "invalid ssh option(s): #{invalid_ssh_options.join(', ')}" if invalid_ssh_options.any?
|
@@ -41,12 +46,20 @@ module Boris
|
|
41
46
|
@connected = @reconnectable = true
|
42
47
|
rescue Net::SSH::AuthenticationFailed
|
43
48
|
warn "connection failed (connection made but credentials not accepted with user #{@user})"
|
49
|
+
@failure_message = CONN_FAILURE_AUTH_FAILED
|
44
50
|
@reconnectable = true
|
45
51
|
rescue Net::SSH::HostKeyMismatch
|
46
|
-
warn
|
52
|
+
warn CONN_FAILURE_HOST_KEY_MISMATCH
|
53
|
+
@failure_message = CONN_FAILURE_HOST_KEY_MISMATCH
|
54
|
+
@reconnectable = false
|
55
|
+
rescue SocketError, Errno::ETIMEDOUT
|
56
|
+
warn CONN_FAILURE_NO_HOST
|
57
|
+
@failure_message = CONN_FAILURE_NO_HOST
|
47
58
|
@reconnectable = false
|
48
59
|
rescue => error
|
49
|
-
|
60
|
+
message = "connection failed (#{error.message})"
|
61
|
+
warn message
|
62
|
+
@failure_message = message
|
50
63
|
@reconnectable = true
|
51
64
|
end
|
52
65
|
|
@@ -102,7 +115,7 @@ module Boris
|
|
102
115
|
elsif data =~ /sorry, try again/i
|
103
116
|
ch.close
|
104
117
|
return_data = []
|
105
|
-
info
|
118
|
+
info 'channel closed (we have a password to supply but it is not accepted)'
|
106
119
|
elsif data =~ /permission denied/i
|
107
120
|
warn "permission denied for this request (#{data.gsub(/\n|\s+/, ', ')})"
|
108
121
|
else
|
@@ -112,6 +125,11 @@ module Boris
|
|
112
125
|
|
113
126
|
# called when something is written to stderr
|
114
127
|
chan.on_extended_data do |ch, type, data|
|
128
|
+
if data =~ /password has expired/i
|
129
|
+
@failure_message = CONN_FAILURE_PASSWORD_EXPIRED
|
130
|
+
ch.close
|
131
|
+
disconnect
|
132
|
+
end
|
115
133
|
error_messages.concat(data.split(/\n/))
|
116
134
|
end
|
117
135
|
|
data/lib/boris/connectors/wmi.rb
CHANGED
@@ -3,6 +3,7 @@ require 'boris/connectors'
|
|
3
3
|
module Boris
|
4
4
|
class WMIConnector < Connector
|
5
5
|
attr_accessor :wmi, :root_wmi, :registry
|
6
|
+
attr_reader :registry_cache
|
6
7
|
|
7
8
|
HKEY_LOCAL_MACHINE = 0x80000002
|
8
9
|
KEY_QUERY_VALUE = 1
|
@@ -16,6 +17,8 @@ module Boris
|
|
16
17
|
# @param [Hash] cred credential we wish to use
|
17
18
|
def initialize(host, cred)
|
18
19
|
super(host, cred)
|
20
|
+
|
21
|
+
@registry_cache = []
|
19
22
|
end
|
20
23
|
|
21
24
|
# Disconnect from the host.
|
@@ -56,17 +59,31 @@ module Boris
|
|
56
59
|
@connected = false
|
57
60
|
if error.message =~ /access is denied/i
|
58
61
|
warn "connection failed (connection made but credentials not accepted with user #{@user})"
|
62
|
+
@failure_message = CONN_FAILURE_AUTH_FAILED
|
59
63
|
@reconnectable = true
|
64
|
+
elsif error.message =~ /call was canceled by the message filter/i
|
65
|
+
warn CONN_FAILURE_RPC_FILTERED
|
66
|
+
@failure_message = CONN_FAILURE_RPC_FILTERED
|
67
|
+
@reconnectable = false
|
60
68
|
elsif error.message =~ /rpc server is unavailable/i
|
61
|
-
warn
|
69
|
+
warn CONN_FAILURE_RPC_UNAVAILABLE
|
70
|
+
@failure_message = CONN_FAILURE_RPC_UNAVAILABLE
|
62
71
|
@reconnectable = false
|
72
|
+
elsif error.message =~ /user credentials cannot be used for local connections/i
|
73
|
+
# clear the credentials then try to connect again
|
74
|
+
warn CONN_FAILURE_LOCAL_CREDENTIALS
|
75
|
+
@user = nil
|
76
|
+
@password = nil
|
77
|
+
establish_connection
|
63
78
|
else
|
64
|
-
|
79
|
+
message = "connection failed (#{error.message.gsub(/\n\s*/, '. ')})"
|
80
|
+
warn message
|
81
|
+
@failure_message = message
|
65
82
|
@reconnectable = true
|
66
83
|
end
|
67
84
|
end
|
68
85
|
|
69
|
-
if @reconnectable == true
|
86
|
+
if @connected == false && @reconnectable == true
|
70
87
|
info 'connection available for retry'
|
71
88
|
elsif @reconnectable == false
|
72
89
|
info 'connection does not seem to be available (so we will not retry)'
|
@@ -95,7 +112,20 @@ module Boris
|
|
95
112
|
access_params.sSubKeyName = key_path
|
96
113
|
access_params.uRequired = permission_to_check
|
97
114
|
|
98
|
-
|
115
|
+
has_access = false
|
116
|
+
|
117
|
+
begin
|
118
|
+
@registry.ExecMethod_('CheckAccess', access_params).bGranted
|
119
|
+
has_access = true
|
120
|
+
rescue => error
|
121
|
+
if error.message =~ /access is denied/i
|
122
|
+
info "access denied on checking access on #{key_path}"
|
123
|
+
else
|
124
|
+
info "error while checking access on #{key_path}"
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
128
|
+
has_access
|
99
129
|
end
|
100
130
|
|
101
131
|
|
@@ -110,18 +140,26 @@ module Boris
|
|
110
140
|
def registry_subkeys_at(key_path)
|
111
141
|
subkeys = []
|
112
142
|
|
113
|
-
debug "reading registry subkeys at
|
143
|
+
debug "reading registry subkeys at #{key_path}"
|
144
|
+
|
145
|
+
found_key = @registry_cache.select{|item| item[:key_path] == key_path}.first
|
114
146
|
|
115
|
-
if
|
147
|
+
if found_key && found_key[:subkeys]
|
148
|
+
debug "subkeys found in cache at #{key_path}"
|
149
|
+
subkeys = found_key[:subkeys]
|
150
|
+
elsif has_access_for(key_path, KEY_ENUMERATE_SUB_KEYS)
|
116
151
|
in_params = @registry.Methods_('EnumKey').inParameters.SpawnInstance_
|
117
152
|
in_params.hDefKey = HKEY_LOCAL_MACHINE
|
118
153
|
in_params.sSubKeyName = key_path
|
119
154
|
|
120
|
-
@registry.ExecMethod_('EnumKey', in_params).sNames
|
121
|
-
|
122
|
-
|
155
|
+
keys_names = @registry.ExecMethod_('EnumKey', in_params).sNames
|
156
|
+
keys_names ? keys_names.each {|key| subkeys << key_path + '\\' + key} : subkeys = []
|
157
|
+
|
158
|
+
@registry_cache << {:key_path=>key_path, :subkeys=>subkeys}
|
123
159
|
else
|
124
|
-
info "no access for enumerating keys at
|
160
|
+
info "no access for enumerating keys at #{key_path}"
|
161
|
+
|
162
|
+
@registry_cache << {:key_path=>key_path, :subkeys=>subkeys}
|
125
163
|
end
|
126
164
|
|
127
165
|
subkeys
|
@@ -140,7 +178,12 @@ module Boris
|
|
140
178
|
|
141
179
|
debug "reading registry values at path #{key_path}"
|
142
180
|
|
143
|
-
|
181
|
+
found_key = @registry_cache.select{|item| item[:key_path] == key_path}.first
|
182
|
+
|
183
|
+
if found_key && found_key[:values]
|
184
|
+
debug "values found in cache (#{key_path})"
|
185
|
+
values = found_key[:values]
|
186
|
+
elsif has_access_for(key_path, KEY_QUERY_VALUE)
|
144
187
|
in_params = @registry.Methods_('EnumValues').inParameters.SpawnInstance_
|
145
188
|
in_params.hDefKey = HKEY_LOCAL_MACHINE
|
146
189
|
in_params.sSubKeyName = key_path
|
@@ -152,27 +195,31 @@ module Boris
|
|
152
195
|
subkey_values ||= []
|
153
196
|
|
154
197
|
subkey_values.each do |value|
|
155
|
-
if value.length
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
198
|
+
break if value.length == 0
|
199
|
+
|
200
|
+
str_params.sValueName = value
|
201
|
+
|
202
|
+
begin
|
203
|
+
x = @registry.ExecMethod_('GetStringValue', str_params).sValue
|
204
|
+
x = @registry.ExecMethod_('GetBinaryValue', str_params).uValue unless x
|
205
|
+
x = @registry.ExecMethod_('GetDWORDValue', str_params).uValue unless x
|
206
|
+
x = @registry.ExecMethod_('GetExpandedStringValue', str_params).sValue unless x
|
207
|
+
x = @registry.ExecMethod_('GetMultiStringValue', str_params).sValue unless x
|
208
|
+
x = @registry.ExecMethod_('GetQWORDValue', str_params).uValue unless x
|
209
|
+
|
210
|
+
values[value.downcase.to_sym] = x
|
211
|
+
rescue
|
212
|
+
if $!.message =~ /invalid method/i
|
213
|
+
warn "unreadable registry value (#{key_path}\\#{value})"
|
171
214
|
end
|
172
215
|
end
|
173
216
|
end
|
217
|
+
|
218
|
+
@registry_cache << {:key_path=>key_path, :values=>values}
|
174
219
|
else
|
175
220
|
info "no access for enumerating values at (#{key_path})"
|
221
|
+
|
222
|
+
@registry_cache << {:key_path=>key_path, :values=>values}
|
176
223
|
end
|
177
224
|
|
178
225
|
values
|