#
# Author:: Doug MacEachern (<dougm@vmware.com>)
# Copyright:: Copyright 2010-2016, 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_relative "../windows"
require_relative "../../exceptions"
require_relative "../../win32/net"
require_relative "../../win32/security"
# wrapper around a subset of the NetUser* APIs.
# nothing Chef specific, but not complete enough to be its own gem, so util for now.
class Chef::Util::Windows::NetUser < Chef::Util::Windows
private
NetUser = Chef::ReservedNames::Win32::NetUser
Security = Chef::ReservedNames::Win32::Security
Win32APIError = Chef::ReservedNames::Win32::API::Error
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,
}.freeze
def transform_usri3(args)
args.inject({}) do |memo, (k, v)|
memo[USER_INFO_3_TRANSFORM[k]] = v
memo
end
end
def usri3_to_hash(usri3)
t = USER_INFO_3_TRANSFORM.invert
usri3.inject({}) do |memo, (k, v)|
memo[t[k]] = v
memo
end
end
def set_info(args)
rc = NetUser.net_user_set_info_l3(nil, @username, transform_usri3(args))
rescue Chef::Exceptions::Win32APIError => e
raise ArgumentError, e
end
public
def initialize(username)
@username = username
end
LOGON32_PROVIDER_DEFAULT = Security::LOGON32_PROVIDER_DEFAULT
LOGON32_LOGON_NETWORK = Security::LOGON32_LOGON_NETWORK
# XXX for an extra painful alternative, see: http://support.microsoft.com/kb/180548
def validate_credentials(passwd)
token = Security.logon_user(@username, nil, passwd,
LOGON32_LOGON_NETWORK, LOGON32_PROVIDER_DEFAULT)
true
rescue Chef::Exceptions::Win32APIError => e
Chef::Log.trace(e)
# we're only interested in the incorrect password failures
if /System Error Code: 1326/.match?(e.to_s)
return false
end
# all other exceptions will assume we cannot logon for a different reason
Chef::Log.trace("Unable to login with the specified credentials. Assuming the credentials are valid.")
true
end
def get_info
begin
ui3 = NetUser.net_user_get_info_l3(nil, @username)
rescue Chef::Exceptions::Win32APIError => e
raise ArgumentError, e
end
usri3_to_hash(ui3)
end
def add(args)
transformed_args = transform_usri3(args)
NetUser.net_user_add_l3(nil, transformed_args)
NetUser.net_local_group_add_member(nil, Chef::ReservedNames::Win32::Security::SID.BuiltinUsers.account_simple_name, args[:name])
end
# FIXME: yard with @yield
def user_modify
user = get_info
user[:last_logon] = user[:units_per_week] = 0 # ignored as per USER_INFO_3 doc
user[:logon_hours] = nil # PBYTE field; \0 == no changes
yield(user)
set_info(user)
end
def update(args)
user_modify do |user|
args.each do |key, val|
user[key] = val
end
end
end
def delete
NetUser.net_user_del(nil, @username)
rescue Chef::Exceptions::Win32APIError => e
raise ArgumentError, e
end
def disable_account
user_modify do |user|
user[:flags] |= NetUser::UF_ACCOUNTDISABLE
# This does not set the password to nil. It (for some reason) means to ignore updating the field.
# See similar behavior for the logon_hours field documented at
# http://msdn.microsoft.com/en-us/library/windows/desktop/aa371338%28v=vs.85%29.aspx
user[:password] = nil
end
end
def enable_account
user_modify do |user|
user[:flags] &= ~NetUser::UF_ACCOUNTDISABLE
# This does not set the password to nil. It (for some reason) means to ignore updating the field.
# See similar behavior for the logon_hours field documented at
# http://msdn.microsoft.com/en-us/library/windows/desktop/aa371338%28v=vs.85%29.aspx
user[:password] = nil
end
end
def check_enabled
(get_info[:flags] & NetUser::UF_ACCOUNTDISABLE) != 0
end
end