diff options
author | Jay Mundrawala <jdmundrawala@gmail.com> | 2015-05-06 16:46:55 -0700 |
---|---|---|
committer | Jay Mundrawala <jdmundrawala@gmail.com> | 2015-05-15 08:51:16 -0700 |
commit | e0fb4b98b6af2c4eb03eab7b84d2ea55cba87d35 (patch) | |
tree | 2bfbbbb4db2e940e8a358156a0190ca1684c04da | |
parent | aa5a7b77d171a919f254dc1b23ee0f7e4347d71c (diff) | |
download | chef-e0fb4b98b6af2c4eb03eab7b84d2ea55cba87d35.tar.gz |
Replace some user creation win32-api calls with ffi
-rw-r--r-- | lib/chef/util/windows/net_user.rb | 56 | ||||
-rw-r--r-- | lib/chef/win32/api.rb | 1 | ||||
-rw-r--r-- | lib/chef/win32/api/net.rb | 49 | ||||
-rw-r--r-- | lib/chef/win32/user.rb | 145 |
4 files changed, 237 insertions, 14 deletions
diff --git a/lib/chef/util/windows/net_user.rb b/lib/chef/util/windows/net_user.rb index 5df1a8aaa4..e79d9212c5 100644 --- a/lib/chef/util/windows/net_user.rb +++ b/lib/chef/util/windows/net_user.rb @@ -18,6 +18,7 @@ require 'chef/util/windows' require 'chef/exceptions' +require 'chef/win32/user' #wrapper around a subset of the NetUser* APIs. #nothing Chef specific, but not complete enough to be its own gem, so util for now. @@ -76,13 +77,52 @@ class Chef::Util::Windows::NetUser < Chef::Util::Windows ] USER_INFO_3_TEMPLATE = - USER_INFO_3.collect { |field| field[1].class == Fixnum ? 'i' : 'L' }.join + USER_INFO_3.collect { |field| field[1].class == Fixnum ? 'i' : 'P' }.join SIZEOF_USER_INFO_3 = #sizeof(USER_INFO_3) USER_INFO_3.inject(0){|sum,item| sum + (item[1].class == Fixnum ? 4 : PTR_SIZE) } + USER_INFO_3_TRANSFORM = { + name: :usri3_name, + password: :usri3_password, + password_age: :usri3_password_age, + priv: :usri3_priv, + home_dir: :usri3_home_dir, + comment: :usri3_comment, + flags: :usri3_flags, + script_path: :usri3_script_path, + auth_flags: :usri3_auth_flags, + full_name: :usri3_full_name, + user_comment: :usri3_usr_comment, + parms: :usri3_parms, + workstations: :usri3_workstations, + last_logon: :usri3_last_logon, + last_logoff: :usri3_last_logoff, + acct_expires: :usri3_acct_expires, + max_storage: :usri3_max_storage, + units_per_week: :usri3_units_per_week, + logon_hours: :usri3_logon_hours, + bad_pw_count: :usri3_bad_pw_count, + num_logons: :usri3_num_logons, + logon_server: :usri3_logon_server, + country_code: :usri3_country_code, + code_page: :usri3_code_page, + user_id: :usri3_user_id, + primary_group_id: :usri3_primary_group_id, + profile: :usri3_profile, + home_dir_drive: :usri3_home_dir_drive, + password_expired: :usri3_password_expired, + } + + def transform_usri3(args) + args.inject({}) do |memo, (k,v)| + memo[USER_INFO_3_TRANSFORM[k]] = v + memo + end + end + def user_info_3(args) USER_INFO_3.collect { |field| args.include?(field[0]) ? args[field[0]] : field[1] @@ -152,17 +192,9 @@ class Chef::Util::Windows::NetUser < Chef::Util::Windows end def add(args) - user = user_info_3(args) - buffer = user_info_3_pack(user) - - rc = NetUserAdd.call(nil, 3, buffer, rc) - if rc != NERR_Success - raise ArgumentError, get_last_error(rc) - end - - #usri3_primary_group_id: - #"When you call the NetUserAdd function, this member must be DOMAIN_GROUP_RID_USERS" - NetLocalGroupAddMembers(nil, multi_to_wide("Users"), 3, buffer[0,PTR_SIZE], 1) + transformed_args = transform_usri3(args) + Chef::ReservedNames::Win32::NetUser::net_user_add_l3(nil, transformed_args) + Chef::ReservedNames::Win32::NetUser::net_local_group_add_member(nil, "Users", args[:name]) end def user_modify(&proc) diff --git a/lib/chef/win32/api.rb b/lib/chef/win32/api.rb index 9f1ecf4c4b..f0bdf705d4 100644 --- a/lib/chef/win32/api.rb +++ b/lib/chef/win32/api.rb @@ -117,6 +117,7 @@ class Chef host.typedef :uint32, :LCID # Locale identifier. For more information, see Locales. host.typedef :uint32, :LCTYPE # Locale information type. For a list, see Locale Information Constants. host.typedef :uint32, :LGRPID # Language group identifier. For a list, see EnumLanguageGroupLocales. + host.typedef :pointer, :LMSTR host.typedef :long, :LONG # 32-bit signed integer. The range is -2,147,483,648 through +...647 decimal. host.typedef :int32, :LONG32 # 32-bit signed integer. The range is -2,147,483,648 through +...647 decimal. host.typedef :int64, :LONG64 # 64-bit signed integer. The range is –9,223,372,036,854,775,808 through +...807 diff --git a/lib/chef/win32/api/net.rb b/lib/chef/win32/api/net.rb index eeb2b078a4..f2cfa93828 100644 --- a/lib/chef/win32/api/net.rb +++ b/lib/chef/win32/api/net.rb @@ -32,8 +32,23 @@ class Chef MAX_PREFERRED_LENGTH = 0xFFFF - NERR_Success = 0 - NERR_UserNotFound = 2221 + DOMAIN_GROUP_RID_USERS = 0x00000201 + + UF_SCRIPT = 0x000001 + UF_ACCOUNTDISABLE = 0x000002 + UF_PASSWD_CANT_CHANGE = 0x000040 + UF_NORMAL_ACCOUNT = 0x000200 + UF_DONT_EXPIRE_PASSWD = 0x010000 + + NERR_Success = 0 + NERR_InvalidComputer = 2351 + NERR_NotPrimary = 2226 + NERR_SpeGroupOp = 2234 + NERR_LastAdmin = 2452 + NERR_BadPassword = 2203 + NERR_PasswordTooShort = 2245 + NERR_UserNotFound = 2221 + ERROR_ACCESS_DENIED = 5 ffi_lib "netapi32" @@ -67,6 +82,18 @@ class Chef :usri3_profile, :LPWSTR, :usri3_home_dir_drive, :LPWSTR, :usri3_password_expired, :DWORD + + + def set(key, val) + if val.class == String + val = FFI::MemoryPointer.from_string(val.to_wstring) + end + self[key] = val + end + end + + class LOCALGROUP_MEMBERS_INFO_3 < FFI::Struct + layout :lgrmi3_domainandname, :LPWSTR end # NET_API_STATUS NetUserEnum( @@ -85,6 +112,24 @@ class Chef # _In_ LPVOID Buffer # ); safe_attach_function :NetApiBufferFree, [ :LPVOID ], :DWORD + +#NET_API_STATUS NetUserAdd( + #_In_ LMSTR servername, + #_In_ DWORD level, + #_In_ LPBYTE buf, + #_Out_ LPDWORD parm_err +#); + safe_attach_function :NetUserAdd, [:LMSTR, :DWORD, :LPBYTE, :LPDWORD ], :DWORD + +#NET_API_STATUS NetLocalGroupAddMembers( +# _In_ LPCWSTR servername, +# _In_ LPCWSTR groupname, +# _In_ DWORD level, +# _In_ LPBYTE buf, +# _In_ DWORD totalentries +#); + safe_attach_function :NetLocalGroupAddMembers, [:LPCWSTR, :LPCWSTR, :DWORD, :LPBYTE, :DWORD ], :DWORD + end end end diff --git a/lib/chef/win32/user.rb b/lib/chef/win32/user.rb new file mode 100644 index 0000000000..095989236e --- /dev/null +++ b/lib/chef/win32/user.rb @@ -0,0 +1,145 @@ +# +# Author:: Jay Mundrawala(<jdm@chef.io>) +# Copyright:: Copyright 2015 Chef Software +# 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/net' +require 'chef/win32/error' +class Chef + module ReservedNames::Win32 + class NetUser + include Chef::ReservedNames::Win32::API::Error + extend Chef::ReservedNames::Win32::API::Error + + include Chef::ReservedNames::Win32::API::Net + extend Chef::ReservedNames::Win32::API::Net + + def self.default_user_info_3 + ui3 = USER_INFO_3.new.tap do |s| + { usri3_name: nil, + usri3_password: nil, + usri3_password_age: 0, + usri3_priv: 0, + usri3_home_dir: nil, + usri3_comment: nil, + usri3_flags: UF_SCRIPT|UF_DONT_EXPIRE_PASSWD|UF_NORMAL_ACCOUNT, + usri3_script_path: nil, + usri3_auth_flags: 0, + usri3_full_name: nil, + usri3_usr_comment: nil, + usri3_parms: nil, + usri3_workstations: nil, + usri3_last_logon: 0, + usri3_last_logoff: 0, + usri3_acct_expires: -1, + usri3_max_storage: -1, + usri3_units_per_week: 0, + usri3_logon_hours: nil, + usri3_bad_pw_count: 0, + usri3_num_logons: 0, + usri3_logon_server: nil, + usri3_country_code: 0, + usri3_code_page: 0, + usri3_user_id: 0, + usri3_primary_group_id: DOMAIN_GROUP_RID_USERS, + usri3_profile: nil, + usri3_home_dir_drive: nil, + usri3_password_expired: 0 + }.each do |(k,v)| + s.set(k, v) + end + end + end + + def self.net_api_error!(code) + formatted_message = "" + formatted_message << "---- Begin Win32 API output ----\n" + formatted_message << "Net Api Error Code: #{code}\n" + + msg = case code + when NERR_InvalidComputer + "The user does not have access to the requested information." + when NERR_NotPrimary + "The operation is allowed only on the primary domain controller of the domain." + when NERR_SpeGroupOp + "This operation is not allowed on this special group." + when NERR_LastAdmin + "This operation is not allowed on the last administrative account." + when NERR_BadUsername + "The user name or group name parameter is invalid." + when NERR_BadPassword + "The password parameter is invalid." + when NERR_UserNotFound + "The user name could not be found." + when NERR_PasswordTooShort + <<END +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 ERROR_ACCESS_DENIED + "The user does not have access to the requested information." + else + "Received unknown error code (#{code})" + end + + formatted_message << "Net Api Error Message: #{msg}\n" + formatted_message << "---- End Win32 API output ----\n" + raise Chef::Exceptions::Win32APIError, msg + "\n" + formatted_message + end + + def self.net_user_add_l3(server_name, args) + param_err = FFI::Buffer.new(:long) + buf = default_user_info_3 + + args.each do |k, v| + buf.set(k, v) + end + + server_name = server_name.to_wstring if server_name + + rc = NetUserAdd(server_name, 3, buf, param_err) + if rc != NERR_Success + if Chef::ReservedNames::Win32::Error.get_last_error != 0 + Chef::ReservedNames::Win32::Error.raise! + else + net_api_error!(rc) + end + end + end + + def self.net_local_group_add_member(server_name, group_name, domain_user) + server_name = server_name.to_wstring if server_name + group_name = group_name.to_wstring + domain_user = domain_user.to_wstring + + buf = LOCALGROUP_MEMBERS_INFO_3.new + buf[:lgrmi3_domainandname] = FFI::MemoryPointer.from_string(domain_user) + + rc = NetLocalGroupAddMembers(server_name, group_name, 3, buf, 1) + + if rc != NERR_Success + if Chef::ReservedNames::Win32::Error.get_last_error != 0 + Chef::ReservedNames::Win32::Error.raise! + else + net_api_error!(rc) + end + end + end + + end + end +end |