NAME
Win32::Security::Recursor
- Security recursion for named objects
SYNOPSIS
use Win32::Security::Recursor;
my $recursor = Win32::Security::Recursor::SE_FILE_OBJECT->new(
payload => sub {
my $self = shift;
my($node_info, $cont_info) = @_;
print $self->node_name($node_info)."\n";
}
);
$recursor->recurse($ARGV[0]);
DESCRIPTION
This module is designed to support scripts that need to recurse through a hierarchy of objects (i.e. a directory tree, registry hive, etc.), and interfacing with the security information on every node. There are a number of reasons this module was developed, instead of simply reusing File::Find
.
Applicability to multiple tree types. While not currently implemented, I tried to architect the interfaces and the internals so that it should be relatively simple to extend the code base to support the registry, Active Directory, and any other hierarchies of NamedObjects.
Applicability to permissions recursion. In particular, it is very common to compare the permissions on a node with the permissions on a parent node. To avoid performance-sapping duplication of effort, the system passes the payload both the information for the node and for its parent.
General performance improvements. Information is cached where appropriate (for instance, testing for whether a node is a container), thus reducing duplicate system calls. System calls were further optimized. For instance, it turns out that
Win32::File::GetAttributes
is over twice as fast as the built-in-d
operator, at least under Perl 5.6.1 - this shaves roughly 0.3 ms per node on a Pent III Xeon 450 (or 30 seconds when scanning 100,000 files!), and even more given that it lets one test for JUNCTION points with almost no additional overhead.Error handling. Error handling is passed through well defined interfaces, thus letting the developer choose how to display and/or record errors.
All of this comes at a price, however, and that is complexity. Some of that is because the problem itself is complex - objects fail to respond to API calls, JUNCTION points can complicate recursion, etc. Some of it is because the module was designed to be as flexible as possible, and so code was broken up into a wide variety of methods, thus making granual overriding possible. The module makes use of Class::Prototyped
to support object-level method overriding without the need for explicit subclassing.
Installation instructions
This installs as part of Win32::Security
. See Win32::Security::NamedObject
for more information.
It depends upon the other Win32::Security
modules and Class::Prototyped
.
ARCHITECTURE
The docs for this module are still under development. The documentation present is correct, but to really understand the module you need to look at the source.
Subclass Organization
There are subclasses of Win32::Security::Recursor
for each type of supported Win32::Security::NamedObject
(i.e. 'SE_FILE_OBJECT'
for now - 'SE_REGISTRY_KEY'
is not yet supported). The subclasses are responsible for implementing hierarchy specific behavior, such as enumerating child nodes, determining whether a node is a container, etc.
Method Reference
new
The new
method is entirely inherited from Class::Prototyped
. A list of slot names and values may be passed if desired using the normal Class::Prototyped::addSlots
syntax.
recurse
The recurse
method is the heart of Win32::Security::Recursor
. It accepts a single object name and recurses through the tree of objects rooted by that object. It does not use recursion, though, but rather a stack-based approach that flattens the recursion into a loop.
First, though, it creates an entirely new object to handle the call sequence. This object inherits from the object upon which recurse
was called, and has a nodes
slot that consists of an anonymous array of nodes remaining to be processed. Each node is a hash consisting of a name
which stores the object-name in question, a parent
which is a reference to the parent node, and keys which store cached responses for the various node information calls.
The currently "active" node is always the last one on the array. Nodes are pushed onto the array in reverse order so that a depth-first search is effected.
Once the first node is on the array, basic flow through the loop looks like this:
Calls
node_filternode
on current nodeCalls
node_filternode
to filter individual node. Ifnode_filternode
returns true, execution proceeds through the loop. The call tonode_filternode
trapsdie
with aneval
, so adie
is treated like a false value. If the call fails ordie
s, then the node is popped off of the array and the loop restarted. This happens here to thatnode_filternode
filters the nodes in the proper order so that any output is sorted appropriately.Calls
payload
on current nodeThe call to
payload
is wrapped in aneval
and any returned$@
is printed toSTDERR
if$self->debug()
is true.Determines list of child nodes and pushes them onto the array
This whole procedure is wrapped in an
eval
. If any part of it fails, any returned$@
is printed toSTDERR
if$self->debug()
is true and then the last node is popped off of the array. The code first callsnode_iscontainer
, and if false simply pops the last node off the array. Otherwise,node_enumchildren
is called to build a list of child nodes (each of which has aparent
that points to the current node).node_filterchildren
is then called, which is responsible for ordering the child nodes as desired and for filtering out any nodes which wouldn't result in any output. Finally, the list of child nodes is reversed and used to replace the active node.
objectType
This returns the objectType
for a given Recursor. Should be overridden by child classes.
debug
This defaults to true. Pass in "[qw(debug constant)] => 0,
" to new
to turn debug
off.
payload
Needs to be overridden to actually do anything!
node_getinfo
Used to get information about a node and/or the parent node. This accepts a list of "requests" and then returns the requested information. Each request consists of a pair of values. The first value should be either 'node'
, 'parent'
, or a node HASH
. The second value should be either an info name or a reference to an array of info names. The permitted info names are:
name
The node object name.
iscontainer
True if the node is a container object, false otherwise.
namedobject
Returns the
Win32::Security::NamedObject
object for this node.dacl
Returns the
Win32::Security::ACL
object for the DACL of this node.ownerTrustee
Returns the owner as Trustee for this node.
ownerSid
Returns the owner as SID for this node.
container
Returns the name of the container that contains this object, if there is one.
The information is returned in a list in the order requested.
Potentially Useful Recursors
In order to make it easier to reuse some of my code, I have taken the liberty of putting some of my recursors into Win32::Security::Recursor
.
Win32::Security::Recursor::SE_FILE_OBJECT::PermDump->new($options)
This takes a ref to an options hash and returns a recursor that implements the same behavior displayed by PermDump.pl
. It takes an optional list of parameters that will be passed to Win32::Security::Recursor::SE_FILE_OBJECT->new
so as to override or define new methods for the recursor.
Options passable in the options hash are:
csv
dirsonly
inherited
owner
recurse
AUTHOR
Toby Ovod-Everett, toby@ovod-everett.org