summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDan Sedlacek <svmastersamurai@users.noreply.github.com>2017-08-08 08:35:38 -0700
committerBryan McLellan <btm@loftninjas.org>2017-08-08 11:35:38 -0400
commit35458abec91b644eb56b5bd38dd413391c069b63 (patch)
tree05d7bff646ca1baa9abad7cd0ec4d9c6b94c1312
parent3d56d89f7ce5f8e3279e7e86ab215d4941fa70c0 (diff)
downloadchef-35458abec91b644eb56b5bd38dd413391c069b63.tar.gz
add option to enable unprivileged symlink creation on windows (#6236)
* add option to pass unprivileged symlink creation on windows * redo check to be everytime depending on OS type and build Signed-off-by: Daniel Sedlacek <dansedlacek@fb.com>
-rw-r--r--lib/chef/win32/api/file.rb1
-rw-r--r--lib/chef/win32/file.rb2
-rw-r--r--lib/chef/win32/version.rb6
-rw-r--r--spec/unit/win32/link_spec.rb73
4 files changed, 82 insertions, 0 deletions
diff --git a/lib/chef/win32/api/file.rb b/lib/chef/win32/api/file.rb
index 355cc81378..6aa2927e1f 100644
--- a/lib/chef/win32/api/file.rb
+++ b/lib/chef/win32/api/file.rb
@@ -67,6 +67,7 @@ class Chef
MAX_PATH = 260
SYMBOLIC_LINK_FLAG_DIRECTORY = 0x1
+ SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE = 0x2
FILE_NAME_NORMALIZED = 0x0
FILE_NAME_OPENED = 0x8
diff --git a/lib/chef/win32/file.rb b/lib/chef/win32/file.rb
index fa3d0f7a9d..03d4496fa8 100644
--- a/lib/chef/win32/file.rb
+++ b/lib/chef/win32/file.rb
@@ -22,6 +22,7 @@ require "chef/win32/api/file"
require "chef/win32/api/security"
require "chef/win32/error"
require "chef/win32/unicode"
+require "chef/win32/version"
class Chef
module ReservedNames::Win32
@@ -60,6 +61,7 @@ class Chef
# TODO do a check for CreateSymbolicLinkW and
# raise NotImplemented exception on older Windows
flags = ::File.directory?(old_name) ? SYMBOLIC_LINK_FLAG_DIRECTORY : 0
+ flags |= SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE if Chef::ReservedNames::Win32::Version.new.win_10_creators_or_higher?
old_name = encode_path(old_name)
new_name = encode_path(new_name)
unless CreateSymbolicLinkW(new_name, old_name, flags)
diff --git a/lib/chef/win32/version.rb b/lib/chef/win32/version.rb
index 3e2d6bc1fe..f8228d40b3 100644
--- a/lib/chef/win32/version.rb
+++ b/lib/chef/win32/version.rb
@@ -30,6 +30,8 @@ class Chef
include Chef::ReservedNames::Win32::API::Macros
include Chef::ReservedNames::Win32::API::System
+ attr_reader :major_version, :minor_version, :build_number
+
# Ruby implementation of
# http://msdn.microsoft.com/en-us/library/ms724833(v=vs.85).aspx
# http://msdn.microsoft.com/en-us/library/ms724358(v=vs.85).aspx
@@ -114,6 +116,10 @@ class Chef
end
end
+ def win_10_creators_or_higher?
+ windows_10? && build_number >= 15063
+ end
+
private
def get_version
diff --git a/spec/unit/win32/link_spec.rb b/spec/unit/win32/link_spec.rb
new file mode 100644
index 0000000000..5f749c034e
--- /dev/null
+++ b/spec/unit/win32/link_spec.rb
@@ -0,0 +1,73 @@
+#
+# Copyright:: Copyright 2012-2017, Chef Software 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 "spec_helper"
+if Chef::Platform.windows?
+ require "chef/win32/api/file"
+ require "chef/win32/file"
+ require "chef/win32/version"
+end
+
+describe Chef::ReservedNames::Win32::File, :windows_only do
+ context "#symlink" do
+ let(:with_privilege) { Chef::ReservedNames::Win32::API::File::SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE }
+ let(:without_privilege) { 0x0 }
+
+ context "an invalid parameter is passed" do
+ it "will throw an exception if an invalid parameter is passed" do
+ allow(File).to receive(:directory?).and_return(false)
+ allow(Chef::ReservedNames::Win32::File).to receive(:encode_path) { |a| a }
+ allow_any_instance_of(Chef::ReservedNames::Win32::Version).to receive(:windows_10?).and_return(true)
+ allow_any_instance_of(Chef::ReservedNames::Win32::Version).to receive(:build_number).and_return(1)
+ allow(Chef::ReservedNames::Win32::File).to receive(:CreateSymbolicLinkW).and_return(nil)
+
+ expect { Chef::ReservedNames::Win32::File.symlink("a", "b") }.to raise_error Chef::Exceptions::Win32APIError
+ end
+ end
+
+ context "a valid parameter is passed" do
+ before(:each) do
+ allow(File).to receive(:directory?).and_return(false)
+ allow(Chef::ReservedNames::Win32::File).to receive(:encode_path) { |a| a }
+ allow(Chef::ReservedNames::Win32::File).to receive(:CreateSymbolicLinkW).with(any_args) { "don't //actually// do this" }
+ end
+
+ it "will not pass the unpirivileged symlink flag if the node is not Windows 10" do
+ allow_any_instance_of(Chef::ReservedNames::Win32::Version).to receive(:windows_10?).and_return(false)
+
+ expect(Chef::ReservedNames::Win32::File).to receive(:CreateSymbolicLinkW).with("b", "a", without_privilege)
+ described_class.symlink("a", "b")
+ end
+
+ it "will not pass the unpirivileged symlink flag if the node is not at least Windows 10 Creators Update" do
+ allow_any_instance_of(Chef::ReservedNames::Win32::Version).to receive(:windows_10?).and_return(true)
+ allow_any_instance_of(Chef::ReservedNames::Win32::Version).to receive(:build_number).and_return(1)
+
+ expect(Chef::ReservedNames::Win32::File).to receive(:CreateSymbolicLinkW).with("b", "a", without_privilege)
+ described_class.symlink("a", "b")
+ end
+
+ it "will pass the unpirivileged symlink flag if the node is Windows 10 Creators Update or higher" do
+ allow_any_instance_of(Chef::ReservedNames::Win32::Version).to receive(:windows_10?).and_return(true)
+ allow_any_instance_of(Chef::ReservedNames::Win32::Version).to receive(:build_number).and_return(15063)
+
+ expect(Chef::ReservedNames::Win32::File).to receive(:CreateSymbolicLinkW).with("b", "a", with_privilege)
+ described_class.symlink("a", "b")
+ end
+ end
+ end
+end