diff options
author | sersut <serdar@opscode.com> | 2014-07-09 16:05:27 -0700 |
---|---|---|
committer | sersut <serdar@opscode.com> | 2014-07-14 13:18:31 -0700 |
commit | 04612158ac256ce46faad599e141ff7d1161fd66 (patch) | |
tree | 5d6ff018c0173c2ec0282bf9e68357964a06e578 /lib/chef | |
parent | 3ec42a522a5b40d1ef217f23831b7417bda65761 (diff) | |
download | chef-04612158ac256ce46faad599e141ff7d1161fd66.tar.gz |
Automatically find out the sid for Administrators so that the specs can run on boxes for which the administrators account is renamed.
Diffstat (limited to 'lib/chef')
-rw-r--r-- | lib/chef/win32/api/net.rb | 90 | ||||
-rw-r--r-- | lib/chef/win32/security/sid.rb | 56 | ||||
-rw-r--r-- | lib/chef/win32/unicode.rb | 14 |
3 files changed, 156 insertions, 4 deletions
diff --git a/lib/chef/win32/api/net.rb b/lib/chef/win32/api/net.rb new file mode 100644 index 0000000000..cb028020cf --- /dev/null +++ b/lib/chef/win32/api/net.rb @@ -0,0 +1,90 @@ +# +# Author:: Serdar Sutay (<serdar@getchef.com>) +# Copyright:: Copyright 2014 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 'chef/win32/api' + +class Chef + module ReservedNames::Win32 + module API + module Net + extend Chef::ReservedNames::Win32::API + + FILTER_TEMP_DUPLICATE_ACCOUNT = 0x0001 + FILTER_NORMAL_ACCOUNT = 0x0002 + FILTER_INTERDOMAIN_TRUST_ACCOUNT = 0x0008 + FILTER_WORKSTATION_TRUST_ACCOUNT = 0x0010 + FILTER_SERVER_TRUST_ACCOUNT = 0x0020 + + MAX_PREFERRED_LENGTH = 0xFFFF + + NERR_Success = 0 + + ffi_lib "netapi32" + + class USER_INFO_3 < FFI::Struct + layout :usri3_name, :LPWSTR, + :usri3_password, :LPWSTR, + :usri3_password_age, :DWORD, + :usri3_priv, :DWORD, + :usri3_home_dir, :LPWSTR, + :usri3_comment, :LPWSTR, + :usri3_flags, :DWORD, + :usri3_script_path, :LPWSTR, + :usri3_auth_flags, :DWORD, + :usri3_full_name, :LPWSTR, + :usri3_usr_comment, :LPWSTR, + :usri3_parms, :LPWSTR, + :usri3_workstations, :LPWSTR, + :usri3_last_logon, :DWORD, + :usri3_last_logoff, :DWORD, + :usri3_acct_expires, :DWORD, + :usri3_max_storage, :DWORD, + :usri3_units_per_week, :DWORD, + :usri3_logon_hours, :PBYTE, + :usri3_bad_pw_count, :DWORD, + :usri3_num_logons, :DWORD, + :usri3_logon_server, :LPWSTR, + :usri3_country_code, :DWORD, + :usri3_code_page, :DWORD, + :usri3_user_id, :DWORD, + :usri3_primary_group_id, :DWORD, + :usri3_profile, :LPWSTR, + :usri3_home_dir_drive, :LPWSTR, + :usri3_password_expired, :DWORD + end + +# NET_API_STATUS NetUserEnum( +# _In_ LPCWSTR servername, +# _In_ DWORD level, +# _In_ DWORD filter, +# _Out_ LPBYTE *bufptr, +# _In_ DWORD prefmaxlen, +# _Out_ LPDWORD entriesread, +# _Out_ LPDWORD totalentries, +# _Inout_ LPDWORD resume_handle +# ); + safe_attach_function :NetUserEnum, [ :LPCWSTR, :DWORD, :DWORD, :LPBYTE, :DWORD, :LPDWORD, :LPDWORD, :LPDWORD ], :DWORD + +# NET_API_STATUS NetApiBufferFree( +# _In_ LPVOID Buffer +# ); + safe_attach_function :NetApiBufferFree, [ :LPVOID ], :DWORD + end + end + end +end diff --git a/lib/chef/win32/security/sid.rb b/lib/chef/win32/security/sid.rb index e1b20224bb..8e9407dc80 100644 --- a/lib/chef/win32/security/sid.rb +++ b/lib/chef/win32/security/sid.rb @@ -17,11 +17,22 @@ # require 'chef/win32/security' +require 'chef/win32/api/net' +require 'chef/win32/api/error' + +require 'wmi-lite/wmi' class Chef module ReservedNames::Win32 class Security class SID + include Chef::ReservedNames::Win32::API::Net + include Chef::ReservedNames::Win32::API::Error + + class << self + include Chef::ReservedNames::Win32::API::Net + include Chef::ReservedNames::Win32::API::Error + end def initialize(pointer, owner = nil) @pointer = pointer @@ -178,13 +189,11 @@ class Chef SID.from_string_sid('S-1-5-32-544') end - # Machine-specific, well-known SIDs - # TODO: don't use strings, dummy def self.None SID.from_account("#{::ENV['COMPUTERNAME']}\\None") end def self.Administrator - SID.from_account("#{::ENV['COMPUTERNAME']}\\Administrator") + SID.from_account("#{::ENV['COMPUTERNAME']}\\#{SID.admin_account_name}") end def self.Guest SID.from_account("#{::ENV['COMPUTERNAME']}\\Guest") @@ -193,6 +202,47 @@ class Chef def self.current_user SID.from_account("#{::ENV['USERDOMAIN']}\\#{::ENV['USERNAME']}") end + + def self.admin_account_name + @admin_account_name ||= begin + admin_account_name = nil + + # Call NetUserEnum to enumerate the users without hitting network + # http://msdn.microsoft.com/en-us/library/windows/desktop/aa370652(v=vs.85).aspx + servername = nil # We are querying the local server + level = 3 # We want USER_INFO_3 structure which contains the SID + filter = FILTER_NORMAL_ACCOUNT # Only query the user accounts + bufptr = FFI::MemoryPointer.new(:pointer) # Buffer which will receive the data + prefmaxlen = MAX_PREFERRED_LENGTH # Let the system allocate the needed amount of memory + entriesread = FFI::Buffer.new(:long).write_long(0) + totalentries = FFI::Buffer.new(:long).write_long(0) + resume_handle = FFI::Buffer.new(:long).write_long(0) + + status = ERROR_MORE_DATA + + while(status == ERROR_MORE_DATA) do + status = NetUserEnum(servername, level, filter, bufptr, prefmaxlen, entriesread, totalentries, resume_handle) + + if (status == NERR_Success || status == ERROR_MORE_DATA) + entriesread.read_long.times.collect do |i| + user_info = USER_INFO_3.new(bufptr.read_pointer + i * USER_INFO_3.size) + # Check if the account is the Administrator account + # RID for the Administrator account is always 500 and it's privilage is set to USER_PRIV_ADMIN + if user_info[:usri3_user_id] == 500 && user_info[:usri3_priv] == 2 # USER_PRIV_ADMIN (2) - Administrator + admin_account_name = user_info[:usri3_name].read_wstring + break + end + end + + # Free the memory allocated by the system + NetApiBufferFree(bufptr.read_pointer) + end + end + + raise "Can not determine the administrator account name." if admin_account_name.nil? + admin_account_name + end + end end end end diff --git a/lib/chef/win32/unicode.rb b/lib/chef/win32/unicode.rb index 1002a4d58f..e7399d5255 100644 --- a/lib/chef/win32/unicode.rb +++ b/lib/chef/win32/unicode.rb @@ -30,7 +30,19 @@ end module FFI class Pointer - def read_wstring(num_wchars) + def read_wstring(num_wchars = nil) + if num_wchars.nil? + # Find the length of the string + length = 0 + last_char = nil + while last_char != "\000\000" do + length += 1 + last_char = self.get_bytes(0,length * 2)[-2..-1] + end + + num_wchars = length + end + Chef::ReservedNames::Win32::Unicode.wide_to_utf8(self.get_bytes(0, num_wchars*2)) end end |