Win32::Perms - Manages object permissions for Windows NT |
Win32::Perms - Manages object permissions for Windows NT
use Win32::Perms;
This extension provides the ability for a Perl script to manage permissions on files, directories, registry keys, network shares and printer shares. The management of permissions is tightly tied to Windows NT (for registry keys, shared directories and printer shares). Windows 95/98 does not support the type of security that relates to permissions. Additionally file and directory permissions only work on drives which have been formatted with the NTFS file system.
Win32::Perms is a Win32 Perl 5 extension that manages permissions on Win32 objects. Valid Win32 objects include files, directories, registry keys, network shares and printer shares.
This thing is very young and needs maturing. All functions work but not all path types are supported yet. Currently the following objects are supported: files directories registry keys network shares printer shares
**Named pipes are not yet supported since they are considered by Win32 to be Kernel objects and not proper files. Although you can pass in a Win32::Perms object when creating a Win32::Pipe object. This will set the specified permissions on the named pipe. For more information refer to the latest version of Win32::Pipe:
http://www.roth.net/perl/pipe/
Okay, this thing needs some explaining. Permissions are placed on Win32 objects such as files, directories and registry keys. Now, let's say that you want to place permissions so user 'cartman' can only read a file but 'administrator' has full access....
The first thing you need to understand is that usernames and groupnames are just strings of text that mean nothing to Win32. The OS does not care what a ``Administrator'' string is. It does care, however, about a funky long thing that looks like:
S-1-5-21-143982112-578333669-1869494990-500
You see, an odd looking number like this is what Win32 sees when it thinks of the Administrator account. When the Administrator logs in Win32 looks up this fancy sting and applies it whenever it needs to identify the user. For humans it is simply easier to refer to 'Administrator'. The string is really a text representation of a binary memory structure which is called a Security IDentifier (SID). Every username, groupname, domainname and machine name have a SID tucked away somewhere in the Registry.
So if you want to grant the user 'cartman' some permissions you must first request Win32 to lookup the SID for the 'cartman' account. The same goes for the 'administrator' account.
Once you have a SID you need to create a thing called an Access Control Entry (ACE). An ACE is a combination of SID, permissions and some flags. You simply make an ACE for cartman and an ACE for administrator. Sounds simple so far...
Now that you have this collection of ACEs you need to glue them altogether. You do this by creating an Access Control List (ACL). An ACL is simply a list of ACEs. Once you have created an ACL you can apply it to the object (file, dir, registry key, etc).
But wait, not so fast. Having created you nice shiny ACL makes you happy and all, but we are not out of this yet. There are two types of ACLs. The type of ACL that most users are familiar with is the one which allows you to determine who can access to what. This is what you created the cartman and administrator ACEs for in the first place. This ACL is known as a Discresionary Access Control List (DACL). Makes sense since it is up to your descression who has access, eh?
The other type of ACL is one that most administrators are familiar with. This is the one which determines if and how the object is audited. This way you can set it such that when the object is successfull opened an entry is placed in the event log. There are successful audits and there are failure audits. This type of audit ACL is known as a System Access Control List (SACL).
By the way, if you have an empty DACL (that is a DACL that contains no ACEs) then that means that nobody will be granted access to the object (except for the owner, but that comes later). When you think about it this makes sense. An empty DACL means that it has no ACEs. Since each ACE describes who has what access; no ACEs must mean that nobody is denied AND nobody is granted access. Therefore no one has access. An empty SACL simply means that no auditing takes place on the object.
Okay, one last wrinkle to toss in. It is possible to create a NULL DACL. A NULL DACL is *NOT* the same as an empty DACL. An empty DACL means that you have a chunk of memory allocated that represents a DACL but no ACEs are inside of it. A NULL DACL means that no memory has been allocated for the DACL. (The NULL comes from the fact that since there is no memory allocated for the DACL you supply a NULL pointer whenever a function requires a DACL). A NULL DACL will be interpreted by Win32 as neither granting access to anyone nor denying access to anyone. This confusion is resolved by Win32 implicitly granting FULL access to EVERYONE! (this is why when you create a new net share in Explorer and do not specify any permissions you see, by default, that full access is granted to everyone -- the share has a NULL DACL)
A NULL DACL is the polar opposite of an EMPTY DACL. NULL DACL means everyone has full access. Empty DACL means no one has any access.
A NULL SACL, by the way, will do the same as an empty SACL (no auditing is performed).
If this makes sense thus far then you are going to find the rest of this as pretty easy.
Two more very simple concepts and then on to the meaning of life: owner and group. Most Win32 objects can have an owner and group. An owner is just a SID of who ``owns'' the object. The owner is never denied access to the object, ever. Even if the DACL explicitly denies access to the user, the fact that he is the owner allows him to get to it. An owner can either be a user SID or a group SID. Yes, the Administrators group can be the owner of a file (which means that anyone who is a member of the Administrators group would share in the ownership).
The last simple thing is just like the owner except that it is a group (aka primary group). This does not really mean much to Win32 but it exists for compatibility with POSIX. An object's primary group can be any Global Group. And that is about it. It does not grant anything neat-o or cool. It simply is. Oh yeah, the owner and group are SIDs. They are not ACEs since there are no permissions and/or flags that are associated with the them. Just simply SIDs.
If you take your DACL and SACL and toss in an owner SID and a primary group SID then you have what is known as a Security Descriptor (SD). Ahh, this is it! This is what a Win32::Perms object really is; a security descriptor!
Most Win32 API functions that allow you to create or manipulate objects allow you to supply a security descriptor. Which means all of this madness applies to any securable Win32 object.
When you create a Win32::Perms object you are creating an empty Security Descriptor. This means that it has no owner SID, no primary group SID and it has both a NULL DACL and a NULL SACL.
If you create a Win32::Perms object passing in a path or Win32::Registry object then an empty SD is created which will immediately import the DACL, SACL, owner and group.
Containers are objects that hold other objects. For example a directory is a container (since it holds file and other directory objects) but a file is not a container. Registry keys are securable containers since they can hold other Registry keys. However there are no securable non-container Registry objects (values could be considered non-container Registry objects except that a value can not be secured). Named Pipes, Network shares and printers are not considered securable containers either.
When you create a Win32::Perms object it will be either a container or a non-container. You can query the Perms object to see if it is a container by calling:
$PermObject->IsContainer();
You can only secure objects that are the same type as the Win32::Perm object. That is to say, a container Perm object can only secure containers and vise-versa. This is so that a directory Perm object that is securing objects recursively do not accidently secure files with a directory Permission.
In this example we will be placing read only permissions for cartman and full permissions for administrator on the file c:\test.txt and removing all references for guest from and then explicity denying joel access to the c:\temp directory.
use Win32::Perms;
# Create an empty SD $File = new Win32::Perms;
# Create and import at the same time $Dir = new Win32::Perms('c:/temp');
# One of two ways to add an ACE $File->Allow('cartman', READ);
# The second way to add an ACE $File->Add( {Account=>'administrator', Mask=>FULL } );
# Set the file's permissions $File->Set('c:/test.txt');
# One of three ways to remove an ACE $Dir->Remove('guest');
# Deny access $Dir->Deny( 'joel', FULL );
# Set the directory permissions (no need to specify the # path since the object was created with it) $Dir->Set();
# If you are curious about the contents of the SD... $Dir->Dump;
# Now let's clear out all entries... $Dir->Remove( -1 );
# Let's get an array of hashes (each hash is an ACE) $File->Get( \@List );
# Now let's add the list to our directory $Dir->Add( @List );
# Now, for jokes, let's set the new directory permissions # No need to specify a path since the object was created with one. $Dir->Set();
The Win32::Perms makes use of user, machine and domain accounts. This can be any valid account. For example:
Joel Administrator Guest SERVER_A$
In addition to accounts, groups are also valid (both local and global groups):
Managers Administrators Guests Friends
To specify a particular account or group from a particular domain you can prepend the account with a domain name followed by a single backslash:
ACCOUNTING\Joel TECH_GROUP\Administrator TECH_GROUP\Domain Admins NEW_YORK\Guests
It is often necessary to specify a user account from a particular machine. The machine can be a Primary Domain Controller, Backup Domain Controller, NT Server, or NT Workstation. Since local groups are machine specific (not domain wide as global groups are) it is necessary to specify which machine a local group belongs to.
To specify a particular machine instead of a domain you specify the proper name of the machine (with double backslashes prepending the name) followed by a backslash and the account or group name:
\\Server_A\Administrators \\JOHNS_PC\IUSR_MACHINE \\PRIMARY_DC\Guest
Account...Name of the account or group. Domain....Domain which contains the account or group (optional). Mask......Permission mask. Type......Type of ACE to create (optional). Flag......ACE flags (optional).
The hashes contained in the resulting array from a call to Dump()
will
contain the appropriate keys. This allows for:
$Perm->Dump( \@Array ); $Perm2->Add( @Array );
Mask can be any combination of constants from Table 1 OR'ed together. Type can be any one single constant from Table 2. Flag can be any combination of constants from Table 3 OR'ed together.
Returns the number of entries successfully added.
Mask can be any combination of constants from Table 1 OR'ed together. Flag can be any combination of constants from Table 3 OR'ed together.
Returns TRUE (1) if successful and FALSE (0) if it fails.
Mask can be any combination of constants from Table 1 OR'ed together. Flag can be any combination of constants from Table 3 OR'ed together.
Returns TRUE (1) if successful and FALSE (0) if it fails.
Mask can be any combination of constants from Table 1 OR'ed together. Flag can be any combination of constants from Table 3 OR'ed together.
Close()
Close()
is
automatically called when the object is destroyed due to an undef(),
delete()
or it falls out of scope.
Returns no value.
$Perm->Dump( \@List ); Win32::Perms::DecodeMask( $List[3], \@Mask, \@FriendlyMask );
Returns the number of permissions names that $Mask represents.
$Perm->Dump( \@List ); Win32::Perms::DecodeType( $List[3], \@Types );
Returns the number of permissions names that $Type represents.
$Perm->Dump( \@List ); Win32::Perms::DecodeFlag( $List[3], \@Flags );
Returns the number of permissions names that $Flag represents.
If an optional array reference is passed in then nothing is printed to STDOUT instead the array is populated with hashes that describe each ACE.
Returns a number representing the total number of ACE's in the object.
GetDacl()
>> This method may be removed in future versions <<
Retrieves the memory address of the Win32::Perms object's Discresionary ACL (DACL). The return value is the memory address of the DACL (a pointer to the DACL).
*** It is not wise to use this function since it points to a memory structure that can change.
Returns a pointer to the Win32::Perms object's Discresionary ACL (DACL).
Returns a number representing the number of groups.
GetSacl()
>> This method may be removed in future versions <<
Retrieves the memory address of the Win32::Perms object's System ACL (SACL -- used for auditing). The return value is the memory address of the SACL (a pointer to the SACL).
*** It is not wise to use this function since it points to a memory structure that can change.
Returns a pointer to the Win32::Perms object's System ACL (SACL).
SD_ABSOLUTE.....This is an SD that contains pointers which points to the DACL, SACL, Owner SID and Group SID. If this type of SD is requested then a value is returned which is the memory address of the SD. SD_RELATIVE.....This is a contguous binary represenation of the SD's DACL, SACL, Owner SID and Group SID. If this type of SD is requested then the entire SD is returned as a byte array. The returned scalar can be quite long (several hundred to several thousand bytes long).
If no type is passed in then it defaults to SD_ABSOLUTE.
Returns either the memory location of the SD (SD_ABSOLUTE), a binary data structure (SD_RELATIVE) or undef (upon failure).
GetVersion()
NOTE: Not all objects have groups (eg. Network shares).
Returns the current group of the object.
Reg: ..............Registry key File: ..............File or directory. Share: .............A network share point. Printer: ...........A network printer
If the object passed in is a valid binary security descriptor then the data imported will be from that SD.
If no type is specified the File: type is assumed.
Examples:
File:c:/temp/file.txt "File://server/share/my/directory with spaces" Reg:HKEY_LOCAL_MACHINE\Software\ActiveWare Reg:\\Server_A\HKEY_LOCAL_MACHINE\Software\ActiveWare Reg://machine_name/hkey_local_machine/currentcontrolset Share://Server_A/MyShareName
NOTE: Forward and backslashes are supported (all forward slashes will first be converted to backslashes).
Returns TRUE if successful and FALSE if failure.
Returns TRUE or FALSE indicating the current state of looking up the primary domain controller.
An $Object can be used instead of a $Path. Currently the only valid object's are a binary security descriptor, a Win32::Perms object and a Win32::Registry object.
If a second parameter is specified it forces the path to be interpreted as the specified type:
PERM_TYPE_NULL............Creates a NULL descriptor. This is the same as a security descriptor with a NULL DACL/SACL. If an object of this type is used to set permissions then the permission will be set to the Default state which is (as of this writing) FULL access to Everyone. NOTE: Refer to the note on NULL Security Descriptors. PERM_TYPE_FILE............The path is a registry key. PERM_TYPE_REGISTRY........The path is to a directory or file. PERM_TYPE_SHARE...........The path is to a network share. PERM_TYPE_PRINTER.........The path is to a network printer. PERM_TYPE_SD..............The object created is only an SD with no association with any objects.
If nothing is passed into the function then a new, empty Win32::Perms object will be created. It can be applied to any object later.
Returns a valid Win32::Perms object otherwise returns undef.
Import()
for details of the $Path.
If the optional first parameter is not specified or is an empty string
then the default path for the object is used. (If you created this object
by specifying a path this is the default path).
**If $Recurse is non zero then the permissions will be set recursively
throughout all sub containers. Refer to SetRecurse()
for details.
Set()
except that it will recurse into all subcontainers (dirs,
keys, etc). Setting permissions on all matching objects found in
subcontainers.
It is very important to understand that when a $Path is specified the last element of the path is the name of the target. This target will be searched for in the specified path and all sub containers.
For example:
If the path is to a file then wildcards can be used such as:
c:\temp\*.tmp
then this will set permissions on all files ending in .tmp in the c:\temp directory. Then all subcontainers withing c:\temp will be searched setting permissions on all files matching *.tmp.
If $Path is:
c:\temp\groovy
then the object called 'groovy' in the path c:\temp will be set. Then all subcontainers within c:\temp will be searched for objects called 'groovy' which will then be set.
If $Path is:
c:\temp
then all objects called temp in the root of the C: drive will be set. Then all sub- containers below C: will be searched. The following objects will be set:
c:\temp c:\temp\test\test2\temp c:\winnt\temp c:\dos\tools\temp
If $Path is a relative path (eg. ``..\test.txt'') then the path is considered relative to the current working directory.
Paths that specify a registry key can not contain wildcards but can use recursion. Recursion does not apply to network or printer shares.
This is functionly the same as calling Set()
passing in a non zero value for the $Recurse parameter.
Set()
method except that the Set()
will set all
information on the object (DACL, SACL, Owner and Group). The methods will only set the
specified information:
SetDacl()..........Sets only the discresionary ACL (permissions). SetSacl()..........Sets only the system ACL (auditing). SetOwner().........Sets only the owner of the object. SetGroup().........Sets only the group of the object.
S-1-5-21-143984352-578909669-1869494990-1009
If a scalar variable is passed in as a second parameter then it is set to the binary reprentation of the same SID. If the specified account is not valid then nothing is returned and if the optional second parameter is specified then it is cleared.
If an optional account is passed in then the owner will be set to that account.
NOTE: Not all objects have groups (eg. Network shares).
Returns the current owner of the object.
Either an account or index can be specified. The index number is the index number that
is a result of calling Dump()
or the Get()
methods.
If -1 is passed as a parameter then all ACEs are removed.
Returns number of removed ACEs.
Either an account or index can be specified. The index number is the index number that
is a result of calling Dump()
or the Get()
methods.
If -1 is passed as a parameter then all ACEs are removed.
Returns number of removed ACEs.
Table 1. Valid ACE permissions. Mask contants: FULL........................Full access (RWDXOP). ALL.........................Same as FULL. CHANGE......................Change access (RWDX). READ........................Read access. WRITE.......................Write access. DELETE......................Delete access. EXECUTE.....................Execute access. NO_ACCESS...................No permissions specified.
Table 2. Valid ACE type constants. Type constants: ALLOW.......................The permission mask is allowed. GRANT.......................Same as ALLOW. DENY........................The permission mask is denied. AUDIT.......................The permission is for auditing. OWNER.......................The account specified is the OWNER (the mask is ignored) GROUP.......................The account specified is the GROUP (the mask is ignored)
Table 3. Valid ACE flag contants. Flag constants: DIRECTORY...................The permission is for a directory. DIR.........................Same as DIRECTORY. KEY.........................The permission is for a registry key. CONTAINER...................The permission is for a container object (dir, registry key, etc). FILE........................The permission is for a file. VALUE.......................The permission is for a registry value. NON_CONTAINER...............The permission is for a non container object (file, registry value, etc). SUCCESS.....................If the type is AUDIT then this means to audit success. FAILURE.....................If the type is AUDIT then this means to audit failure.
When specifying a path for the following methods:
new Import Set
you can use one of two formats:
path object_type:path
The first format (path) is simply the path of the object such as:
c:\temp\file.txt d:\Program Files\Office \\server\share \\server\share\dir\file.txt \\server\printer HKEY_LOCAL_MACHINE\Software \\machine\HKEY_LOCAL_MACHINE\Software
The second format (object_type:path) is the path prepended by a object type and colon:
file:c:\temp\file.txt dir:d:\Program Files\Office share:\\server\share file:\\server\share\dir\file.txt printer:\\server\printer registry:HKEY_LOCAL_MACHINE\Software registry:\\machine\HKEY_LOCAL_MACHINE\Software
Note that file: and dir: are synonyms; either can be used.
The Official Win32::Perms Home Page:
http://www.roth.net/perl/perms/
Dave Roth ( http://www.roth.net/contact/ )
Copyright (c) 1998-2002 Dave Roth
Courtesy of Roth Consulting ( http://www.roth.net/ )
This library may be copied or modified only under the terms of either the Artistic License or the GNU General Public License, which may be found in the Perl 5.0 source kit.
Win32::Perms - Manages object permissions for Windows NT |