module Puppet::Util::Windows::User
Constants
- ERROR_ACCOUNT_DISABLED
- ERROR_ACCOUNT_RESTRICTION
msdn.microsoft.com/en-us/library/windows/desktop/ms681385(v=vs.85).aspx These error codes indicate successful authentication but failure to logon for a separate reason
- ERROR_INVALID_LOGON_HOURS
- ERROR_INVALID_WORKSTATION
- POLICY_AUDIT_LOG_ADMIN
- POLICY_CREATE_ACCOUNT
- POLICY_CREATE_PRIVILEGE
- POLICY_CREATE_SECRET
- POLICY_GET_PRIVATE_INFORMATION
- POLICY_LOOKUP_NAMES
- POLICY_NOTIFICATION
- POLICY_SERVER_ADMIN
- POLICY_SET_AUDIT_REQUIREMENTS
- POLICY_SET_DEFAULT_QUOTA_LIMITS
- POLICY_TRUST_ADMIN
- POLICY_VIEW_AUDIT_INFORMATION
- POLICY_VIEW_LOCAL_INFORMATION
ACCESS_MASK flags for Policy Objects docs.microsoft.com/en-us/openspecs/windows_protocols/ms-lsad/b61b7268-987a-420b-84f9-6c75f8dc8558
- SECURITY_MAX_SID_SIZE
msdn.microsoft.com/en-us/library/windows/desktop/ee207397(v=vs.85).aspx
- WELL_KNOWN_SID_TYPE
msdn.microsoft.com/en-us/library/windows/desktop/aa379650(v=vs.85).aspx
Public Class Methods
# File lib/puppet/util/windows/user.rb 10 def admin? 11 return false unless check_token_membership 12 13 # if Vista or later, check for unrestricted process token 14 elevated_supported = Puppet::Util::Windows::Process.supports_elevated_security? 15 return elevated_supported ? Puppet::Util::Windows::Process.elevated_security? : true 16 end
# File lib/puppet/util/windows/user.rb 47 def check_token_membership 48 is_admin = false 49 FFI::MemoryPointer.new(:byte, SECURITY_MAX_SID_SIZE) do |sid_pointer| 50 FFI::MemoryPointer.new(:dword, 1) do |size_pointer| 51 size_pointer.write_uint32(SECURITY_MAX_SID_SIZE) 52 53 if CreateWellKnownSid(:WinBuiltinAdministratorsSid, FFI::Pointer::NULL, sid_pointer, size_pointer) == FFI::WIN32_FALSE 54 raise Puppet::Util::Windows::Error.new(_("Failed to create administrators SID")) 55 end 56 end 57 58 if IsValidSid(sid_pointer) == FFI::WIN32_FALSE 59 raise Puppet::Util::Windows::Error.new(_("Invalid SID")) 60 end 61 62 FFI::MemoryPointer.new(:win32_bool, 1) do |ismember_pointer| 63 if CheckTokenMembership(FFI::Pointer::NULL_HANDLE, sid_pointer, ismember_pointer) == FFI::WIN32_FALSE 64 raise Puppet::Util::Windows::Error.new(_("Failed to check membership")) 65 end 66 67 # Is administrators SID enabled in calling thread's access token? 68 is_admin = ismember_pointer.read_win32_bool 69 end 70 end 71 72 is_admin 73 end
Check if a given user is one of the default system accounts These accounts do not have a password and all checks done through logon attempt will fail docs.microsoft.com/en-us/windows/security/identity-protection/access-control/local-accounts#default-local-system-accounts
# File lib/puppet/util/windows/user.rb 30 def default_system_account?(name) 31 user_sid = Puppet::Util::Windows::SID.name_to_sid(name) 32 [Puppet::Util::Windows::SID::LocalSystem, Puppet::Util::Windows::SID::NtLocal, Puppet::Util::Windows::SID::NtNetwork].include?(user_sid) 33 end
# File lib/puppet/util/windows/user.rb 148 def get_rights(name) 149 user_info = Puppet::Util::Windows::SID.name_to_principal(name.sub(/^\.\\/, "#{Puppet::Util::Windows::ADSI.computer_name}\\")) 150 return "" unless user_info 151 152 rights = [] 153 rights_pointer = FFI::MemoryPointer.new(:pointer) 154 number_of_rights = FFI::MemoryPointer.new(:ulong) 155 sid_pointer = FFI::MemoryPointer.new(:byte, user_info.sid_bytes.length).write_array_of_uchar(user_info.sid_bytes) 156 157 new_lsa_policy_handle do |policy_handle| 158 result = LsaEnumerateAccountRights(policy_handle.read_pointer, sid_pointer, rights_pointer, number_of_rights) 159 check_lsa_nt_status_and_raise_failures(result, "LsaEnumerateAccountRights") 160 end 161 162 number_of_rights.read_ulong.times do |index| 163 right = LSA_UNICODE_STRING.new(rights_pointer.read_pointer + index * LSA_UNICODE_STRING.size) 164 rights << right[:Buffer].read_arbitrary_wide_string_up_to 165 end 166 167 result = LsaFreeMemory(rights_pointer.read_pointer) 168 check_lsa_nt_status_and_raise_failures(result, "LsaFreeMemory") 169 170 rights.join(",") 171 end
# File lib/puppet/util/windows/user.rb 125 def load_profile(user, password) 126 logon_user(user, password) do |token| 127 FFI::MemoryPointer.from_string_to_wide_string(user) do |lpUserName| 128 pi = PROFILEINFO.new 129 pi[:dwSize] = PROFILEINFO.size 130 pi[:dwFlags] = 1 # PI_NOUI - prevents display of profile error msgs 131 pi[:lpUserName] = lpUserName 132 133 # Load the profile. Since it doesn't exist, it will be created 134 if LoadUserProfileW(token, pi.pointer) == FFI::WIN32_FALSE 135 raise Puppet::Util::Windows::Error.new(_("Failed to load user profile %{user}") % { user: user.inspect }) 136 end 137 138 Puppet.debug("Loaded profile for #{user}") 139 140 if UnloadUserProfile(token, pi[:hProfile]) == FFI::WIN32_FALSE 141 raise Puppet::Util::Windows::Error.new(_("Failed to unload user profile %{user}") % { user: user.inspect }) 142 end 143 end 144 end 145 end
The name of the account in all locales is `LocalSystem`. `.LocalSystem` or `ComputerNameLocalSystem' can also be used. This account is not recognized by the security subsystem, so you cannot specify its name in a call to the `LookupAccountName` function. docs.microsoft.com/en-us/windows/win32/services/localsystem-account
# File lib/puppet/util/windows/user.rb 22 def localsystem?(name) 23 ["LocalSystem", ".\\LocalSystem", "#{Puppet::Util::Windows::ADSI.computer_name}\\LocalSystem"].any?{ |s| s.casecmp(name) == 0 } 24 end
# File lib/puppet/util/windows/user.rb 93 def logon_user(name, password, domain = '.', &block) 94 fLOGON32_PROVIDER_DEFAULT = 0 95 fLOGON32_LOGON_INTERACTIVE = 2 96 fLOGON32_LOGON_NETWORK = 3 97 98 token = nil 99 begin 100 FFI::MemoryPointer.new(:handle, 1) do |token_pointer| 101 #try logon using network else try logon using interactive mode 102 if logon_user_by_logon_type(name, domain, password, fLOGON32_LOGON_NETWORK, fLOGON32_PROVIDER_DEFAULT, token_pointer) == FFI::WIN32_FALSE 103 if logon_user_by_logon_type(name, domain, password, fLOGON32_LOGON_INTERACTIVE, fLOGON32_PROVIDER_DEFAULT, token_pointer) == FFI::WIN32_FALSE 104 raise Puppet::Util::Windows::Error.new(_("Failed to logon user %{name}") % {name: name.inspect}) 105 end 106 end 107 108 yield token = token_pointer.read_handle 109 end 110 ensure 111 FFI::WIN32.CloseHandle(token) if token 112 end 113 114 # token has been closed by this point 115 true 116 end
# File lib/puppet/util/windows/user.rb 76 def password_is?(name, password, domain = '.') 77 begin 78 logon_user(name, password, domain) { |token| } 79 rescue Puppet::Util::Windows::Error => detail 80 81 authenticated_error_codes = Set[ 82 ERROR_ACCOUNT_RESTRICTION, 83 ERROR_INVALID_LOGON_HOURS, 84 ERROR_INVALID_WORKSTATION, 85 ERROR_ACCOUNT_DISABLED, 86 ] 87 88 return authenticated_error_codes.include?(detail.code) 89 end 90 end
# File lib/puppet/util/windows/user.rb 186 def remove_rights(name, rights) 187 rights_pointer = new_lsa_unicode_strings_pointer(rights) 188 user_info = Puppet::Util::Windows::SID.name_to_principal(name.sub(/^\.\\/, "#{Puppet::Util::Windows::ADSI.computer_name}\\")) 189 sid_pointer = FFI::MemoryPointer.new(:byte, user_info.sid_bytes.length).write_array_of_uchar(user_info.sid_bytes) 190 191 new_lsa_policy_handle do |policy_handle| 192 result = LsaRemoveAccountRights(policy_handle.read_pointer, sid_pointer, false, rights_pointer, rights.size) 193 check_lsa_nt_status_and_raise_failures(result, "LsaRemoveAccountRights") 194 end 195 end
# File lib/puppet/util/windows/user.rb 174 def set_rights(name, rights) 175 rights_pointer = new_lsa_unicode_strings_pointer(rights) 176 user_info = Puppet::Util::Windows::SID.name_to_principal(name.sub(/^\.\\/, "#{Puppet::Util::Windows::ADSI.computer_name}\\")) 177 sid_pointer = FFI::MemoryPointer.new(:byte, user_info.sid_bytes.length).write_array_of_uchar(user_info.sid_bytes) 178 179 new_lsa_policy_handle do |policy_handle| 180 result = LsaAddAccountRights(policy_handle.read_pointer, sid_pointer, rights_pointer, rights.size) 181 check_lsa_nt_status_and_raise_failures(result, "LsaAddAccountRights") 182 end 183 end
Private Class Methods
docs.microsoft.com/en-us/openspecs/windows_protocols/ms-erref/18d8fbe8-a967-4f1c-ae50-99ca8e491d2d
# File lib/puppet/util/windows/user.rb 247 def self.check_lsa_nt_status_and_raise_failures(status, method_name) 248 error_code = LsaNtStatusToWinError(status) 249 250 error_reason = case error_code.to_s(16) 251 when '0' # ERROR_SUCCESS 252 return # Method call succeded 253 when '2' # ERROR_FILE_NOT_FOUND 254 return # No rights/privilleges assigned to given user 255 when '5' # ERROR_ACCESS_DENIED 256 "Access is denied. Please make sure that puppet is running as administrator." 257 when '521' # ERROR_NO_SUCH_PRIVILEGE 258 "One or more of the given rights/privilleges are incorrect." 259 when '6ba' # RPC_S_SERVER_UNAVAILABLE 260 "The RPC server is unavailable or given domain name is invalid." 261 end 262 263 raise Puppet::Error.new("Calling `#{method_name}` returned 'Win32 Error Code 0x%08X'. #{error_reason}" % error_code) 264 end
# File lib/puppet/util/windows/user.rb 119 def self.logon_user_by_logon_type(name, domain, password, logon_type, logon_provider, token) 120 LogonUserW(wide_string(name), wide_string(domain), password.nil? ? FFI::Pointer::NULL : wide_string(password), logon_type, logon_provider, token) 121 end
# File lib/puppet/util/windows/user.rb 214 def self.new_lsa_policy_handle 215 access = 0 216 access |= POLICY_LOOKUP_NAMES 217 access |= POLICY_CREATE_ACCOUNT 218 policy_handle = FFI::MemoryPointer.new(:pointer) 219 220 result = LsaOpenPolicy(nil, LSA_OBJECT_ATTRIBUTES.new, access, policy_handle) 221 check_lsa_nt_status_and_raise_failures(result, "LsaOpenPolicy") 222 223 begin 224 yield policy_handle 225 ensure 226 result = LsaClose(policy_handle.read_pointer) 227 check_lsa_nt_status_and_raise_failures(result, "LsaClose") 228 end 229 end
# File lib/puppet/util/windows/user.rb 232 def self.new_lsa_unicode_strings_pointer(strings) 233 lsa_unicode_strings_pointer = FFI::MemoryPointer.new(LSA_UNICODE_STRING, strings.size) 234 235 strings.each_with_index do |string, index| 236 lsa_string = LSA_UNICODE_STRING.new(lsa_unicode_strings_pointer + index * LSA_UNICODE_STRING.size) 237 lsa_string[:Buffer] = FFI::MemoryPointer.from_string(wide_string(string)) 238 lsa_string[:Length] = string.length * 2 239 lsa_string[:MaximumLength] = lsa_string[:Length] + 2 240 end 241 242 lsa_unicode_strings_pointer 243 end
Private Instance Methods
# File lib/puppet/util/windows/user.rb 10 def admin? 11 return false unless check_token_membership 12 13 # if Vista or later, check for unrestricted process token 14 elevated_supported = Puppet::Util::Windows::Process.supports_elevated_security? 15 return elevated_supported ? Puppet::Util::Windows::Process.elevated_security? : true 16 end
# File lib/puppet/util/windows/user.rb 47 def check_token_membership 48 is_admin = false 49 FFI::MemoryPointer.new(:byte, SECURITY_MAX_SID_SIZE) do |sid_pointer| 50 FFI::MemoryPointer.new(:dword, 1) do |size_pointer| 51 size_pointer.write_uint32(SECURITY_MAX_SID_SIZE) 52 53 if CreateWellKnownSid(:WinBuiltinAdministratorsSid, FFI::Pointer::NULL, sid_pointer, size_pointer) == FFI::WIN32_FALSE 54 raise Puppet::Util::Windows::Error.new(_("Failed to create administrators SID")) 55 end 56 end 57 58 if IsValidSid(sid_pointer) == FFI::WIN32_FALSE 59 raise Puppet::Util::Windows::Error.new(_("Invalid SID")) 60 end 61 62 FFI::MemoryPointer.new(:win32_bool, 1) do |ismember_pointer| 63 if CheckTokenMembership(FFI::Pointer::NULL_HANDLE, sid_pointer, ismember_pointer) == FFI::WIN32_FALSE 64 raise Puppet::Util::Windows::Error.new(_("Failed to check membership")) 65 end 66 67 # Is administrators SID enabled in calling thread's access token? 68 is_admin = ismember_pointer.read_win32_bool 69 end 70 end 71 72 is_admin 73 end
Check if a given user is one of the default system accounts These accounts do not have a password and all checks done through logon attempt will fail docs.microsoft.com/en-us/windows/security/identity-protection/access-control/local-accounts#default-local-system-accounts
# File lib/puppet/util/windows/user.rb 30 def default_system_account?(name) 31 user_sid = Puppet::Util::Windows::SID.name_to_sid(name) 32 [Puppet::Util::Windows::SID::LocalSystem, Puppet::Util::Windows::SID::NtLocal, Puppet::Util::Windows::SID::NtNetwork].include?(user_sid) 33 end
# File lib/puppet/util/windows/user.rb 148 def get_rights(name) 149 user_info = Puppet::Util::Windows::SID.name_to_principal(name.sub(/^\.\\/, "#{Puppet::Util::Windows::ADSI.computer_name}\\")) 150 return "" unless user_info 151 152 rights = [] 153 rights_pointer = FFI::MemoryPointer.new(:pointer) 154 number_of_rights = FFI::MemoryPointer.new(:ulong) 155 sid_pointer = FFI::MemoryPointer.new(:byte, user_info.sid_bytes.length).write_array_of_uchar(user_info.sid_bytes) 156 157 new_lsa_policy_handle do |policy_handle| 158 result = LsaEnumerateAccountRights(policy_handle.read_pointer, sid_pointer, rights_pointer, number_of_rights) 159 check_lsa_nt_status_and_raise_failures(result, "LsaEnumerateAccountRights") 160 end 161 162 number_of_rights.read_ulong.times do |index| 163 right = LSA_UNICODE_STRING.new(rights_pointer.read_pointer + index * LSA_UNICODE_STRING.size) 164 rights << right[:Buffer].read_arbitrary_wide_string_up_to 165 end 166 167 result = LsaFreeMemory(rights_pointer.read_pointer) 168 check_lsa_nt_status_and_raise_failures(result, "LsaFreeMemory") 169 170 rights.join(",") 171 end
# File lib/puppet/util/windows/user.rb 125 def load_profile(user, password) 126 logon_user(user, password) do |token| 127 FFI::MemoryPointer.from_string_to_wide_string(user) do |lpUserName| 128 pi = PROFILEINFO.new 129 pi[:dwSize] = PROFILEINFO.size 130 pi[:dwFlags] = 1 # PI_NOUI - prevents display of profile error msgs 131 pi[:lpUserName] = lpUserName 132 133 # Load the profile. Since it doesn't exist, it will be created 134 if LoadUserProfileW(token, pi.pointer) == FFI::WIN32_FALSE 135 raise Puppet::Util::Windows::Error.new(_("Failed to load user profile %{user}") % { user: user.inspect }) 136 end 137 138 Puppet.debug("Loaded profile for #{user}") 139 140 if UnloadUserProfile(token, pi[:hProfile]) == FFI::WIN32_FALSE 141 raise Puppet::Util::Windows::Error.new(_("Failed to unload user profile %{user}") % { user: user.inspect }) 142 end 143 end 144 end 145 end
The name of the account in all locales is `LocalSystem`. `.LocalSystem` or `ComputerNameLocalSystem' can also be used. This account is not recognized by the security subsystem, so you cannot specify its name in a call to the `LookupAccountName` function. docs.microsoft.com/en-us/windows/win32/services/localsystem-account
# File lib/puppet/util/windows/user.rb 22 def localsystem?(name) 23 ["LocalSystem", ".\\LocalSystem", "#{Puppet::Util::Windows::ADSI.computer_name}\\LocalSystem"].any?{ |s| s.casecmp(name) == 0 } 24 end
# File lib/puppet/util/windows/user.rb 93 def logon_user(name, password, domain = '.', &block) 94 fLOGON32_PROVIDER_DEFAULT = 0 95 fLOGON32_LOGON_INTERACTIVE = 2 96 fLOGON32_LOGON_NETWORK = 3 97 98 token = nil 99 begin 100 FFI::MemoryPointer.new(:handle, 1) do |token_pointer| 101 #try logon using network else try logon using interactive mode 102 if logon_user_by_logon_type(name, domain, password, fLOGON32_LOGON_NETWORK, fLOGON32_PROVIDER_DEFAULT, token_pointer) == FFI::WIN32_FALSE 103 if logon_user_by_logon_type(name, domain, password, fLOGON32_LOGON_INTERACTIVE, fLOGON32_PROVIDER_DEFAULT, token_pointer) == FFI::WIN32_FALSE 104 raise Puppet::Util::Windows::Error.new(_("Failed to logon user %{name}") % {name: name.inspect}) 105 end 106 end 107 108 yield token = token_pointer.read_handle 109 end 110 ensure 111 FFI::WIN32.CloseHandle(token) if token 112 end 113 114 # token has been closed by this point 115 true 116 end
# File lib/puppet/util/windows/user.rb 76 def password_is?(name, password, domain = '.') 77 begin 78 logon_user(name, password, domain) { |token| } 79 rescue Puppet::Util::Windows::Error => detail 80 81 authenticated_error_codes = Set[ 82 ERROR_ACCOUNT_RESTRICTION, 83 ERROR_INVALID_LOGON_HOURS, 84 ERROR_INVALID_WORKSTATION, 85 ERROR_ACCOUNT_DISABLED, 86 ] 87 88 return authenticated_error_codes.include?(detail.code) 89 end 90 end
# File lib/puppet/util/windows/user.rb 186 def remove_rights(name, rights) 187 rights_pointer = new_lsa_unicode_strings_pointer(rights) 188 user_info = Puppet::Util::Windows::SID.name_to_principal(name.sub(/^\.\\/, "#{Puppet::Util::Windows::ADSI.computer_name}\\")) 189 sid_pointer = FFI::MemoryPointer.new(:byte, user_info.sid_bytes.length).write_array_of_uchar(user_info.sid_bytes) 190 191 new_lsa_policy_handle do |policy_handle| 192 result = LsaRemoveAccountRights(policy_handle.read_pointer, sid_pointer, false, rights_pointer, rights.size) 193 check_lsa_nt_status_and_raise_failures(result, "LsaRemoveAccountRights") 194 end 195 end
# File lib/puppet/util/windows/user.rb 174 def set_rights(name, rights) 175 rights_pointer = new_lsa_unicode_strings_pointer(rights) 176 user_info = Puppet::Util::Windows::SID.name_to_principal(name.sub(/^\.\\/, "#{Puppet::Util::Windows::ADSI.computer_name}\\")) 177 sid_pointer = FFI::MemoryPointer.new(:byte, user_info.sid_bytes.length).write_array_of_uchar(user_info.sid_bytes) 178 179 new_lsa_policy_handle do |policy_handle| 180 result = LsaAddAccountRights(policy_handle.read_pointer, sid_pointer, rights_pointer, rights.size) 181 check_lsa_nt_status_and_raise_failures(result, "LsaAddAccountRights") 182 end 183 end