puppet 2.7.6 → 2.7.8

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of puppet might be problematic. Click here for more details.

Files changed (206) hide show
  1. data/CHANGELOG +168 -0
  2. data/conf/auth.conf +5 -4
  3. data/conf/redhat/puppet.spec +16 -1
  4. data/conf/solaris/pkginfo +2 -2
  5. data/conf/suse/puppet.spec +9 -3
  6. data/ext/upload_facts.rb +120 -0
  7. data/install.rb +11 -16
  8. data/lib/puppet.rb +1 -1
  9. data/lib/puppet/application/agent.rb +0 -3
  10. data/lib/puppet/application/apply.rb +0 -3
  11. data/lib/puppet/application/queue.rb +21 -1
  12. data/lib/puppet/defaults.rb +6 -4
  13. data/lib/puppet/face/file/store.rb +1 -1
  14. data/lib/puppet/feature/base.rb +2 -1
  15. data/lib/puppet/file_bucket/dipper.rb +3 -2
  16. data/lib/puppet/file_serving/content.rb +1 -1
  17. data/lib/puppet/file_serving/metadata.rb +5 -2
  18. data/lib/puppet/indirector/facts/inventory_service.rb +20 -0
  19. data/lib/puppet/indirector/file_bucket_file/file.rb +3 -2
  20. data/lib/puppet/indirector/report/processor.rb +1 -1
  21. data/lib/puppet/network/handler/filebucket.rb +2 -0
  22. data/lib/puppet/network/handler/fileserver.rb +1 -1
  23. data/lib/puppet/network/handler/master.rb +1 -0
  24. data/lib/puppet/network/handler/report.rb +2 -0
  25. data/lib/puppet/network/handler/runner.rb +1 -0
  26. data/lib/puppet/network/handler/status.rb +2 -0
  27. data/lib/puppet/network/http/mongrel/rest.rb +8 -1
  28. data/lib/puppet/network/http_server.rb +3 -0
  29. data/lib/puppet/network/http_server/mongrel.rb +129 -0
  30. data/lib/puppet/network/rest_authconfig.rb +12 -4
  31. data/lib/puppet/parameter.rb +18 -0
  32. data/lib/puppet/parser/compiler.rb +1 -1
  33. data/lib/puppet/parser/grammar.ra +1 -1
  34. data/lib/puppet/parser/parser.rb +360 -350
  35. data/lib/puppet/property.rb +3 -3
  36. data/lib/puppet/provider/augeas/augeas.rb +1 -1
  37. data/lib/puppet/provider/exec/windows.rb +6 -7
  38. data/lib/puppet/provider/file/windows.rb +9 -2
  39. data/lib/puppet/provider/group/aix.rb +8 -8
  40. data/lib/puppet/provider/group/groupadd.rb +1 -3
  41. data/lib/puppet/provider/group/ldap.rb +8 -10
  42. data/lib/puppet/provider/group/windows_adsi.rb +8 -2
  43. data/lib/puppet/provider/package/aix.rb +1 -1
  44. data/lib/puppet/provider/package/macports.rb +3 -3
  45. data/lib/puppet/provider/package/msi.rb +12 -5
  46. data/lib/puppet/provider/package/nim.rb +1 -1
  47. data/lib/puppet/provider/package/pkgdmg.rb +3 -3
  48. data/lib/puppet/provider/package/ports.rb +1 -1
  49. data/lib/puppet/provider/scheduled_task/win32_taskscheduler.rb +560 -0
  50. data/lib/puppet/provider/service/base.rb +2 -2
  51. data/lib/puppet/provider/service/bsd.rb +4 -3
  52. data/lib/puppet/provider/service/daemontools.rb +25 -25
  53. data/lib/puppet/provider/service/debian.rb +6 -4
  54. data/lib/puppet/provider/service/freebsd.rb +1 -1
  55. data/lib/puppet/provider/service/gentoo.rb +4 -3
  56. data/lib/puppet/provider/service/init.rb +3 -8
  57. data/lib/puppet/provider/service/launchd.rb +129 -96
  58. data/lib/puppet/provider/service/redhat.rb +2 -3
  59. data/lib/puppet/provider/service/runit.rb +20 -20
  60. data/lib/puppet/provider/service/smf.rb +8 -7
  61. data/lib/puppet/provider/service/src.rb +5 -6
  62. data/lib/puppet/provider/service/systemd.rb +1 -1
  63. data/lib/puppet/provider/service/upstart.rb +3 -5
  64. data/lib/puppet/provider/service/windows.rb +7 -7
  65. data/lib/puppet/provider/sshkey/parsed.rb +2 -3
  66. data/lib/puppet/provider/user/aix.rb +21 -21
  67. data/lib/puppet/provider/user/hpux.rb +3 -1
  68. data/lib/puppet/provider/user/ldap.rb +7 -7
  69. data/lib/puppet/provider/user/user_role_add.rb +10 -6
  70. data/lib/puppet/provider/user/useradd.rb +3 -1
  71. data/lib/puppet/provider/user/windows_adsi.rb +4 -3
  72. data/lib/puppet/rb_tree_map.rb +388 -0
  73. data/lib/puppet/reference/configuration.rb +7 -7
  74. data/lib/puppet/reference/indirection.rb +5 -6
  75. data/lib/puppet/reference/metaparameter.rb +3 -1
  76. data/lib/puppet/reference/network.rb +8 -8
  77. data/lib/puppet/reference/providers.rb +17 -21
  78. data/lib/puppet/reference/type.rb +12 -9
  79. data/lib/puppet/resource.rb +2 -5
  80. data/lib/puppet/resource/catalog.rb +1 -1
  81. data/lib/puppet/ssl/certificate_request.rb +70 -0
  82. data/lib/puppet/ssl/host.rb +6 -0
  83. data/lib/puppet/transaction.rb +158 -55
  84. data/lib/puppet/transaction/event_manager.rb +1 -1
  85. data/lib/puppet/type.rb +60 -30
  86. data/lib/puppet/type/augeas.rb +83 -49
  87. data/lib/puppet/type/computer.rb +1 -1
  88. data/lib/puppet/type/cron.rb +11 -11
  89. data/lib/puppet/type/exec.rb +28 -21
  90. data/lib/puppet/type/file.rb +17 -7
  91. data/lib/puppet/type/file/content.rb +2 -2
  92. data/lib/puppet/type/file/ensure.rb +15 -12
  93. data/lib/puppet/type/file/mode.rb +30 -5
  94. data/lib/puppet/type/file/source.rb +11 -10
  95. data/lib/puppet/type/file/target.rb +2 -2
  96. data/lib/puppet/type/filebucket.rb +1 -1
  97. data/lib/puppet/type/group.rb +4 -5
  98. data/lib/puppet/type/host.rb +1 -1
  99. data/lib/puppet/type/interface.rb +13 -10
  100. data/lib/puppet/type/k5login.rb +6 -6
  101. data/lib/puppet/type/macauthorization.rb +37 -36
  102. data/lib/puppet/type/maillist.rb +2 -2
  103. data/lib/puppet/type/mcx.rb +6 -6
  104. data/lib/puppet/type/mount.rb +3 -2
  105. data/lib/puppet/type/notify.rb +1 -1
  106. data/lib/puppet/type/package.rb +24 -23
  107. data/lib/puppet/type/router.rb +4 -1
  108. data/lib/puppet/type/schedule.rb +52 -44
  109. data/lib/puppet/type/scheduled_task.rb +222 -0
  110. data/lib/puppet/type/selmodule.rb +10 -6
  111. data/lib/puppet/type/service.rb +11 -11
  112. data/lib/puppet/type/ssh_authorized_key.rb +2 -5
  113. data/lib/puppet/type/sshkey.rb +1 -1
  114. data/lib/puppet/type/stage.rb +1 -1
  115. data/lib/puppet/type/tidy.rb +10 -8
  116. data/lib/puppet/type/user.rb +61 -53
  117. data/lib/puppet/type/vlan.rb +4 -4
  118. data/lib/puppet/type/whit.rb +6 -2
  119. data/lib/puppet/type/yumrepo.rb +33 -31
  120. data/lib/puppet/type/zfs.rb +34 -32
  121. data/lib/puppet/type/zone.rb +21 -19
  122. data/lib/puppet/type/zpool.rb +3 -3
  123. data/lib/puppet/util.rb +24 -6
  124. data/lib/puppet/util/adsi.rb +12 -7
  125. data/lib/puppet/util/checksums.rb +1 -1
  126. data/lib/puppet/util/diff.rb +1 -1
  127. data/lib/puppet/util/nagios_maker.rb +2 -2
  128. data/lib/puppet/util/reference.rb +16 -17
  129. data/lib/puppet/util/settings/file_setting.rb +14 -2
  130. data/lib/puppet/util/windows/security.rb +96 -32
  131. data/spec/integration/file_serving/terminus_helper_spec.rb +1 -1
  132. data/spec/integration/indirector/direct_file_server_spec.rb +9 -15
  133. data/spec/integration/indirector/file_content/file_server_spec.rb +1 -1
  134. data/spec/integration/indirector/file_metadata/file_server_spec.rb +1 -1
  135. data/spec/integration/provider/package_spec.rb +4 -0
  136. data/spec/integration/provider/service/init_spec.rb +8 -2
  137. data/spec/integration/reference/providers_spec.rb +1 -1
  138. data/spec/integration/ssl/certificate_request_spec.rb +1 -2
  139. data/spec/integration/ssl/certificate_revocation_list_spec.rb +1 -2
  140. data/spec/integration/ssl/host_spec.rb +1 -2
  141. data/spec/integration/transaction_spec.rb +25 -17
  142. data/spec/integration/type/exec_spec.rb +77 -0
  143. data/spec/integration/type/file_spec.rb +322 -2
  144. data/spec/integration/util/windows/security_spec.rb +393 -230
  145. data/spec/integration/util_spec.rb +16 -0
  146. data/spec/lib/puppet_spec/files.rb +3 -7
  147. data/spec/unit/application/apply_spec.rb +0 -9
  148. data/spec/unit/application/inspect_spec.rb +1 -0
  149. data/spec/unit/configurer/downloader_spec.rb +3 -3
  150. data/spec/unit/face/certificate_spec.rb +6 -2
  151. data/spec/unit/file_bucket/dipper_spec.rb +67 -10
  152. data/spec/unit/file_bucket/file_spec.rb +22 -28
  153. data/spec/unit/file_serving/content_spec.rb +1 -1
  154. data/spec/unit/file_serving/metadata_spec.rb +30 -3
  155. data/spec/unit/indirector/facts/inventory_service_spec.rb +22 -0
  156. data/spec/unit/indirector/file_bucket_file/file_spec.rb +21 -24
  157. data/spec/unit/indirector/node/store_configs_spec.rb +1 -0
  158. data/spec/unit/indirector/resource/ral_spec.rb +1 -1
  159. data/spec/unit/indirector/resource_type/parser_spec.rb +2 -2
  160. data/spec/unit/indirector/rest_spec.rb +1 -1
  161. data/spec/unit/network/handler/ca_spec.rb +1 -1
  162. data/spec/unit/network/http/mongrel/rest_spec.rb +54 -25
  163. data/spec/unit/parameter_spec.rb +36 -0
  164. data/spec/unit/parser/parser_spec.rb +4 -0
  165. data/spec/unit/property_spec.rb +2 -2
  166. data/spec/unit/provider/exec/windows_spec.rb +2 -8
  167. data/spec/unit/provider/file/posix_spec.rb +6 -0
  168. data/spec/unit/provider/file/windows_spec.rb +18 -0
  169. data/spec/unit/provider/group/windows_adsi_spec.rb +22 -6
  170. data/spec/unit/provider/mount/parsed_spec.rb +1 -1
  171. data/spec/unit/provider/package/msi_spec.rb +2 -2
  172. data/spec/unit/provider/scheduled_task/win32_taskscheduler_spec.rb +1571 -0
  173. data/spec/unit/provider/service/launchd_spec.rb +143 -130
  174. data/spec/unit/provider/ssh_authorized_key/parsed_spec.rb +5 -0
  175. data/spec/unit/provider/user/user_role_add_spec.rb +39 -9
  176. data/spec/unit/provider/user/useradd_spec.rb +1 -1
  177. data/spec/unit/provider/user/windows_adsi_spec.rb +8 -1
  178. data/spec/unit/rb_tree_map_spec.rb +572 -0
  179. data/spec/unit/resource/catalog_spec.rb +1 -1
  180. data/spec/unit/simple_graph_spec.rb +9 -9
  181. data/spec/unit/ssl/host_spec.rb +60 -12
  182. data/spec/unit/transaction/report_spec.rb +3 -3
  183. data/spec/unit/transaction_spec.rb +394 -11
  184. data/spec/unit/type/exec_spec.rb +35 -15
  185. data/spec/unit/type/file/content_spec.rb +11 -10
  186. data/spec/unit/type/file/mode_spec.rb +73 -19
  187. data/spec/unit/type/file/source_spec.rb +1 -1
  188. data/spec/unit/type/file_spec.rb +15 -0
  189. data/spec/unit/type/group_spec.rb +1 -1
  190. data/spec/unit/type/mount_spec.rb +5 -5
  191. data/spec/unit/type/resources_spec.rb +3 -3
  192. data/spec/unit/type/scheduled_task_spec.rb +102 -0
  193. data/spec/unit/type/ssh_authorized_key_spec.rb +2 -3
  194. data/spec/unit/type/user_spec.rb +2 -1
  195. data/spec/unit/type_spec.rb +48 -4
  196. data/spec/unit/util/adsi_spec.rb +18 -7
  197. data/spec/unit/util/checksums_spec.rb +20 -2
  198. data/spec/unit/util/execution_stub_spec.rb +10 -5
  199. data/spec/unit/util/logging_spec.rb +6 -6
  200. data/spec/unit/util/rdoc/parser_spec.rb +1 -1
  201. data/spec/unit/util/reference_spec.rb +29 -0
  202. data/spec/unit/util/settings/file_setting_spec.rb +8 -2
  203. data/spec/unit/util_spec.rb +115 -0
  204. data/test/other/transactions.rb +5 -11
  205. data/test/ral/type/exec.rb +1 -1
  206. metadata +24 -11
@@ -37,7 +37,7 @@ module Puppet
37
37
  ensurable
38
38
 
39
39
  newproperty(:disk, :array_matching => :all, :parent => Puppet::Property::VDev) do
40
- desc "The disk(s) for this pool. Can be an array or space separated string"
40
+ desc "The disk(s) for this pool. Can be an array or a space separated string."
41
41
  end
42
42
 
43
43
  newproperty(:mirror, :array_matching => :all, :parent => Puppet::Property::MultiVDev) do
@@ -71,7 +71,7 @@ module Puppet
71
71
  end
72
72
 
73
73
  newproperty(:log, :array_matching => :all, :parent => Puppet::Property::VDev) do
74
- desc "Log disks for this pool. (doesn't support mirroring yet)"
74
+ desc "Log disks for this pool. This type does not currently support mirroring of log disks."
75
75
  end
76
76
 
77
77
  newparam(:pool) do
@@ -80,7 +80,7 @@ module Puppet
80
80
  end
81
81
 
82
82
  newparam(:raid_parity) do
83
- desc "Determines parity when using raidz property."
83
+ desc "Determines parity when using the `raidz` parameter."
84
84
  end
85
85
 
86
86
  validate do
@@ -191,7 +191,15 @@ module Util
191
191
  return bin if FileTest.file? bin and FileTest.executable? bin
192
192
  else
193
193
  ENV['PATH'].split(File::PATH_SEPARATOR).each do |dir|
194
- dest=File.join(dir, bin)
194
+ dest = File.expand_path(File.join(dir, bin))
195
+ if Puppet.features.microsoft_windows? && File.extname(dest).empty?
196
+ exts = ENV['PATHEXT']
197
+ exts = exts ? exts.split(File::PATH_SEPARATOR) : %w[.COM .EXE .BAT .CMD]
198
+ exts.each do |ext|
199
+ destext = File.expand_path(dest + ext)
200
+ return destext if FileTest.file? destext and FileTest.executable? destext
201
+ end
202
+ end
195
203
  return dest if FileTest.file? dest and FileTest.executable? dest
196
204
  end
197
205
  end
@@ -355,17 +363,21 @@ module Util
355
363
  stdout = arguments[:squelch] ? File.open(null_file, 'w') : Tempfile.new('puppet')
356
364
  stderr = arguments[:combine] ? stdout : File.open(null_file, 'w')
357
365
 
358
-
359
366
  exec_args = [command, arguments, stdin, stdout, stderr]
360
367
 
361
368
  if execution_stub = Puppet::Util::ExecutionStub.current_value
362
369
  return execution_stub.call(*exec_args)
363
370
  elsif Puppet.features.posix?
364
371
  child_pid = execute_posix(*exec_args)
365
- child_status = Process.waitpid2(child_pid).last.exitstatus
372
+ exit_status = Process.waitpid2(child_pid).last.exitstatus
366
373
  elsif Puppet.features.microsoft_windows?
367
374
  child_pid = execute_windows(*exec_args)
368
- child_status = Process.waitpid2(child_pid).last
375
+ exit_status = Process.waitpid2(child_pid).last
376
+ # $CHILD_STATUS is not set when calling win32/process Process.create
377
+ # and since it's read-only, we can't set it. But we can execute a
378
+ # a shell that simply returns the desired exit status, which has the
379
+ # desired effect.
380
+ %x{#{ENV['COMSPEC']} /c exit #{exit_status}}
369
381
  end
370
382
 
371
383
  [stdin, stdout, stderr].each {|io| io.close rescue nil}
@@ -376,8 +388,8 @@ module Util
376
388
  Puppet.warning "Could not get output" unless output
377
389
  end
378
390
 
379
- if arguments[:failonfail] and child_status != 0
380
- raise ExecutionFailure, "Execution of '#{str}' returned #{child_status}: #{output}"
391
+ if arguments[:failonfail] and exit_status != 0
392
+ raise ExecutionFailure, "Execution of '#{str}' returned #{exit_status}: #{output}"
381
393
  end
382
394
 
383
395
  output
@@ -490,6 +502,12 @@ module Util
490
502
  end
491
503
  end
492
504
  module_function :secure_open
505
+
506
+ # Because IO#binread is only available in 1.9
507
+ def binread(file)
508
+ File.open(file, 'rb') { |f| f.read }
509
+ end
510
+ module_function :binread
493
511
  end
494
512
  end
495
513
 
@@ -51,12 +51,13 @@ module Puppet::Util::ADSI
51
51
 
52
52
  def sid_for_account(name)
53
53
  sid = nil
54
-
55
- execquery(
56
- "SELECT Sid from Win32_Account
57
- WHERE Name = '#{name}' AND LocalAccount = true"
58
- ).each {|u| sid ||= u.Sid}
59
-
54
+ if name =~ /\\/
55
+ domain, name = name.split('\\', 2)
56
+ query = "SELECT Sid from Win32_Account WHERE Name = '#{name}' AND Domain = '#{domain}' AND LocalAccount = true"
57
+ else
58
+ query = "SELECT Sid from Win32_Account WHERE Name = '#{name}' AND LocalAccount = true"
59
+ end
60
+ execquery(query).each { |u| sid ||= u.Sid }
60
61
  sid
61
62
  end
62
63
  end
@@ -138,7 +139,7 @@ module Puppet::Util::ADSI
138
139
  def groups
139
140
  # WIN32OLE objects aren't enumerable, so no map
140
141
  groups = []
141
- native_user.Groups.each {|g| groups << g.Name}
142
+ native_user.Groups.each {|g| groups << g.Name} rescue nil
142
143
  groups
143
144
  end
144
145
 
@@ -174,6 +175,8 @@ module Puppet::Util::ADSI
174
175
  end
175
176
 
176
177
  def self.create(name)
178
+ # Windows error 1379: The specified local group already exists.
179
+ raise Puppet::Error.new( "Cannot create user if group '#{name}' exists." ) if Puppet::Util::ADSI::Group.exists? name
177
180
  new(name, Puppet::Util::ADSI.create(name, 'user'))
178
181
  end
179
182
 
@@ -264,6 +267,8 @@ module Puppet::Util::ADSI
264
267
  end
265
268
 
266
269
  def self.create(name)
270
+ # Windows error 2224: The account already exists.
271
+ raise Puppet::Error.new( "Cannot create group if user '#{name}' exists." ) if Puppet::Util::ADSI::User.exists? name
267
272
  new(name, Puppet::Util::ADSI.create(name, 'group'))
268
273
  end
269
274
 
@@ -136,7 +136,7 @@ module Puppet::Util::Checksums
136
136
  # Perform an incremental checksum on a file.
137
137
  def checksum_file(digest, filename, lite = false)
138
138
  buffer = lite ? 512 : 4096
139
- File.open(filename, 'r') do |file|
139
+ File.open(filename, 'rb') do |file|
140
140
  while content = file.read(buffer)
141
141
  digest << content
142
142
  break if lite
@@ -67,7 +67,7 @@ module Puppet::Util::Diff
67
67
  tempfile.open
68
68
  tempfile.print string
69
69
  tempfile.close
70
- print diff(path, tempfile.path)
70
+ notice "\n" + diff(path, tempfile.path)
71
71
  tempfile.delete
72
72
  end
73
73
  end
@@ -16,7 +16,7 @@ module Puppet::Util::NagiosMaker
16
16
  type.ensurable
17
17
 
18
18
  type.newparam(nagtype.namevar, :namevar => true) do
19
- desc "The name parameter for Nagios type #{nagtype.name}"
19
+ desc "The name of this nagios_#{nagtype.name} resource."
20
20
  end
21
21
 
22
22
  # We deduplicate the parameters because it makes sense to allow Naginator to have dupes.
@@ -33,7 +33,7 @@ module Puppet::Util::NagiosMaker
33
33
  end
34
34
 
35
35
  type.newproperty(:target) do
36
- desc 'target'
36
+ desc 'The target.'
37
37
 
38
38
  defaultto do
39
39
  resource.class.defaultprovider.default_target
@@ -81,10 +81,20 @@ class Puppet::Util::Reference
81
81
  self.dynamic
82
82
  end
83
83
 
84
- def h(name, level)
84
+ def markdown_header(name, level)
85
85
  "#{HEADER_LEVELS[level]} #{name}\n\n"
86
86
  end
87
87
 
88
+ def markdown_definitionlist(term, definition)
89
+ lines = definition.split("\n")
90
+ str = "#{term}\n: #{lines.shift}\n"
91
+ lines.each do |line|
92
+ str << " " if line =~ /\S/
93
+ str << "#{line}\n"
94
+ end
95
+ str << "\n"
96
+ end
97
+
88
98
  def initialize(name, options = {}, &block)
89
99
  @name = name
90
100
  options.each do |option, value|
@@ -109,31 +119,20 @@ class Puppet::Util::Reference
109
119
  ":#{name.to_s.capitalize}: #{value}\n"
110
120
  end
111
121
 
112
- def paramwrap(name, text, options = {})
113
- options[:level] ||= 5
114
- #str = "#{name} : "
115
- str = h(name, options[:level])
116
- str += "- **namevar**\n\n" if options[:namevar]
117
- str += text
118
- #str += text.gsub(/\n/, "\n ")
119
-
120
- str += "\n\n"
121
- end
122
-
123
122
  def text
124
123
  puts output
125
124
  end
126
125
 
127
126
  def to_markdown(withcontents = true)
128
127
  # First the header
129
- text = h(@title, 1)
130
- text += "\n\n**This page is autogenerated; any changes will get overwritten** *(last generated on #{Time.now.to_s})*\n\n"
128
+ text = markdown_header(@title, 1)
129
+ text << "\n\n**This page is autogenerated; any changes will get overwritten** *(last generated on #{Time.now.to_s})*\n\n"
131
130
 
132
- text += @header
131
+ text << @header
133
132
 
134
- text += generate
133
+ text << generate
135
134
 
136
- text += self.class.footer if withcontents
135
+ text << self.class.footer if withcontents
137
136
 
138
137
  text
139
138
  end
@@ -91,7 +91,20 @@ class Puppet::Util::Settings::FileSetting < Puppet::Util::Settings::Setting
91
91
  resource = Puppet::Resource.new(:file, path)
92
92
 
93
93
  if Puppet[:manage_internal_file_permissions]
94
- resource[:mode] = self.mode if self.mode
94
+ if self.mode
95
+ # This ends up mimicking the munge method of the mode
96
+ # parameter to make sure that we're always passing the string
97
+ # version of the octal number. If we were setting the
98
+ # 'should' value for mode rather than the 'is', then the munge
99
+ # method would be called for us automatically. Normally, one
100
+ # wouldn't need to call the munge method manually, since
101
+ # 'should' gets set by the provider and it should be able to
102
+ # provide the data in the appropriate format.
103
+ mode = self.mode
104
+ mode = mode.to_i(8) if mode.is_a?(String)
105
+ mode = mode.to_s(8)
106
+ resource[:mode] = mode
107
+ end
95
108
 
96
109
  # REMIND fails on Windows because chown/chgrp functionality not supported yet
97
110
  if Puppet.features.root? and !Puppet.features.microsoft_windows?
@@ -122,4 +135,3 @@ class Puppet::Util::Settings::FileSetting < Puppet::Util::Settings::Setting
122
135
  }
123
136
  end
124
137
  end
125
-
@@ -60,6 +60,7 @@
60
60
  # (and different) owner or group.
61
61
 
62
62
  require 'puppet/util/windows'
63
+ require 'pathname'
63
64
 
64
65
  require 'win32/security'
65
66
 
@@ -68,6 +69,7 @@ require 'windows/handle'
68
69
  require 'windows/security'
69
70
  require 'windows/process'
70
71
  require 'windows/memory'
72
+ require 'windows/volume'
71
73
 
72
74
  module Puppet::Util::Windows::Security
73
75
  include Windows::File
@@ -76,6 +78,7 @@ module Puppet::Util::Windows::Security
76
78
  include Windows::Process
77
79
  include Windows::Memory
78
80
  include Windows::MSVCRT::Buffer
81
+ include Windows::Volume
79
82
 
80
83
  extend Puppet::Util::Windows::Security
81
84
 
@@ -92,6 +95,7 @@ module Puppet::Util::Windows::Security
92
95
  S_IRWXU = 0000700
93
96
  S_IRWXG = 0000070
94
97
  S_IRWXO = 0000007
98
+ S_ISVTX = 0001000
95
99
  S_IEXTRA = 02000000 # represents an extra ace
96
100
 
97
101
  # constants that are missing from Windows::Security
@@ -116,6 +120,8 @@ module Puppet::Util::Windows::Security
116
120
  # SE_BACKUP_NAME privilege in their process token can get the owner
117
121
  # for objects they do not have read access to.
118
122
  def get_owner(path)
123
+ return unless supports_acl?(path)
124
+
119
125
  get_sid(OWNER_SECURITY_INFORMATION, path)
120
126
  end
121
127
 
@@ -136,9 +142,24 @@ module Puppet::Util::Windows::Security
136
142
  # SE_BACKUP_NAME privilege in their process token can get the group
137
143
  # for objects they do not have read access to.
138
144
  def get_group(path)
145
+ return unless supports_acl?(path)
146
+
139
147
  get_sid(GROUP_SECURITY_INFORMATION, path)
140
148
  end
141
149
 
150
+ def supports_acl?(path)
151
+ flags = 0.chr * 4
152
+
153
+ root = Pathname.new(path).enum_for(:ascend).to_a.last.to_s
154
+ # 'A trailing backslash is required'
155
+ root = "#{root}\\" unless root =~ /[\/\\]$/
156
+ unless GetVolumeInformation(root, nil, 0, nil, nil, flags, nil, 0)
157
+ raise Puppet::Util::Windows::Error.new("Failed to get volume information")
158
+ end
159
+
160
+ (flags.unpack('L')[0] & Windows::File::FILE_PERSISTENT_ACLS) != 0
161
+ end
162
+
142
163
  def change_sid(old_sid, new_sid, info, path)
143
164
  if old_sid != new_sid
144
165
  mode = get_mode(path)
@@ -173,15 +194,23 @@ module Puppet::Util::Windows::Security
173
194
  end
174
195
 
175
196
  def add_attributes(path, flags)
176
- set_attributes(path, get_attributes(path) | flags)
197
+ oldattrs = get_attributes(path)
198
+
199
+ if (oldattrs | flags) != oldattrs
200
+ set_attributes(path, oldattrs | flags)
201
+ end
177
202
  end
178
203
 
179
204
  def remove_attributes(path, flags)
180
- set_attributes(path, get_attributes(path) & ~flags)
205
+ oldattrs = get_attributes(path)
206
+
207
+ if (oldattrs & ~flags) != oldattrs
208
+ set_attributes(path, oldattrs & ~flags)
209
+ end
181
210
  end
182
211
 
183
212
  def set_attributes(path, flags)
184
- raise Puppet::Util::Windows::Error.new("Failed to set file attributes") if SetFileAttributes(path, flags) == 0
213
+ raise Puppet::Util::Windows::Error.new("Failed to set file attributes") unless SetFileAttributes(path, flags)
185
214
  end
186
215
 
187
216
  MASK_TO_MODE = {
@@ -192,15 +221,17 @@ module Puppet::Util::Windows::Security
192
221
 
193
222
  # Get the mode of the object referenced by +path+. The returned
194
223
  # integer value represents the POSIX-style read, write, and execute
195
- # modes for the user, group, and other classes, e.g. 0640. Other
196
- # modes, e.g. S_ISVTX, are not supported. Any user with read access
197
- # to an object can get the mode. Only a user with the SE_BACKUP_NAME
198
- # privilege in their process token can get the mode for objects they
199
- # do not have read access to.
224
+ # modes for the user, group, and other classes, e.g. 0640. Any user
225
+ # with read access to an object can get the mode. Only a user with
226
+ # the SE_BACKUP_NAME privilege in their process token can get the
227
+ # mode for objects they do not have read access to.
200
228
  def get_mode(path)
229
+ return unless supports_acl?(path)
230
+
201
231
  owner_sid = get_owner(path)
202
232
  group_sid = get_group(path)
203
233
  well_known_world_sid = Win32::Security::SID::Everyone
234
+ well_known_nobody_sid = Win32::Security::SID::Nobody
204
235
 
205
236
  with_privilege(SE_BACKUP_NAME) do
206
237
  open_file(path, READ_CONTROL) do |handle|
@@ -226,6 +257,13 @@ module Puppet::Util::Windows::Security
226
257
  mode |= (v << 6) | (v << 3) | v
227
258
  end
228
259
  end
260
+ if File.directory?(path) and (ace[:mask] & (FILE_WRITE_DATA | FILE_EXECUTE | FILE_DELETE_CHILD)) == (FILE_WRITE_DATA | FILE_EXECUTE)
261
+ mode |= S_ISVTX;
262
+ end
263
+ when well_known_nobody_sid
264
+ if (ace[:mask] & FILE_APPEND_DATA).nonzero?
265
+ mode |= S_ISVTX
266
+ end
229
267
  else
230
268
  #puts "Warning, unable to map SID into POSIX mode: #{ace[:sid]}"
231
269
  mode |= S_IEXTRA
@@ -248,17 +286,18 @@ module Puppet::Util::Windows::Security
248
286
  S_IROTH => FILE_GENERIC_READ,
249
287
  S_IWOTH => FILE_GENERIC_WRITE,
250
288
  S_IXOTH => (FILE_GENERIC_EXECUTE & ~FILE_READ_ATTRIBUTES),
251
- (S_IWOTH | S_IXOTH) => FILE_DELETE_CHILD,
252
289
  }
253
290
 
254
291
  # Set the mode of the object referenced by +path+ to the specified
255
292
  # +mode+. The mode should be specified as POSIX-stye read, write,
256
293
  # and execute modes for the user, group, and other classes,
257
- # e.g. 0640. Other modes, e.g. S_ISVTX, are not supported. By
258
- # default, the DACL is set to protected, meaning it does not inherit
259
- # access control entries from parent objects. This can be changed by
260
- # setting +protected+ to false. The owner of the object (with
261
- # READ_CONTROL and WRITE_DACL access) can always change the
294
+ # e.g. 0640. The sticky bit, S_ISVTX, is supported, but is only
295
+ # meaningful for directories. If set, group and others are not
296
+ # allowed to delete child objects for which they are not the owner.
297
+ # By default, the DACL is set to protected, meaning it does not
298
+ # inherit access control entries from parent objects. This can be
299
+ # changed by setting +protected+ to false. The owner of the object
300
+ # (with READ_CONTROL and WRITE_DACL access) can always change the
262
301
  # mode. Only a user with the SE_BACKUP_NAME and SE_RESTORE_NAME
263
302
  # privileges in their process token can change the mode for objects
264
303
  # that they do not have read and write access to.
@@ -266,10 +305,12 @@ module Puppet::Util::Windows::Security
266
305
  owner_sid = get_owner(path)
267
306
  group_sid = get_group(path)
268
307
  well_known_world_sid = Win32::Security::SID::Everyone
308
+ well_known_nobody_sid = Win32::Security::SID::Nobody
269
309
 
270
310
  owner_allow = STANDARD_RIGHTS_ALL | FILE_READ_ATTRIBUTES | FILE_WRITE_ATTRIBUTES
271
311
  group_allow = STANDARD_RIGHTS_READ | FILE_READ_ATTRIBUTES | SYNCHRONIZE
272
312
  other_allow = STANDARD_RIGHTS_READ | FILE_READ_ATTRIBUTES | SYNCHRONIZE
313
+ nobody_allow = 0
273
314
 
274
315
  MODE_TO_MASK.each do |k,v|
275
316
  if ((mode >> 6) & k) == k
@@ -283,12 +324,36 @@ module Puppet::Util::Windows::Security
283
324
  end
284
325
  end
285
326
 
327
+ if (mode & S_ISVTX).nonzero?
328
+ nobody_allow |= FILE_APPEND_DATA;
329
+ end
330
+
331
+ isdir = File.directory?(path)
332
+
333
+ if isdir
334
+ if (mode & (S_IWUSR | S_IXUSR)) == (S_IWUSR | S_IXUSR)
335
+ owner_allow |= FILE_DELETE_CHILD
336
+ end
337
+ if (mode & (S_IWGRP | S_IXGRP)) == (S_IWGRP | S_IXGRP) and (mode & S_ISVTX) == 0
338
+ group_allow |= FILE_DELETE_CHILD
339
+ end
340
+ if (mode & (S_IWOTH | S_IXOTH)) == (S_IWOTH | S_IXOTH) and (mode & S_ISVTX) == 0
341
+ other_allow |= FILE_DELETE_CHILD
342
+ end
343
+ end
344
+
286
345
  # if owner and group the same, then map group permissions to the one owner ACE
287
346
  isownergroup = owner_sid == group_sid
288
347
  if isownergroup
289
348
  owner_allow |= group_allow
290
349
  end
291
350
 
351
+ # if any ACE allows write, then clear readonly bit, but do this before we overwrite
352
+ # the DACl and lose our ability to set the attribute
353
+ if ((owner_allow | group_allow | other_allow ) & FILE_WRITE_DATA) == FILE_WRITE_DATA
354
+ remove_attributes(path, FILE_ATTRIBUTE_READONLY)
355
+ end
356
+
292
357
  set_acl(path, protected) do |acl|
293
358
  #puts "ace: owner #{owner_sid}, mask 0x#{owner_allow.to_s(16)}"
294
359
  add_access_allowed_ace(acl, owner_allow, owner_sid)
@@ -301,8 +366,11 @@ module Puppet::Util::Windows::Security
301
366
  #puts "ace: other #{well_known_world_sid}, mask 0x#{other_allow.to_s(16)}"
302
367
  add_access_allowed_ace(acl, other_allow, well_known_world_sid)
303
368
 
369
+ #puts "ace: nobody #{well_known_nobody_sid}, mask 0x#{nobody_allow.to_s(16)}"
370
+ add_access_allowed_ace(acl, nobody_allow, well_known_nobody_sid)
371
+
304
372
  # add inheritable aces for child dirs and files that are created within the dir
305
- if File.directory?(path)
373
+ if isdir
306
374
  inherit = INHERIT_ONLY_ACE | OBJECT_INHERIT_ACE | CONTAINER_INHERIT_ACE
307
375
 
308
376
  add_access_allowed_ace(acl, owner_allow, Win32::Security::SID::CreatorOwner, inherit)
@@ -311,11 +379,6 @@ module Puppet::Util::Windows::Security
311
379
  end
312
380
  end
313
381
 
314
- # if any ACE allows write, then clear readonly bit
315
- if ((owner_allow | group_allow | other_allow ) & FILE_WRITE_DATA) == FILE_WRITE_DATA
316
- remove_attributes(path, FILE_ATTRIBUTE_READONLY)
317
- end
318
-
319
382
  nil
320
383
  end
321
384
 
@@ -331,7 +394,7 @@ module Puppet::Util::Windows::Security
331
394
  raise Puppet::Util::Windows::Error.new("Failed to initialize ACL")
332
395
  end
333
396
 
334
- raise Puppet::Util::Windows::Error.new("Invalid DACL") if IsValidAcl(acl) == 0
397
+ raise Puppet::Util::Windows::Error.new("Invalid DACL") unless IsValidAcl(acl)
335
398
 
336
399
  yield acl
337
400
 
@@ -348,9 +411,9 @@ module Puppet::Util::Windows::Security
348
411
 
349
412
  def add_access_allowed_ace(acl, mask, sid, inherit = NO_INHERITANCE)
350
413
  string_to_sid_ptr(sid) do |sid_ptr|
351
- raise Puppet::Util::Windows::Error.new("Invalid SID") if IsValidSid(sid_ptr) == 0
414
+ raise Puppet::Util::Windows::Error.new("Invalid SID") unless IsValidSid(sid_ptr)
352
415
 
353
- if AddAccessAllowedAceEx(acl, ACL_REVISION, inherit, mask, sid_ptr) == 0
416
+ unless AddAccessAllowedAceEx(acl, ACL_REVISION, inherit, mask, sid_ptr)
354
417
  raise Puppet::Util::Windows::Error.new("Failed to add access control entry")
355
418
  end
356
419
  end
@@ -358,9 +421,9 @@ module Puppet::Util::Windows::Security
358
421
 
359
422
  def add_access_denied_ace(acl, mask, sid)
360
423
  string_to_sid_ptr(sid) do |sid_ptr|
361
- raise Puppet::Util::Windows::Error.new("Invalid SID") if IsValidSid(sid_ptr) == 0
424
+ raise Puppet::Util::Windows::Error.new("Invalid SID") unless IsValidSid(sid_ptr)
362
425
 
363
- if AddAccessDeniedAce(acl, ACL_REVISION, mask, sid_ptr) == 0
426
+ unless AddAccessDeniedAce(acl, ACL_REVISION, mask, sid_ptr)
364
427
  raise Puppet::Util::Windows::Error.new("Failed to add access control entry")
365
428
  end
366
429
  end
@@ -369,7 +432,7 @@ module Puppet::Util::Windows::Security
369
432
  def get_dacl(handle)
370
433
  get_dacl_ptr(handle) do |dacl_ptr|
371
434
  # REMIND: need to handle NULL DACL
372
- raise Puppet::Util::Windows::Error.new("Invalid DACL") if IsValidAcl(dacl_ptr) == 0
435
+ raise Puppet::Util::Windows::Error.new("Invalid DACL") unless IsValidAcl(dacl_ptr)
373
436
 
374
437
  # ACL structure, size and count are the important parts. The
375
438
  # size includes both the ACL structure and all the ACEs.
@@ -390,7 +453,8 @@ module Puppet::Util::Windows::Security
390
453
 
391
454
  0.upto(ace_count - 1) do |i|
392
455
  ace_ptr = [0].pack('L')
393
- next if GetAce(dacl_ptr, i, ace_ptr) == 0
456
+
457
+ next unless GetAce(dacl_ptr, i, ace_ptr)
394
458
 
395
459
  # ACE structures vary depending on the type. All structures
396
460
  # begin with an ACE header, which specifies the type, flags
@@ -492,9 +556,9 @@ module Puppet::Util::Windows::Security
492
556
  sid_buf = 0.chr * 256
493
557
  str_ptr = 0.chr * 4
494
558
 
495
- raise Puppet::Util::Windows::Error.new("Invalid SID") if IsValidSid(psid) == 0
559
+ raise Puppet::Util::Windows::Error.new("Invalid SID") unless IsValidSid(psid)
496
560
 
497
- raise Puppet::Util::Windows::Error.new("Failed to convert binary SID") if ConvertSidToStringSid(psid, str_ptr) == 0
561
+ raise Puppet::Util::Windows::Error.new("Failed to convert binary SID") unless ConvertSidToStringSid(psid, str_ptr)
498
562
 
499
563
  begin
500
564
  strncpy(sid_buf, str_ptr.unpack('L')[0], sid_buf.size - 1)
@@ -562,14 +626,14 @@ module Puppet::Util::Windows::Security
562
626
  tmpLuid = 0.chr * 8
563
627
 
564
628
  # Get the LUID for specified privilege.
565
- if LookupPrivilegeValue("", privilege, tmpLuid) == 0
629
+ unless LookupPrivilegeValue("", privilege, tmpLuid)
566
630
  raise Puppet::Util::Windows::Error.new("Failed to lookup privilege")
567
631
  end
568
632
 
569
633
  # DWORD + [LUID + DWORD]
570
634
  tkp = [1].pack('L') + tmpLuid + [enable ? SE_PRIVILEGE_ENABLED : 0].pack('L')
571
635
 
572
- if AdjustTokenPrivileges(token, 0, tkp, tkp.length , nil, nil) == 0
636
+ unless AdjustTokenPrivileges(token, 0, tkp, tkp.length , nil, nil)
573
637
  raise Puppet::Util::Windows::Error.new("Failed to adjust process privileges")
574
638
  end
575
639
  end
@@ -579,7 +643,7 @@ module Puppet::Util::Windows::Security
579
643
  def with_process_token(access)
580
644
  token = 0.chr * 4
581
645
 
582
- if OpenProcessToken(GetCurrentProcess(), access, token) == 0
646
+ unless OpenProcessToken(GetCurrentProcess(), access, token)
583
647
  raise Puppet::Util::Windows::Error.new("Failed to open process token")
584
648
  end
585
649
  begin