diff -u librack-ruby-1.1.0/debian/changelog librack-ruby-1.1.0/debian/changelog --- librack-ruby-1.1.0/debian/changelog +++ librack-ruby-1.1.0/debian/changelog @@ -1,3 +1,13 @@ +librack-ruby (1.1.0-4+squeeze1) squeeze-security; urgency=high + + * Non Maintainer Upload. + * Create cherry-picked patches for Security Fix (Closes: #700226 #698440 #653963). + - CVE-2011-5036: Backport `Limit the size of parameter keys` + - CVE-2013-0184: Reimplement-auth-scheme-fix. + - CVE-2013-0263: Use-secure_compare-for-hmac-comparison. + + -- KURASHIKI Satoru Thu, 26 Sep 2013 20:05:14 +0900 + librack-ruby (1.1.0-4) unstable; urgency=low * Team upload. only in patch2: unchanged: --- librack-ruby-1.1.0.orig/test/spec_rack_request.rb +++ librack-ruby-1.1.0/test/spec_rack_request.rb @@ -66,6 +66,32 @@ lambda { req.POST }.should.raise(RuntimeError) end + specify "limit the keys from the GET query string" do + env = Rack::MockRequest.env_for("/?foo=bar") + + old, Rack::Utils.key_space_limit = Rack::Utils.key_space_limit, 1 + begin + req = Rack::Request.new(env) + lambda { req.GET }.should.raise(RangeError) + ensure + Rack::Utils.key_space_limit = old + end + end + + specify "limit the keys from the POST form data" do + env = Rack::MockRequest.env_for("", + "REQUEST_METHOD" => 'POST', + :input => "foo=bar&quux=bla") + + old, Rack::Utils.key_space_limit = Rack::Utils.key_space_limit, 1 + begin + req = Rack::Request.new(env) + lambda { req.POST }.should.raise(RangeError) + ensure + Rack::Utils.key_space_limit = old + end + end + specify "can parse POST data when method is POST and no Content-Type given" do req = Rack::Request.new \ Rack::MockRequest.env_for("/?foo=quux", @@ -295,6 +321,29 @@ req.media_type_params['bling'].should.equal 'bam' end + specify "raise RangeError if the key space is exhausted" do + input = < "multipart/form-data, boundary=AaB03x", + "CONTENT_LENGTH" => input.size, + :input => input) + + old, Rack::Utils.key_space_limit = Rack::Utils.key_space_limit, 1 + begin + lambda { Rack::Utils::Multipart.parse_multipart(env) }.should.raise(RangeError) + ensure + Rack::Utils.key_space_limit = old + end + end + specify "can parse multipart form data" do # Adapted from RFC 1867. input = < err # this is EPIPE if Rack shuts us down + err + end + end + + fixture = { + "CONTENT_TYPE" => "multipart/form-data; boundary=AaB03x", + "CONTENT_LENGTH" => rd.length.to_s, + :input => rd, + } + + env = Rack::MockRequest.env_for '/', fixture + lambda { + Rack::Multipart.parse_multipart(env) + }.should.raise(EOFError) + rd.close + + err = thr.value + err.should.be.instance_of Errno::EPIPE + wr.close + end + specify "should parse multipart upload with text file" do env = Rack::MockRequest.env_for("/", multipart_fixture(:text)) params = Rack::Utils::Multipart.parse_multipart(env) only in patch2: unchanged: --- librack-ruby-1.1.0.orig/lib/rack.rb +++ librack-ruby-1.1.0/lib/rack.rb @@ -71,6 +71,18 @@ autoload :Params, "rack/auth/digest/params" autoload :Request, "rack/auth/digest/request" end + + # Not all of the following schemes are "standards", but they are used often. + @schemes = %w[basic digest bearer mac token oauth oauth2] + + def self.add_scheme scheme + @schemes << scheme + @schemes.uniq! + end + + def self.schemes + @schemes.dup + end end module Session only in patch2: unchanged: --- librack-ruby-1.1.0.orig/lib/rack/utils.rb +++ librack-ruby-1.1.0/lib/rack/utils.rb @@ -28,6 +28,14 @@ DEFAULT_SEP = /[&;] */n + class << self + attr_accessor :key_space_limit + end + + # The default number of bytes to allow parameter keys to take up. + # This helps prevent a rogue client from flooding a Request. + self.key_space_limit = 65536 + # Stolen from Mongrel, with some small modifications: # Parses a query string by breaking it up at the '&' # and ';' characters. You can also use this to parse @@ -36,8 +44,19 @@ def parse_query(qs, d = nil) params = {} + max_key_space = Utils.key_space_limit + bytes = 0 + (qs || '').split(d ? /[#{d}] */n : DEFAULT_SEP).each do |p| k, v = p.split('=', 2).map { |x| unescape(x) } + + if k + bytes += k.size + if bytes > max_key_space + raise RangeError, "exceeded available parameter key space" + end + end + if v =~ /^("|')(.*)\1$/ v = $2.gsub('\\'+$1, $1) end @@ -59,8 +78,19 @@ def parse_nested_query(qs, d = nil) params = {} + max_key_space = Utils.key_space_limit + bytes = 0 + (qs || '').split(d ? /[#{d}] */n : DEFAULT_SEP).each do |p| k, v = unescape(p).split('=', 2) + + if k + bytes += k.size + if bytes > max_key_space + raise RangeError, "exceeded available parameter key space" + end + end + normalize_params(params, k, v) end @@ -234,6 +264,18 @@ end module_function :bytesize + # Constant time string comparison. + def secure_compare(a, b) + return false unless bytesize(a) == bytesize(b) + + l = a.unpack("C*") + + r, i = 0, -1 + b.each_byte { |v| r |= v ^ l[i+=1] } + r == 0 + end + module_function :secure_compare + # Context allows the use of a compatible middleware at different points # in a request handling stack. A compatible middleware must define # #context which should take the arguments env and app. The first of which @@ -465,9 +507,19 @@ status = input.read(boundary_size, read_buffer) raise EOFError, "bad content body" unless status == boundary + EOL + @read_buf << read_buffer + + while @read_buf.gsub!(/\A([^\n]*\n)/, '') + buffer_read = $1 + return if buffer_read == boundary_size + end + raise EOFError, "bad content body" if Utils.bytesize(@buf) >= bufsize rx = /(?:#{EOL})?#{Regexp.quote boundary}(#{EOL}|--)/n + max_key_space = Utils.key_space_limit + bytes = 0 + loop { head = nil body = '' @@ -482,6 +534,13 @@ content_type = head[/Content-Type: (.*)#{EOL}/ni, 1] name = head[/Content-Disposition:.*\s+name="?([^\";]*)"?/ni, 1] || head[/Content-ID:\s*([^#{EOL}]*)/ni, 1] + if name + bytes += name.size + if bytes > max_key_space + raise RangeError, "exceeded available parameter key space" + end + end + if content_type || filename body = Tempfile.new("RackMultipart") body.binmode if body.respond_to?(:binmode) only in patch2: unchanged: --- librack-ruby-1.1.0.orig/lib/rack/auth/abstract/request.rb +++ librack-ruby-1.1.0/lib/rack/auth/abstract/request.rb @@ -15,7 +15,11 @@ end def scheme - @scheme ||= parts.first.downcase.to_sym + @scheme ||= + begin + s = parts.first.downcase + Rack::Auth.schemes.include?(s) ? s.to_sym : s + end end def params only in patch2: unchanged: --- librack-ruby-1.1.0.orig/lib/rack/session/cookie.rb +++ librack-ruby-1.1.0/lib/rack/session/cookie.rb @@ -46,7 +46,7 @@ if @secret && session_data session_data, digest = session_data.split("--") - session_data = nil unless digest == generate_hmac(session_data) + session_data = nil unless Rack::Utils.secure_compare(digest, generate_hmac(session_data)) end begin only in patch2: unchanged: --- librack-ruby-1.1.0.orig/bin/rackup +++ librack-ruby-1.1.0/bin/rackup @@ -1,4 +1,4 @@ -#!/usr/bin/env ruby +#! /usr/bin/ruby1.8 require "rack" Rack::Server.start