summaryrefslogtreecommitdiff
path: root/lib/chef/provider/windows_script.rb
blob: a93319a35a0e8dee49499fe34b94fdb92ce404af (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
#
# Author:: Adam Edwards (<adamed@chef.io>)
# Copyright:: Copyright (c) Chef Software Inc.
# License:: Apache License, Version 2.0
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
#     http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
#

require_relative "script"
require_relative "../mixin/windows_architecture_helper"
require_relative "../win32/security" if ChefUtils.windows?
require "tempfile" unless defined?(Tempfile)

class Chef
  class Provider
    class WindowsScript < Chef::Provider::Script

      protected

      attr_accessor :script_file_path

      include Chef::Mixin::WindowsArchitectureHelper

      def target_architecture
        @target_architecture ||= if new_resource.architecture.nil?
                                   node_windows_architecture(run_context.node)
                                 else
                                   new_resource.architecture
                                 end
      end

      def basepath
        if forced_32bit_override_required?(run_context.node, target_architecture)
          wow64_directory
        else
          run_context.node["kernel"]["os_info"]["system_directory"]
        end
      end

      def with_wow64_redirection_disabled
        wow64_redirection_state = nil

        if wow64_architecture_override_required?(run_context.node, target_architecture)
          wow64_redirection_state = disable_wow64_file_redirection(run_context.node)
        end

        begin
          yield
        rescue
          raise
        ensure
          unless wow64_redirection_state.nil?
            restore_wow64_file_redirection(run_context.node, wow64_redirection_state)
          end
        end
      end

      def command
        "\"#{interpreter}\" #{flags} \"#{script_file_path}\""
      end

      def grant_alternate_user_read_access(file_path)
        # Do nothing if an alternate user isn't specified -- the file
        # will already have the correct permissions for the user as part
        # of the default ACL behavior on Windows.
        return if new_resource.user.nil?

        # Duplicate the script file's existing DACL
        # so we can add an ACE later
        securable_object = Chef::ReservedNames::Win32::Security::SecurableObject.new(file_path)
        aces = securable_object.security_descriptor.dacl.reduce([]) { |result, current| result.push(current) }

        username = new_resource.user

        if new_resource.domain
          username = new_resource.domain + '\\' + new_resource.user
        end

        # Create an ACE that allows the alternate user read access to the script
        # file so it can be read and executed.
        user_sid = Chef::ReservedNames::Win32::Security::SID.from_account(username)
        read_ace = Chef::ReservedNames::Win32::Security::ACE.access_allowed(user_sid, Chef::ReservedNames::Win32::API::Security::GENERIC_READ | Chef::ReservedNames::Win32::API::Security::GENERIC_EXECUTE, 0)
        aces.push(read_ace)
        acl = Chef::ReservedNames::Win32::Security::ACL.create(aces)

        # This actually applies the modified DACL to the file
        # Use parentheses to bypass RuboCop / ChefStyle warning
        # about useless setter
        (securable_object.dacl = acl)
      end

      def with_temp_script_file
        Tempfile.open(["chef-script", script_extension]) do |script_file|
          script_file.puts(code)
          script_file.close

          grant_alternate_user_read_access(script_file.path)

          # This needs to be set here so that the call to #command in Execute works.
          self.script_file_path = script_file.path

          yield

          self.script_file_path = nil
        end
      end

      def input
        nil
      end

      public

      action :run do
        with_wow64_redirection_disabled do
          with_temp_script_file do
            super()
          end
        end
      end

      def script_extension
        raise Chef::Exceptions::Override, "You must override #{__method__} in #{self}"
      end
    end
  end
end