diff --git a/Changes b/Changes index 8747ff61e1..9e46970756 100644 --- a/Changes +++ b/Changes @@ -33,6 +33,11 @@ netdiscovery/netinventory: * Thanks to @QuickNerd357, Brocade devices will now show serial number and firmware informations. +collect: +* Thanks to David Durieux, add support for dynamic pattern in registry key + collect under win32. The dynamic pattern is '**' to glob subkeys like in: + HKEY_USERS/**/Software/**/**/CurrentVersion + maintenance: * bump Maintenance task version to 1.1 * disable Maintenance task if no maintenance module could be used diff --git a/lib/FusionInventory/Agent/Tools/Win32.pm b/lib/FusionInventory/Agent/Tools/Win32.pm index d9dddf4d35..02c9ed3aeb 100644 --- a/lib/FusionInventory/Agent/Tools/Win32.pm +++ b/lib/FusionInventory/Agent/Tools/Win32.pm @@ -250,6 +250,16 @@ sub getRegistryValue { return _call_win32_ole_dependent_api($win32_ole_dependent_api); } + # Handle differently paths including /**/ pattern + if ($root =~ m/\/\*\*(?:\/.*|)$/ || $keyName eq '**') { + return _getRegistryDynamic( + logger => $params{logger}, + path => "$root/$keyName", + valueName => $valueName, + withtype => $params{withtype} + ); + } + my $key = _getRegistryKey( logger => $params{logger}, root => $root, @@ -260,7 +270,7 @@ sub getRegistryValue { if ($valueName eq '*') { my %ret; - foreach (keys %$key) { + foreach (grep { m|^/| } keys %$key) { s{^/}{}; $ret{$_} = $params{withtype} ? [$key->GetValue($_)] : $key->{"/$_"} ; } @@ -367,7 +377,7 @@ sub getRegistryKey { ); } -sub _getRegistryKey { +sub _getRegistryRoot { my (%params) = @_; ## no critic (ProhibitBitwise) @@ -381,11 +391,72 @@ sub _getRegistryKey { ) if $params{logger}; return; } + return $rootKey; +} + +sub _getRegistryKey { + my (%params) = @_; + + my $rootKey = _getRegistryRoot(%params) + or return; + my $key = $rootKey->Open($params{keyName}); return $key; } +sub _getRegistryDynamic { + my (%params) = @_; + + my %ret; + my $valueName = $params{valueName}; + + my @rootparts = split(/\/+\*\*\/+/, $params{path}.'/', 2); + my $first = shift(@rootparts); + my $second = shift(@rootparts) || ''; + $first .= '/'; + $second = '/'.$second; + $second =~ s|/*$||; + + my $rootSub = _getRegistryRoot( + root => $first, + logger => $params{logger} + ); + return unless defined($rootSub); + + foreach my $sub ($rootSub->SubKeyNames) { + if ($second =~ m/\/+\*\*(?:\/.*|)/) { + my $subret = _getRegistryDynamic( + logger => $params{logger}, + path => $first.$sub.$second, + valueName => $valueName, + withtype => $params{withtype} + ); + next unless defined($subret); + my ($subkey) = $second =~ /^([^*]+)\*\*(?:\/.*|)$/; + foreach my $subretkey (keys %$subret) { + $ret{$sub.$subkey.$subretkey} = $subret->{$subretkey}; + } + } else { + my $key = _getRegistryRoot( + root => $first.$sub.$second, + logger => $params{logger} + ); + next unless defined($key); + + if ($valueName eq '*') { + foreach (grep { m|^/| } keys %$key) { + s{^/}{}; + $ret{$sub.$second."/".$_} = $params{withtype} ? [$key->GetValue($_)] : $key->{"/$_"} ; + } + } elsif (exists($key->{"/$valueName"})) { + $ret{$sub.$second."/".$valueName} = $params{withtype} ? [$key->GetValue($valueName)] : $key->{"/$valueName"} ; + } + } + } + return \%ret; +} + sub _getRegistryKeyFromWMI{ my (%params) = @_; diff --git a/t/agent/tools/win32.t b/t/agent/tools/win32.t old mode 100755 new mode 100644 index 5eaa448c90..72ab908e46 --- a/t/agent/tools/win32.t +++ b/t/agent/tools/win32.t @@ -172,6 +172,47 @@ my %register = ( '/TEMP' => '%SystemRoot%\\TEMP', '/OS' => 'Windows_NT', } + }, + 'HKEY_USERS/' => { + 'S-1-5-21-2246875202-1293753324-4206800371-500/' => { + 'Software/' => { + 'SimonTatham/' => { + 'PuTTY/' => { + '/Version' => '4.1', + 'SshHostKeys/' => { + '/rsa2@22:192.168.20.32' => '76f523a6eec4ea6b' + }, + '/username' => 'johndoe' + } + }, + 'Mozilla/' => { + 'Firefox/' => { + '/Version' => '59.0' + } + } + } + }, + 'S-1-5-21-2246875202-1293753324-4206800567-500/' => { + '/DisplayName' => 'Janine', + 'Software/' => { + 'SimonTatham/' => { + 'PuTTY/' => { + '/Version' => '5.2', + '/username' => 'jane', + 'SshHostKeys/' => { + '/rsa2@22:192.168.20.54' => 'fdfb3a2eeaa7' + } + } + }, + 'Mozilla/' => { + 'Firefox/' => { + '/Version' => '62.0', + 'Configuration/' => {}, + '/Timeout' => '15' + } + } + } + } } ); @@ -235,7 +276,6 @@ my %regval_tests = ( _expected => { 'ClientID' => '0x12345678', 'Version' => '12.0.72365', - 'subkey/' => undef } }, 'teamviewerid-withtype' => { @@ -249,12 +289,80 @@ my %regval_tests = ( _expected => { 'ClientID' => [ '0x12345678', REG_DWORD() ], 'Version' => [ '12.0.72365', REG_SZ() ], - 'subkey/' => [] } }, 'temp-env' => { path => 'HKEY_LOCAL_MACHINE/CurrentControlSet/Control/Session Manager/Environment/TEMP', _expected => '%SystemRoot%\\TEMP' + }, + 'putty_keys' => { + path => 'HKEY_USERS/**/Software/SimonTatham/PuTTY/SshHostKeys/*', + _expected => { + 'S-1-5-21-2246875202-1293753324-4206800371-500/Software/SimonTatham/PuTTY/SshHostKeys/rsa2@22:192.168.20.32' + => '76f523a6eec4ea6b', + 'S-1-5-21-2246875202-1293753324-4206800567-500/Software/SimonTatham/PuTTY/SshHostKeys/rsa2@22:192.168.20.54' + => 'fdfb3a2eeaa7' + } + }, + 'users_software_versions' => { + path => 'HKEY_USERS/**/Software/Mozilla/Firefox/Version', + _expected => { + 'S-1-5-21-2246875202-1293753324-4206800567-500/Software/Mozilla/Firefox/Version' => '62.0', + 'S-1-5-21-2246875202-1293753324-4206800371-500/Software/Mozilla/Firefox/Version' => '59.0' + } + }, + 'users_softwares_versions' => { + path => 'HKEY_USERS/**/Software/**/**/Version', + _expected => { + 'S-1-5-21-2246875202-1293753324-4206800371-500/Software/SimonTatham/PuTTY/Version' => '4.1', + 'S-1-5-21-2246875202-1293753324-4206800567-500/Software/SimonTatham/PuTTY/Version' => '5.2', + 'S-1-5-21-2246875202-1293753324-4206800371-500/Software/Mozilla/Firefox/Version' => '59.0', + 'S-1-5-21-2246875202-1293753324-4206800567-500/Software/Mozilla/Firefox/Version' => '62.0' + } + }, + 'users_software_values' => { + path => 'HKEY_USERS/**/Software/**/**/*', + _expected => { + 'S-1-5-21-2246875202-1293753324-4206800371-500/Software/SimonTatham/PuTTY/Version' => '4.1', + 'S-1-5-21-2246875202-1293753324-4206800371-500/Software/SimonTatham/PuTTY/username' => 'johndoe', + 'S-1-5-21-2246875202-1293753324-4206800567-500/Software/SimonTatham/PuTTY/Version' => '5.2', + 'S-1-5-21-2246875202-1293753324-4206800567-500/Software/Mozilla/Firefox/Version' => '62.0', + 'S-1-5-21-2246875202-1293753324-4206800371-500/Software/Mozilla/Firefox/Version' => '59.0', + 'S-1-5-21-2246875202-1293753324-4206800567-500/Software/SimonTatham/PuTTY/username' => 'jane', + 'S-1-5-21-2246875202-1293753324-4206800567-500/Software/Mozilla/Firefox/Timeout' => '15' + } + }, + 'users_software_values' => { + path => 'HKEY_USERS/**/Software/**/Firefox/*', + _expected => { + 'S-1-5-21-2246875202-1293753324-4206800567-500/Software/Mozilla/Firefox/Version' => '62.0', + 'S-1-5-21-2246875202-1293753324-4206800371-500/Software/Mozilla/Firefox/Version' => '59.0', + 'S-1-5-21-2246875202-1293753324-4206800567-500/Software/Mozilla/Firefox/Timeout' => '15' + } + }, + 'users_vars' => { + path => 'HKEY_USERS/**/*', + _expected => { + 'S-1-5-21-2246875202-1293753324-4206800567-500/DisplayName' => 'Janine' + } + }, + 'users_displayname' => { + path => 'HKEY_USERS/**/DisplayName', + _expected => { + 'S-1-5-21-2246875202-1293753324-4206800567-500/DisplayName' => 'Janine' + } + }, + 'bad_glob_on_values' => { + path => 'HKEY_USERS/**/Software/**/**/**', + _expected => {} + }, + 'bad_glob_on_values_2' => { + path => 'HKEY_USERS/S-1-5-21-2246875202-1293753324-4206800371-500/Software/Mozilla/Firefox/**', + _expected => undef + }, + 'bad_glob_on_values_3' => { + path => 'HKEY_USERS/**/**', + _expected => {} } ); @@ -307,6 +415,28 @@ SKIP: { } ); + $module->mock( + '_getRegistryRoot', + sub { + my (%params) = @_; + return unless $params{root}; + my $root; + if (exists($register{$params{root}})) { + $root = { %{$register{$params{root}}} }; + } else { + $root = \%register; + foreach my $part (split('/',$params{root})) { + return unless $root->{$part.'/'}; + $root = { %{$root->{$part.'/'}} } + } + } + # Bless leaf as expected + map { bless $root->{$_}, 'Win32::TieRegistry' } grep { m|/$| } keys %{$root}; + bless $root, 'Win32::TieRegistry'; + return $root; + } + ); + FusionInventory::Agent::Tools::Win32->use('getRegistryKey'); foreach my $test (keys %regkey_tests) { diff --git a/t/lib/fake/windows/Win32/TieRegistry.pm b/t/lib/fake/windows/Win32/TieRegistry.pm index ba55a15a7a..9becd25ffd 100644 --- a/t/lib/fake/windows/Win32/TieRegistry.pm +++ b/t/lib/fake/windows/Win32/TieRegistry.pm @@ -32,4 +32,10 @@ sub GetValue { : $self->{$value} ; } +sub SubKeyNames { + my ($self, $key) = @_ ; + my @keys = map { s|/$|| && $_ } grep { m|/$| } keys(%{$self}); + return @keys; +} + 1;