File Coverage

blib/lib/Win32/SDDL.pm
Criterion Covered Total %
statement 7 9 77.7
branch n/a
condition n/a
subroutine 3 3 100.0
pod n/a
total 10 12 83.3


line stmt bran cond sub pod time code
1             package Win32::SDDL;
2             # ABSTRACT: Parser for the Windows Security Descriptor Definition Language (SDDL)
3 2     2   25820 use strict;
  2         2  
  2         46  
4 2     2   6 use warnings;
  2         2  
  2         55  
5             our $VERSION = '0.07';
6 2     2   955 use Win32::OLE;
  0            
  0            
7              
8             my $CONSTANTS = {};
9             my $TRUSTEE_CONSTANTS = {};
10              
11             #Takes a 'Type' argument that modifies the constants
12             #TODO: Only takes 'service' as a type.
13             # Research other types that modify meaning of constants
14             sub new{
15             my $class = shift;
16             my $self = {};
17             $self->{Type} = shift;
18             $self->{SDString} = '';
19             $self->{ACL} = {};
20             _initialize($self->{Type},$CONSTANTS,$TRUSTEE_CONSTANTS) or die("Unable to initialize Constants!\n");
21             bless($self, $class) || die("Unable to bless '$self'!\n");
22             return $self;
23             }
24              
25             #Imports an SDDL string
26             sub Import{
27             my $self = shift;
28             $self->{SDString} = shift;
29             my $SDType = $self->{Type};
30             my %const = %{$CONSTANTS};
31             my %trustees = %{$TRUSTEE_CONSTANTS};
32             my @updateConstants = ();
33             my $index = 0;
34             $self->{ACL} = [];
35              
36             #Make sure that it's a valid object.
37             unless(ref($self) eq 'Win32::SDDL'){
38             die("'$self' is not a valid Win32::SDDL object!\n".ref($self)."\n");
39             }
40              
41             unless($self->{SDString}){
42             return 2;
43             }
44              
45             #Check that the SDDL string is in a valid format
46             my @rights = $self->{SDString} =~ /\((.*?)\)/g;
47             unless($self->{SDString}){
48             return; # Returns empty list (list context) or undef (scalar context)
49             }
50              
51             #Cycle through the ACEs
52             foreach my $sec(@rights){
53             push @{$self->{ACL}},Win32::SDDL::ACE->new($sec,\%trustees,\%const) || die("Unable to parse '$sec' for Win32::SDDL::ACE object creation!\n");
54             }
55             return 1;
56             }
57              
58              
59             #Initializes the constants
60             sub _initialize{
61             my $type = shift;
62             $type ||= '';
63             my $constants = shift;
64             my $trusteeConstants = shift;
65              
66             #We only have one valid type at the moment
67             if($type and $type ne 'service'){
68             warn("Unsupported Type '$type'!\n");
69             return 0;
70             }
71              
72             #Skip this if the hash is already populated
73             unless(scalar(keys %{$constants})){
74             %{$constants} = (
75             #ACE Types
76             A => "ACCESS_ALLOWED",
77             D => "ACCESS_DENIED",
78             OA => "ACCESS_ALLOWED_OBJECT",
79             OD => "ACCESS_DENIED_OBJECT",
80             AU => "SYSTEM_AUDIT",
81             AL => "SYSTEM_ALARM",
82             OU => "SYSTEM_AUDIT_OBJECT",
83             OL => "SYSTEM_ALARM_OBJECT",
84              
85             #ACE Flags
86             CI => "CONTAINER_INHERIT",
87             OI => "OBJECT_INHERIT",
88             NP => "NO_PROPAGATE_INHERIT",
89             IO => "INHERIT_ONLY",
90             ID => "INHERITED",
91             SA => "SUCCESSFUL_ACCESS",
92             FA => "FAILED_ACCESS",
93              
94             #Generic Access Rights
95             GA => "GENERIC_ALL",
96             GR => "GENERIC_READ",
97             GW => "GENERIC_WRITE",
98             GX => "GENERIC_EXECUTE",
99              
100             #Standard Access Rights
101             RC => "READ_CONTROL",
102             SD => "DELETE",
103             WD => "WRITE_DAC",
104             WO => "WRITE_OWNER",
105              
106             #Directory Service Object Access Rights
107             RP => "DS_READ_PROP",
108             WP => "DS_WRITE_PROP",
109             CC => "DS_CREATE_CHILD",
110             DC => "DS_DELETE_CHILD",
111             LC => "DS_LIST",
112             SW => "DS_SELF",
113             LO => "DS_LIST_OBJECT",
114             DT => "DS_DELETE_TREE",
115              
116             #File Access Rights
117             FA => "FILE_ALL_ACCESS",
118             FR => "FILE_GENERIC_READ",
119             FW => "FILE_GENERIC_WRITE",
120             FX => "FILE_GENERIC_EXECUTE",
121              
122             #Registry Access Rights
123             KA => "KEY_ALL_ACCESS",
124             KR => "KEY_READ",
125             KW => "KEY_WRITE",
126             KE => "KEY_EXECUTE",
127              
128             );
129              
130             #Change some constants if the type is service
131             if($type eq 'service'){
132             $constants->{CC} = "Query Configuration";
133             $constants->{DC} = "Change Configuration";
134             $constants->{LC} = "Query State";
135             $constants->{SW} = "Enumerate Dependencies";
136             $constants->{RP} = "Start";
137             $constants->{WP} = "Stop";
138             $constants->{DT} = "Pause";
139             $constants->{LO} = "Interrogate";
140             $constants->{CR} = "User Defined";
141             $constants->{SD} = "Delete";
142             $constants->{RC} = "Read Control";
143             $constants->{WD} = "Change Permissions";
144             $constants->{WO} = "Change Owner";
145             }
146             }
147              
148             #Skip if the hash has already been populated
149             unless(scalar(keys %{$trusteeConstants})){
150             %{$trusteeConstants} = (
151             #Trustees
152             AO => "Account Operators",
153             RU => "Pre-Windows 2k Access",
154             AN => "Anonymous Logon",
155             AU => "Authenticated Users",
156             BA => "Built-in Administrators",
157             BG => "Built-in Guests",
158             BO => "Backup Operators",
159             BU => "Built-in Users",
160             CA => "Certificate Server Admins",
161             CG => "Creator Group",
162             CO => "Creator Owner",
163             DA => "Domain Administrators",
164             DC => "Domain Computers",
165             DD => "Domain Controllers",
166             DG => "Domain Guests",
167             DU => "Domain Users",
168             EA => "Enterprise Administrators",
169             ED => "Enterprise Domain Controllers",
170             WD => "Everyone",
171             PA => "Group Policy Administrators",
172             IU => "Interactively Logged-on User",
173             LA => "Local Administrator",
174             LG => "Local Guest",
175             LS => "Local Service Account",
176             SY => "Local System",
177             NU => "Network Logon User",
178             NO => "Network Configuration Operators",
179             NS => "Network Service Account",
180             PO => "Printer Operators",
181             PS => "Personal Self",
182             PU => "Power Users",
183             RS => "RAS Servers Group",
184             RD => "Terminal Server Users",
185             RE => "Replicator",
186             RC => "Restricted Code",
187             SA => "Schema Administrators",
188             SO => "Server Operators",
189             SU => "Service Logon User",
190             CY => "Crypto Operators",
191             IS => "Anonymous Internet Users",
192             MU => "Performance Monitor Users",
193             OW => "Owner Rights SID",
194             RM => "RMS Service"
195             );
196             }
197             return 1;
198              
199             }
200              
201             #Translates the text SID to a readable name.
202             sub _translateSID{
203             my $SID = shift;
204             $SID or die("_translateSID() Unable to translate empty SID.");
205             my $WMI = Win32::OLE->GetObject("winmgmts:{impersonationLevel=impersonate,(Security)}!\\\\.\\root\\cimv2") or return(0);
206             my $obj = $WMI->Get("Win32_SID='".$SID."'");
207             unless($obj->{AccountName}){
208             return 0;
209             }
210             return join("\\",($obj->{ReferencedDomainName},$obj->{AccountName}));
211             }
212              
213             package Win32::SDDL::ACE;
214             use strict;
215             use warnings;
216              
217             sub new{
218             my $class = shift;
219             my $sec = shift;
220             my %trustees = %{shift()};
221             my %const = %{shift()};
222             my $self = {};
223             ($self->{Type},$self->{_flags},$self->{_perms},$self->{ObjectType},$self->{InheritedObjectType},$self->{Trustee}) = split(/;/,$sec);
224              
225             #Grab each two-letter permission string and translate it if it is a valid constant
226             my @perms = $self->{_perms} =~ /\w\w/g or die("Invalid ACE Perms String '$self->{_perms}'!\n");
227             foreach my $perm(@perms){
228             if($const{$perm}){
229             $perm = $const{$perm};
230             }
231             }
232             $self->{AccessMask} = [@perms];
233              
234             #Translate the Type (allow, deny, or audit)
235             if( my $type = $const{$self->{Type}}){
236             $self->{Type} = $type;
237             }
238              
239             #Translate the ACE flags
240             my @flags;
241             @flags = ($self->{_flags} =~ /\w\w/g) if $self->{_flags};
242             foreach my $flag(@flags){
243             if($const{$flag}){
244             $flag = $const{$flag};
245             }
246             }
247             $self->{Flags} = [@flags];
248              
249             #Translate the SID to a readable name if possible.
250             #Cache the results in %trustees
251             if($trustees{$self->{Trustee}}){
252             $self->{Trustee} = $trustees{$self->{Trustee}};
253             }elsif(my $account = Win32::SDDL::_translateSID($self->{Trustee})){
254             $trustees{$self->{Trustee}} = $account;
255             $self->{Trustee} = $account;
256             }
257             bless($self,$class) || die("Unable to bless '$self'!\n");
258             return $self;
259             }
260              
261              
262             1;
263              
264             __END__