diff options
author | Seth Chisamore <schisamo@opscode.com> | 2012-10-30 10:39:35 -0400 |
---|---|---|
committer | Seth Chisamore <schisamo@opscode.com> | 2012-10-30 10:39:35 -0400 |
commit | 24dc69a9a97e82a6e4207de68d6dcc664178249b (patch) | |
tree | 19bb289c9f88b4bbab066bc56b95d6d222fd5c35 /lib/chef/win32/security | |
parent | 9348c1c9c80ee757354d624b7dc1b78ebc7605c4 (diff) | |
download | chef-24dc69a9a97e82a6e4207de68d6dcc664178249b.tar.gz |
[OC-3564] move core Chef to the repo root \o/ \m/
The opscode/chef repository now only contains the core Chef library code
used by chef-client, knife and chef-solo!
Diffstat (limited to 'lib/chef/win32/security')
-rw-r--r-- | lib/chef/win32/security/ace.rb | 125 | ||||
-rw-r--r-- | lib/chef/win32/security/acl.rb | 101 | ||||
-rw-r--r-- | lib/chef/win32/security/securable_object.rb | 109 | ||||
-rw-r--r-- | lib/chef/win32/security/security_descriptor.rb | 93 | ||||
-rw-r--r-- | lib/chef/win32/security/sid.rb | 199 | ||||
-rw-r--r-- | lib/chef/win32/security/token.rb | 64 |
6 files changed, 691 insertions, 0 deletions
diff --git a/lib/chef/win32/security/ace.rb b/lib/chef/win32/security/ace.rb new file mode 100644 index 0000000000..efd44b1c85 --- /dev/null +++ b/lib/chef/win32/security/ace.rb @@ -0,0 +1,125 @@ +# +# Author:: John Keiser (<jkeiser@opscode.com>) +# Copyright:: Copyright 2011 Opscode, 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/win32/security' +require 'chef/win32/security/sid' +require 'chef/win32/memory' + +require 'ffi' + +class Chef + module ReservedNames::Win32 + class Security + class ACE + + def initialize(pointer, owner = nil) + if Chef::ReservedNames::Win32::API::Security::ACE_WITH_MASK_AND_SID.supports?(pointer.read_uchar) + @struct = Chef::ReservedNames::Win32::API::Security::ACE_WITH_MASK_AND_SID.new pointer + else + # TODO Support ALL the things + @struct = Chef::ReservedNames::Win32::API::Security::ACE_HEADER.new pointer + end + # Keep a reference to the actual owner of this memory so we don't get freed + @owner = owner + end + + def self.size_with_sid(sid) + Chef::ReservedNames::Win32::API::Security::ACE_WITH_MASK_AND_SID.offset_of(:SidStart) + sid.size + end + + def self.access_allowed(sid, mask, flags = 0) + create_ace_with_mask_and_sid(Chef::ReservedNames::Win32::API::Security::ACCESS_ALLOWED_ACE_TYPE, flags, mask, sid) + end + + def self.access_denied(sid, mask, flags = 0) + create_ace_with_mask_and_sid(Chef::ReservedNames::Win32::API::Security::ACCESS_DENIED_ACE_TYPE, flags, mask, sid) + end + + attr_reader :struct + + def ==(other) + type == other.type && flags == other.flags && mask == other.mask && sid == other.sid + end + + def dup + ACE.create_ace_with_mask_and_sid(type, flags, mask, sid) + end + + def flags + struct[:AceFlags] + end + + def flags=(val) + struct[:AceFlags] = val + end + + def explicit? + ! inherited? + end + + def inherited? + (struct[:AceFlags] & Chef::ReservedNames::Win32::API::Security::INHERITED_ACE) != 0 + end + + def mask + struct[:Mask] + end + + def mask=(val) + struct[:Mask] = val + end + + def pointer + struct.pointer + end + + def size + struct[:AceSize] + end + + def sid + # The SID runs off the end of the structure, starting at :SidStart. + # Use pointer arithmetic to get a pointer to that location. + Chef::ReservedNames::Win32::Security::SID.new(struct.pointer + struct.offset_of(:SidStart)) + end + + def to_s + "#{sid.account_name}/flags:#{flags.to_s(16)}/mask:#{mask.to_s(16)}" + end + + def type + struct[:AceType] + end + + private + + def self.create_ace_with_mask_and_sid(type, flags, mask, sid) + size_needed = size_with_sid(sid) + pointer = FFI::MemoryPointer.new size_needed + struct = Chef::ReservedNames::Win32::API::Security::ACE_WITH_MASK_AND_SID.new pointer + struct[:AceType] = type + struct[:AceFlags] = flags + struct[:AceSize] = size_needed + struct[:Mask] = mask + Chef::ReservedNames::Win32::Memory.memcpy(struct.pointer + struct.offset_of(:SidStart), sid.pointer, sid.size) + ACE.new(struct.pointer) + end + end + end + end +end
\ No newline at end of file diff --git a/lib/chef/win32/security/acl.rb b/lib/chef/win32/security/acl.rb new file mode 100644 index 0000000000..fd43b75cbf --- /dev/null +++ b/lib/chef/win32/security/acl.rb @@ -0,0 +1,101 @@ +# +# Author:: John Keiser (<jkeiser@opscode.com>) +# Copyright:: Copyright 2011 Opscode, 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/win32/security' +require 'chef/win32/security/ace' +require 'ffi' + +class Chef + module ReservedNames::Win32 + class Security + class ACL + include Enumerable + + def initialize(pointer, owner = nil) + @struct = Chef::ReservedNames::Win32::API::Security::ACLStruct.new pointer + # Keep a reference to the actual owner of this memory so that it isn't freed out from under us + # TODO this could be avoided if we could mark a pointer's parent manually + @owner = owner + end + + def self.create(aces) + aces_size = aces.inject(0) { |sum,ace| sum + ace.size } + acl_size = align_dword(Chef::ReservedNames::Win32::API::Security::ACLStruct.size + aces_size) # What the heck is 94??? + acl = Chef::ReservedNames::Win32::Security.initialize_acl(acl_size) + aces.each { |ace| Chef::ReservedNames::Win32::Security.add_ace(acl, ace) } + acl + end + + attr_reader :struct + + def ==(other) + return false if length != other.length + 0.upto(length-1) do |i| + return false if self[i] != other[i] + end + return true + end + + def pointer + struct.pointer + end + + def [](index) + Chef::ReservedNames::Win32::Security.get_ace(self, index) + end + + def delete_at(index) + Chef::ReservedNames::Win32::Security.delete_ace(self, index) + end + + def each + 0.upto(length-1) { |i| yield self[i] } + end + + def insert(index, *aces) + aces.reverse_each { |ace| add_ace(self, ace, index) } + end + + def length + struct[:AceCount] + end + + def push(*aces) + aces.each { |ace| Chef::ReservedNames::Win32::Security.add_ace(self, ace) } + end + + def unshift(*aces) + aces.each { |ace| Chef::ReservedNames::Win32::Security.add_ace(self, ace, 0) } + end + + def valid? + Chef::ReservedNames::Win32::Security.is_valid_acl(self) + end + + def to_s + "[#{self.collect { |ace| ace.to_s }.join(", ")}]" + end + private + + def self.align_dword(size) + (size + 4 - 1) & 0xfffffffc + end + end + end + end +end diff --git a/lib/chef/win32/security/securable_object.rb b/lib/chef/win32/security/securable_object.rb new file mode 100644 index 0000000000..00655c9bab --- /dev/null +++ b/lib/chef/win32/security/securable_object.rb @@ -0,0 +1,109 @@ +# +# Author:: John Keiser (<jkeiser@opscode.com>) +# Copyright:: Copyright 2011 Opscode, 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/win32/security' +require 'chef/win32/security/acl' +require 'chef/win32/security/sid' + +class Chef + module ReservedNames::Win32 + class Security + class SecurableObject + + def initialize(path, type = :SE_FILE_OBJECT) + @path = path + @type = type + end + + attr_reader :path + attr_reader :type + + SecurityConst = Chef::ReservedNames::Win32::API::Security + + # This method predicts what the rights mask would be on an object + # if you created an ACE with the given mask. Specifically, it looks for + # generic attributes like GENERIC_READ, and figures out what specific + # attributes will be set. This is important if you want to try to + # compare an existing ACE with one you want to create. + def predict_rights_mask(generic_mask) + mask = generic_mask + #mask |= Chef::ReservedNames::Win32::API::Security::STANDARD_RIGHTS_READ if (mask | Chef::ReservedNames::Win32::API::Security::GENERIC_READ) != 0 + #mask |= Chef::ReservedNames::Win32::API::Security::STANDARD_RIGHTS_WRITE if (mask | Chef::ReservedNames::Win32::API::Security::GENERIC_WRITE) != 0 + #mask |= Chef::ReservedNames::Win32::API::Security::STANDARD_RIGHTS_EXECUTE if (mask | Chef::ReservedNames::Win32::API::Security::GENERIC_EXECUTE) != 0 + #mask |= Chef::ReservedNames::Win32::API::Security::STANDARD_RIGHTS_ALL if (mask | Chef::ReservedNames::Win32::API::Security::GENERIC_ALL) != 0 + if type == :SE_FILE_OBJECT + mask |= Chef::ReservedNames::Win32::API::Security::FILE_GENERIC_READ if (mask & Chef::ReservedNames::Win32::API::Security::GENERIC_READ) != 0 + mask |= Chef::ReservedNames::Win32::API::Security::FILE_GENERIC_WRITE if (mask & Chef::ReservedNames::Win32::API::Security::GENERIC_WRITE) != 0 + mask |= Chef::ReservedNames::Win32::API::Security::FILE_GENERIC_EXECUTE if (mask & Chef::ReservedNames::Win32::API::Security::GENERIC_EXECUTE) != 0 + mask |= Chef::ReservedNames::Win32::API::Security::FILE_ALL_ACCESS if (mask & Chef::ReservedNames::Win32::API::Security::GENERIC_ALL) != 0 + else + raise "Unimplemented object type for predict_security_mask: #{type}" + end + mask &= ~(Chef::ReservedNames::Win32::API::Security::GENERIC_READ | Chef::ReservedNames::Win32::API::Security::GENERIC_WRITE | Chef::ReservedNames::Win32::API::Security::GENERIC_EXECUTE | Chef::ReservedNames::Win32::API::Security::GENERIC_ALL) + mask + end + + def security_descriptor(include_sacl = false) + security_information = Chef::ReservedNames::Win32::API::Security::OWNER_SECURITY_INFORMATION | Chef::ReservedNames::Win32::API::Security::GROUP_SECURITY_INFORMATION | Chef::ReservedNames::Win32::API::Security::DACL_SECURITY_INFORMATION + if include_sacl + security_information |= Chef::ReservedNames::Win32::API::Security::SACL_SECURITY_INFORMATION + Security.with_privileges("SeSecurityPrivilege") do + Security.get_named_security_info(path, type, security_information) + end + else + Security.get_named_security_info(path, type, security_information) + end + end + + def dacl=(val) + Security.set_named_security_info(path, type, :dacl => val) + end + + # You don't set dacl_inherits without also setting dacl, + # because Windows gets angry and denies you access. So + # if you want to do that, you may as well do both at once. + def set_dacl(dacl, dacl_inherits) + Security.set_named_security_info(path, type, :dacl => dacl, :dacl_inherits => dacl_inherits) + end + + def group=(val) + Security.set_named_security_info(path, type, :group => val) + end + + def owner=(val) + # TODO to fix serious permissions problems, we may need to enable SeBackupPrivilege. But we might need it (almost) everywhere else, too. + Security.with_privileges("SeTakeOwnershipPrivilege", "SeRestorePrivilege") do + Security.set_named_security_info(path, type, :owner => val) + end + end + + def sacl=(val) + Security.with_privileges("SeSecurityPrivilege") do + Security.set_named_security_info(path, type, :sacl => val) + end + end + + def set_sacl(sacl, sacl_inherits) + Security.with_privileges("SeSecurityPrivilege") do + Security.set_named_security_info(path, type, :sacl => sacl, :sacl_inherits => sacl_inherits) + end + end + end + end + end +end diff --git a/lib/chef/win32/security/security_descriptor.rb b/lib/chef/win32/security/security_descriptor.rb new file mode 100644 index 0000000000..658e9104b4 --- /dev/null +++ b/lib/chef/win32/security/security_descriptor.rb @@ -0,0 +1,93 @@ +# +# Author:: John Keiser (<jkeiser@opscode.com>) +# Copyright:: Copyright 2011 Opscode, 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/win32/security' +require 'chef/win32/security/acl' +require 'chef/win32/security/sid' + +class Chef + module ReservedNames::Win32 + class Security + class SecurityDescriptor + + def initialize(pointer) + @pointer = pointer + end + + attr_reader :pointer + + def absolute? + !self_relative? + end + + def control + control, version = Chef::ReservedNames::Win32::Security.get_security_descriptor_control(self) + control + end + + def dacl + raise "DACL not present" if !dacl_present? + present, acl, defaulted = Chef::ReservedNames::Win32::Security.get_security_descriptor_dacl(self) + acl + end + + def dacl_inherits? + (control & Chef::ReservedNames::Win32::API::Security::SE_DACL_PROTECTED) == 0 + end + + def dacl_present? + (control & Chef::ReservedNames::Win32::API::Security::SE_DACL_PRESENT) != 0 + end + + def group + result, defaulted = Chef::ReservedNames::Win32::Security.get_security_descriptor_group(self) + result + end + + def owner + result, defaulted = Chef::ReservedNames::Win32::Security.get_security_descriptor_owner(self) + result + end + + def sacl + raise "SACL not present" if !sacl_present? + Security.with_privileges("SeSecurityPrivilege") do + present, acl, defaulted = Chef::ReservedNames::Win32::Security.get_security_descriptor_sacl(self) + acl + end + end + + def sacl_inherits? + (control & Chef::ReservedNames::Win32::API::Security::SE_SACL_PROTECTED) == 0 + end + + def sacl_present? + (control & Chef::ReservedNames::Win32::API::Security::SE_SACL_PRESENT) != 0 + end + + def self_relative? + (control & Chef::ReservedNames::Win32::API::Security::SE_SELF_RELATIVE) != 0 + end + + def valid? + Chef::ReservedNames::Win32::Security.is_valid_security_descriptor(self) + end + end + end + end +end diff --git a/lib/chef/win32/security/sid.rb b/lib/chef/win32/security/sid.rb new file mode 100644 index 0000000000..7ca21eee79 --- /dev/null +++ b/lib/chef/win32/security/sid.rb @@ -0,0 +1,199 @@ +# +# Author:: John Keiser (<jkeiser@opscode.com>) +# Copyright:: Copyright 2011 Opscode, 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/win32/security' + +class Chef + module ReservedNames::Win32 + class Security + class SID + + def initialize(pointer, owner = nil) + @pointer = pointer + # Keep a reference to the actual owner of this memory so we don't get freed + @owner = owner + end + + def self.from_account(name) + domain, sid, use = Chef::ReservedNames::Win32::Security.lookup_account_name(name) + sid + end + + def self.from_string_sid(string_sid) + Chef::ReservedNames::Win32::Security::convert_string_sid_to_sid(string_sid) + end + + def ==(other) + other != nil && Chef::ReservedNames::Win32::Security.equal_sid(self, other) + end + + attr_reader :pointer + + def account + Chef::ReservedNames::Win32::Security.lookup_account_sid(self) + end + + def account_name + domain, name, use = account + (domain != nil && domain.length > 0) ? "#{domain}\\#{name}" : name + end + + def size + Chef::ReservedNames::Win32::Security.get_length_sid(self) + end + + def to_s + Chef::ReservedNames::Win32::Security.convert_sid_to_string_sid(self) + end + + def valid? + Chef::ReservedNames::Win32::Security.is_valid_sid(self) + end + + # Well-known SIDs + def self.Null + SID.from_string_sid('S-1-0') + end + def self.Nobody + SID.from_string_sid('S-1-0-0') + end + def self.World + SID.from_string_sid('S-1-1') + end + def self.Everyone + SID.from_string_sid('S-1-1-0') + end + def self.Local + SID.from_string_sid('S-1-2') + end + def self.Creator + SID.from_string_sid('S-1-3') + end + def self.CreatorOwner + SID.from_string_sid('S-1-3-0') + end + def self.CreatorGroup + SID.from_string_sid('S-1-3-1') + end + def self.CreatorOwnerServer + SID.from_string_sid('S-1-3-2') + end + def self.CreatorGroupServer + SID.from_string_sid('S-1-3-3') + end + def self.NonUnique + SID.from_string_sid('S-1-4') + end + def self.Nt + SID.from_string_sid('S-1-5') + end + def self.Dialup + SID.from_string_sid('S-1-5-1') + end + def self.Network + SID.from_string_sid('S-1-5-2') + end + def self.Batch + SID.from_string_sid('S-1-5-3') + end + def self.Interactive + SID.from_string_sid('S-1-5-4') + end + def self.Service + SID.from_string_sid('S-1-5-6') + end + def self.Anonymous + SID.from_string_sid('S-1-5-7') + end + def self.Proxy + SID.from_string_sid('S-1-5-8') + end + def self.EnterpriseDomainControllers + SID.from_string_sid('S-1-5-9') + end + def self.PrincipalSelf + SID.from_string_sid('S-1-5-10') + end + def self.AuthenticatedUsers + SID.from_string_sid('S-1-5-11') + end + def self.RestrictedCode + SID.from_string_sid('S-1-5-12') + end + def self.TerminalServerUsers + SID.from_string_sid('S-1-5-13') + end + def self.LocalSystem + SID.from_string_sid('S-1-5-18') + end + def self.NtLocal + SID.from_string_sid('S-1-5-19') + end + def self.NtNetwork + SID.from_string_sid('S-1-5-20') + end + def self.BuiltinAdministrators + SID.from_string_sid('S-1-5-32-544') + end + def self.BuiltinUsers + SID.from_string_sid('S-1-5-32-545') + end + def self.Guests + SID.from_string_sid('S-1-5-32-546') + end + def self.PowerUsers + SID.from_string_sid('S-1-5-32-547') + end + def self.AccountOperators + SID.from_string_sid('S-1-5-32-548') + end + def self.ServerOperators + SID.from_string_sid('S-1-5-32-549') + end + def self.PrintOperators + SID.from_string_sid('S-1-5-32-550') + end + def self.BackupOperators + SID.from_string_sid('S-1-5-32-551') + end + def self.Replicators + SID.from_string_sid('S-1-5-32-552') + end + def self.Administrators + SID.from_string_sid('S-1-5-32-544') + end + + # Machine-specific, well-known SIDs + # TODO: don't use strings, dummy + def self.None + SID.from_account("#{::ENV['COMPUTERNAME']}\\None") + end + def self.Administrator + SID.from_account("#{::ENV['COMPUTERNAME']}\\Administrator") + end + def self.Guest + SID.from_account("#{::ENV['COMPUTERNAME']}\\Guest") + end + + def self.current_user + SID.from_account("#{::ENV['USERDOMAIN']}\\#{::ENV['USERNAME']}") + end + end + end + end +end
\ No newline at end of file diff --git a/lib/chef/win32/security/token.rb b/lib/chef/win32/security/token.rb new file mode 100644 index 0000000000..ded4fc080e --- /dev/null +++ b/lib/chef/win32/security/token.rb @@ -0,0 +1,64 @@ +# +# Author:: John Keiser (<jkeiser@opscode.com>) +# Copyright:: Copyright 2011 Opscode, 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/win32/security' +require 'chef/win32/api/security' + +require 'ffi' + +class Chef + module ReservedNames::Win32 + class Security + class Token + + def initialize(handle) + @handle = handle + end + + attr_reader :handle + + def enable_privileges(*privilege_names) + # Build the list of privileges we want to set + new_privileges = Chef::ReservedNames::Win32::API::Security::TOKEN_PRIVILEGES.new( + FFI::MemoryPointer.new(Chef::ReservedNames::Win32::API::Security::TOKEN_PRIVILEGES.size_with_privileges(privilege_names.length))) + new_privileges[:PrivilegeCount] = 0 + privilege_names.each do |privilege_name| + luid = Chef::ReservedNames::Win32::API::Security::LUID.new + # Ignore failure (with_privileges TRIES but does not guarantee success-- + # APIs down the line will fail if privilege escalation fails) + if Chef::ReservedNames::Win32::API::Security.LookupPrivilegeValueW(nil, privilege_name.to_wstring, luid) + new_privilege = new_privileges.privilege(new_privileges[:PrivilegeCount]) + new_privilege[:Luid][:LowPart] = luid[:LowPart] + new_privilege[:Luid][:HighPart] = luid[:HighPart] + new_privilege[:Attributes] = Chef::ReservedNames::Win32::API::Security::SE_PRIVILEGE_ENABLED + new_privileges[:PrivilegeCount] = new_privileges[:PrivilegeCount] + 1 + end + end + + old_privileges = Chef::ReservedNames::Win32::Security.adjust_token_privileges(self, new_privileges) + end + + def adjust_privileges(privileges_struct) + if privileges_struct[:PrivilegeCount] > 0 + Chef::ReservedNames::Win32::Security::adjust_token_privileges(self, privileges_struct) + end + end + end + end + end +end |