diff options
author | Kartik Null Cating-Subramanian <ksubramanian@chef.io> | 2015-08-04 16:38:28 -0400 |
---|---|---|
committer | Kartik Null Cating-Subramanian <ksubramanian@chef.io> | 2015-08-04 16:38:28 -0400 |
commit | f384bf216caf76efc19309011c3f8e4a598924f9 (patch) | |
tree | 84b4ca3433a6d88294351f82abd3421c3f6e29e9 | |
parent | fe6676bab51390429674b0ecd4670924ffb09cc5 (diff) | |
parent | 0f32e154df91b60a7f5981dc029c35e933482fe7 (diff) | |
download | chef-f384bf216caf76efc19309011c3f8e4a598924f9.tar.gz |
Merge pull request #3728 from chef/jdm/win-groups
Rewrite NetLocalGroup things to use FFI
-rw-r--r-- | lib/chef/exceptions.rb | 17 | ||||
-rw-r--r-- | lib/chef/util/windows/net_group.rb | 191 | ||||
-rw-r--r-- | lib/chef/win32/api.rb | 1 | ||||
-rw-r--r-- | lib/chef/win32/api/net.rb | 57 | ||||
-rw-r--r-- | lib/chef/win32/net.rb | 113 | ||||
-rw-r--r-- | spec/functional/resource/group_spec.rb | 15 |
6 files changed, 278 insertions, 116 deletions
diff --git a/lib/chef/exceptions.rb b/lib/chef/exceptions.rb index 16f985acaa..e3649c068b 100644 --- a/lib/chef/exceptions.rb +++ b/lib/chef/exceptions.rb @@ -129,6 +129,23 @@ class Chef class EnclosingDirectoryDoesNotExist < ArgumentError; end # Errors originating from calls to the Win32 API class Win32APIError < RuntimeError; end + + class Win32NetAPIError < Win32APIError + attr_reader :msg, :error_code + def initialize(msg, error_code) + @msg = msg + @error_code = error_code + + formatted_message = "" + formatted_message << "---- Begin Win32 API output ----\n" + formatted_message << "Net Api Error Code: #{error_code}\n" + formatted_message << "Net Api Error Message: #{msg}\n" + formatted_message << "---- End Win32 API output ----\n" + + super(formatted_message) + end + end + # Thrown when Win32 API layer binds to non-existent Win32 function. Occurs # when older versions of Windows don't support newer Win32 API functions. class Win32APIFunctionNotImplemented < NotImplementedError; end diff --git a/lib/chef/util/windows/net_group.rb b/lib/chef/util/windows/net_group.rb index 924bd392f9..2085747eb9 100644 --- a/lib/chef/util/windows/net_group.rb +++ b/lib/chef/util/windows/net_group.rb @@ -1,106 +1,85 @@ -# -# Author:: Doug MacEachern (<dougm@vmware.com>) -# Copyright:: Copyright (c) 2010 VMware, 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/util/windows' - -#wrapper around a subset of the NetGroup* APIs. -#nothing Chef specific, but not complete enough to be its own gem, so util for now. -class Chef::Util::Windows::NetGroup < Chef::Util::Windows - - private - - def pack_str(s) - [str_to_ptr(s)].pack('L') - end - - def modify_members(members, func) - buffer = 0.chr * (members.size * PTR_SIZE) - members.each_with_index do |member,offset| - buffer[offset*PTR_SIZE,PTR_SIZE] = pack_str(multi_to_wide(member)) - end - rc = func.call(nil, @name, 3, buffer, members.size) - if rc != NERR_Success - raise ArgumentError, get_last_error(rc) - end - end - - public - - def initialize(groupname) - @name = multi_to_wide(groupname) - end - - def local_get_members - group_members = [] - handle = 0.chr * PTR_SIZE - rc = ERROR_MORE_DATA - - while rc == ERROR_MORE_DATA - ptr = 0.chr * PTR_SIZE - nread = 0.chr * PTR_SIZE - total = 0.chr * PTR_SIZE - - rc = NetLocalGroupGetMembers.call(nil, @name, 0, ptr, -1, - nread, total, handle) - if (rc == NERR_Success) || (rc == ERROR_MORE_DATA) - ptr = ptr.unpack('L')[0] - nread = nread.unpack('i')[0] - members = 0.chr * (nread * PTR_SIZE ) #nread * sizeof(LOCALGROUP_MEMBERS_INFO_0) - memcpy(members, ptr, members.size) - - # 1 pointer field in LOCALGROUP_MEMBERS_INFO_0, offset 0 is lgrmi0_sid - nread.times do |i| - sid_address = members[i * PTR_SIZE, PTR_SIZE].unpack('L')[0] - sid_ptr = FFI::Pointer.new(sid_address) - member_sid = Chef::ReservedNames::Win32::Security::SID.new(sid_ptr) - group_members << member_sid.to_s - end - NetApiBufferFree(ptr) - else - raise ArgumentError, get_last_error(rc) - end - end - group_members - end - - def local_add - rc = NetLocalGroupAdd.call(nil, 0, pack_str(@name), nil) - if rc != NERR_Success - raise ArgumentError, get_last_error(rc) - end - end - - def local_set_members(members) - modify_members(members, NetLocalGroupSetMembers) - end - - def local_add_members(members) - modify_members(members, NetLocalGroupAddMembers) - end - - def local_delete_members(members) - modify_members(members, NetLocalGroupDelMembers) - end - - def local_delete - rc = NetLocalGroupDel.call(nil, @name) - if rc != NERR_Success - raise ArgumentError, get_last_error(rc) - end - end -end +#
+# Author:: Doug MacEachern (<dougm@vmware.com>)
+# Copyright:: Copyright (c) 2010 VMware, 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/util/windows'
+require 'chef/win32/net'
+
+#wrapper around a subset of the NetGroup* APIs.
+class Chef::Util::Windows::NetGroup
+
+ private
+
+ def groupname
+ @groupname
+ end
+
+ public
+
+ def initialize(groupname)
+ @groupname = groupname
+ end
+
+ def local_get_members
+ begin
+ Chef::ReservedNames::Win32::NetUser::net_local_group_get_members(nil, groupname)
+ rescue Chef::Exceptions::Win32NetAPIError => e
+ raise ArgumentError, e.msg
+ end
+ end
+
+ def local_add
+ begin
+ Chef::ReservedNames::Win32::NetUser::net_local_group_add(nil, groupname)
+ rescue Chef::Exceptions::Win32NetAPIError => e
+ raise ArgumentError, e.msg
+ end
+ end
+
+ def local_set_members(members)
+ begin
+ Chef::ReservedNames::Win32::NetUser::net_local_group_set_members(nil, groupname, members)
+ rescue Chef::Exceptions::Win32NetAPIError => e
+ raise ArgumentError, e.msg
+ end
+ end
+
+ def local_add_members(members)
+ begin
+ Chef::ReservedNames::Win32::NetUser::net_local_group_add_members(nil, groupname, members)
+ rescue Chef::Exceptions::Win32NetAPIError => e
+ raise ArgumentError, e.msg
+ end
+ end
+
+ def local_delete_members(members)
+ begin
+ Chef::ReservedNames::Win32::NetUser::net_local_group_del_members(nil, groupname, members)
+ rescue Chef::Exceptions::Win32NetAPIError => e
+ raise ArgumentError, e.msg
+ end
+
+ end
+
+ def local_delete
+ begin
+ Chef::ReservedNames::Win32::NetUser::net_local_group_del(nil, groupname)
+ rescue Chef::Exceptions::Win32NetAPIError => e
+ raise ArgumentError, e.msg
+ end
+ end
+end
diff --git a/lib/chef/win32/api.rb b/lib/chef/win32/api.rb index e9d273808a..4786222bd4 100644 --- a/lib/chef/win32/api.rb +++ b/lib/chef/win32/api.rb @@ -188,6 +188,7 @@ class Chef host.typedef :pointer, :PCRYPTPROTECT_PROMPTSTRUCT # Pointer to a CRYPTOPROTECT_PROMPTSTRUCT. host.typedef :pointer, :PDATA_BLOB # Pointer to a DATA_BLOB. host.typedef :pointer, :PTSTR # A PWSTR if UNICODE is defined, a PSTR otherwise. + host.typedef :pointer, :PSID host.typedef :pointer, :PUCHAR # Pointer to a UCHAR. host.typedef :pointer, :PUHALF_PTR # Pointer to a UHALF_PTR. host.typedef :pointer, :PUINT # Pointer to a UINT. diff --git a/lib/chef/win32/api/net.rb b/lib/chef/win32/api/net.rb index 72caf46628..082cf4bb9a 100644 --- a/lib/chef/win32/api/net.rb +++ b/lib/chef/win32/api/net.rb @@ -49,7 +49,9 @@ class Chef NERR_BadPassword = 2203 NERR_PasswordTooShort = 2245 NERR_UserNotFound = 2221 + NERR_GroupNotFound = 2220 ERROR_ACCESS_DENIED = 5 + ERROR_MORE_DATA = 234 ffi_lib "netapi32" @@ -132,10 +134,47 @@ class Chef end end + class LOCALGROUP_MEMBERS_INFO_0 < FFI::Struct + layout :lgrmi0_sid, :PSID + end + class LOCALGROUP_MEMBERS_INFO_3 < FFI::Struct layout :lgrmi3_domainandname, :LPWSTR end + class LOCALGROUP_INFO_0 < FFI::Struct + layout :lgrpi0_name, :LPWSTR + end + +#NET_API_STATUS NetLocalGroupAdd( + #_In_ LPCWSTR servername, + #_In_ DWORD level, + #_In_ LPBYTE buf, + #_Out_ LPDWORD parm_err +#); + safe_attach_function :NetLocalGroupAdd, [ :LPCWSTR, :DWORD, :LPBYTE, :LPDWORD], :DWORD + +#NET_API_STATUS NetLocalGroupDel( + #_In_ LPCWSTR servername, + #_In_ LPCWSTR groupname +#); + safe_attach_function :NetLocalGroupDel, [ :LPCWSTR, :LPCWSTR], :DWORD + +#NET_API_STATUS NetLocalGroupGetMembers( + #_In_ LPCWSTR servername, + #_In_ LPCWSTR localgroupname, + #_In_ DWORD level, + #_Out_ LPBYTE *bufptr, + #_In_ DWORD prefmaxlen, + #_Out_ LPDWORD entriesread, + #_Out_ LPDWORD totalentries, + #_Inout_ PDWORD_PTR resumehandle +#); + safe_attach_function :NetLocalGroupGetMembers, [ + :LPCWSTR, :LPCWSTR, :DWORD, :LPBYTE, :DWORD, + :LPDWORD, :LPDWORD, :PDWORD_PTR + ], :DWORD + # NET_API_STATUS NetUserEnum( # _In_ LPCWSTR servername, # _In_ DWORD level, @@ -170,6 +209,24 @@ class Chef #); safe_attach_function :NetLocalGroupAddMembers, [:LPCWSTR, :LPCWSTR, :DWORD, :LPBYTE, :DWORD ], :DWORD +#NET_API_STATUS NetLocalGroupSetMembers( +# _In_ LPCWSTR servername, +# _In_ LPCWSTR groupname, +# _In_ DWORD level, +# _In_ LPBYTE buf, +# _In_ DWORD totalentries +#); + safe_attach_function :NetLocalGroupSetMembers, [:LPCWSTR, :LPCWSTR, :DWORD, :LPBYTE, :DWORD ], :DWORD + +#NET_API_STATUS NetLocalGroupDelMembers( +# _In_ LPCWSTR servername, +# _In_ LPCWSTR groupname, +# _In_ DWORD level, +# _In_ LPBYTE buf, +# _In_ DWORD totalentries +#); + safe_attach_function :NetLocalGroupDelMembers, [:LPCWSTR, :LPCWSTR, :DWORD, :LPBYTE, :DWORD ], :DWORD + #NET_API_STATUS NetUserGetInfo( # _In_ LPCWSTR servername, # _In_ LPCWSTR username, diff --git a/lib/chef/win32/net.rb b/lib/chef/win32/net.rb index 1349091eb9..0de310daf1 100644 --- a/lib/chef/win32/net.rb +++ b/lib/chef/win32/net.rb @@ -91,19 +91,72 @@ The password is shorter than required. (The password could also be too long, be too recent in its change history, not have enough unique characters, or not meet another password policy requirement.) END + when NERR_GroupNotFound + "The group name could not be found." when ERROR_ACCESS_DENIED "The user does not have access to the requested information." else "Received unknown error code (#{code})" end - formatted_message = "" - formatted_message << "---- Begin Win32 API output ----\n" - formatted_message << "Net Api Error Code: #{code}\n" - formatted_message << "Net Api Error Message: #{msg}\n" - formatted_message << "---- End Win32 API output ----\n" + raise Chef::Exceptions::Win32NetAPIError.new(msg, code) + end + + def self.net_local_group_add(server_name, group_name) + server_name = wstring(server_name) + group_name = wstring(group_name) + + buf = LOCALGROUP_INFO_0.new + buf[:lgrpi0_name] = FFI::MemoryPointer.from_string(group_name) + + rc = NetLocalGroupAdd(server_name, 0, buf, nil) + if rc != NERR_Success + net_api_error!(rc) + end + end + + def self.net_local_group_del(server_name, group_name) + server_name = wstring(server_name) + group_name = wstring(group_name) + + rc = NetLocalGroupDel(server_name, group_name) + if rc != NERR_Success + net_api_error!(rc) + end + end + + def self.net_local_group_get_members(server_name, group_name) + server_name = wstring(server_name) + group_name = wstring(group_name) + + buf = FFI::MemoryPointer.new(:pointer) + entries_read_ptr = FFI::MemoryPointer.new(:long) + total_read_ptr = FFI::MemoryPointer.new(:long) + resume_handle_ptr = FFI::MemoryPointer.new(:pointer) + + rc = ERROR_MORE_DATA + group_members = [] + while rc == ERROR_MORE_DATA + rc = NetLocalGroupGetMembers( + server_name, group_name, 0, buf, -1, + entries_read_ptr, total_read_ptr, resume_handle_ptr + ) + + nread = entries_read_ptr.read_long + nread.times do |i| + member = LOCALGROUP_MEMBERS_INFO_0.new(buf.read_pointer + + (i * LOCALGROUP_MEMBERS_INFO_0.size)) + member_sid = Chef::ReservedNames::Win32::Security::SID.new(member[:lgrmi0_sid]) + group_members << member_sid.to_s + end + NetApiBufferFree(buf.read_pointer) + end + + if rc != NERR_Success + net_api_error!(rc) + end - raise Chef::Exceptions::Win32APIError, msg + "\n" + formatted_message + group_members end def self.net_user_add_l3(server_name, args) @@ -185,6 +238,54 @@ END end end + def self.members_to_lgrmi3(members) + buf = FFI::MemoryPointer.new(LOCALGROUP_MEMBERS_INFO_3, members.size) + members.size.times.collect do |i| + member_info = LOCALGROUP_MEMBERS_INFO_3.new( + buf + i * LOCALGROUP_MEMBERS_INFO_3.size) + member_info[:lgrmi3_domainandname] = FFI::MemoryPointer.from_string(wstring(members[i])) + member_info + end + end + + def self.net_local_group_add_members(server_name, group_name, members) + server_name = wstring(server_name) + group_name = wstring(group_name) + + lgrmi3s = members_to_lgrmi3(members) + rc = NetLocalGroupAddMembers( + server_name, group_name, 3, lgrmi3s[0], members.size) + + if rc != NERR_Success + net_api_error!(rc) + end + end + + def self.net_local_group_set_members(server_name, group_name, members) + server_name = wstring(server_name) + group_name = wstring(group_name) + + lgrmi3s = members_to_lgrmi3(members) + rc = NetLocalGroupSetMembers( + server_name, group_name, 3, lgrmi3s[0], members.size) + + if rc != NERR_Success + net_api_error!(rc) + end + end + + def self.net_local_group_del_members(server_name, group_name, members) + server_name = wstring(server_name) + group_name = wstring(group_name) + + lgrmi3s = members_to_lgrmi3(members) + rc = NetLocalGroupDelMembers( + server_name, group_name, 3, lgrmi3s[0], members.size) + + if rc != NERR_Success + net_api_error!(rc) + end + end end end end diff --git a/spec/functional/resource/group_spec.rb b/spec/functional/resource/group_spec.rb index 529af52d4e..19a4d25404 100644 --- a/spec/functional/resource/group_spec.rb +++ b/spec/functional/resource/group_spec.rb @@ -207,13 +207,16 @@ describe Chef::Resource::Group, :requires_root_or_running_windows, :not_supporte describe "when the users doesn't exist" do describe "when append is not set" do it "should raise an error" do - expect { @grp_resource.run_action(tested_action) }.to raise_error + expect { group_resource.run_action(tested_action) }.to raise_error(Chef::Exceptions::Win32APIError) end end describe "when append is set" do + before do + group_resource.append(true) + end it "should raise an error" do - expect { @grp_resource.run_action(tested_action) }.to raise_error + expect { group_resource.run_action(tested_action) }.to raise_error(Chef::Exceptions::Win32APIError) end end end @@ -231,6 +234,10 @@ describe Chef::Resource::Group, :requires_root_or_running_windows, :not_supporte group_should_exist(group_name) end + after(:each) do + group_resource.run_action(:remove) + end + describe "when updating membership" do it "raises an error for a non well-formed domain name" do group_resource.members [invalid_domain_user_name] @@ -256,7 +263,7 @@ describe Chef::Resource::Group, :requires_root_or_running_windows, :not_supporte end end - let(:group_name) { "t-#{SecureRandom.random_number(9999)}" } + let(:group_name) { "group#{SecureRandom.random_number(9999)}" } let(:included_members) { nil } let(:excluded_members) { nil } let(:group_resource) { @@ -300,7 +307,7 @@ theoldmanwalkingdownthestreetalwayshadagoodsmileonhisfacetheoldmanwalking\ downthestreetalwayshadagoodsmileonhisfacetheoldmanwalkingdownthestreeQQQQQQ" } it "should not create a group" do - expect { group_resource.run_action(:create) }.to raise_error + expect { group_resource.run_action(:create) }.to raise_error(ArgumentError) group_should_not_exist(group_name) end end |