From 65b5b66a83c978dca22b9c4e7acb6034bd736755 Mon Sep 17 00:00:00 2001 From: Todd Zullinger Date: Sun, 23 Oct 2011 21:45:12 -0400 Subject: [PATCH] Update to 2.6.12, fixes CVE-2011-3872 This includes and upstream patch to restore Mongrel XMLRPC functionality (upstream #10244) which was accidentally dropped in 2.6.12. --- ...Restore-Mongrel-XMLRPC-functionality.patch | 161 +++++++++ ...uncate-remotely-sourced-files-on-404.patch | 273 --------------- ...ugins-even-if-you-re-filtering-on-ta.patch | 123 ------- ...stub-Puppet-Resource-Reference-for-u.patch | 182 ---------- ...st-directory-traversal-attacks-2.6.x.patch | 138 -------- 2.6.x-9791-TOCTOU-in-ssh-auth-keys-type.patch | 107 ------ ...dictable-temporary-filename-in-ralsh.patch | 69 ---- ...rector-file-backed-terminus-base-cla.patch | 330 ------------------ ...an-overwrite-arbitrary-files-as-root.patch | 40 --- puppet-2.6.12.tar.gz.asc | 17 + puppet-2.6.6.tar.gz.sign | 17 - puppet.spec | 43 +-- sources | 2 +- 13 files changed, 190 insertions(+), 1312 deletions(-) create mode 100644 0001-10244-Restore-Mongrel-XMLRPC-functionality.patch delete mode 100644 0001-4922-Don-t-truncate-remotely-sourced-files-on-404.patch delete mode 100644 0001-5073-Download-plugins-even-if-you-re-filtering-on-ta.patch delete mode 100644 0001-5428-More-fully-stub-Puppet-Resource-Reference-for-u.patch delete mode 100644 0001-Resist-directory-traversal-attacks-2.6.x.patch delete mode 100644 2.6.x-9791-TOCTOU-in-ssh-auth-keys-type.patch delete mode 100644 2.6.x-9792-Predictable-temporary-filename-in-ralsh.patch delete mode 100644 2.6.x-9793-secure-indirector-file-backed-terminus-base-cla.patch delete mode 100644 2.6.x-9794-k5login-can-overwrite-arbitrary-files-as-root.patch create mode 100644 puppet-2.6.12.tar.gz.asc delete mode 100644 puppet-2.6.6.tar.gz.sign diff --git a/0001-10244-Restore-Mongrel-XMLRPC-functionality.patch b/0001-10244-Restore-Mongrel-XMLRPC-functionality.patch new file mode 100644 index 0000000..d1ba7d4 --- /dev/null +++ b/0001-10244-Restore-Mongrel-XMLRPC-functionality.patch @@ -0,0 +1,161 @@ +From 908aef3579534f7718dfbdeb24fad94591186a3f Mon Sep 17 00:00:00 2001 +From: Nick Lewis +Date: Mon, 24 Oct 2011 10:13:33 -0700 +Subject: [PATCH] (#10244) Restore Mongrel XMLRPC functionality + +This code was over-eagerly removed, when it turns out to actually +still be necessary for backward compatibility with XMLRPC clients. +--- + lib/puppet/network/http_server.rb | 3 + + lib/puppet/network/http_server/mongrel.rb | 130 +++++++++++++++++++++++++++++ + 2 files changed, 133 insertions(+), 0 deletions(-) + create mode 100644 lib/puppet/network/http_server.rb + create mode 100644 lib/puppet/network/http_server/mongrel.rb + +diff --git a/lib/puppet/network/http_server.rb b/lib/puppet/network/http_server.rb +new file mode 100644 +index 0000000..e3826a6 +--- /dev/null ++++ b/lib/puppet/network/http_server.rb +@@ -0,0 +1,3 @@ ++# Just a stub, so we can correctly scope other classes. ++module Puppet::Network::HTTPServer # :nodoc: ++end +diff --git a/lib/puppet/network/http_server/mongrel.rb b/lib/puppet/network/http_server/mongrel.rb +new file mode 100644 +index 0000000..ce0401a +--- /dev/null ++++ b/lib/puppet/network/http_server/mongrel.rb +@@ -0,0 +1,129 @@ ++#!/usr/bin/env ruby ++# File: 06-11-14-mongrel_xmlrpc.rb ++# Author: Manuel Holtgrewe ++# ++# Copyright (c) 2006 Manuel Holtgrewe, 2007 Luke Kanies ++# ++# This file is based heavily on a file retrieved from ++# http://ttt.ggnore.net/2006/11/15/xmlrpc-with-mongrel-and-ruby-off-rails/ ++ ++require 'rubygems' ++require 'mongrel' ++require 'xmlrpc/server' ++require 'puppet/network/xmlrpc/server' ++require 'puppet/network/http_server' ++require 'puppet/network/client_request' ++require 'puppet/network/handler' ++ ++require 'resolv' ++ ++# This handler can be hooked into Mongrel to accept HTTP requests. After ++# checking whether the request itself is sane, the handler forwards it ++# to an internal instance of XMLRPC::BasicServer to process it. ++# ++# You can access the server by calling the Handler's "xmlrpc_server" ++# attribute accessor method and add XMLRPC handlers there. For example: ++# ++#
++# handler = XmlRpcHandler.new
++# handler.xmlrpc_server.add_handler("my.add") { |a, b| a.to_i + b.to_i }
++# 
++module Puppet::Network ++ class HTTPServer::Mongrel < ::Mongrel::HttpHandler ++ attr_reader :xmlrpc_server ++ ++ def initialize(handlers) ++ if Puppet[:debug] ++ $mongrel_debug_client = true ++ Puppet.debug 'Mongrel client debugging enabled. [$mongrel_debug_client = true].' ++ end ++ # Create a new instance of BasicServer. We are supposed to subclass it ++ # but that does not make sense since we would not introduce any new ++ # behaviour and we have to subclass Mongrel::HttpHandler so our handler ++ # works for Mongrel. ++ @xmlrpc_server = Puppet::Network::XMLRPCServer.new ++ handlers.each do |name| ++ unless handler = Puppet::Network::Handler.handler(name) ++ raise ArgumentError, "Invalid handler #{name}" ++ end ++ @xmlrpc_server.add_handler(handler.interface, handler.new({})) ++ end ++ end ++ ++ # This method produces the same results as XMLRPC::CGIServer.serve ++ # from Ruby's stdlib XMLRPC implementation. ++ def process(request, response) ++ # Make sure this has been a POST as required for XMLRPC. ++ request_method = request.params[Mongrel::Const::REQUEST_METHOD] || Mongrel::Const::GET ++ if request_method != "POST" ++ response.start(405) { |head, out| out.write("Method Not Allowed") } ++ return ++ end ++ ++ # Make sure the user has sent text/xml data. ++ request_mime = request.params["CONTENT_TYPE"] || "text/plain" ++ if parse_content_type(request_mime).first != "text/xml" ++ response.start(400) { |head, out| out.write("Bad Request") } ++ return ++ end ++ ++ # Make sure there is data in the body at all. ++ length = request.params[Mongrel::Const::CONTENT_LENGTH].to_i ++ if length <= 0 ++ response.start(411) { |head, out| out.write("Length Required") } ++ return ++ end ++ ++ # Check the body to be valid. ++ if request.body.nil? or request.body.size != length ++ response.start(400) { |head, out| out.write("Bad Request") } ++ return ++ end ++ ++ info = client_info(request) ++ ++ # All checks above passed through ++ response.start(200) do |head, out| ++ head["Content-Type"] = "text/xml; charset=utf-8" ++ begin ++ out.write(@xmlrpc_server.process(request.body, info)) ++ rescue => detail ++ puts detail.backtrace ++ raise ++ end ++ end ++ end ++ ++ private ++ ++ def client_info(request) ++ params = request.params ++ ip = params["HTTP_X_FORWARDED_FOR"] ? params["HTTP_X_FORWARDED_FOR"].split(',').last.strip : params["REMOTE_ADDR"] ++ # JJM #906 The following dn.match regular expression is forgiving ++ # enough to match the two Distinguished Name string contents ++ # coming from Apache, Pound or other reverse SSL proxies. ++ if dn = params[Puppet[:ssl_client_header]] and dn_matchdata = dn.match(/^.*?CN\s*=\s*(.*)/) ++ client = dn_matchdata[1].to_str ++ valid = (params[Puppet[:ssl_client_verify_header]] == 'SUCCESS') ++ else ++ begin ++ client = Resolv.getname(ip) ++ rescue => detail ++ Puppet.err "Could not resolve #{ip}: #{detail}" ++ client = "unknown" ++ end ++ valid = false ++ end ++ ++ info = Puppet::Network::ClientRequest.new(client, ip, valid) ++ ++ info ++ end ++ ++ # Taken from XMLRPC::ParseContentType ++ def parse_content_type(str) ++ a, *b = str.split(";") ++ return a.strip, *b ++ end ++ end ++end +-- +1.7.7 + diff --git a/0001-4922-Don-t-truncate-remotely-sourced-files-on-404.patch b/0001-4922-Don-t-truncate-remotely-sourced-files-on-404.patch deleted file mode 100644 index 1b4f1ce..0000000 --- a/0001-4922-Don-t-truncate-remotely-sourced-files-on-404.patch +++ /dev/null @@ -1,273 +0,0 @@ -From 743e03930758d17ed35fc6b73f7c2c68d8212137 Mon Sep 17 00:00:00 2001 -From: Nick Lewis -Date: Mon, 28 Feb 2011 13:40:18 -0800 -Subject: [PATCH/puppet] (#4922) Don't truncate remotely-sourced files on 404 - -We were 'handling' 404's on remote file content retrieval by returning nil -rather than raising an exception. This caused no content to be written to the -temporary file, but still appeared successful, so the destination file was -overwritten, instead of preserved. Now we just handle 404 like any other -error. - -Note that the root cause of these 404s seems to have been #4319, which has been -fixed. However, in the event we do happen to get a 404 here, it's better not to -have code to specifically handle it incorrectly. - -Paired-With: Max Martin -Reviewed-By: Matt Robinson ---- - lib/puppet/type/file/content.rb | 1 - - spec/unit/type/file/content_spec.rb | 175 ++++++++--------------------------- - 2 files changed, 38 insertions(+), 138 deletions(-) - -diff --git a/lib/puppet/type/file/content.rb b/lib/puppet/type/file/content.rb -index 63c0aaf..1b36acb 100755 ---- a/lib/puppet/type/file/content.rb -+++ b/lib/puppet/type/file/content.rb -@@ -194,7 +194,6 @@ module Puppet - connection = Puppet::Network::HttpPool.http_instance(source_or_content.server, source_or_content.port) - connection.request_get(indirection2uri(request), add_accept_encoding({"Accept" => "raw"})) do |response| - case response.code -- when "404"; nil - when /^2/; uncompress(response) { |uncompressor| response.read_body { |chunk| yield uncompressor.uncompress(chunk) } } - else - # Raise the http error if we didn't get a 'success' of some kind. -diff --git a/spec/unit/type/file/content_spec.rb b/spec/unit/type/file/content_spec.rb -index 9178c94..7d23399 100755 ---- a/spec/unit/type/file/content_spec.rb -+++ b/spec/unit/type/file/content_spec.rb -@@ -4,15 +4,14 @@ Dir.chdir(File.dirname(__FILE__)) { (s = lambda { |f| File.exist?(f) ? require(f - - content = Puppet::Type.type(:file).attrclass(:content) - describe content do -+ include PuppetSpec::Files - before do -- @resource = Puppet::Type.type(:file).new :path => "/foo/bar" -+ @filename = tmpfile('testfile') -+ @resource = Puppet::Type.type(:file).new :path => @filename -+ File.open(@filename, 'w') {|f| f.write "initial file content"} - content.stubs(:standalone?).returns(false) - end - -- it "should be a subclass of Property" do -- content.superclass.must == Puppet::Property -- end -- - describe "when determining the checksum type" do - it "should use the type specified in the source checksum if a source is set" do - @resource[:source] = "/foo" -@@ -249,10 +248,10 @@ describe content do - describe "when writing" do - before do - @content = content.new(:resource => @resource) -- @fh = stub_everything - end - - it "should attempt to read from the filebucket if no actual content nor source exists" do -+ @fh = File.open(@filename, 'w') - @content.should = "{md5}foo" - @content.resource.bucket.class.any_instance.stubs(:getfile).returns "foo" - @content.write(@fh) -@@ -302,166 +301,68 @@ describe content do - - describe "from local source" do - before(:each) do -- @content.stubs(:actual_content).returns(nil) -- @source = stub_everything 'source', :local? => true, :full_path => "/path/to/source" -- @resource.stubs(:parameter).with(:source).returns @source -- -- @sum = stub_everything 'sum' -- @resource.stubs(:parameter).with(:checksum).returns(@sum) -- -- @digest = stub_everything 'digest' -- @sum.stubs(:sum_stream).yields(@digest) -- -- @file = stub_everything 'file' -- File.stubs(:open).yields(@file) -- @file.stubs(:read).with(8192).returns("chunk1").then.returns("chunk2").then.returns(nil) -- end -- -- it "should open the local file" do -- File.expects(:open).with("/path/to/source", "r") -- @content.write(@fh) -- end -+ @resource = Puppet::Type.type(:file).new :path => @filename, :backup => false -+ @sourcename = tmpfile('source') -+ @source_content = "source file content"*10000 -+ @sourcefile = File.open(@sourcename, 'w') {|f| f.write @source_content} - -- it "should read the local file by chunks" do -- @file.expects(:read).with(8192).returns("chunk1").then.returns(nil) -- @content.write(@fh) -+ @content = @resource.newattr(:content) -+ @source = @resource.newattr(:source) -+ @source.stubs(:metadata).returns stub_everything('metadata', :source => @sourcename, :ftype => 'file') - end - -- it "should write each chunk to the file" do -- @fh.expects(:print).with("chunk1").then.with("chunk2") -- @content.write(@fh) -- end -- -- it "should pass each chunk to the current sum stream" do -- @digest.expects(:<<).with("chunk1").then.with("chunk2") -- @content.write(@fh) -+ it "should copy content from the source to the file" do -+ @resource.write(@source) -+ File.read(@filename).should == @source_content - end - - it "should return the checksum computed" do -- @sum.stubs(:sum_stream).yields(@digest).returns("checksum") -- @content.write(@fh).should == "checksum" -+ File.open(@filename, 'w') do |file| -+ @content.write(file).should == "{md5}#{Digest::MD5.hexdigest(@source_content)}" -+ end - end - end - - describe "from remote source" do - before(:each) do -- @response = stub_everything 'mock response', :code => "404" -+ @resource = Puppet::Type.type(:file).new :path => @filename, :backup => false -+ @response = stub_everything 'response', :code => "200" -+ @source_content = "source file content"*10000 -+ @response.stubs(:read_body).multiple_yields(*(["source file content"]*10000)) -+ - @conn = stub_everything 'connection' - @conn.stubs(:request_get).yields(@response) - Puppet::Network::HttpPool.stubs(:http_instance).returns @conn - -- @content.stubs(:actual_content).returns(nil) -- @source = stub_everything 'source', :local? => false, :full_path => "/path/to/source", :server => "server", :port => 1234 -- @resource.stubs(:parameter).with(:source).returns @source -- -- @sum = stub_everything 'sum' -- @resource.stubs(:parameter).with(:checksum).returns(@sum) -- -- @digest = stub_everything 'digest' -- @sum.stubs(:sum_stream).yields(@digest) -- end -- -- it "should open a network connection to source server and port" do -- Puppet::Network::HttpPool.expects(:http_instance).with("server", 1234).returns @conn -- @content.write(@fh) -- end -- -- it "should send the correct indirection uri" do -- @conn.expects(:request_get).with { |uri,headers| uri == "/production/file_content/path/to/source" }.yields(@response) -- @content.write(@fh) -+ @content = @resource.newattr(:content) -+ @sourcename = "puppet:///test/foo" -+ @source = @resource.newattr(:source) -+ @source.stubs(:metadata).returns stub_everything('metadata', :source => @sourcename, :ftype => 'file') - end - -- it "should return nil if source is not found" do -- @response.expects(:code).returns("404") -- @content.write(@fh).should == nil -+ it "should write the contents to the file" do -+ @resource.write(@source) -+ File.read(@filename).should == @source_content - end - - it "should not write anything if source is not found" do -- @response.expects(:code).returns("404") -- @fh.expects(:print).never -- @content.write(@fh).should == nil -+ @response.stubs(:code).returns("404") -+ lambda {@resource.write(@source)}.should raise_error(Net::HTTPError) { |e| e.message =~ /404/ } -+ File.read(@filename).should == "initial file content" - end - - it "should raise an HTTP error in case of server error" do -- @response.expects(:code).returns("500") -- lambda { @content.write(@fh) }.should raise_error -- end -- -- it "should write content by chunks" do -- @response.expects(:code).returns("200") -- @response.expects(:read_body).multiple_yields("chunk1","chunk2") -- @fh.expects(:print).with("chunk1").then.with("chunk2") -- @content.write(@fh) -- end -- -- it "should pass each chunk to the current sum stream" do -- @response.expects(:code).returns("200") -- @response.expects(:read_body).multiple_yields("chunk1","chunk2") -- @digest.expects(:<<).with("chunk1").then.with("chunk2") -- @content.write(@fh) -+ @response.stubs(:code).returns("500") -+ lambda { @content.write(@fh) }.should raise_error { |e| e.message.include? @source_content } - end - - it "should return the checksum computed" do -- @response.expects(:code).returns("200") -- @response.expects(:read_body).multiple_yields("chunk1","chunk2") -- @sum.expects(:sum_stream).yields(@digest).returns("checksum") -- @content.write(@fh).should == "checksum" -- end -- -- it "should get the current accept encoding header value" do -- @content.expects(:add_accept_encoding) -- @content.write(@fh) -- end -- -- it "should uncompress body on error" do -- @response.expects(:code).returns("500") -- @response.expects(:body).returns("compressed body") -- @content.expects(:uncompress_body).with(@response).returns("uncompressed") -- lambda { @content.write(@fh) }.should raise_error { |e| e.message =~ /uncompressed/ } -- end -- -- it "should uncompress chunk by chunk" do -- uncompressor = stub_everything 'uncompressor' -- @content.expects(:uncompress).with(@response).yields(uncompressor) -- @response.expects(:code).returns("200") -- @response.expects(:read_body).multiple_yields("chunk1","chunk2") -- -- uncompressor.expects(:uncompress).with("chunk1").then.with("chunk2") -- @content.write(@fh) -- end -- -- it "should write uncompressed chunks to the file" do -- uncompressor = stub_everything 'uncompressor' -- @content.expects(:uncompress).with(@response).yields(uncompressor) -- @response.expects(:code).returns("200") -- @response.expects(:read_body).multiple_yields("chunk1","chunk2") -- -- uncompressor.expects(:uncompress).with("chunk1").returns("uncompressed1") -- uncompressor.expects(:uncompress).with("chunk2").returns("uncompressed2") -- -- @fh.expects(:print).with("uncompressed1") -- @fh.expects(:print).with("uncompressed2") -- -- @content.write(@fh) -- end -- -- it "should pass each uncompressed chunk to the current sum stream" do -- uncompressor = stub_everything 'uncompressor' -- @content.expects(:uncompress).with(@response).yields(uncompressor) -- @response.expects(:code).returns("200") -- @response.expects(:read_body).multiple_yields("chunk1","chunk2") -- -- uncompressor.expects(:uncompress).with("chunk1").returns("uncompressed1") -- uncompressor.expects(:uncompress).with("chunk2").returns("uncompressed2") -- -- @digest.expects(:<<).with("uncompressed1").then.with("uncompressed2") -- @content.write(@fh) -+ File.open(@filename, 'w') do |file| -+ @content.write(file).should == "{md5}#{Digest::MD5.hexdigest(@source_content)}" -+ end - end - end - -- describe "from a filebucket" do -- end -- - # These are testing the implementation rather than the desired behaviour; while that bites, there are a whole - # pile of other methods in the File type that depend on intimate details of this implementation and vice-versa. - # If these blow up, you are gonna have to review the callers to make sure they don't explode! --daniel 2011-02-01 --- -1.7.4.1 - diff --git a/0001-5073-Download-plugins-even-if-you-re-filtering-on-ta.patch b/0001-5073-Download-plugins-even-if-you-re-filtering-on-ta.patch deleted file mode 100644 index 3501750..0000000 --- a/0001-5073-Download-plugins-even-if-you-re-filtering-on-ta.patch +++ /dev/null @@ -1,123 +0,0 @@ -From 852fb9744320c253772c85e52b262b0290fb7dd4 Mon Sep 17 00:00:00 2001 -From: Matt Robinson -Date: Tue, 15 Mar 2011 16:13:15 -0700 -Subject: [PATCH/puppet] (#5073) Download plugins even if you're filtering on tags - -When we eval a resource in transaction.rb it was being skipped when -filtering on tags and downloading the plugins. There's a lot of -complicated conditions for whether to skip a resource, but this is a -condensed version of the path that was causing plugins not to be -downloaded. - - skip? - missing_tags? - !ignore_tags? - !host_config - -The Puppet::Configurer::Downloader creates separate catalogs and applies them -to get custom facts and plugins, so should be setting host_config to false. -Puppet::Util::Settings also sets host_config to false when you call use on -settings, while normal catalog application defaults to true. - -Thanks to Stefan Schulte for suggesting -the implementation fix. ---- - lib/puppet/configurer/downloader.rb | 1 + - lib/puppet/configurer/plugin_handler.rb | 9 +++++++- - spec/unit/configurer/downloader_spec.rb | 32 +++++++++++++++++++++--------- - 3 files changed, 31 insertions(+), 11 deletions(-) - -diff --git a/lib/puppet/configurer/downloader.rb b/lib/puppet/configurer/downloader.rb -index 1b587ed..b369620 100644 ---- a/lib/puppet/configurer/downloader.rb -+++ b/lib/puppet/configurer/downloader.rb -@@ -50,6 +50,7 @@ class Puppet::Configurer::Downloader - - def catalog - catalog = Puppet::Resource::Catalog.new -+ catalog.host_config = false - catalog.add_resource(file) - catalog - end -diff --git a/lib/puppet/configurer/plugin_handler.rb b/lib/puppet/configurer/plugin_handler.rb -index cfc6b5a..ae088f2 100644 ---- a/lib/puppet/configurer/plugin_handler.rb -+++ b/lib/puppet/configurer/plugin_handler.rb -@@ -9,7 +9,14 @@ module Puppet::Configurer::PluginHandler - # Retrieve facts from the central server. - def download_plugins - return nil unless download_plugins? -- Puppet::Configurer::Downloader.new("plugin", Puppet[:plugindest], Puppet[:pluginsource], Puppet[:pluginsignore]).evaluate.each { |file| load_plugin(file) } -+ plugin_downloader = Puppet::Configurer::Downloader.new( -+ "plugin", -+ Puppet[:plugindest], -+ Puppet[:pluginsource], -+ Puppet[:pluginsignore] -+ ) -+ -+ plugin_downloader.evaluate.each { |file| load_plugin(file) } - end - - def load_plugin(file) -diff --git a/spec/unit/configurer/downloader_spec.rb b/spec/unit/configurer/downloader_spec.rb -index c57f39f..4080263 100755 ---- a/spec/unit/configurer/downloader_spec.rb -+++ b/spec/unit/configurer/downloader_spec.rb -@@ -5,6 +5,8 @@ require File.dirname(__FILE__) + '/../../spec_helper' - require 'puppet/configurer/downloader' - - describe Puppet::Configurer::Downloader do -+ require 'puppet_spec/files' -+ include PuppetSpec::Files - it "should require a name" do - lambda { Puppet::Configurer::Downloader.new }.should raise_error(ArgumentError) - end -@@ -96,25 +98,35 @@ describe Puppet::Configurer::Downloader do - - describe "when creating the catalog to do the downloading" do - before do -- @dler = Puppet::Configurer::Downloader.new("foo", "path", "source") -+ @dler = Puppet::Configurer::Downloader.new("foo", "/download/path", "source") - end - - it "should create a catalog and add the file to it" do -- file = mock 'file' -- catalog = mock 'catalog' -- -- @dler.expects(:file).returns file -- -- Puppet::Resource::Catalog.expects(:new).returns catalog -- catalog.expects(:add_resource).with(file) -+ catalog = @dler.catalog -+ catalog.resources.size.should == 1 -+ catalog.resources.first.class.should == Puppet::Type::File -+ catalog.resources.first.name.should == "/download/path" -+ end - -- @dler.catalog.should equal(catalog) -+ it "should specify that it is not managing a host catalog" do -+ @dler.catalog.host_config.should == false - end -+ - end - - describe "when downloading" do - before do -- @dler = Puppet::Configurer::Downloader.new("foo", "path", "source") -+ @dl_name = tmpfile("downloadpath") -+ source_name = tmpfile("source") -+ File.open(source_name, 'w') {|f| f.write('hola mundo') } -+ @dler = Puppet::Configurer::Downloader.new("foo", @dl_name, source_name) -+ end -+ -+ it "should not skip downloaded resources when filtering on tags" do -+ Puppet[:tags] = 'maytag' -+ @dler.evaluate -+ -+ File.exists?(@dl_name).should be_true - end - - it "should log that it is downloading" do --- -1.7.4.1 - diff --git a/0001-5428-More-fully-stub-Puppet-Resource-Reference-for-u.patch b/0001-5428-More-fully-stub-Puppet-Resource-Reference-for-u.patch deleted file mode 100644 index 55566ec..0000000 --- a/0001-5428-More-fully-stub-Puppet-Resource-Reference-for-u.patch +++ /dev/null @@ -1,182 +0,0 @@ -From ff9e2425a58bb2b1ab836e440c3344b4012623c5 Mon Sep 17 00:00:00 2001 -From: Jacob Helwig -Date: Fri, 25 Feb 2011 17:03:56 -0800 -Subject: [PATCH/puppet] (#5428) More fully "stub" Puppet::Resource::Reference for use with storedconfigs - -The Puppet::Resource::Reference class wasn't stubbing enough of the 0.25.x -behavior to satisfy the needs of storedconfigs. Since P::R::Reference, -and Puppet::Resource were merged as part of 2.6.x, we can pretend that -P::Resource is P::R::Reference for the purposes of loading data from -storedconfigs. This should still satisfy the over-the-wire serialization -needs of 0.25.x. - -This also changes internal references to @parameters in -Puppet::Resource(::Reference) to go through a parameters method. This -allows us to "initialize" this instance variable lazily, since loading via -YAML bypasses the normal initialize method. - -Paired-with: Daniel Pittman -Reviewed-by: Markus Roberts ---- - lib/puppet/resource.rb | 35 +++++++++++++++++++---------------- - spec/unit/resource_spec.rb | 26 ++++++++++++++++++++++++-- - 2 files changed, 43 insertions(+), 18 deletions(-) - -diff --git a/lib/puppet/resource.rb b/lib/puppet/resource.rb -index e832804..e47fc7e 100644 ---- a/lib/puppet/resource.rb -+++ b/lib/puppet/resource.rb -@@ -5,6 +5,11 @@ require 'puppet/util/pson' - # The simplest resource class. Eventually it will function as the - # base class for all resource-like behaviour. - class Puppet::Resource -+ # This stub class is only needed for serialization compatibility with 0.25.x. -+ # Specifically, it exists to provide a compatibility API when using YAML -+ # serialized objects loaded from StoreConfigs. -+ Reference = Puppet::Resource -+ - include Puppet::Util::Tagging - - require 'puppet/resource/type_collection_helper' -@@ -104,7 +109,7 @@ class Puppet::Resource - # be overridden at some point, but this works for now. - %w{has_key? keys length delete empty? <<}.each do |method| - define_method(method) do |*args| -- @parameters.send(method, *args) -+ parameters.send(method, *args) - end - end - -@@ -112,13 +117,13 @@ class Puppet::Resource - # to lower-case symbols. - def []=(param, value) - validate_parameter(param) if validate_parameters -- @parameters[parameter_name(param)] = value -+ parameters[parameter_name(param)] = value - end - - # Return a given parameter's value. Converts all passed names - # to lower-case symbols. - def [](param) -- @parameters[parameter_name(param)] -+ parameters[parameter_name(param)] - end - - def ==(other) -@@ -140,11 +145,11 @@ class Puppet::Resource - - # Iterate over each param/value pair, as required for Enumerable. - def each -- @parameters.each { |p,v| yield p, v } -+ parameters.each { |p,v| yield p, v } - end - - def include?(parameter) -- super || @parameters.keys.include?( parameter_name(parameter) ) -+ super || parameters.keys.include?( parameter_name(parameter) ) - end - - # These two methods are extracted into a Helper -@@ -170,14 +175,6 @@ class Puppet::Resource - end - end - -- # This stub class is only needed for serialization compatibility with 0.25.x -- class Reference -- attr_accessor :type,:title -- def initialize(type,title) -- @type,@title = type,title -- end -- end -- - # Create our resource. - def initialize(type, title = nil, attributes = {}) - @parameters = {} -@@ -204,7 +201,7 @@ class Puppet::Resource - tag(self.type) - tag(self.title) if valid_tag?(self.title) - -- @reference = Reference.new(@type,@title) # for serialization compatibility with 0.25.x -+ @reference = self # for serialization compatibility with 0.25.x - if strict? and ! resource_type - if @type == 'Class' - raise ArgumentError, "Could not find declared class #{title}" -@@ -234,7 +231,7 @@ class Puppet::Resource - - # Produce a simple hash of our parameters. - def to_hash -- parse_title.merge @parameters -+ parse_title.merge parameters - end - - def to_s -@@ -256,7 +253,7 @@ class Puppet::Resource - # Convert our resource to Puppet code. - def to_manifest - "%s { '%s':\n%s\n}" % [self.type.to_s.downcase, self.title, -- @parameters.collect { |p, v| -+ parameters.collect { |p, v| - if v.is_a? Array - " #{p} => [\'#{v.join("','")}\']" - else -@@ -422,4 +419,10 @@ class Puppet::Resource - return { :name => title.to_s } - end - end -+ -+ def parameters -+ # @parameters could have been loaded from YAML, causing it to be nil (by -+ # bypassing initialize). -+ @parameters ||= {} -+ end - end -diff --git a/spec/unit/resource_spec.rb b/spec/unit/resource_spec.rb -index ff31b24..4c1dc49 100755 ---- a/spec/unit/resource_spec.rb -+++ b/spec/unit/resource_spec.rb -@@ -99,11 +99,11 @@ describe Puppet::Resource do - end - - it 'should fail if strict is set and type does not exist' do -- lambda { Puppet::Resource.new('foo', 'title', {:strict=>true}) }.should raise_error(ArgumentError, 'Invalid resource type foo') -+ lambda { Puppet::Resource.new('foo', 'title', {:strict=>true}) }.should raise_error(ArgumentError, 'Invalid resource type foo') - end - - it 'should fail if strict is set and class does not exist' do -- lambda { Puppet::Resource.new('Class', 'foo', {:strict=>true}) }.should raise_error(ArgumentError, 'Could not find declared class foo') -+ lambda { Puppet::Resource.new('Class', 'foo', {:strict=>true}) }.should raise_error(ArgumentError, 'Could not find declared class foo') - end - - it "should fail if the title is a hash and the type is not a valid resource reference string" do -@@ -463,6 +463,28 @@ describe Puppet::Resource do - end - end - -+ describe "when loading 0.25.x storedconfigs YAML" do -+ before :each do -+ @old_storedconfig_yaml = %q{--- !ruby/object:Puppet::Resource::Reference -+builtin_type: -+title: /tmp/bar -+type: File -+} -+ end -+ -+ it "should deserialize a Puppet::Resource::Reference without exceptions" do -+ lambda { YAML.load(@old_storedconfig_yaml) }.should_not raise_error -+ end -+ -+ it "should deserialize as a Puppet::Resource::Reference as a Puppet::Resource" do -+ YAML.load(@old_storedconfig_yaml).class.should == Puppet::Resource -+ end -+ -+ it "should to_hash properly" do -+ YAML.load(@old_storedconfig_yaml).to_hash.should == { :path => "/tmp/bar" } -+ end -+ end -+ - describe "when converting to a RAL resource" do - it "should use the resource type's :new method to create the resource if the resource is of a builtin type" do - resource = Puppet::Resource.new("file", @basepath+"/my/file") --- -1.7.4.1 - diff --git a/0001-Resist-directory-traversal-attacks-2.6.x.patch b/0001-Resist-directory-traversal-attacks-2.6.x.patch deleted file mode 100644 index 8c5bb49..0000000 --- a/0001-Resist-directory-traversal-attacks-2.6.x.patch +++ /dev/null @@ -1,138 +0,0 @@ -From 0a92a70a22b7e85ef60ed9b4d4070433b5ec3220 Mon Sep 17 00:00:00 2001 -From: Daniel Pittman -Date: Sat, 24 Sep 2011 12:44:20 -0700 -Subject: [PATCH] Resist directory traversal attacks through indirections. - -In various versions of Puppet it was possible to cause a directory traversal -attack through the SSLFile indirection base class. This was variously -triggered through the user-supplied key, or the Subject of the certificate, in -the code. - -Now, we detect bad patterns down in the base class for our indirections, and -fail hard on them. This reduces the attack surface with as little disruption -to the overall codebase as possible, making it suitable to deploy as part of -older, stable versions of Puppet. - -In the long term we will also address this higher up the stack, to prevent -these problems from reoccurring, but for now this will suffice. - -Huge thanks to Kristian Erik Hermansen for the -responsible disclosure, and useful analysis, around this defect. - -Signed-off-by: Daniel Pittman ---- - lib/puppet/indirector.rb | 7 +++++++ - lib/puppet/indirector/ssl_file.rb | 6 +++++- - lib/puppet/indirector/yaml.rb | 5 +++++ - spec/unit/indirector/ssl_file_spec.rb | 19 +++++++++++++++++++ - spec/unit/indirector/yaml_spec.rb | 14 ++++++++++++++ - 5 files changed, 50 insertions(+), 1 deletions(-) - -diff --git a/lib/puppet/indirector.rb b/lib/puppet/indirector.rb -index e6472f4..fd6bf30 100644 ---- a/lib/puppet/indirector.rb -+++ b/lib/puppet/indirector.rb -@@ -68,4 +68,11 @@ module Puppet::Indirector - self.class.indirection.save key, self - end - end -+ -+ -+ # Helper definition for indirections that handle filenames. -+ BadNameRegexp = Regexp.union(/^\.\./, -+ %r{[\\/]}, -+ "\0", -+ /(?i)^[a-z]:/) - end -diff --git a/lib/puppet/indirector/ssl_file.rb b/lib/puppet/indirector/ssl_file.rb -index 531180f..4510499 100644 ---- a/lib/puppet/indirector/ssl_file.rb -+++ b/lib/puppet/indirector/ssl_file.rb -@@ -52,8 +52,12 @@ class Puppet::Indirector::SslFile < Puppet::Indirector::Terminus - (collection_directory || file_location) or raise Puppet::DevError, "No file or directory setting provided; terminus #{self.class.name} cannot function" - end - -- # Use a setting to determine our path. - def path(name) -+ if name =~ Puppet::Indirector::BadNameRegexp then -+ Puppet.crit("directory traversal detected in #{self.class}: #{name.inspect}") -+ raise ArgumentError, "invalid key" -+ end -+ - if ca?(name) and ca_location - ca_location - elsif collection_directory -diff --git a/lib/puppet/indirector/yaml.rb b/lib/puppet/indirector/yaml.rb -index 23997e9..4c488da 100644 ---- a/lib/puppet/indirector/yaml.rb -+++ b/lib/puppet/indirector/yaml.rb -@@ -43,6 +43,11 @@ class Puppet::Indirector::Yaml < Puppet::Indirector::Terminus - - # Return the path to a given node's file. - def path(name,ext='.yaml') -+ if name =~ Puppet::Indirector::BadNameRegexp then -+ Puppet.crit("directory traversal detected in #{self.class}: #{name.inspect}") -+ raise ArgumentError, "invalid key" -+ end -+ - base = Puppet.run_mode.master? ? Puppet[:yamldir] : Puppet[:clientyamldir] - File.join(base, self.class.indirection_name.to_s, name.to_s + ext) - end -diff --git a/spec/unit/indirector/ssl_file_spec.rb b/spec/unit/indirector/ssl_file_spec.rb -index 37098a7..4760bd7 100755 ---- a/spec/unit/indirector/ssl_file_spec.rb -+++ b/spec/unit/indirector/ssl_file_spec.rb -@@ -87,6 +87,25 @@ describe Puppet::Indirector::SslFile do - it "should set them in the setting directory, with the certificate name plus '.pem', if a directory setting is available" do - @searcher.path(@cert.name).should == @certpath - end -+ -+ ['../foo', '..\\foo', './../foo', '.\\..\\foo', -+ '/foo', '//foo', '\\foo', '\\\\goo', -+ "test\0/../bar", "test\0\\..\\bar", -+ "..\\/bar", "/tmp/bar", "/tmp\\bar", "tmp\\bar", -+ " / bar", " /../ bar", " \\..\\ bar", -+ "c:\\foo", "c:/foo", "\\\\?\\UNC\\bar", "\\\\foo\\bar", -+ "\\\\?\\c:\\foo", "//?/UNC/bar", "//foo/bar", -+ "//?/c:/foo", -+ ].each do |input| -+ it "should resist directory traversal attacks (#{input.inspect})" do -+ expect { @searcher.path(input) }.to raise_error -+ end -+ end -+ -+ # REVISIT: Should probably test MS-DOS reserved names here, too, since -+ # they would represent a vulnerability on a Win32 system, should we ever -+ # support that path. Don't forget that 'CON.foo' == 'CON' -+ # --daniel 2011-09-24 - end - - describe "when finding certificates on disk" do -diff --git a/spec/unit/indirector/yaml_spec.rb b/spec/unit/indirector/yaml_spec.rb -index 86c13c5..c8fadf7 100755 ---- a/spec/unit/indirector/yaml_spec.rb -+++ b/spec/unit/indirector/yaml_spec.rb -@@ -63,6 +63,20 @@ describe Puppet::Indirector::Yaml, " when choosing file location" do - it "should use the object's name to determine the file name" do - @store.path(:me).should =~ %r{me.yaml$} - end -+ -+ ['../foo', '..\\foo', './../foo', '.\\..\\foo', -+ '/foo', '//foo', '\\foo', '\\\\goo', -+ "test\0/../bar", "test\0\\..\\bar", -+ "..\\/bar", "/tmp/bar", "/tmp\\bar", "tmp\\bar", -+ " / bar", " /../ bar", " \\..\\ bar", -+ "c:\\foo", "c:/foo", "\\\\?\\UNC\\bar", "\\\\foo\\bar", -+ "\\\\?\\c:\\foo", "//?/UNC/bar", "//foo/bar", -+ "//?/c:/foo", -+ ].each do |input| -+ it "should resist directory traversal attacks (#{input.inspect})" do -+ expect { @store.path(input) }.to raise_error -+ end -+ end - end - - describe Puppet::Indirector::Yaml, " when storing objects as YAML" do --- -1.7.4.4 - diff --git a/2.6.x-9791-TOCTOU-in-ssh-auth-keys-type.patch b/2.6.x-9791-TOCTOU-in-ssh-auth-keys-type.patch deleted file mode 100644 index a1b7728..0000000 --- a/2.6.x-9791-TOCTOU-in-ssh-auth-keys-type.patch +++ /dev/null @@ -1,107 +0,0 @@ -From 8d9575775737c08c6cbfdf7f9a22f2ea4ab21b20 Mon Sep 17 00:00:00 2001 -From: Ricky Zhou -Date: Mon, 29 Aug 2011 16:01:12 -0400 -Subject: [PATCH] Drop privileges before creating and chmodding SSH keys. - -Previously, potentially abusable chown and chmod calls were performed as -root. This tries to moves as much as possible into code which is run -after privileges have been dropped. - -Huge thanks to Ricky Zhou for discovering this and -supplying the security fix. Awesome work. - -Signed-off-by: Daniel Pittman ---- - lib/puppet/provider/ssh_authorized_key/parsed.rb | 19 ++++++++++--------- - .../provider/ssh_authorized_key/parsed_spec.rb | 16 ++++++++-------- - 2 files changed, 18 insertions(+), 17 deletions(-) - -diff --git a/lib/puppet/provider/ssh_authorized_key/parsed.rb b/lib/puppet/provider/ssh_authorized_key/parsed.rb -index 6a3855c..5243477 100644 ---- a/lib/puppet/provider/ssh_authorized_key/parsed.rb -+++ b/lib/puppet/provider/ssh_authorized_key/parsed.rb -@@ -56,21 +56,22 @@ require 'puppet/provider/parsedfile' - def flush - raise Puppet::Error, "Cannot write SSH authorized keys without user" unless @resource.should(:user) - raise Puppet::Error, "User '#{@resource.should(:user)}' does not exist" unless uid = Puppet::Util.uid(@resource.should(:user)) -- unless File.exist?(dir = File.dirname(target)) -- Puppet.debug "Creating #{dir}" -- Dir.mkdir(dir, dir_perm) -- File.chown(uid, nil, dir) -- end -- - # ParsedFile usually calls backup_target much later in the flush process, - # but our SUID makes that fail to open filebucket files for writing. - # Fortunately, there's already logic to make sure it only ever happens once, - # so calling it here supresses the later attempt by our superclass's flush method. - self.class.backup_target(target) - -- Puppet::Util::SUIDManager.asuser(@resource.should(:user)) { super } -- File.chown(uid, nil, target) -- File.chmod(file_perm, target) -+ Puppet::Util::SUIDManager.asuser(@resource.should(:user)) do -+ unless File.exist?(dir = File.dirname(target)) -+ Puppet.debug "Creating #{dir}" -+ Dir.mkdir(dir, dir_perm) -+ end -+ -+ super -+ -+ File.chmod(file_perm, target) -+ end - end - - # parse sshv2 option strings, wich is a comma separated list of -diff --git a/spec/unit/provider/ssh_authorized_key/parsed_spec.rb b/spec/unit/provider/ssh_authorized_key/parsed_spec.rb -index 2e5be16..64935df 100755 ---- a/spec/unit/provider/ssh_authorized_key/parsed_spec.rb -+++ b/spec/unit/provider/ssh_authorized_key/parsed_spec.rb -@@ -133,15 +133,15 @@ describe provider_class do - @provider.flush - end - -- it "should chown the directory to the user" do -+ it "should absolutely not chown the directory to the user" do - uid = Puppet::Util.uid("random_bob") -- File.expects(:chown).with(uid, nil, "/tmp/.ssh_dir") -+ File.expects(:chown).never - @provider.flush - end - -- it "should chown the key file to the user" do -+ it "should absolutely not chown the key file to the user" do - uid = Puppet::Util.uid("random_bob") -- File.expects(:chown).with(uid, nil, "/tmp/.ssh_dir/place_to_put_authorized_keys") -+ File.expects(:chown).never - @provider.flush - end - -@@ -177,11 +177,11 @@ describe provider_class do - @provider.flush - end - -- it "should chown the directory to the user if it creates it" do -+ it "should absolutely not chown the directory to the user if it creates it" do - File.stubs(:exist?).with(@dir).returns false - Dir.stubs(:mkdir).with(@dir,0700) - uid = Puppet::Util.uid("nobody") -- File.expects(:chown).with(uid, nil, @dir) -+ File.expects(:chown).never - @provider.flush - end - -@@ -192,9 +192,9 @@ describe provider_class do - @provider.flush - end - -- it "should chown the key file to the user" do -+ it "should absolutely not chown the key file to the user" do - uid = Puppet::Util.uid("nobody") -- File.expects(:chown).with(uid, nil, File.expand_path("~nobody/.ssh/authorized_keys")) -+ File.expects(:chown).never - @provider.flush - end - --- -1.7.6.4 - diff --git a/2.6.x-9792-Predictable-temporary-filename-in-ralsh.patch b/2.6.x-9792-Predictable-temporary-filename-in-ralsh.patch deleted file mode 100644 index 5d9647b..0000000 --- a/2.6.x-9792-Predictable-temporary-filename-in-ralsh.patch +++ /dev/null @@ -1,69 +0,0 @@ -From 906da37374def334b62722acf84e4b0d1324e1f7 Mon Sep 17 00:00:00 2001 -From: Daniel Pittman -Date: Wed, 28 Sep 2011 23:35:19 -0700 -Subject: [PATCH] (#9792) Predictable temporary filename in ralsh. - -When ralsh is used in edit mode the temporary filename is in a shared -directory, and is absolutely predictable. Worse, it won't be touched until -well after the startup of the command. - -It can be tricked into writing through a symlink to edit any file on the -system, or to create through it, but worse - the file is reopened with the -same name later, so it can have the target replaced between edit and -operate... - -The only possible mitigation comes from the system editor and the behaviour it -has around editing through symbolic links, which is very weak. - -This improves this to prefer the current working directory for the temporary -file, and to be somewhat less predictable and more safe in conjuring it into -being. - -Signed-off-by: Daniel Pittman ---- - lib/puppet/application/resource.rb | 27 +++++++++++++++++---------- - 1 files changed, 17 insertions(+), 10 deletions(-) - -diff --git a/lib/puppet/application/resource.rb b/lib/puppet/application/resource.rb -index bc4faf5..3e4147e 100644 ---- a/lib/puppet/application/resource.rb -+++ b/lib/puppet/application/resource.rb -@@ -88,18 +88,25 @@ class Puppet::Application::Resource < Puppet::Application - end.map(&format).join("\n") - - if options[:edit] -- file = "/tmp/x2puppet-#{Process.pid}.pp" -+ require 'tempfile' -+ # Prefer the current directory, which is more likely to be secure -+ # and, in the case of interactive use, accessible to the user. -+ tmpfile = Tempfile.new('x2puppet', Dir.pwd) - begin -- File.open(file, "w") do |f| -- f.puts text -- end -- ENV["EDITOR"] ||= "vi" -- system(ENV["EDITOR"], file) -- system("puppet -v #{file}") -+ # sync write, so nothing buffers before we invoke the editor. -+ tmpfile.sync = true -+ tmpfile.puts text -+ -+ # edit the content -+ system(ENV["EDITOR"] || 'vi', tmpfile.path) -+ -+ # ...and, now, pass that file to puppet to apply. Because -+ # many editors rename or replace the original file we need to -+ # feed the pathname, not the file content itself, to puppet. -+ system('puppet -v ' + tmpfile.path) - ensure -- #if FileTest.exists? file -- # File.unlink(file) -- #end -+ # The temporary file will be safely removed. -+ tmpfile.close(true) - end - else - puts text --- -1.7.6.4 - diff --git a/2.6.x-9793-secure-indirector-file-backed-terminus-base-cla.patch b/2.6.x-9793-secure-indirector-file-backed-terminus-base-cla.patch deleted file mode 100644 index 3e15fa1..0000000 --- a/2.6.x-9793-secure-indirector-file-backed-terminus-base-cla.patch +++ /dev/null @@ -1,330 +0,0 @@ -From 40f025a9ae0373e2e642f7811face3486bfa34d4 Mon Sep 17 00:00:00 2001 -From: Daniel Pittman -Date: Thu, 29 Sep 2011 00:07:16 -0700 -Subject: [PATCH] (#9793) "secure" indirector file backed terminus base class. - -The file base class in the indirector trusted the request key directly, which -made it vulnerable to the same potential for injection attacks as other -terminus base classes. - -However, this is somewhat mitigated by the fact that base class is entirely -unused. We can simple eliminate it from the system, because nothing is more -secure than code that doesn't exist. - -The only consumer of the code was in the tests, and didn't care what base -class was used, so that was substituted with a continuing class. - -Signed-off-by: Daniel Pittman ---- - lib/puppet/indirector/file.rb | 79 -------------- - spec/unit/indirector/file_spec.rb | 181 --------------------------------- - spec/unit/indirector/terminus_spec.rb | 6 +- - 3 files changed, 3 insertions(+), 263 deletions(-) - delete mode 100644 lib/puppet/indirector/file.rb - delete mode 100755 spec/unit/indirector/file_spec.rb - -diff --git a/lib/puppet/indirector/file.rb b/lib/puppet/indirector/file.rb -deleted file mode 100644 -index b3746b7..0000000 ---- a/lib/puppet/indirector/file.rb -+++ /dev/null -@@ -1,79 +0,0 @@ --require 'puppet/indirector/terminus' -- --# Store instances as files, usually serialized using some format. --class Puppet::Indirector::File < Puppet::Indirector::Terminus -- # Where do we store our data? -- def data_directory -- name = Puppet.run_mode.master? ? :server_datadir : :client_datadir -- -- File.join(Puppet.settings[name], self.class.indirection_name.to_s) -- end -- -- def file_format(path) -- path =~ /\.(\w+)$/ and return $1 -- end -- -- def file_path(request) -- File.join(data_directory, request.key + ".#{serialization_format}") -- end -- -- def latest_path(request) -- files = Dir.glob(File.join(data_directory, request.key + ".*")) -- return nil if files.empty? -- -- # Return the newest file. -- files.sort { |a, b| File.stat(b).mtime <=> File.stat(a).mtime }[0] -- end -- -- def serialization_format -- model.default_format -- end -- -- # Remove files on disk. -- def destroy(request) -- begin -- removed = false -- Dir.glob(File.join(data_directory, request.key.to_s + ".*")).each do |file| -- removed = true -- File.unlink(file) -- end -- rescue => detail -- raise Puppet::Error, "Could not remove #{request.key}: #{detail}" -- end -- -- raise Puppet::Error, "Could not find files for #{request.key} to remove" unless removed -- end -- -- # Return a model instance for a given file on disk. -- def find(request) -- return nil unless path = latest_path(request) -- format = file_format(path) -- -- raise ArgumentError, "File format #{format} is not supported by #{self.class.indirection_name}" unless model.support_format?(format) -- -- begin -- return model.convert_from(format, File.read(path)) -- rescue => detail -- raise Puppet::Error, "Could not convert path #{path} into a #{self.class.indirection_name}: #{detail}" -- end -- end -- -- # Save a new file to disk. -- def save(request) -- path = file_path(request) -- -- dir = File.dirname(path) -- -- raise Puppet::Error.new("Cannot save #{request.key}; parent directory #{dir} does not exist") unless File.directory?(dir) -- -- begin -- File.open(path, "w") { |f| f.print request.instance.render(serialization_format) } -- rescue => detail -- raise Puppet::Error, "Could not write #{request.key}: #{detail}" % [request.key, detail] -- end -- end -- -- def path(key) -- key -- end --end -diff --git a/spec/unit/indirector/file_spec.rb b/spec/unit/indirector/file_spec.rb -deleted file mode 100755 -index 86673f0..0000000 ---- a/spec/unit/indirector/file_spec.rb -+++ /dev/null -@@ -1,181 +0,0 @@ --#!/usr/bin/env ruby -- --require File.dirname(__FILE__) + '/../../spec_helper' --require 'puppet/indirector/file' -- -- --describe Puppet::Indirector::File do -- before :each do -- Puppet::Indirector::Terminus.stubs(:register_terminus_class) -- @model = mock 'model' -- @indirection = stub 'indirection', :name => :mystuff, :register_terminus_type => nil, :model => @model -- Puppet::Indirector::Indirection.stubs(:instance).returns(@indirection) -- -- @file_class = Class.new(Puppet::Indirector::File) do -- def self.to_s -- "Testing::Mytype" -- end -- end -- -- @searcher = @file_class.new -- -- @path = "/my/file" -- @dir = "/my" -- -- @request = stub 'request', :key => @path -- end -- -- describe "when finding files" do -- it "should provide a method to return file contents at a specified path" do -- @searcher.should respond_to(:find) -- end -- -- it "should use the server data directory plus the indirection name if the run_mode is master" do -- Puppet.run_mode.expects(:master?).returns true -- Puppet.settings.expects(:value).with(:server_datadir).returns "/my/dir" -- -- @searcher.data_directory.should == File.join("/my/dir", "mystuff") -- end -- -- it "should use the client data directory plus the indirection name if the run_mode is not master" do -- Puppet.run_mode.expects(:master?).returns false -- Puppet.settings.expects(:value).with(:client_datadir).returns "/my/dir" -- -- @searcher.data_directory.should == File.join("/my/dir", "mystuff") -- end -- -- it "should use the newest file in the data directory matching the indirection key without extension" do -- @searcher.expects(:data_directory).returns "/data/dir" -- @request.stubs(:key).returns "foo" -- Dir.expects(:glob).with("/data/dir/foo.*").returns %w{/data1.stuff /data2.stuff} -- -- stat1 = stub 'data1', :mtime => (Time.now - 5) -- stat2 = stub 'data2', :mtime => Time.now -- File.expects(:stat).with("/data1.stuff").returns stat1 -- File.expects(:stat).with("/data2.stuff").returns stat2 -- -- @searcher.latest_path(@request).should == "/data2.stuff" -- end -- -- it "should return nil when no files are found" do -- @searcher.stubs(:latest_path).returns nil -- -- @searcher.find(@request).should be_nil -- end -- -- it "should determine the file format from the file extension" do -- @searcher.file_format("/data2.pson").should == "pson" -- end -- -- it "should fail if the model does not support the file format" do -- @searcher.stubs(:latest_path).returns "/my/file.pson" -- -- @model.expects(:support_format?).with("pson").returns false -- -- lambda { @searcher.find(@request) }.should raise_error(ArgumentError) -- end -- end -- -- describe "when saving files" do -- before do -- @content = "my content" -- @file = stub 'file', :content => @content, :path => @path, :name => @path, :render => "mydata" -- @request.stubs(:instance).returns @file -- end -- -- it "should provide a method to save file contents at a specified path" do -- @searcher.should respond_to(:save) -- end -- -- it "should choose the file extension based on the default format of the model" do -- @model.expects(:default_format).returns "pson" -- -- @searcher.serialization_format.should == "pson" -- end -- -- it "should place the file in the data directory, named after the indirection, key, and format" do -- @searcher.stubs(:data_directory).returns "/my/dir" -- @searcher.stubs(:serialization_format).returns "pson" -- -- @request.stubs(:key).returns "foo" -- @searcher.file_path(@request).should == File.join("/my/dir", "foo.pson") -- end -- -- it "should fail intelligently if the file's parent directory does not exist" do -- @searcher.stubs(:file_path).returns "/my/dir/file.pson" -- @searcher.stubs(:serialization_format).returns "pson" -- -- @request.stubs(:key).returns "foo" -- File.expects(:directory?).with(File.join("/my/dir")).returns(false) -- -- proc { @searcher.save(@request) }.should raise_error(Puppet::Error) -- end -- -- it "should render the instance using the file format and print it to the file path" do -- @searcher.stubs(:file_path).returns "/my/file.pson" -- @searcher.stubs(:serialization_format).returns "pson" -- -- File.stubs(:directory?).returns true -- -- @request.instance.expects(:render).with("pson").returns "data" -- -- fh = mock 'filehandle' -- File.expects(:open).with("/my/file.pson", "w").yields fh -- fh.expects(:print).with("data") -- -- @searcher.save(@request) -- end -- -- it "should fail intelligently if a file cannot be written" do -- filehandle = mock 'file' -- File.stubs(:directory?).returns(true) -- File.stubs(:open).yields(filehandle) -- filehandle.expects(:print).raises(ArgumentError) -- -- @searcher.stubs(:file_path).returns "/my/file.pson" -- @model.stubs(:default_format).returns "pson" -- -- @instance.stubs(:render).returns "stuff" -- -- proc { @searcher.save(@request) }.should raise_error(Puppet::Error) -- end -- end -- -- describe "when removing files" do -- it "should provide a method to remove files" do -- @searcher.should respond_to(:destroy) -- end -- -- it "should remove files in all formats found in the data directory that match the request key" do -- @searcher.stubs(:data_directory).returns "/my/dir" -- @request.stubs(:key).returns "me" -- -- Dir.expects(:glob).with(File.join("/my/dir", "me.*")).returns %w{/one /two} -- -- File.expects(:unlink).with("/one") -- File.expects(:unlink).with("/two") -- -- @searcher.destroy(@request) -- end -- -- it "should throw an exception if no file is found" do -- @searcher.stubs(:data_directory).returns "/my/dir" -- @request.stubs(:key).returns "me" -- -- Dir.expects(:glob).with(File.join("/my/dir", "me.*")).returns [] -- -- proc { @searcher.destroy(@request) }.should raise_error(Puppet::Error) -- end -- -- it "should fail intelligently if a file cannot be removed" do -- @searcher.stubs(:data_directory).returns "/my/dir" -- @request.stubs(:key).returns "me" -- -- Dir.expects(:glob).with(File.join("/my/dir", "me.*")).returns %w{/one} -- -- File.expects(:unlink).with("/one").raises ArgumentError -- -- proc { @searcher.destroy(@request) }.should raise_error(Puppet::Error) -- end -- end --end -diff --git a/spec/unit/indirector/terminus_spec.rb b/spec/unit/indirector/terminus_spec.rb -index 826b934..63cb7cc 100755 ---- a/spec/unit/indirector/terminus_spec.rb -+++ b/spec/unit/indirector/terminus_spec.rb -@@ -3,7 +3,7 @@ - require File.dirname(__FILE__) + '/../../spec_helper' - require 'puppet/defaults' - require 'puppet/indirector' --require 'puppet/indirector/file' -+require 'puppet/indirector/memory' - - describe Puppet::Indirector::Terminus do - before :each do -@@ -202,14 +202,14 @@ describe Puppet::Indirector::Terminus, " when parsing class constants for indire - @subclass.expects(:indirection=).with(:test_ind) - @subclass.stubs(:name=) - @subclass.stubs(:terminus_type=) -- Puppet::Indirector::File.inherited(@subclass) -+ Puppet::Indirector::Memory.inherited(@subclass) - end - - it "should convert the indirection name to a downcased symbol" do - @subclass.expects(:indirection=).with(:test_ind) - @subclass.stubs(:name=) - @subclass.stubs(:terminus_type=) -- Puppet::Indirector::File.inherited(@subclass) -+ Puppet::Indirector::Memory.inherited(@subclass) - end - - it "should convert camel case to lower case with underscores as word separators" do --- -1.7.6.4 - diff --git a/2.6.x-9794-k5login-can-overwrite-arbitrary-files-as-root.patch b/2.6.x-9794-k5login-can-overwrite-arbitrary-files-as-root.patch deleted file mode 100644 index 7fe08d6..0000000 --- a/2.6.x-9794-k5login-can-overwrite-arbitrary-files-as-root.patch +++ /dev/null @@ -1,40 +0,0 @@ -From bdf728edc4c0b0e0e416f9d3e542b6815a4d3c0a Mon Sep 17 00:00:00 2001 -From: Daniel Pittman -Date: Thu, 29 Sep 2011 00:32:49 -0700 -Subject: [PATCH] (#9794) k5login can overwrite arbitrary files as root - -The k5login type is typically used to manage a file in the home directory of a -user; the explicit purpose of the files is to allow access to other users. - -It writes to the target file directly, as root, without doing anything to -secure the file. That would allow the owner of the home directory to symlink -to anything on the system, and have it replaced with the correct content of -the file. Which is a fairly obvious escalation to root the next time Puppet -runs. - -Now, instead, fix that to securely write the target file in a predictable and -secure fashion, using the `secure_open` helper. - -Signed-off-by: Daniel Pittman ---- - lib/puppet/type/k5login.rb | 4 +++- - 1 files changed, 3 insertions(+), 1 deletions(-) - -diff --git a/lib/puppet/type/k5login.rb b/lib/puppet/type/k5login.rb -index eac142f..2e87ca9 100644 ---- a/lib/puppet/type/k5login.rb -+++ b/lib/puppet/type/k5login.rb -@@ -79,7 +79,9 @@ Puppet::Type.newtype(:k5login) do - - private - def write(value) -- File.open(@resource[:name], "w") { |f| f.puts value.join("\n") } -+ Puppet::Util.secure_open(@resource[:name], "w") do |f| -+ f.puts value.join("\n") -+ end - end - end - end --- -1.7.6.4 - diff --git a/puppet-2.6.12.tar.gz.asc b/puppet-2.6.12.tar.gz.asc new file mode 100644 index 0000000..6aae796 --- /dev/null +++ b/puppet-2.6.12.tar.gz.asc @@ -0,0 +1,17 @@ +-----BEGIN PGP SIGNATURE----- +Version: GnuPG v1.4.11 (GNU/Linux) + +iQIcBAABAgAGBQJOomPfAAoJEBBUt6JL1uww6QYP/0xT7OQnK5TZ0Q94KWHRHmje +UZxqhNKt3+xlH74wNM0W81HWJNvRkhVHu0ez8S3ERnExAdFfXG4lkr1kLmmQhKeN +xLW9xN5A31GU+SnjDhRtzzCujFEeexw4ZlWTKdrWtwvli7P/katInxXlNKqpZujl +IDq4+WhjrJ9/4sE0VqjrlOwfOjJPbFMg5M1MNDkS3P5VffHLhp2wdbeFmQH1TpHi +qhEh+vmJw9WO1+z0v/kgL2S8YQH4kCJ82vGG9xfxF5fIwgrL7xVxU4a1FS4Oypy6 +2Vff9tP9iBKGErTUwOSbxeJDkHRuQ3oc2hGTUfR8cmAZ5YUavbbIqbWPvOd142rF ++vDyxpcUO+tSZn4o12Wj+sZww+KuviHyexk3BmxNAPOW2UPMPfU9CcaZdkuKV7d3 +CyJ4dWg9YX7wY42C+rh7ztQ9LW4hWGcmdvroknfMMJdrR8ARAby0fbApeB5V42Rk +fuh45I7GRlQMKcMhJR/nJM5/OL1Bjn5nyAkL6JoddJZO0LVBswTmcbgmhJaRlF6M +YL92nFrGmKiltlEoAAslSKgDMkZJCdaTv2PQxrtpMEp6wBYSfNF0h0u92gKEltkJ +/6eXIcyGIQAVWwuLPqgvZXtqMx9irB1xJTr41MVwkqZzy6kct/dAXXrosi+xg9eM +FpTONL84pFqy0qkbbM/Q +=ipk9 +-----END PGP SIGNATURE----- diff --git a/puppet-2.6.6.tar.gz.sign b/puppet-2.6.6.tar.gz.sign deleted file mode 100644 index 9ff429a..0000000 --- a/puppet-2.6.6.tar.gz.sign +++ /dev/null @@ -1,17 +0,0 @@ ------BEGIN PGP SIGNATURE----- -Version: GnuPG v1.4.10 (GNU/Linux) - -iQIcBAABAgAGBQJNeAIeAAoJEBBUt6JL1uwwLqMP/A7L+ikXqLTc3X4gaPuiJcuI -dJW9fmzbOaeU1Bit4CaoA8e3gS2u+GybaxpW8eYhwAVGwws6NeKfoLXk7xXAl0X7 -pke5LMWl87vGw7orzeA/ck5QO4Gxel+OsDCXO18kRpnPswX/4FnaH8sF8u+y5kUU -2NuDabKPkMiDLjyC5TdXFP397hj0bRLmJjd2WtYo41nbjBJR6l3JOXWo5LlxGx6+ -8fzkqDXiNe/vERXm9As42SCehZ0QybRGEW8SgaXjLt4eXO4YJSpRhShRHP2f9d5C -3pUUnUHVuoQUWuNNA0W7wzS8CSKkmDVHyW5hWsMiyyE0u5THvOHBV8UvQ10QAF90 -mvcxiqwJYlxvPhuLGyEqGF+XpMqu4cS+f7ikNWtbacOdTY+uFLg5qSk25RwFGld7 -ASvlMP+H/Dp8uaxorCZt3dCTETmGbFWFiBgRrHRef6GR2H6gKqzDaRPoW30MNrVM -RAmb7xHHYeBoDf4msi5TJE546S3xfUwB/XjCNrQ8myuzg6engCgEmKB122BLq/DN -MmTZQx8DViYH+oPNQnOBkuFr3FeGNii+sZzC5VHyir3/8ksSQ4dSnKWbS8p8mGnP -5yR/WrUguN1HCdimKANWyGGfiKmNgADrDlOT3iUhjZs09ZR3IR2mTWVCLFcyu/Ft -ukGK0gYzxjK/2FF6uHoR -=/iZd ------END PGP SIGNATURE----- diff --git a/puppet.spec b/puppet.spec index 1add98f..9803813 100644 --- a/puppet.spec +++ b/puppet.spec @@ -5,32 +5,15 @@ %global confdir conf/redhat Name: puppet -Version: 2.6.6 -Release: 3%{?dist} +Version: 2.6.12 +Release: 1%{?dist} Summary: A network tool for managing many disparate systems License: GPLv2 URL: http://puppetlabs.com -Source0: http://puppetlabs.com/downloads/%{name}/%{name}-%{version}.tar.gz -Source1: http://puppetlabs.com/downloads/%{name}/%{name}-%{version}.tar.gz.sign -# http://projects.puppetlabs.com/issues/5428 -Patch0: 0001-5428-More-fully-stub-Puppet-Resource-Reference-for-u.patch -# http://projects.puppetlabs.com/issues/4922 -Patch1: 0001-4922-Don-t-truncate-remotely-sourced-files-on-404.patch -# http://projects.puppetlabs.com/issues/5073 -Patch2: 0001-5073-Download-plugins-even-if-you-re-filtering-on-ta.patch -# http://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2011-3848 -Patch3: 0001-Resist-directory-traversal-attacks-2.6.x.patch -# http://projects.puppetlabs.com/issues/9791 -# http://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2011-3870 -Patch4: 2.6.x-9791-TOCTOU-in-ssh-auth-keys-type.patch -# http://projects.puppetlabs.com/issues/9792 -# http://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2011-3871 -Patch5: 2.6.x-9792-Predictable-temporary-filename-in-ralsh.patch -# http://projects.puppetlabs.com/issues/9794 -# http://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2011-3869 -Patch6: 2.6.x-9794-k5login-can-overwrite-arbitrary-files-as-root.patch -# http://projects.puppetlabs.com/issues/9793 -Patch7: 2.6.x-9793-secure-indirector-file-backed-terminus-base-cla.patch +Source0: http://downloads.puppetlabs.com/%{name}/%{name}-%{version}.tar.gz +Source1: http://downloads.puppetlabs.com/%{name}/%{name}-%{version}.tar.gz.asc +# https://projects.puppetlabs.com/issues/10244 +Patch0: 0001-10244-Restore-Mongrel-XMLRPC-functionality.patch Group: System Environment/Base @@ -86,16 +69,8 @@ The server can also function as a certificate authority and file server. %prep %setup -q %patch0 -p1 -%patch1 -p1 -%patch2 -p1 -%patch3 -p1 -%patch4 -p1 -%patch5 -p1 -%patch6 -p1 -%patch7 -p1 patch -s -p1 < conf/redhat/rundir-perms.patch - %build # Fix some rpmlint complaints for f in mac_dscl.pp mac_dscl_revert.pp \ @@ -163,7 +138,7 @@ echo "D /var/run/%{name} 0755 %{name} %{name} -" > \ %files %defattr(-, root, root, 0755) -%doc CHANGELOG COPYING LICENSE README README.queueing examples +%doc CHANGELOG COPYING LICENSE README.md README.queueing examples %{_bindir}/pi %{_bindir}/puppet %{_bindir}/ralsh @@ -280,6 +255,10 @@ fi rm -rf %{buildroot} %changelog +* Sun Oct 23 2011 Todd Zullinger - 2.6.12-1 +- Update to 2.6.12, fixes CVE-2011-3872 +- Add upstream patch to restore Mongrel XMLRPC functionality (upstream #10244) + * Thu Sep 29 2011 Todd Zullinger - 2.6.6-3 - Apply upstream patches for CVE-2011-3869, CVE-2011-3870, CVE-2011-3871, and upstream #9793 diff --git a/sources b/sources index 4396540..6288f93 100644 --- a/sources +++ b/sources @@ -1 +1 @@ -58315e94ff00aedc4a19177877c3e865 puppet-2.6.6.tar.gz +3851b1a33cde9d697d5c5c21ef795438 puppet-2.6.12.tar.gz