From 8fb2058d825f1e795153255ea99ecc4aa70ed414 Mon Sep 17 00:00:00 2001 From: naruse Date: Tue, 5 Jul 2011 05:06:05 +0000 Subject: * lib/webrick/httpauth/digestauth.rb (_authenticate): Literal texts in HTTP ABNF is case-insensitive (RFC2616 2.1), and a ample implementation in RFC2617 also ignores the case of algorithms. So now this ignores those cases. [ruby-dev:43965] [Feature #4936] * lib/webrick/httpauth/digestauth.rb (initialize): Because of above, opera_hack is useless and removed. git-svn-id: svn+ssh://ci.ruby-lang.org/ruby/trunk@32410 b2dd03c8-39d4-4d8f-98ff-823fe69b080e --- ChangeLog | 11 +++++ lib/webrick/httpauth/digestauth.rb | 7 +--- test/webrick/test_httpauth.rb | 85 ++++++++++++++++++++++++++++++++++++++ 3 files changed, 98 insertions(+), 5 deletions(-) diff --git a/ChangeLog b/ChangeLog index e49ff03220..129163388c 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,14 @@ +Tue Jul 5 14:05:43 2011 NARUSE, Yui + + * lib/webrick/httpauth/digestauth.rb (_authenticate): + Literal texts in HTTP ABNF is case-insensitive (RFC2616 2.1), + and a ample implementation in RFC2617 also ignores the case + of algorithms. So now this ignores those cases. + [ruby-dev:43965] [Feature #4936] + + * lib/webrick/httpauth/digestauth.rb (initialize): + Because of above, opera_hack is useless and removed. + Tue Jul 5 01:30:01 2011 Yusuke Endoh * thread_pthread.c (native_sleep): cut the waiting time up to diff --git a/lib/webrick/httpauth/digestauth.rb b/lib/webrick/httpauth/digestauth.rb index 8f7f32f82b..4e47fe163f 100644 --- a/lib/webrick/httpauth/digestauth.rb +++ b/lib/webrick/httpauth/digestauth.rb @@ -83,7 +83,6 @@ module WEBrick @nonce_expire_period = @config[:NonceExpirePeriod] @nonce_expire_delta = @config[:NonceExpireDelta] @internet_explorer_hack = @config[:InternetExplorerHack] - @opera_hack = @config[:OperaHack] case @algorithm when 'MD5','MD5-sess' @@ -175,8 +174,7 @@ module WEBrick end auth_req['algorithm'] ||= 'MD5' - if auth_req['algorithm'] != @algorithm && - (@opera_hack && auth_req['algorithm'] != @algorithm.upcase) + if auth_req['algorithm'].upcase != @algorithm.upcase error('%s: algorithm unmatch. "%s" for "%s"', auth_req['username'], auth_req['algorithm'], @algorithm) return false @@ -212,8 +210,7 @@ module WEBrick nonce_is_invalid = true end - if /-sess$/ =~ auth_req['algorithm'] || - (@opera_hack && /-SESS$/ =~ auth_req['algorithm']) + if /-sess$/i =~ auth_req['algorithm'] ha1 = hexdigest(password, auth_req['nonce'], auth_req['cnonce']) else ha1 = password diff --git a/test/webrick/test_httpauth.rb b/test/webrick/test_httpauth.rb index a6066908c1..6cfdcc4553 100644 --- a/test/webrick/test_httpauth.rb +++ b/test/webrick/test_httpauth.rb @@ -79,4 +79,89 @@ class TestWEBrickHTTPAuth < Test::Unit::TestCase } tmpfile.close(true) end + + DIGESTRES_ = / + ([a-zA-z\-]+) + [\s\t]*(?:\r\n[\s\t]*)* + = + [\s\t]*(?:\r\n[\s\t]*)* + (?: + "((?:[^"]+|\\[\x00-\x7F])*)" | + ([!\#$%&'*+\-.0-9A-Z^_`a-z|~]+) + )/x + + def test_digest_auth + TestWEBrick.start_httpserver{|server, addr, port, log| + realm = "WEBrick's realm" + path = "/digest_auth" + + tmpfile = Tempfile.new("test_webrick_auth") + tmpfile.close + tmp_pass = WEBrick::HTTPAuth::Htdigest.new(tmpfile.path) + tmp_pass.set_passwd(realm, "webrick", "supersecretpassword") + tmp_pass.set_passwd(realm, "foo", "supersecretpassword") + tmp_pass.flush + + htdigest = WEBrick::HTTPAuth::Htdigest.new(tmpfile.path) + users = [] + htdigest.each{|user, pass| users << user } + assert_equal(2, users.size, log.call) + assert(users.member?("webrick"), log.call) + assert(users.member?("foo"), log.call) + + auth = WEBrick::HTTPAuth::DigestAuth.new( + :Realm => realm, :UserDB => htdigest, + :Algorithm => 'MD5', + :Logger => server.logger + ) + server.mount_proc(path){|req, res| + auth.authenticate(req, res) + res.body = "hoge" + } + + Net::HTTP.start(addr, port) do |http| + g = Net::HTTP::Get.new(path) + params = {} + http.request(g) do |res| + assert_equal('401', res.code, log.call) + res["www-authenticate"].scan(DIGESTRES_) do |key, quoted, token| + params[key.downcase] = token || quoted.delete('\\') + end + params['uri'] = "http://#{addr}:#{port}#{path}" + end + + g['Authorization'] = credentials_for_request('webrick', "supersecretpassword", params) + http.request(g){|res| assert_equal("hoge", res.body, log.call)} + + params['algorithm'].downcase! #4936 + g['Authorization'] = credentials_for_request('webrick', "supersecretpassword", params) + http.request(g){|res| assert_equal("hoge", res.body, log.call)} + + g['Authorization'] = credentials_for_request('webrick', "not super", params) + http.request(g){|res| assert_not_equal("hoge", res.body, log.call)} + end + } + end + + private + def credentials_for_request(user, password, params) + cnonce = "hoge" + nonce_count = 1 + ha1 = "#{user}:#{params['realm']}:#{password}" + ha2 = "GET:#{params['uri']}" + request_digest = + "#{Digest::MD5.hexdigest(ha1)}:" \ + "#{params['nonce']}:#{'%08x' % nonce_count}:#{cnonce}:#{params['qop']}:" \ + "#{Digest::MD5.hexdigest(ha2)}" + p header = "Digest username=\"#{user}\"" \ + ", realm=\"#{params['realm']}\"" \ + ", nonce=\"#{params['nonce']}\"" \ + ", uri=\"#{params['uri']}\"" \ + ", qop=#{params['qop']}" \ + ", nc=#{'%08x' % nonce_count}" \ + ", cnonce=\"#{cnonce}\"" \ + ", response=\"#{Digest::MD5.hexdigest(request_digest)}\"" \ + ", opaque=\"#{params['opaque']}\"" \ + ", algorithm=#{params['algorithm']}" + end end -- cgit v1.2.3