summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJay Mundrawala <jdmundrawala@gmail.com>2015-05-06 16:46:55 -0700
committerJay Mundrawala <jdmundrawala@gmail.com>2015-05-15 08:51:16 -0700
commite0fb4b98b6af2c4eb03eab7b84d2ea55cba87d35 (patch)
tree2bfbbbb4db2e940e8a358156a0190ca1684c04da
parentaa5a7b77d171a919f254dc1b23ee0f7e4347d71c (diff)
downloadchef-e0fb4b98b6af2c4eb03eab7b84d2ea55cba87d35.tar.gz
Replace some user creation win32-api calls with ffi
-rw-r--r--lib/chef/util/windows/net_user.rb56
-rw-r--r--lib/chef/win32/api.rb1
-rw-r--r--lib/chef/win32/api/net.rb49
-rw-r--r--lib/chef/win32/user.rb145
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