Skip to content

Commit 8d44955

Browse files
committed
Merge remote-tracking branch 'ethan/ticket/master/15559-dont-remove-windows-SYSTEM-permissions'
* ethan/ticket/master/15559-dont-remove-windows-SYSTEM-permissions: (#15559) Windows security test fails under SYSTEM
2 parents 653895b + 5a2d7b8 commit 8d44955

File tree

3 files changed

+79
-17
lines changed

3 files changed

+79
-17
lines changed

lib/puppet/util/windows/security.rb

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -284,12 +284,15 @@ def get_mode(path)
284284
mode |= S_ISVTX
285285
end
286286
when well_known_system_sid
287-
mode &= ~S_ISYSTEM_MISSING
288287
else
289288
#puts "Warning, unable to map SID into POSIX mode: #{ace[:sid]}"
290289
mode |= S_IEXTRA
291290
end
292291

292+
if ace[:sid] == well_known_system_sid
293+
mode &= ~S_ISYSTEM_MISSING
294+
end
295+
293296
# if owner and group the same, then user and group modes are the OR of both
294297
if owner_sid == group_sid
295298
mode |= ((mode & S_IRWXG) << 3) | ((mode & S_IRWXU) >> 3)

spec/integration/type/file_spec.rb

Lines changed: 26 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1049,12 +1049,16 @@ def build_path(dir)
10491049
# read the externally created file SYSTEM ACE(s)
10501050
system_aces = get_aces_for_path_by_sid(path, @sids[:system])
10511051
system_aces.should_not be_empty
1052-
system_aces.each do |ace|
1053-
ace[:mask].should == Windows::File::FILE_ALL_ACCESS
1052+
# when running under SYSTEM account, multiple ACEs come back
1053+
# so we only care that we have at least one of these
1054+
system_aces.any? do |ace|
10541055
inherited = Windows::Security::INHERITED_ACE
1055-
(ace[:flags] & inherited).should == inherited
1056-
end
1056+
ace[:mask] == Windows::File::FILE_ALL_ACCESS &&
1057+
(ace[:flags] & inherited) == inherited
1058+
end.should be_true
10571059

1060+
# when running under SYSTEM user, must force the group to properly verify
1061+
@file[:group] = 'None'
10581062
catalog.apply
10591063

10601064
# should still have the same SYSTEM ACE(s), but with inheritance stripped
@@ -1115,18 +1119,32 @@ def build_path(dir)
11151119
catalog.add_resource @directory
11161120
end
11171121

1118-
it "that already exist (SYSTEM ACE remains unmodified)" do
1122+
it "that already exist by removing the inherited SYSTEM ACE but adding an uninherited one" do
11191123
FileUtils.mkdir(dir)
11201124

11211125
# read the externally created file SYSTEM ACE(s)
11221126
system_aces = get_aces_for_path_by_sid(dir, @sids[:system])
11231127
system_aces.should_not be_empty
1124-
system_aces.each { |ace| ace[:mask].should == Windows::File::FILE_ALL_ACCESS }
11251128

1129+
inherited = Windows::Security::INHERITED_ACE
1130+
1131+
# when running under SYSTEM account, multiple ACEs come back
1132+
# so we only care that we have at least one of these
1133+
system_aces.any? do |ace|
1134+
ace[:mask] == Windows::File::FILE_ALL_ACCESS &&
1135+
(ace[:flags] & inherited) == inherited
1136+
end.should be_true
1137+
1138+
# when running under SYSTEM user, must force the group to properly verify
1139+
@directory[:group] = 'Administrators'
11261140
catalog.apply
11271141

1128-
# should still have the same SYSTEM ACE(s)
1129-
get_aces_for_path_by_sid(dir, @sids[:system]).should == system_aces
1142+
# should still have the same SYSTEM ACE(s), but with inheritance stripped
1143+
system_aces = get_aces_for_path_by_sid(dir, @sids[:system])
1144+
system_aces.each do |ace|
1145+
ace[:mask].should == Windows::File::FILE_ALL_ACCESS
1146+
(ace[:flags] & inherited).should_not == inherited
1147+
end
11301148
end
11311149

11321150
it "that are new where SYSTEM is not the given group or owner (SYSTEM ACE remains FULL)" do

spec/integration/util/windows/security_spec.rb

Lines changed: 49 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -18,30 +18,51 @@ class WindowsSecurityTester
1818
:current_user => Puppet::Util::Windows::Security.name_to_sid(Sys::Admin.get_login),
1919
:system => Win32::Security::SID::LocalSystem,
2020
:admin => Puppet::Util::Windows::Security.name_to_sid("Administrator"),
21+
:administrators => Win32::Security::SID::BuiltinAdministrators,
2122
:guest => Puppet::Util::Windows::Security.name_to_sid("Guest"),
2223
:users => Win32::Security::SID::BuiltinUsers,
2324
:power_users => Win32::Security::SID::PowerUsers,
25+
:none => Win32::Security::SID::Nobody,
2426
}
2527
end
2628

2729
let (:sids) { @sids }
2830
let (:winsec) { WindowsSecurityTester.new }
2931

32+
def set_group_depending_on_current_user(path)
33+
if sids[:current_user] == sids[:system]
34+
# if the current user is SYSTEM, by setting the group to
35+
# guest, SYSTEM is automagically given full control, so instead
36+
# override that behavior with SYSTEM as group and a specific mode
37+
winsec.set_group(sids[:system], path)
38+
mode = winsec.get_mode(path)
39+
winsec.set_mode(mode & ~WindowsSecurityTester::S_IRWXG, path)
40+
else
41+
winsec.set_group(sids[:guest], path)
42+
end
43+
end
44+
3045
shared_examples_for "only child owner" do
3146
it "should allow child owner" do
3247
check_child_owner
3348
end
3449

3550
it "should deny parent owner" do
36-
lambda { check_parent_owner }.should raise_error(Errno::EACCES)
51+
pending("when running as SYSTEM the absence of a SYSTEM group/owner causes full access to be added for SYSTEM",
52+
:if => sids[:current_user] == sids[:system]) do
53+
lambda { check_parent_owner }.should raise_error(Errno::EACCES)
54+
end
3755
end
3856

3957
it "should deny group" do
4058
lambda { check_group }.should raise_error(Errno::EACCES)
4159
end
4260

4361
it "should deny other" do
44-
lambda { check_other }.should raise_error(Errno::EACCES)
62+
pending("when running as SYSTEM the absence of a SYSTEM group/owner causes full access to be added for SYSTEM",
63+
:if => sids[:current_user] == sids[:system]) do
64+
lambda { check_other }.should raise_error(Errno::EACCES)
65+
end
4566
end
4667
end
4768

@@ -127,10 +148,15 @@ class WindowsSecurityTester
127148
# new file has SYSTEM
128149
system_aces = winsec.get_aces_for_path_by_sid(path, sids[:system])
129150
system_aces.should_not be_empty
130-
system_aces.each { |ace| ace[:mask].should == WindowsSecurityTester::FILE_ALL_ACCESS }
151+
152+
# when running under SYSTEM account, multiple ACEs come back
153+
# so we only care that we have at least one of these
154+
system_aces.any? do |ace|
155+
ace[:mask] == Windows::File::FILE_ALL_ACCESS
156+
end.should be_true
131157

132158
winsec.set_group(sids[:power_users], path)
133-
winsec.set_owner(sids[:current_user], path)
159+
winsec.set_owner(sids[:administrators], path)
134160

135161
# and should still have a noninherited SYSTEM ACE granting full control
136162
system_aces = winsec.get_aces_for_path_by_sid(path, sids[:system])
@@ -175,8 +201,13 @@ class WindowsSecurityTester
175201
# new file has SYSTEM
176202
system_aces = winsec.get_aces_for_path_by_sid(path, sids[:system])
177203
system_aces.should_not be_empty
178-
system_aces.each { |ace| ace[:mask].should == WindowsSecurityTester::FILE_ALL_ACCESS }
204+
# when running under SYSTEM account, multiple ACEs come back
205+
# so we only care that we have at least one of these
206+
system_aces.any? do |ace|
207+
ace[:mask] == WindowsSecurityTester::FILE_ALL_ACCESS
208+
end.should be_true
179209

210+
winsec.set_group(sids[:none], path)
180211
winsec.set_mode(0600, path)
181212

182213
# and should still have the same SYSTEM ACE(s)
@@ -204,6 +235,8 @@ class WindowsSecurityTester
204235

205236
describe "for read-only objects" do
206237
before :each do
238+
winsec.set_group(sids[:none], path)
239+
winsec.set_mode(0600, path)
207240
winsec.add_attributes(path, WindowsSecurityTester::FILE_ATTRIBUTE_READONLY)
208241
(winsec.get_attributes(path) & WindowsSecurityTester::FILE_ATTRIBUTE_READONLY).should be_nonzero
209242
end
@@ -218,7 +251,12 @@ class WindowsSecurityTester
218251
(winsec.get_attributes(path) & WindowsSecurityTester::FILE_ATTRIBUTE_READONLY).should be_nonzero
219252

220253
system_aces = winsec.get_aces_for_path_by_sid(path, sids[:system])
221-
system_aces.each { |ace| ace[:mask].should == WindowsSecurityTester::FILE_ALL_ACCESS }
254+
255+
# when running under SYSTEM account, and set_group / set_owner hasn't been called
256+
# SYSTEM full access will be restored
257+
system_aces.any? do |ace|
258+
ace[:mask] == Windows::File::FILE_ALL_ACCESS
259+
end.should be_true
222260
end
223261
end
224262

@@ -297,7 +335,7 @@ class WindowsSecurityTester
297335
describe "for an administrator", :if => Puppet.features.root? do
298336
before :each do
299337
winsec.set_mode(WindowsSecurityTester::S_IRWXU | WindowsSecurityTester::S_IRWXG, path)
300-
winsec.set_group(sids[:guest], path)
338+
set_group_depending_on_current_user(path)
301339
winsec.set_owner(sids[:guest], path)
302340
lambda { File.open(path, 'r') }.should raise_error(Errno::EACCES)
303341
end
@@ -474,7 +512,10 @@ def check_other
474512
end
475513

476514
it "should deny other" do
477-
lambda { check_other }.should raise_error(Errno::EACCES)
515+
pending("when running as SYSTEM the absence of a SYSTEM group/owner causes full access to be added for SYSTEM",
516+
:if => sids[:current_user] == sids[:system]) do
517+
lambda { check_other }.should raise_error(Errno::EACCES)
518+
end
478519
end
479520
end
480521

0 commit comments

Comments
 (0)