NAME
Win32::Security::ACL
- Win32 ACL manipulation
SYNOPSIS
use Win32::Security::ACL;
my $acl = Win32::Security::ACL->new('FILE', $acl_string);
my $acl2 = Win32::Security::ACL->new('FILE', @Aces);
DESCRIPTION
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). Note that it is still in development, so for now support for modification of ACLs is pretty much non-existent.
Win32::Security::ACL
uses the flyweight design pattern in conjunction with a persistent cache of demand-computed properties. The result is that parsing of ACLs is only done for unique ACLs, and that the ACL objects themselves are very lightweight.
Installation instructions
This installs with MakeMaker as part of Win32::Security
.
To install via MakeMaker, it's the usual procedure - download from CPAN, extract, type "perl Makefile.PL", "nmake" then "nmake install". Don't do an "nmake test" because the I haven't written a test suite yet.
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
.
ARCHITECTURE
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.
Class Structure
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:
Win32::Security::ACL
Win32::Security::ACL::SE_FILE_OBJECT
Flyweight Objects w/ Cached Demand-Computed Properties
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 scalar reference to the anonymous 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
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.
Method Reference
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.
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 $_
like grep
does. The returned ACEs are clone
d to ensure that modifications to them do not modify the cached ACE values for that ACL.
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.
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 handle occluded permissions properly (for instance, if an container has an inherited permission granting READ access to Domain Users and someone adds an explicit and inherited 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). As in aces
, the returned ACEs are clone
d for safety.
compare_inherited
Accepts $inheritable
, a Win32::Security::ACL
object, as a parameter. The second object should ideally be generated by a call to inheritable
, and should be comprised solely of ACEs marked as INHERITED_ACE
. The method compares the ACEs on the receiver marked as inherited with the ACEs for the passed object like so:
Filters out ACEs not marked as
INHERITED_ACE
from the list of those on the receiver.It starts at the beginning of the resulting lists and removes ACEs that match. This process stops at the first non-matching pair.
It starts at the end of the resulting lists and removes ACEs that match. This process stops at the first non-matching pair.
It looks for a single 'CREATOR OWNER' ACE in the remainined entries of the passed object. If it finds zero or more than one, then it skips to the next step. It then looks for potentially matching entries in the remaining entries for the receiver. If it finds one and only one entry that matches on the
rawtype
,rawflags
, andrawmask
, it presumes that the entry in question resulted from the 'CREATOR OWNER' ACE and removes both of them. If it finds no entry that matches, it presumes that 'CREATOR OWNER' was occluded by one of the other ACEs and moves the 'CREATOR OWNER' entry from the passed object list. If it finds multiple matching entries, it does nothing.
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:
- I
-
ACE is properly inherited from
$inheritable
. - M
-
ACE should have been inherited from
$inheritable
, but is missing! - W
-
ACE marked as
INHERITED_ACE
, but there is no corresponding ACE to inherit in$inheritable
. - X
-
ACE explicitly assigned to object (i.e.
INHERITED_ACE
is not set).
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 in $_
like grep
does.
AUTHOR
Toby Ovod-Everett, tovod-everett@alascom.att.com