File Coverage

blib/lib/Win32/SDDL.pm
Criterion Covered Total %
statement 1 3 33.3
branch n/a
condition n/a
subroutine 1 1 100.0
pod n/a
total 2 4 50.0


line stmt bran cond sub pod time code
1             $VERSION = '0.06';
2            
3             package Win32::SDDL;
4 1     1   29526 use Win32::OLE;
  0            
  0            
5            
6             my $CONSTANTS = {};
7             my $TRUSTEE_CONSTANTS = {};
8            
9             #Takes a 'Type' argument that modifies the constants
10             #TODO: Only takes 'service' as a type.
11             # Research other types that modify meaning of constants
12             sub new{
13             my $class = shift;
14             my $self = {};
15             $self->{Type} = shift;
16             $self->{SDString} = '';
17             $self->{ACL} = {};
18             _initialize($self->{Type},$CONSTANTS,$TRUSTEE_CONSTANTS) or die("Unable to initialize Constants!\n");
19             bless($self) || die("Unable to bless '$self'!\n");
20             return $self;
21             }
22            
23             #Imports an SDDL string
24             sub Import{
25             my $self = shift;
26             $self->{SDString} = shift;
27             my $SDType = $self->{Type};
28             my %const = %{$CONSTANTS};
29             my %trustees = %{$TRUSTEE_CONSTANTS};
30             my @updateConstants = ();
31             my $index = 0;
32             $self->{ACL} = [];
33            
34             #Make sure that it's a valid object.
35             unless(ref($self) eq 'Win32::SDDL'){
36             die("'$self' is not a valid Win32::SDDL object!\n".ref($self)."\n");
37             }
38            
39             unless($self->{SDString}){
40             return 2;
41             }
42            
43             #Check that the SDDL string is in a valid format
44             my @rights = $self->{SDString} =~ /\((.*?)\)/g;
45             unless($self->{SDString}){
46             return undef;
47             }
48            
49             #Cycle through the ACEs
50             foreach my $sec(@rights){
51             push @{$self->{ACL}},Win32::SDDL::ACE->new($sec,\%trustees,\%const) || die("Unable to parse '$sec' for Win32::SDDL::ACE object creation!\n");
52             }
53             return 1;
54             }
55            
56            
57             #Initializes the constants
58             sub _initialize{
59             my $type = shift;
60             $type ||= '';
61             my $constants = shift;
62             my $trusteeConstants = shift;
63            
64             #We only have one valid type at the moment
65             if($type and $type ne 'service'){
66             warn("Unsupported Type '$type'!\n");
67             return 0;
68             }
69            
70             #Skip this if the hash is already populated
71             unless(scalar(keys %{$constants})){
72             %{$constants} = (
73             #ACE Types
74             A => "ACCESS_ALLOWED",
75             D => "ACCESS_DENIED",
76             OA => "ACCESS_ALLOWED_OBJECT",
77             OD => "ACCESS_DENIED_OBJECT",
78             AU => "SYSTEM_AUDIT",
79             AL => "SYSTEM_ALARM",
80             OU => "SYSTEM_AUDIT_OBJECT",
81             OL => "SYSTEM_ALARM_OBJECT",
82            
83             #ACE Flags
84             CI => "CONTAINER_INHERIT",
85             OI => "OBJECT_INHERIT",
86             NP => "NO_PROPAGATE_INHERIT",
87             IO => "INHERIT_ONLY",
88             ID => "INHERITED",
89             SA => "SUCCESSFUL_ACCESS",
90             FA => "FAILED_ACCESS",
91            
92             #Generic Access Rights
93             GA => "GENERIC_ALL",
94             GR => "GENERIC_READ",
95             GW => "GENERIC_WRITE",
96             GX => "GENERIC_EXECUTE",
97            
98             #Standard Access Rights
99             RC => "READ_CONTROL",
100             SD => "DELETE",
101             WD => "WRITE_DAC",
102             WO => "WRITE_OWNER",
103            
104             #Directory Service Object Access Rights
105             RP => "DS_READ_PROP",
106             WP => "DS_WRITE_PROP",
107             CC => "DS_CREATE_CHILD",
108             DC => "DS_DELETE_CHILD",
109             LC => "DS_LIST",
110             SW => "DS_SELF",
111             LO => "DS_LIST_OBJECT",
112             DT => "DS_DELETE_TREE",
113            
114             #File Access Rights
115             FA => "FILE_ALL_ACCESS",
116             FR => "FILE_GENERIC_READ",
117             FW => "FILE_GENERIC_WRITE",
118             FX => "FILE_GENERIC_EXECUTE",
119            
120             #Registry Access Rights
121             KA => "KEY_ALL_ACCESS",
122             KR => "KEY_READ",
123             KW => "KEY_WRITE",
124             KE => "KEY_EXECUTE",
125            
126             );
127            
128             #Change some constants if the type is service
129             if($type eq 'service'){
130             $constants->{CC} = "Query Configuration";
131             $constants->{DC} = "Change Configuration";
132             $constants->{LC} = "Query State";
133             $constants->{SW} = "Enumerate Dependencies";
134             $constants->{RP} = "Start";
135             $constants->{WP} = "Stop";
136             $constants->{DT} = "Pause";
137             $constants->{LO} = "Interrogate";
138             $constants->{CR} = "User Defined";
139             $constants->{SD} = "Delete";
140             $constants->{RC} = "Read Control";
141             $constants->{WD} = "Change Permissions";
142             $constants->{WO} = "Change Owner";
143             }
144             }
145            
146             #Skip if the hash has already been populated
147             unless(scalar(keys %{$trusteeConstants})){
148             %{$trusteeConstants} = (
149             #Trustees
150             AO => "Account Operators",
151             RU => "Pre-Windows 2k Access",
152             AN => "Anonymous Logon",
153             AU => "Authenticated Users",
154             BA => "Built-in Administrators",
155             BG => "Built-in Guests",
156             BO => "Backup Operators",
157             BU => "Built-in Users",
158             CA => "Certificate Server Admins",
159             CG => "Creator Group",
160             CO => "Creator Owner",
161             DA => "Domain Administrators",
162             DC => "Domain Computers",
163             DD => "Domain Controllers",
164             DG => "Domain Guests",
165             DU => "Domain Users",
166             EA => "Enterprise Administrators",
167             ED => "Enterprise Domain Controllers",
168             WD => "Everyone",
169             PA => "Group Policy Administrators",
170             IU => "Interactively Logged-on User",
171             LA => "Local Administrator",
172             LG => "Local Guest",
173             LS => "Local Service Account",
174             SY => "Local System",
175             NU => "Network Logon User",
176             NO => "Network Configuration Operators",
177             NS => "Network Service Account",
178             PO => "Printer Operators",
179             PS => "Personal Self",
180             PU => "Power Users",
181             RS => "RAS Servers Group",
182             RD => "Terminal Server Users",
183             RE => "Replicator",
184             RC => "Restricted Code",
185             SA => "Schema Administrators",
186             SO => "Server Operators",
187             SU => "Service Logon User",
188             CY => "Crypto Operators",
189             IS => "Anonymous Internet Users",
190             MU => "Performance Monitor Users",
191             OW => "Owner Rights SID",
192             RM => "RMS Service"
193             );
194             }
195             return 1;
196            
197             }
198            
199             #Translates the text SID to a readable name.
200             sub _translateSID{
201             my $SID = shift;
202             $SID or die("_translateSID() Unable to translate empty SID.");
203             my $WMI = Win32::OLE->GetObject("winmgmts:{impersonationLevel=impersonate,(Security)}!\\\\.\\root\\cimv2") or return(0);
204             my $obj = $WMI->Get("Win32_SID='".$SID."'");
205             unless($obj->{AccountName}){
206             return 0;
207             }
208             return join("\\",($obj->{ReferencedDomainName},$obj->{AccountName}));
209             }
210            
211             package Win32::SDDL::ACE;
212            
213             sub new{
214             my $class = shift;
215             my $sec = shift;
216             my %trustees = %{shift()};
217             my %const = %{shift()};
218             my $self = {};
219             ($self->{Type},$self->{_flags},$self->{_perms},$self->{ObjectType},$self->{InheritedObjectType},$self->{Trustee}) = split(/;/,$sec);
220            
221             #Grab each two-letter permission string and translate it if it is a valid constant
222             my @perms = $self->{_perms} =~ /\w\w/g or die("Invalid ACE Perms String '$self->{_perms}'!\n");
223             foreach my $perm(@perms){
224             if($const{$perm}){
225             $perm = $const{$perm};
226             }
227             }
228             $self->{AccessMask} = [@perms];
229            
230             #Translate the Type (allow, deny, or audit)
231             if( my $type = $const{$self->{Type}}){
232             $self->{Type} = $type;
233             }
234            
235             #Translate the ACE flags
236             my @flags = ($self->{_flags} =~ /\w\w/g) if $self->{_flags};
237             foreach my $flag(@flags){
238             if($const{$flag}){
239             $flag = $const{$flag};
240             }
241             }
242             $self->{Flags} = [@flags];
243            
244             #Translate the SID to a readable name if possible.
245             #Cache the results in %trustees
246             if($trustees{$self->{Trustee}}){
247             $self->{Trustee} = $trustees{$self->{Trustee}};
248             }elsif(my $account = Win32::SDDL::_translateSID($self->{Trustee})){
249             $trustees{$self->{Trustee}} = $account;
250             $self->{Trustee} = $account;
251             }
252             bless($self) || die("Unable to bless '$self'!\n");
253             return $self;
254             }
255            
256            
257             1;
258            
259            
260             =head1 NAME
261            
262             Win32::SDDL - SDDL parsing module for Windows
263            
264             =head1 SYNOPSIS
265            
266             use Win32::SDDL;
267            
268             my $sddl = Win32::SDDL->new( 'service' );
269            
270             $sddl->Import( 'D:(A;;CCLCSWLOCRRC;;;AU)(A;;CCLCSWRPLOCRRC;;;PU)' );
271            
272             foreach my $mask( @{$sddl->{ACL}} ){
273             $trustees{ $mask->{Trustee} } = 1;
274             }
275            
276             my @trustees = sort keys %trustees;
277            
278            
279             print scalar( @{$sddl->{ACL}} )." entries found.\n";
280            
281            
282             =head1 DESCRIPTION
283            
284             This module was created to aid in interpreting SDDL strings commonly used in
285             Windows to represent access control lists. SDDL stands for Security Descriptor
286             Definition Language. Because SDDL uses many predefined constants, it can be
287             difficult to read. This module provides an object-oriented interface for
288             converting and using the information in SDDL strings.
289            
290             I
291            
292             =head1 METHODS
293            
294             =over 5
295            
296             =item Win32::SDDL->new( *type* );
297            
298             Example:
299            
300             my $sddl = Win32::SDDL->new( 'service' );
301            
302             Creates a new Win32::SDDL object. Optionally, an object type can be provided.
303             The only optional type supported at present is 'service'. This will change
304             the value of certain constants as they have a different meaning for services
305             than they do for files, registry keys, or other objects.
306            
307             =item $sddl->Import( $sddl_string );
308            
309             Example:
310            
311             my $sddl_string = 'D:(A;;CCLCSWLOCRRC;;;AU)(A;;CCLCSWRPLOCRRC;;;PU)';
312            
313             $sddl->Import( $sddl_string ) or die( "Error! Unable to import '$sddl_string'!\n" );
314            
315             =back
316            
317             =head1 PROPERTIES
318            
319             All Win32::SDDL objects have the following properties:
320            
321             =over 5
322            
323             =item $sddl->{SDString}
324            
325             The currently loaded SDDL string
326            
327             =item $sddl->{Type}
328            
329             The type of SDDL string (changes the description of some constants).
330            
331             =item $sddl->{ACL}
332            
333             An array of Win32::SDDL::ACE objects.
334            
335             Each object has the following properties:
336            
337             =over 10
338            
339             =item Flags
340            
341             An array of flags translated into English.
342            
343             =item AccessMask
344            
345             An array of permissions translated into English.
346            
347             =item Type
348            
349             The type of ACE (SYSTEM_AUDIT,ACCESS_ALLOW, or ACCESS_DENY).
350            
351             =item objectType
352            
353             A GUID representing the object type for the ACE (usually empty).
354            
355             =item InheritedObjectType
356            
357             A GUID representing the parent object type if it exists.
358            
359             =item Trustee
360            
361             The Trustee name.
362            
363             =back
364            
365             =back
366            
367             =head1 UPDATE HISTORY
368            
369             =item See the Changes file.
370            
371             =head1 BUGS/CHANGES NEEDED
372            
373             =over 5
374            
375             =item Replace Win32::OLE Dependency
376            
377             Right now I'm using WMI to translate SIDs to account names. I would like to
378             find a way to import the Win32 API with a minimal footprint to reduce the size
379             of the module for people who distribute packaged executables and archives of
380             their scripts.
381            
382             =item B
383            
384             Please contact me if you have any requests or suggestions.
385            
386             =back
387            
388             =head1 SEE ALSO
389            
390             L
391            
392             =head1 COPYRIGHT
393            
394             Copyright 2006 Tim Johnson
395            
396             This module is free software; you can redistribute it and/or
397             modify it under the same terms as Perl itself.
398            
399             =head1 AUTHOR
400            
401             Tim Johnson
402            
403             =cut
404