summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorKartik Null Cating-Subramanian <ksubramanian@chef.io>2015-08-04 16:38:28 -0400
committerKartik Null Cating-Subramanian <ksubramanian@chef.io>2015-08-04 16:38:28 -0400
commitf384bf216caf76efc19309011c3f8e4a598924f9 (patch)
tree84b4ca3433a6d88294351f82abd3421c3f6e29e9
parentfe6676bab51390429674b0ecd4670924ffb09cc5 (diff)
parent0f32e154df91b60a7f5981dc029c35e933482fe7 (diff)
downloadchef-f384bf216caf76efc19309011c3f8e4a598924f9.tar.gz
Merge pull request #3728 from chef/jdm/win-groups
Rewrite NetLocalGroup things to use FFI
-rw-r--r--lib/chef/exceptions.rb17
-rw-r--r--lib/chef/util/windows/net_group.rb191
-rw-r--r--lib/chef/win32/api.rb1
-rw-r--r--lib/chef/win32/api/net.rb57
-rw-r--r--lib/chef/win32/net.rb113
-rw-r--r--spec/functional/resource/group_spec.rb15
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