diff --git a/Gemfile b/Gemfile index 5145f12ca..9f4109aba 100644 --- a/Gemfile +++ b/Gemfile @@ -1,32 +1,32 @@ source :rubygems gemspec group(:development, :test) do gem "facter", "~> 1.6.4", :require => false gem "rack", "~> 1.4.1", :require => false gem "rspec", "~> 2.10.0", :require => false gem "mocha", "~> 0.10.5", :require => false end platforms :mswin, :mingw do # See http://jenkins.puppetlabs.com/ for current Gem listings for the Windows # CI Jobs. gem "sys-admin", "~> 1.5.6", :require => false gem "win32-api", "~> 1.4.8", :require => false gem "win32-dir", "~> 0.3.7", :require => false gem "win32-eventlog", "~> 0.5.3", :require => false gem "win32-process", "~> 0.6.5", :require => false - gem "win32-security", "~> 0.1.2", :require => false + gem "win32-security", "~> 0.1.4", :require => false gem "win32-service", "~> 0.7.2", :require => false gem "win32-taskscheduler", "~> 0.2.2", :require => false gem "win32console", "~> 1.3.2", :require => false gem "windows-api", "~> 0.4.1", :require => false gem "windows-pr", "~> 1.2.1", :require => false end if File.exists? "#{__FILE__}.local" eval(File.read("#{__FILE__}.local"), binding) end # vim:filetype=ruby diff --git a/Gemfile.lock b/Gemfile.lock index 445d46707..e457e0926 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -1,44 +1,44 @@ PATH remote: . specs: puppet (2.7.19) facter (~> 1.5) GEM remote: http://rubygems.org/ specs: diff-lcs (1.1.3) facter (1.6.11) metaclass (0.0.1) mocha (0.10.5) metaclass (~> 0.0.1) rack (1.4.1) rspec (2.10.0) rspec-core (~> 2.10.0) rspec-expectations (~> 2.10.0) rspec-mocks (~> 2.10.0) rspec-core (2.10.1) rspec-expectations (2.10.0) diff-lcs (~> 1.1.3) rspec-mocks (2.10.1) PLATFORMS ruby DEPENDENCIES facter (~> 1.6.4) mocha (~> 0.10.5) puppet! rack (~> 1.4.1) rspec (~> 2.10.0) sys-admin (~> 1.5.6) win32-api (~> 1.4.8) win32-dir (~> 0.3.7) win32-eventlog (~> 0.5.3) win32-process (~> 0.6.5) - win32-security (~> 0.1.2) + win32-security (~> 0.1.4) win32-service (~> 0.7.2) win32-taskscheduler (~> 0.2.2) win32console (~> 1.3.2) windows-api (~> 0.4.1) windows-pr (~> 1.2.1) diff --git a/lib/puppet/util/windows/sid.rb b/lib/puppet/util/windows/sid.rb index 5ce46345b..e3446633b 100644 --- a/lib/puppet/util/windows/sid.rb +++ b/lib/puppet/util/windows/sid.rb @@ -1,57 +1,86 @@ require 'puppet/util/windows' module Puppet::Util::Windows module SID require 'windows/security' include ::Windows::Security require 'windows/memory' include ::Windows::Memory require 'windows/msvcrt/string' include ::Windows::MSVCRT::String # missing from Windows::Error ERROR_NONE_MAPPED = 1332 ERROR_INVALID_SID_STRUCTURE = 1337 + # Convert a name into a SID string, e.g. "S-1-5-32-544". If the name is + # already in SID string form, just return it. Otherwise, convert the name, + # possibly DOMAIN\name, to a SID + def name_to_sid(name) + # Apparently, we accept a symbol.. + name = name.to_s if name + + # if it's in SID string form, return it, otherwise, lookup sid + is_sid = Win32::Security::SID.string_to_sid(name) rescue nil + + is_sid ? name : Win32::Security::SID.new(name).to_s + rescue + nil + end + + # Convert a SID string, e.g. "S-1-5-32-544" to a name, + # e.g. BUILTIN\Administrator. + def sid_to_name(value) + sid = Win32::Security::SID.new(Win32::Security::SID.string_to_sid(value)) + + if sid.domain and sid.domain.length > 0 + "#{sid.domain}\\#{sid.account}" + else + sid.account + end + rescue + nil + end + # Convert a SID pointer to a string, e.g. "S-1-5-32-544". def sid_ptr_to_string(psid) sid_buf = 0.chr * 256 str_ptr = 0.chr * 4 raise Puppet::Util::Windows::Error.new("Invalid SID") unless IsValidSid(psid) raise Puppet::Util::Windows::Error.new("Failed to convert binary SID") unless ConvertSidToStringSid(psid, str_ptr) begin strncpy(sid_buf, str_ptr.unpack('L')[0], sid_buf.size - 1) sid_buf[sid_buf.size - 1] = 0.chr return sid_buf.strip ensure LocalFree(str_ptr.unpack('L')[0]) end end # Convert a SID string, e.g. "S-1-5-32-544" to a pointer (containing the # address of the binary SID structure). The returned value can be used in # Win32 APIs that expect a PSID, e.g. IsValidSid. def string_to_sid_ptr(string, &block) sid_buf = 0.chr * 80 string_addr = [string].pack('p*').unpack('L')[0] raise Puppet::Util::Windows::Error.new("Failed to convert string SID: #{string}") unless ConvertStringSidToSid(string_addr, sid_buf) sid_ptr = sid_buf.unpack('L')[0] begin if block_given? yield sid_ptr else true end ensure LocalFree(sid_ptr) end end end end diff --git a/spec/unit/util/windows/sid_spec.rb b/spec/unit/util/windows/sid_spec.rb index 589322003..232adc64c 100755 --- a/spec/unit/util/windows/sid_spec.rb +++ b/spec/unit/util/windows/sid_spec.rb @@ -1,50 +1,84 @@ #!/usr/bin/env ruby require 'spec_helper' describe "Puppet::Util::Windows::SID", :if => Puppet.features.microsoft_windows? do if Puppet.features.microsoft_windows? class SIDTester include Puppet::Util::Windows::SID end end - let(:subject) { SIDTester.new } - let(:sid) { Win32::Security::SID::LocalSystem } - let(:invalid_sid) { 'bogus' } + let(:subject) { SIDTester.new } + let(:sid) { Win32::Security::SID::LocalSystem } + let(:invalid_sid) { 'bogus' } + let(:unknown_sid) { 'S-0-0-0' } + let(:unknown_name) { 'chewbacca' } + + context "#name_to_sid" do + it "should return nil if the account does not exist" do + subject.name_to_sid(unknown_name).should be_nil + end + + it "should accept unqualified account name" do + subject.name_to_sid('SYSTEM').should == sid + end + + it "should be case-insensitive" do + subject.name_to_sid('SYSTEM').should == subject.name_to_sid('system') + end + + it "should accept domain qualified account names" do + subject.name_to_sid('NT AUTHORITY\SYSTEM').should == sid + end + + it "should be the identity function for any sid" do + subject.name_to_sid(sid).should == sid + end + end + + context "#sid_to_name" do + it "should return nil if given a sid for an account that doesn't exist" do + subject.sid_to_name(unknown_sid).should be_nil + end + + it "should accept a sid" do + subject.sid_to_name(sid).should == "NT AUTHORITY\\SYSTEM" + end + end context "#sid_ptr_to_string" do it "should raise if given an invalid sid" do expect { subject.sid_ptr_to_string(nil) }.to raise_error(Puppet::Error, /Invalid SID/) end it "should yield a valid sid pointer" do string = nil subject.string_to_sid_ptr(sid) do |ptr| string = subject.sid_ptr_to_string(ptr) end string.should == sid end end context "#string_to_sid_ptr" do it "should accept well known SID" do subject.string_to_sid_ptr(sid).should be_true end it "should yield sid_ptr" do ptr = nil subject.string_to_sid_ptr(sid) do |p| ptr = p end ptr.should_not be_nil end it "should raise on an invalid sid" do expect { subject.string_to_sid_ptr(invalid_sid) }.to raise_error(Puppet::Error, /Failed to convert string SID/) end end end