summaryrefslogtreecommitdiff
path: root/lib/chef/util/windows/logon_session.rb
blob: 3d1f8588259ba770bbf878d2fe2156733f7c0ac7 (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
#
# Author:: Adam Edwards (<adamed@chef.io>)
#
# Copyright:: Copyright (c) 2015 Chef Software, Inc.
#
# 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 "chef/win32/api/security" if Chef::Platform.windows?
require "chef/mixin/wide_string"

class Chef
  class Util
    class Windows
      class LogonSession
        include Chef::Mixin::WideString

        def initialize(username, password, domain = nil)
          if username.nil? || password.nil?
            raise ArgumentError, "The logon session must be initialize with non-nil user name and password parameters"
          end

          @original_username = username
          @original_password = password
          @original_domain = domain
          @token = FFI::Buffer.new(:pointer)
          @session_opened = false
          @impersonating = false
        end

        def open
          if session_opened
            raise "Attempted to open a logon session that was already open."
          end

          username = wstring(original_username)
          password = wstring(original_password)
          domain = wstring(original_domain)

          status = Chef::ReservedNames::Win32::API::Security.LogonUserW(username, domain, password, Chef::ReservedNames::Win32::API::Security::LOGON32_LOGON_NEW_CREDENTIALS, Chef::ReservedNames::Win32::API::Security::LOGON32_PROVIDER_DEFAULT, token)

          if !status
            last_error = FFI::LastError.error
            raise Chef::Exceptions::Win32APIError, "Logon for user `#{original_username}` failed with Win32 status #{last_error}."
          end

          @session_opened = true
        end

        def close
          puts "UserContext DEBUG: in close"
          validate_session_open!

          if impersonating
            puts "UserContext DEBUG: impersonating, calling restore_user_context"
            restore_user_context
          end

          Chef::ReservedNames::Win32::API::System.CloseHandle(token.read_ulong)
          @token = nil
          @session_opened = false
        end

        def set_user_context
          validate_session_open!

          if ! session_opened
            raise "Attempted to set the user context before opening a session."
          end

          if impersonating
            raise "Attempt to set the user context when the user context is already set."
          end

          status = Chef::ReservedNames::Win32::API::Security.ImpersonateLoggedOnUser(token.read_ulong)

          if !status
            last_error = FFI::LastError.error
            raise Chef::Exceptions::Win32APIError, "Attempt to impersonate user `#{original_username}` failed with Win32 status #{last_error}."
          end

          @impersonating = true
        end

        def restore_user_context
          puts "UserContext DEBUG: calling restore_user_context"
          validate_session_open!

          if impersonating
            puts "UserContext DEBUG: impersonating, calling RevertToSelf"
            status = Chef::ReservedNames::Win32::API::Security.RevertToSelf
            puts "UserContext DEBUG: RevertToSelf return: '#{status}'"

            if !status
              last_error = FFI::LastError.error
              raise Chef::Exceptions::Win32APIError, "Unable to restore user context with Win32 status #{last_error}."
            end
          end

          @impersonating = false
        end

        protected

        attr_reader :original_username
        attr_reader :original_password
        attr_reader :original_domain

        attr_reader :token
        attr_reader :session_opened
        attr_reader :impersonating

        def validate_session_open!
          if ! session_opened
            raise "Attempted to set the user context before opening a session."
          end
        end
      end
    end
  end
end