webrick 1.3.1 → 1.4.3

Sign up to get free protection for your applications and to get access to all the features.

Potentially problematic release.


This version of webrick might be problematic. Click here for more details.

Files changed (64) hide show
  1. checksums.yaml +7 -0
  2. data/lib/webrick.rb +6 -6
  3. data/lib/webrick/accesslog.rb +9 -1
  4. data/lib/webrick/cgi.rb +58 -5
  5. data/lib/webrick/compat.rb +2 -1
  6. data/lib/webrick/config.rb +47 -10
  7. data/lib/webrick/cookie.rb +69 -7
  8. data/lib/webrick/htmlutils.rb +4 -2
  9. data/lib/webrick/httpauth.rb +6 -5
  10. data/lib/webrick/httpauth/authenticator.rb +13 -8
  11. data/lib/webrick/httpauth/basicauth.rb +16 -8
  12. data/lib/webrick/httpauth/digestauth.rb +35 -32
  13. data/lib/webrick/httpauth/htdigest.rb +12 -8
  14. data/lib/webrick/httpauth/htgroup.rb +10 -6
  15. data/lib/webrick/httpauth/htpasswd.rb +46 -9
  16. data/lib/webrick/httpauth/userdb.rb +1 -0
  17. data/lib/webrick/httpproxy.rb +93 -48
  18. data/lib/webrick/httprequest.rb +192 -27
  19. data/lib/webrick/httpresponse.rb +182 -62
  20. data/lib/webrick/https.rb +90 -2
  21. data/lib/webrick/httpserver.rb +45 -15
  22. data/lib/webrick/httpservlet.rb +6 -5
  23. data/lib/webrick/httpservlet/abstract.rb +5 -6
  24. data/lib/webrick/httpservlet/cgi_runner.rb +3 -2
  25. data/lib/webrick/httpservlet/cgihandler.rb +22 -10
  26. data/lib/webrick/httpservlet/erbhandler.rb +4 -3
  27. data/lib/webrick/httpservlet/filehandler.rb +136 -65
  28. data/lib/webrick/httpservlet/prochandler.rb +15 -1
  29. data/lib/webrick/httpstatus.rb +24 -14
  30. data/lib/webrick/httputils.rb +132 -13
  31. data/lib/webrick/httpversion.rb +28 -1
  32. data/lib/webrick/log.rb +25 -5
  33. data/lib/webrick/server.rb +234 -74
  34. data/lib/webrick/ssl.rb +100 -12
  35. data/lib/webrick/utils.rb +98 -69
  36. data/lib/webrick/version.rb +6 -1
  37. metadata +66 -72
  38. data/README.txt +0 -21
  39. data/sample/webrick/demo-app.rb +0 -66
  40. data/sample/webrick/demo-multipart.cgi +0 -12
  41. data/sample/webrick/demo-servlet.rb +0 -6
  42. data/sample/webrick/demo-urlencoded.cgi +0 -12
  43. data/sample/webrick/hello.cgi +0 -11
  44. data/sample/webrick/hello.rb +0 -8
  45. data/sample/webrick/httpd.rb +0 -23
  46. data/sample/webrick/httpproxy.rb +0 -25
  47. data/sample/webrick/httpsd.rb +0 -33
  48. data/test/openssl/utils.rb +0 -313
  49. data/test/ruby/envutil.rb +0 -208
  50. data/test/webrick/test_cgi.rb +0 -134
  51. data/test/webrick/test_cookie.rb +0 -131
  52. data/test/webrick/test_filehandler.rb +0 -285
  53. data/test/webrick/test_httpauth.rb +0 -167
  54. data/test/webrick/test_httpproxy.rb +0 -282
  55. data/test/webrick/test_httprequest.rb +0 -411
  56. data/test/webrick/test_httpresponse.rb +0 -49
  57. data/test/webrick/test_httpserver.rb +0 -305
  58. data/test/webrick/test_httputils.rb +0 -96
  59. data/test/webrick/test_httpversion.rb +0 -40
  60. data/test/webrick/test_server.rb +0 -67
  61. data/test/webrick/test_utils.rb +0 -64
  62. data/test/webrick/utils.rb +0 -58
  63. data/test/webrick/webrick.cgi +0 -36
  64. data/test/webrick/webrick_long_filename.cgi +0 -36
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: false
1
2
  #
2
3
  # httpauth.rb -- HTTP access authentication
3
4
  #
@@ -8,11 +9,11 @@
8
9
  #
9
10
  # $IPR: httpauth.rb,v 1.14 2003/07/22 19:20:42 gotoyuzo Exp $
10
11
 
11
- require 'webrick/httpauth/basicauth'
12
- require 'webrick/httpauth/digestauth'
13
- require 'webrick/httpauth/htpasswd'
14
- require 'webrick/httpauth/htdigest'
15
- require 'webrick/httpauth/htgroup'
12
+ require_relative 'httpauth/basicauth'
13
+ require_relative 'httpauth/digestauth'
14
+ require_relative 'httpauth/htpasswd'
15
+ require_relative 'httpauth/htdigest'
16
+ require_relative 'httpauth/htgroup'
16
17
 
17
18
  module WEBrick
18
19
 
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: false
1
2
  #--
2
3
  # httpauth/authenticator.rb -- Authenticator mix-in module.
3
4
  #
@@ -16,10 +17,10 @@ module WEBrick
16
17
 
17
18
  module Authenticator
18
19
 
19
- RequestField = "Authorization"
20
- ResponseField = "WWW-Authenticate"
21
- ResponseInfoField = "Authentication-Info"
22
- AuthException = HTTPStatus::Unauthorized
20
+ RequestField = "Authorization" # :nodoc:
21
+ ResponseField = "WWW-Authenticate" # :nodoc:
22
+ ResponseInfoField = "Authentication-Info" # :nodoc:
23
+ AuthException = HTTPStatus::Unauthorized # :nodoc:
23
24
 
24
25
  ##
25
26
  # Method of authentication, must be overridden by the including class
@@ -43,6 +44,8 @@ module WEBrick
43
44
 
44
45
  private
45
46
 
47
+ # :stopdoc:
48
+
46
49
  ##
47
50
  # Initializes the authenticator from +config+
48
51
 
@@ -96,6 +99,8 @@ module WEBrick
96
99
  log(:info, fmt, *args)
97
100
  end
98
101
  end
102
+
103
+ # :startdoc:
99
104
  end
100
105
 
101
106
  ##
@@ -103,10 +108,10 @@ module WEBrick
103
108
  # authentication schemes for proxies.
104
109
 
105
110
  module ProxyAuthenticator
106
- RequestField = "Proxy-Authorization"
107
- ResponseField = "Proxy-Authenticate"
108
- InfoField = "Proxy-Authentication-Info"
109
- AuthException = HTTPStatus::ProxyAuthenticationRequired
111
+ RequestField = "Proxy-Authorization" # :nodoc:
112
+ ResponseField = "Proxy-Authenticate" # :nodoc:
113
+ InfoField = "Proxy-Authentication-Info" # :nodoc:
114
+ AuthException = HTTPStatus::ProxyAuthenticationRequired # :nodoc:
110
115
  end
111
116
  end
112
117
  end
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: false
1
2
  #
2
3
  # httpauth/basicauth.rb -- HTTP basic access authentication
3
4
  #
@@ -7,9 +8,9 @@
7
8
  #
8
9
  # $IPR: basicauth.rb,v 1.5 2003/02/20 07:15:47 gotoyuzo Exp $
9
10
 
10
- require 'webrick/config'
11
- require 'webrick/httpstatus'
12
- require 'webrick/httpauth/authenticator'
11
+ require_relative '../config'
12
+ require_relative '../httpstatus'
13
+ require_relative 'authenticator'
13
14
 
14
15
  module WEBrick
15
16
  module HTTPAuth
@@ -23,7 +24,7 @@ module WEBrick
23
24
  #
24
25
  # config = { :Realm => 'BasicAuth example realm' }
25
26
  #
26
- # htpasswd = WEBrick::HTTPAuth::Htpasswd.new 'my_password_file'
27
+ # htpasswd = WEBrick::HTTPAuth::Htpasswd.new 'my_password_file', password_hash: :bcrypt
27
28
  # htpasswd.set_passwd config[:Realm], 'username', 'password'
28
29
  # htpasswd.flush
29
30
  #
@@ -34,7 +35,7 @@ module WEBrick
34
35
  class BasicAuth
35
36
  include Authenticator
36
37
 
37
- AuthScheme = "Basic"
38
+ AuthScheme = "Basic" # :nodoc:
38
39
 
39
40
  ##
40
41
  # Used by UserDB to create a basic password entry
@@ -80,7 +81,15 @@ module WEBrick
80
81
  error("%s: the user is not allowed.", userid)
81
82
  challenge(req, res)
82
83
  end
83
- if password.crypt(encpass) != encpass
84
+
85
+ case encpass
86
+ when /\A\$2[aby]\$/
87
+ password_matches = BCrypt::Password.new(encpass.sub(/\A\$2[aby]\$/, '$2a$')) == password
88
+ else
89
+ password_matches = password.crypt(encpass) == encpass
90
+ end
91
+
92
+ unless password_matches
84
93
  error("%s: password unmatch.", userid)
85
94
  challenge(req, res)
86
95
  end
@@ -89,8 +98,7 @@ module WEBrick
89
98
  end
90
99
 
91
100
  ##
92
- # Returns a challenge response which asks for for authentication
93
- # information
101
+ # Returns a challenge response which asks for authentication information
94
102
 
95
103
  def challenge(req, res)
96
104
  res[@response_field] = "#{@auth_scheme} realm=\"#{@realm}\""
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: false
1
2
  #
2
3
  # httpauth/digestauth.rb -- HTTP digest access authentication
3
4
  #
@@ -11,9 +12,9 @@
11
12
  #
12
13
  # $IPR: digestauth.rb,v 1.5 2003/02/20 07:15:47 gotoyuzo Exp $
13
14
 
14
- require 'webrick/config'
15
- require 'webrick/httpstatus'
16
- require 'webrick/httpauth/authenticator'
15
+ require_relative '../config'
16
+ require_relative '../httpstatus'
17
+ require_relative 'authenticator'
17
18
  require 'digest/md5'
18
19
  require 'digest/sha1'
19
20
 
@@ -45,9 +46,22 @@ module WEBrick
45
46
  class DigestAuth
46
47
  include Authenticator
47
48
 
48
- AuthScheme = "Digest"
49
- OpaqueInfo = Struct.new(:time, :nonce, :nc)
50
- attr_reader :algorithm, :qop
49
+ AuthScheme = "Digest" # :nodoc:
50
+
51
+ ##
52
+ # Struct containing the opaque portion of the digest authentication
53
+
54
+ OpaqueInfo = Struct.new(:time, :nonce, :nc) # :nodoc:
55
+
56
+ ##
57
+ # Digest authentication algorithm
58
+
59
+ attr_reader :algorithm
60
+
61
+ ##
62
+ # Quality of protection. RFC 2617 defines "auth" and "auth-int"
63
+
64
+ attr_reader :qop
51
65
 
52
66
  ##
53
67
  # Used by UserDB to create a digest password entry
@@ -97,7 +111,7 @@ module WEBrick
97
111
  @instance_key = hexdigest(self.__id__, Time.now.to_i, Process.pid)
98
112
  @opaques = {}
99
113
  @last_nonce_expire = Time.now
100
- @mutex = Mutex.new
114
+ @mutex = Thread::Mutex.new
101
115
  end
102
116
 
103
117
  ##
@@ -115,8 +129,7 @@ module WEBrick
115
129
  end
116
130
 
117
131
  ##
118
- # Returns a challenge response which asks for for authentication
119
- # information
132
+ # Returns a challenge response which asks for authentication information
120
133
 
121
134
  def challenge(req, res, stale=false)
122
135
  nonce = generate_next_nonce(req)
@@ -142,6 +155,8 @@ module WEBrick
142
155
 
143
156
  private
144
157
 
158
+ # :stopdoc:
159
+
145
160
  MustParams = ['username','realm','nonce','uri','response']
146
161
  MustParamsAuth = ['cnonce','nc']
147
162
 
@@ -189,7 +204,7 @@ module WEBrick
189
204
 
190
205
  password = @userdb.get_passwd(@realm, auth_req['username'], @reload_db)
191
206
  unless password
192
- error('%s: the user is not allowd.', auth_req['username'])
207
+ error('%s: the user is not allowed.', auth_req['username'])
193
208
  return false
194
209
  end
195
210
 
@@ -220,9 +235,11 @@ module WEBrick
220
235
  ha2 = hexdigest(req.request_method, auth_req['uri'])
221
236
  ha2_res = hexdigest("", auth_req['uri'])
222
237
  elsif auth_req['qop'] == "auth-int"
223
- ha2 = hexdigest(req.request_method, auth_req['uri'],
224
- hexdigest(req.body))
225
- ha2_res = hexdigest("", auth_req['uri'], hexdigest(res.body))
238
+ body_digest = @h.new
239
+ req.body { |chunk| body_digest.update(chunk) }
240
+ body_digest = body_digest.hexdigest
241
+ ha2 = hexdigest(req.request_method, auth_req['uri'], body_digest)
242
+ ha2_res = hexdigest("", auth_req['uri'], body_digest)
226
243
  end
227
244
 
228
245
  if auth_req['qop'] == "auth" || auth_req['qop'] == "auth-int"
@@ -273,23 +290,8 @@ module WEBrick
273
290
 
274
291
  def split_param_value(string)
275
292
  ret = {}
276
- while string.bytesize != 0
277
- case string
278
- when /^\s*([\w\-\.\*\%\!]+)=\s*\"((\\.|[^\"])*)\"\s*,?/
279
- key = $1
280
- matched = $2
281
- string = $'
282
- ret[key] = matched.gsub(/\\(.)/, "\\1")
283
- when /^\s*([\w\-\.\*\%\!]+)=\s*([^,\"]*),?/
284
- key = $1
285
- matched = $2
286
- string = $'
287
- ret[key] = matched.clone
288
- when /^s*^,/
289
- string = $'
290
- else
291
- break
292
- end
293
+ string.scan(/\G\s*([\w\-.*%!]+)=\s*(?:\"((?>\\.|[^\"])*)\"|([^,\"]*))\s*,?/) do
294
+ ret[$1] = $3 || $2.gsub(/\\(.)/, "\\1")
293
295
  end
294
296
  ret
295
297
  end
@@ -297,7 +299,7 @@ module WEBrick
297
299
  def generate_next_nonce(req)
298
300
  now = "%012d" % req.request_time.to_i
299
301
  pk = hexdigest(now, @instance_key)[0,32]
300
- nonce = [now + ":" + pk].pack("m*").chop # it has 60 length of chars.
302
+ nonce = [now + ":" + pk].pack("m0") # it has 60 length of chars.
301
303
  nonce
302
304
  end
303
305
 
@@ -375,6 +377,7 @@ module WEBrick
375
377
  @h.hexdigest(args.join(":"))
376
378
  end
377
379
 
380
+ # :startdoc:
378
381
  end
379
382
 
380
383
  ##
@@ -384,7 +387,7 @@ module WEBrick
384
387
  include ProxyAuthenticator
385
388
 
386
389
  private
387
- def check_uri(req, auth_req)
390
+ def check_uri(req, auth_req) # :nodoc:
388
391
  return true
389
392
  end
390
393
  end
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: false
1
2
  #
2
3
  # httpauth/htdigest.rb -- Apache compatible htdigest file
3
4
  #
@@ -7,8 +8,8 @@
7
8
  #
8
9
  # $IPR: htdigest.rb,v 1.4 2003/07/22 19:20:45 gotoyuzo Exp $
9
10
 
10
- require 'webrick/httpauth/userdb'
11
- require 'webrick/httpauth/digestauth'
11
+ require_relative 'userdb'
12
+ require_relative 'digestauth'
12
13
  require 'tempfile'
13
14
 
14
15
  module WEBrick
@@ -37,9 +38,9 @@ module WEBrick
37
38
  @path = path
38
39
  @mtime = Time.at(0)
39
40
  @digest = Hash.new
40
- @mutex = Mutex::new
41
+ @mutex = Thread::Mutex::new
41
42
  @auth_type = DigestAuth
42
- open(@path,"a").close unless File::exist?(@path)
43
+ File.open(@path,"a").close unless File.exist?(@path)
43
44
  reload
44
45
  end
45
46
 
@@ -50,7 +51,7 @@ module WEBrick
50
51
  mtime = File::mtime(@path)
51
52
  if mtime > @mtime
52
53
  @digest.clear
53
- open(@path){|io|
54
+ File.open(@path){|io|
54
55
  while line = io.gets
55
56
  line.chomp!
56
57
  user, realm, pass = line.split(/:/, 3)
@@ -70,13 +71,16 @@ module WEBrick
70
71
 
71
72
  def flush(output=nil)
72
73
  output ||= @path
73
- tmp = Tempfile.new("htpasswd", File::dirname(output))
74
+ tmp = Tempfile.create("htpasswd", File::dirname(output))
75
+ renamed = false
74
76
  begin
75
77
  each{|item| tmp.puts(item.join(":")) }
76
78
  tmp.close
77
79
  File::rename(tmp.path, output)
78
- rescue
79
- tmp.close(true)
80
+ renamed = true
81
+ ensure
82
+ tmp.close
83
+ File.unlink(tmp.path) if !renamed
80
84
  end
81
85
  end
82
86
 
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: false
1
2
  #
2
3
  # httpauth/htgroup.rb -- Apache compatible htgroup file
3
4
  #
@@ -35,7 +36,7 @@ module WEBrick
35
36
  @path = path
36
37
  @mtime = Time.at(0)
37
38
  @group = Hash.new
38
- open(@path,"a").close unless File::exist?(@path)
39
+ File.open(@path,"a").close unless File.exist?(@path)
39
40
  reload
40
41
  end
41
42
 
@@ -45,7 +46,7 @@ module WEBrick
45
46
  def reload
46
47
  if (mtime = File::mtime(@path)) > @mtime
47
48
  @group.clear
48
- open(@path){|io|
49
+ File.open(@path){|io|
49
50
  while line = io.gets
50
51
  line.chomp!
51
52
  group, members = line.split(/:\s*/)
@@ -62,15 +63,18 @@ module WEBrick
62
63
 
63
64
  def flush(output=nil)
64
65
  output ||= @path
65
- tmp = Tempfile.new("htgroup", File::dirname(output))
66
+ tmp = Tempfile.create("htgroup", File::dirname(output))
66
67
  begin
67
68
  @group.keys.sort.each{|group|
68
69
  tmp.puts(format("%s: %s", group, self.members(group).join(" ")))
69
70
  }
71
+ ensure
70
72
  tmp.close
71
- File::rename(tmp.path, output)
72
- rescue
73
- tmp.close(true)
73
+ if $!
74
+ File.unlink(tmp.path)
75
+ else
76
+ return File.rename(tmp.path, output)
77
+ end
74
78
  end
75
79
  end
76
80
 
@@ -1,3 +1,4 @@
1
+ # frozen_string_literal: false
1
2
  #
2
3
  # httpauth/htpasswd -- Apache compatible htpasswd file
3
4
  #
@@ -7,8 +8,8 @@
7
8
  #
8
9
  # $IPR: htpasswd.rb,v 1.4 2003/07/22 19:20:45 gotoyuzo Exp $
9
10
 
10
- require 'webrick/httpauth/userdb'
11
- require 'webrick/httpauth/basicauth'
11
+ require_relative 'userdb'
12
+ require_relative 'basicauth'
12
13
  require 'tempfile'
13
14
 
14
15
  module WEBrick
@@ -34,12 +35,30 @@ module WEBrick
34
35
  ##
35
36
  # Open a password database at +path+
36
37
 
37
- def initialize(path)
38
+ def initialize(path, password_hash: nil)
38
39
  @path = path
39
40
  @mtime = Time.at(0)
40
41
  @passwd = Hash.new
41
42
  @auth_type = BasicAuth
42
- open(@path,"a").close unless File::exist?(@path)
43
+ @password_hash = password_hash
44
+
45
+ case @password_hash
46
+ when nil
47
+ # begin
48
+ # require "string/crypt"
49
+ # rescue LoadError
50
+ # warn("Unable to load string/crypt, proceeding with deprecated use of String#crypt, consider using password_hash: :bcrypt")
51
+ # end
52
+ @password_hash = :crypt
53
+ when :crypt
54
+ # require "string/crypt"
55
+ when :bcrypt
56
+ require "bcrypt"
57
+ else
58
+ raise ArgumentError, "only :crypt and :bcrypt are supported for password_hash keyword argument"
59
+ end
60
+
61
+ File.open(@path,"a").close unless File.exist?(@path)
43
62
  reload
44
63
  end
45
64
 
@@ -50,11 +69,19 @@ module WEBrick
50
69
  mtime = File::mtime(@path)
51
70
  if mtime > @mtime
52
71
  @passwd.clear
53
- open(@path){|io|
72
+ File.open(@path){|io|
54
73
  while line = io.gets
55
74
  line.chomp!
56
75
  case line
57
76
  when %r!\A[^:]+:[a-zA-Z0-9./]{13}\z!
77
+ if @password_hash == :bcrypt
78
+ raise StandardError, ".htpasswd file contains crypt password, only bcrypt passwords supported"
79
+ end
80
+ user, pass = line.split(":")
81
+ when %r!\A[^:]+:\$2[aby]\$\d{2}\$.{53}\z!
82
+ if @password_hash == :crypt
83
+ raise StandardError, ".htpasswd file contains bcrypt password, only crypt passwords supported"
84
+ end
58
85
  user, pass = line.split(":")
59
86
  when /:\$/, /:{SHA}/
60
87
  raise NotImplementedError,
@@ -75,13 +102,16 @@ module WEBrick
75
102
 
76
103
  def flush(output=nil)
77
104
  output ||= @path
78
- tmp = Tempfile.new("htpasswd", File::dirname(output))
105
+ tmp = Tempfile.create("htpasswd", File::dirname(output))
106
+ renamed = false
79
107
  begin
80
108
  each{|item| tmp.puts(item.join(":")) }
81
109
  tmp.close
82
110
  File::rename(tmp.path, output)
83
- rescue
84
- tmp.close(true)
111
+ renamed = true
112
+ ensure
113
+ tmp.close
114
+ File.unlink(tmp.path) if !renamed
85
115
  end
86
116
  end
87
117
 
@@ -98,7 +128,14 @@ module WEBrick
98
128
  # Sets a password in the database for +user+ in +realm+ to +pass+.
99
129
 
100
130
  def set_passwd(realm, user, pass)
101
- @passwd[user] = make_passwd(realm, user, pass)
131
+ if @password_hash == :bcrypt
132
+ # Cost of 5 to match Apache default, and because the
133
+ # bcrypt default of 10 will introduce significant delays
134
+ # for every request.
135
+ @passwd[user] = BCrypt::Password.create(pass, :cost=>5)
136
+ else
137
+ @passwd[user] = make_passwd(realm, user, pass)
138
+ end
102
139
  end
103
140
 
104
141
  ##