Win32::Security::ACL - Win32 ACL manipulation |
new
clone
dump
dbmObjectType
rawAcl
objectType
isNullAcl
aces
aclRevision
has_creatorowner
sids
inheritable
compare_inherited
addAces
deleteAces
Win32::Security::ACL
- Win32 ACL manipulation
use Win32::Security::ACL;
my $acl = Win32::Security::ACL->new('FILE', $acl_string); my $acl2 = Win32::Security::ACL->new('FILE', @Aces);
Win32::Security::ACL
and its subclasses provide an interface for interacting
with Win32 ACLs (Access Control Lists). The subclasses allow for variation in
mask behavior (different privileges apply to files than apply to registry keys
and so forth).
Win32::Security::ACL
uses the flyweight design pattern in conjunction with an
in-memory cache of demand-computed properties. The result is that parsing of
ACLs is only done once for each unique ACL, and that the ACL objects themselves
are very lightweight. Double-indirection is used in the ACL objects to provide
for mutability without invalidating the cache.
This installs as part of Win32-Security
. See
Win32::Security::NamedObject
for more information.
It depends upon Class::Prototyped
which should be installable via PPM or
available on CPAN. It also depends upon Win32::Security::ACE
, which is
installed as part of Win32-Security
.
Win32::Security::ACL
uses some OO tricks to boost performance and clean up
the design. Here's a quick overview of the internal architecture, should you
care! It is possible to use Win32::Security::ACL
objects without
understanding or reading any of this, because the public interface is designed
to hide as much of the details as possible. After all, that's the point of OO
design. If, however, you want to boost performance or to muck about in the
internals, it's worth understanding how things were done.
Win32::Security::ACL
uses single inheritance similar to the _ObjectType
side of the multiple inheritance in Win32::Security::ACE
. While not
technically necessary, it was done in order to parallel the ACE design, and so
that the data caches could be maintained independently for each Object Type.
With that in mind, the class hierarchy looks like this:
On the typical computer systems, there are very few unique ACLs. There may be
hundred or thousands, but usually there are orders of magnitude fewer ACLs than
there are objects to which they are applied. In order to reduce the computation
involved in analyzing them, the Win32::Security::ACL
caches all the
information computed about each ACL in a central store (actually, multiple
central stores - one for each Named Object type) based on the binary form
(rawAcl
). The object returned by a call to new
is a a reference to a
reference to the hash for that rawAcl
in the central store. Because it isn't
a direct reference to the hash, it is possible to switch which hash the object
points to on the fly. This allows the Win32::Security::ACL
objects to be
mutable while maintaining the immutability of the central store. It also makes
each individual Win32::Security::ACL
object incredibly lightweight, since it
is only composed of a single blessed scalar. To be safe, you may wish to
clone
ACLs before modifying them, just to make sure that you aren't modifying
someone else's ACL object. The properties are computed as needed, but the
results are cached in the central store.
For instance, once aces
has been computed for a given rawAcl
,
it can be found from the object as $$self->{aces}
. This
should be used with care, although in some instances it is possible to reduce
the number of method calls (should this be necessary for performance reasons) by
making calls like so:
$$acl->{aces} || [$acl->aces()];
That provides a fail-safe should the aces
value have not yet been computed
while eliminating the method call if it has been. Note that $acl->aces()
also derefences the array stored in the cache.
In order to defend against accidental manipulation, return values from the calls
(although not from the direct access, obviously) are deep-copied one layer deep.
That means that the results of $acl->aces()
can be safely manipulated
without harming the ACL, but that the results of $$acl->{aces}
should be
treated as read-only.
Win32::Security::ACL
and Win32::Security::ACE
objects returned are
clone
d (using inlined code to reduce the performance hit). The values
returned from the /^dbm.*/
calls are not cloned, however, so be careful
there.
new
This creates a new Win32::Security::ACL
object.The various calling forms are:
Win32::Security::ACL->new($objectType, $rawAcl)
Win32::Security::ACL->new($objectType, @aces)
"Win32::Security::ACL::$objectType"->new($rawAcl)
"Win32::Security::ACL::$objectType"->new(@aces)
$acl_object->new($rawAcl)
$acl_object->new(@aces)
Note that when using $objectType
in the package name, the value needs to be
canonicalized (i.e. SE_FILE_OBJECT
, not the alias FILE
). If the
$objectType
has already been canonicalized, improved performance can be
realized by making the call on the fully-qualified package name and thus
avoiding the call to redo the canonicalization. Aliases are permitted when
passed as a parameter to the call.
To create a NULL ACL, pass an empty string (which will be interpreted as an
empty rawAcl
). Passing an empty list of ACEs creates an empty ACL, which is
totally different from a NULL ACL.
If called on an Win32::Security::ACL
object, it creates a new ACL object of
the same subclass comprised of the passed list of ACEs.
ACEs can be passed either as Win32::Security::ACE
objects or as anonymous
arrays of parameters to be passed to
Win32::Security::ACE::$objectType->New()
.
clone
This creates a new Win32::Security::ACL
object that is identical in all
forms, except for identity, to the original object. Because of the flyweight
design pattern, this is a very inexpensive operation. However, should you wish
to avoid the overhead of a method call, you can inline the code like so:
bless(\(my $o = ${$obj}), ref($obj));
Basically, it derefences the scalar reference, assigns it to a temporary
lexical, creates a reference to that, and then blesses it into the original
package. Nifty, eh? Syntax stolen (with a few modifications) from
Data::Dumper
output.
dump
This returns a dump of the Win32::Security::ACL
object in a format useful for
debugging.
dbmObjectType
Returns the Data::BitMask
object for interacting with Named Object Types.
See Win32::Security::ACE->dbmObjectType()
for more explanation.
rawAcl
Returns the binary string form of the ACL
objectType
Returns the type of object to which the ACE is or should be attached.
isNullAcl
Tests for a NULL ACL.
aces
Returns a list of Win32::Security::ACE
objects. The ACEs are in the same
order as they are in the ACL.
It accepts an optional filter. The filter should be an anonymous subroutine
that looks for the ACE in $_
and that returns true or false like the block
passed to grep
does (note that unlike grep {} @list
, it is neccessary
to specify sub
to ensure that the block is interpreted as an anonymous
subroutine and not an anonymous hash). The returned ACEs are clone
d to
ensure that modifications to them do not modify the cached ACE values for that
ACL (this is done before passing them to the optional anonymous subroutine,
so it is safe for that subroutine to modify the ACEs).
aclRevision
Returns the ACL Revision for the ACL. In general, this should be 2
(ACL_REVISION
) for normal ACLs and 4
(ACL_REVISION_DS
) for ACLs that
contain object-specific ACEs.
has_creatorowner
Returns 1 if the ACL in question contains a dreaded and evil CREATOR OWNER
ACE, 0 if it doesn't.
sids
Returns a list of all unique SIDs present in the ACL, except for CREATOR
OWNER
and the null SID.
inheritable
Accepts a type (either 'OBJECT'
or 'CONTAINER'
). Returns the list of ACEs
that would be inherited by a newly created child OBJECT
or CONTAINER
if
the parent has this ACL. It handles occluded permissions properly (I hope).
For instance, if an container has an inherited permission granting READ
access to Domain Users and someone adds explicit fully-inheritable FULL
access to Domain Users to that container, child objects will not receive the
inherited READ
access because it is fully occluded by the also inherited
FULL
access. The exact algorithms for this had to be developed through trial
and error as I could find no documentation on the exact behavior. As in
aces
, the returned ACEs are clone
d for safety.
If the ACL in question contains a dreaded and evil CREATOR OWNER
ACE and the
ACE applies to the object in question, then a placeholder ACE is returned with
a null SID - the null SID should be replaced with whatever the appropriate
trustee might be. This may be in addition to the inheritable CREATOR OWNER
ACE itself.
compare_inherited
Accepts $inheritable
, a Win32::Security::ACL
object, which should ideally
be generated by a call to inheritable
on the parent object. It should be
comprised solely of ACEs with the INHERITED_ACE
flag.
The method compares the ACEs on the receiver marked as inherited with the ACEs
for the passed object using a very simple algorithm. First, it filters out ACEs
not marked as INHERITED_ACE
from the list of those on the receiver (these
will be addressed later). Then it starts at the beginning of the two lists of
ACEs and removes ACEs that match. If there are remaining ACEs, it removes
matching ACEs from the end.
It deals with null SIDs in the $inheritable
object (implying an ACE resulting
from a CREATOR OWNER
ACE) by testing all of the SIDs in $self
as possible
standins for the null SID. If any of these result in a perfect match, then life
is good. Otherwise, the results are returned after testing with the null SID
unchanged. The algorithm does not currently deal with situations where there
are multiple CREATOR OWNER
permissions that were set at different times and
thus the bound owner for the permissions is different, and it does not deal
with CREATOR GROUP
.
It returns a list of anonymous arrays, the first consisting of an ACL and the
second consisting of an $IMWX
value that can be interpreted as so:
$inheritable
.
$inheritable
, but is missing!
INHERITED_ACE
, but there is no corresponding ACE to inherit in
$inheritable
.
INHERITED_ACE
is not set).
Note that the I
, W
, and X
ACEs indicate those actually present on the
receiver, in the same order they are present on the receiver. The I
, M
,
and X
ACEs indicate those that should be present, in the same order they
should be present.
If you pass a true value for the optional second parameter $flat
, the
returned data will be flattened into a single list. This is more difficult to
interact with, but because the anonymous arrays don't have to be built, it is
faster. In both cases, the returned values are clone
d to ensure the safety
of the cached data.
addAces
Adds ACEs to the Win32::Security::ACL
object. ACEs may be passed as
Win32::Security::ACE
objects, rawAce
strings, or anonymous arrays of
parameters to be passed to "Win32::Security::ACE::$objectType"->new()
.
The $objectType
value will be generated from the existing ACL. If the
existing ACEs in the ACL are not in the proper order, they will end up reordered
as specified in http://support.microsoft.com/default.aspx?scid=kb;en-us;269159 .
deleteAces
Deletes all ACEs matched by the passed filter from the ACL. The filter should
be an anonymous subroutine that looks for the ACEs one-by-one in $_
and
returns 1 if they should be deleted.
Toby Ovod-Everett, toby@ovod-everett.org
Win32::Security::ACL - Win32 ACL manipulation |