htauth 1.0.1 → 1.0.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -3,80 +3,80 @@ require 'digest/md5'
3
3
 
4
4
  module HTAuth
5
5
 
6
- # an implementation of the MD5 based encoding algorithm
7
- # as used in the apache htpasswd -m option
8
- class Md5 < Algorithm
9
-
10
- DIGEST_LENGTH = 16
11
-
12
- def initialize(params = {})
13
- @salt = params['salt'] || params[:salt] || gen_salt
14
- end
6
+ # an implementation of the MD5 based encoding algorithm
7
+ # as used in the apache htpasswd -m option
8
+ class Md5 < Algorithm
15
9
 
16
- def prefix
17
- "$apr1$"
18
- end
10
+ DIGEST_LENGTH = 16
11
+
12
+ def initialize(params = {})
13
+ @salt = params['salt'] || params[:salt] || gen_salt
14
+ end
19
15
 
20
- # this algorigthm pulled straight from apr_md5_encode() and converted to ruby syntax
21
- def encode(password)
22
- primary = ::Digest::MD5.new
23
- primary << password
24
- primary << prefix
25
- primary << @salt
26
-
27
- md5_t = ::Digest::MD5.digest("#{password}#{@salt}#{password}")
28
-
29
- l = password.length
30
- while l > 0 do
31
- slice_size = ( l > DIGEST_LENGTH ) ? DIGEST_LENGTH : l
32
- primary << md5_t[0, slice_size]
33
- l -= DIGEST_LENGTH
34
- end
35
-
36
- # weirdness
37
- l = password.length
38
- while l != 0
39
- case (l & 1)
40
- when 1
41
- primary << 0.chr
42
- when 0
43
- primary << password[0,1]
44
- end
45
- l >>= 1
46
- end
47
-
48
- pd = primary.digest
49
-
50
- encoded_password = "#{prefix}#{@salt}$"
51
-
52
- # apr_md5_encode has this comment about a 60Mhz Pentium above this loop.
53
- 1000.times do |x|
54
- ctx = ::Digest::MD5.new
55
- ctx << (( ( x & 1 ) == 1 ) ? password : pd[0,DIGEST_LENGTH])
56
- (ctx << @salt) unless ( x % 3 ) == 0
57
- (ctx << password) unless ( x % 7 ) == 0
58
- ctx << (( ( x & 1 ) == 0 ) ? password : pd[0,DIGEST_LENGTH])
59
- pd = ctx.digest
60
- end
61
-
62
-
63
- l = (pd[ 0]<<16) | (pd[ 6]<<8) | pd[12]
64
- encoded_password << to_64(l, 4)
65
-
66
- l = (pd[ 1]<<16) | (pd[ 7]<<8) | pd[13]
67
- encoded_password << to_64(l, 4)
68
-
69
- l = (pd[ 2]<<16) | (pd[ 8]<<8) | pd[14]
70
- encoded_password << to_64(l, 4)
71
-
72
- l = (pd[ 3]<<16) | (pd[ 9]<<8) | pd[15]
73
- encoded_password << to_64(l, 4)
74
-
75
- l = (pd[ 4]<<16) | (pd[10]<<8) | pd[ 5]
76
- encoded_password << to_64(l, 4)
77
- encoded_password << to_64(pd[11],2)
78
-
79
- return encoded_password
16
+ def prefix
17
+ "$apr1$"
18
+ end
19
+
20
+ # this algorigthm pulled straight from apr_md5_encode() and converted to ruby syntax
21
+ def encode(password)
22
+ primary = ::Digest::MD5.new
23
+ primary << password
24
+ primary << prefix
25
+ primary << @salt
26
+
27
+ md5_t = ::Digest::MD5.digest("#{password}#{@salt}#{password}")
28
+
29
+ l = password.length
30
+ while l > 0 do
31
+ slice_size = ( l > DIGEST_LENGTH ) ? DIGEST_LENGTH : l
32
+ primary << md5_t[0, slice_size]
33
+ l -= DIGEST_LENGTH
34
+ end
35
+
36
+ # weirdness
37
+ l = password.length
38
+ while l != 0
39
+ case (l & 1)
40
+ when 1
41
+ primary << 0.chr
42
+ when 0
43
+ primary << password[0,1]
80
44
  end
45
+ l >>= 1
46
+ end
47
+
48
+ pd = primary.digest
49
+
50
+ encoded_password = "#{prefix}#{@salt}$"
51
+
52
+ # apr_md5_encode has this comment about a 60Mhz Pentium above this loop.
53
+ 1000.times do |x|
54
+ ctx = ::Digest::MD5.new
55
+ ctx << (( ( x & 1 ) == 1 ) ? password : pd[0,DIGEST_LENGTH])
56
+ (ctx << @salt) unless ( x % 3 ) == 0
57
+ (ctx << password) unless ( x % 7 ) == 0
58
+ ctx << (( ( x & 1 ) == 0 ) ? password : pd[0,DIGEST_LENGTH])
59
+ pd = ctx.digest
60
+ end
61
+
62
+
63
+ l = (pd[ 0]<<16) | (pd[ 6]<<8) | pd[12]
64
+ encoded_password << to_64(l, 4)
65
+
66
+ l = (pd[ 1]<<16) | (pd[ 7]<<8) | pd[13]
67
+ encoded_password << to_64(l, 4)
68
+
69
+ l = (pd[ 2]<<16) | (pd[ 8]<<8) | pd[14]
70
+ encoded_password << to_64(l, 4)
71
+
72
+ l = (pd[ 3]<<16) | (pd[ 9]<<8) | pd[15]
73
+ encoded_password << to_64(l, 4)
74
+
75
+ l = (pd[ 4]<<16) | (pd[10]<<8) | pd[ 5]
76
+ encoded_password << to_64(l, 4)
77
+ encoded_password << to_64(pd[11],2)
78
+
79
+ return encoded_password
81
80
  end
81
+ end
82
82
  end
@@ -4,173 +4,172 @@ require 'htauth/passwd_file'
4
4
  require 'ostruct'
5
5
  require 'optparse'
6
6
 
7
- require 'rubygems'
8
7
  require 'highline'
9
8
 
10
9
  module HTAuth
11
- class Passwd
10
+ class Passwd
12
11
 
13
- MAX_PASSWD_LENGTH = 255
12
+ MAX_PASSWD_LENGTH = 255
14
13
 
15
- attr_accessor :passwd_file
14
+ attr_accessor :passwd_file
16
15
 
17
- def initialize
18
- @passwd_file = nil
19
- end
16
+ def initialize
17
+ @passwd_file = nil
18
+ end
20
19
 
21
- def options
22
- if @options.nil? then
23
- @options = ::OpenStruct.new
24
- @options.batch_mode = false
25
- @options.file_mode = File::ALTER
26
- @options.passwdfile = nil
27
- @options.algorithm = Algorithm::EXISTING
28
- @options.send_to_stdout = false
29
- @options.show_version = false
30
- @options.show_help = false
31
- @options.username = nil
32
- @options.delete_entry = false
33
- @options.password = ""
34
- end
35
- @options
36
- end
20
+ def options
21
+ if @options.nil? then
22
+ @options = ::OpenStruct.new
23
+ @options.batch_mode = false
24
+ @options.file_mode = File::ALTER
25
+ @options.passwdfile = nil
26
+ @options.algorithm = Algorithm::EXISTING
27
+ @options.send_to_stdout = false
28
+ @options.show_version = false
29
+ @options.show_help = false
30
+ @options.username = nil
31
+ @options.delete_entry = false
32
+ @options.password = ""
33
+ end
34
+ @options
35
+ end
37
36
 
38
- def option_parser
39
- if not @option_parser then
40
- @option_parser = OptionParser.new do |op|
41
- op.banner = <<EOB
37
+ def option_parser
38
+ if not @option_parser then
39
+ @option_parser = OptionParser.new do |op|
40
+ op.banner = <<EOB
42
41
  Usage:
43
- #{op.program_name} [-cmdpsD] passwordfile username
44
- #{op.program_name} -b[cmdpsD] passwordfile username password
42
+ #{op.program_name} [-cmdpsD] passwordfile username
43
+ #{op.program_name} -b[cmdpsD] passwordfile username password
45
44
 
46
- #{op.program_name} -n[mdps] username
47
- #{op.program_name} -nb[mdps] username password
45
+ #{op.program_name} -n[mdps] username
46
+ #{op.program_name} -nb[mdps] username password
48
47
  EOB
49
48
 
50
- op.separator ""
51
-
52
- op.on("-b", "--batch", "Batch mode, get the password from the command line, rather than prompt") do |b|
53
- options.batch_mode = b
54
- end
55
-
56
- op.on("-c", "--create", "Create a new file; this overwrites an existing file.") do |c|
57
- options.file_mode = HTAuth::File::CREATE
58
- end
59
-
60
- op.on("-d", "--crypt", "Force CRYPT encryption of the password (default).") do |c|
61
- options.algorithm = "crypt"
62
- end
63
-
64
- op.on("-D", "--delete", "Delete the specified user.") do |d|
65
- options.delete_entry = d
66
- end
67
-
68
- op.on("-h", "--help", "Display this help.") do |h|
69
- options.show_help = h
70
- end
71
-
72
- op.on("-m", "--md5", "Force MD5 encryption of the password (default on Windows).") do |m|
73
- options.algorithm = "md5"
74
- end
75
-
76
- op.on("-n", "--stdout", "Do not update the file; Display the results on stdout instead.") do |n|
77
- options.send_to_stdout = true
78
- options.passwdfile = HTAuth::File::STDOUT_FLAG
79
- end
80
-
81
- op.on("-p", "--plaintext", "Do not encrypt the password (plaintext).") do |p|
82
- options.algorithm = "plaintext"
83
- end
84
-
85
- op.on("-s", "--sha1", "Force SHA encryption of the password.") do |s|
86
- options.algorithm = "sha1"
87
- end
88
-
89
- op.on("-v", "--version", "Show version info.") do |v|
90
- options.show_version = v
91
- end
92
- end
93
- end
94
- @option_parser
95
- end
49
+ op.separator ""
96
50
 
97
- def show_help
98
- $stdout.puts option_parser
99
- exit 1
100
- end
51
+ op.on("-b", "--batch", "Batch mode, get the password from the command line, rather than prompt") do |b|
52
+ options.batch_mode = b
53
+ end
101
54
 
102
- def show_version
103
- $stdout.puts "#{option_parser.program_name}: version #{HTAuth::VERSION}"
104
- exit 1
105
- end
55
+ op.on("-c", "--create", "Create a new file; this overwrites an existing file.") do |c|
56
+ options.file_mode = HTAuth::File::CREATE
57
+ end
58
+
59
+ op.on("-d", "--crypt", "Force CRYPT encryption of the password (default).") do |c|
60
+ options.algorithm = "crypt"
61
+ end
62
+
63
+ op.on("-D", "--delete", "Delete the specified user.") do |d|
64
+ options.delete_entry = d
65
+ end
66
+
67
+ op.on("-h", "--help", "Display this help.") do |h|
68
+ options.show_help = h
69
+ end
70
+
71
+ op.on("-m", "--md5", "Force MD5 encryption of the password (default on Windows).") do |m|
72
+ options.algorithm = "md5"
73
+ end
74
+
75
+ op.on("-n", "--stdout", "Do not update the file; Display the results on stdout instead.") do |n|
76
+ options.send_to_stdout = true
77
+ options.passwdfile = HTAuth::File::STDOUT_FLAG
78
+ end
79
+
80
+ op.on("-p", "--plaintext", "Do not encrypt the password (plaintext).") do |p|
81
+ options.algorithm = "plaintext"
82
+ end
83
+
84
+ op.on("-s", "--sha1", "Force SHA encryption of the password.") do |s|
85
+ options.algorithm = "sha1"
86
+ end
106
87
 
107
- def parse_options(argv)
108
- begin
109
- option_parser.parse!(argv)
110
- show_version if options.show_version
111
- show_help if options.show_help
112
-
113
- raise ::OptionParser::ParseError, "Unable to send to stdout AND create a new file" if options.send_to_stdout and (options.file_mode == File::CREATE)
114
- raise ::OptionParser::ParseError, "a username is needed" if options.send_to_stdout and argv.size < 1
115
- raise ::OptionParser::ParseError, "a username and password are needed" if options.send_to_stdout and options.batch_mode and ( argv.size < 2 )
116
- raise ::OptionParser::ParseError, "a passwordfile, username and password are needed " if not options.send_to_stdout and options.batch_mode and ( argv.size < 3 )
117
- raise ::OptionParser::ParseError, "a passwordfile and username are needed" if argv.size < 2
118
-
119
- options.passwdfile = argv.shift unless options.send_to_stdout
120
- options.username = argv.shift
121
- options.password = argv.shift if options.batch_mode
122
-
123
- rescue ::OptionParser::ParseError => pe
124
- $stderr.puts "ERROR: #{option_parser.program_name} - #{pe}"
125
- show_help
126
- exit 1
127
- end
88
+ op.on("-v", "--version", "Show version info.") do |v|
89
+ options.show_version = v
90
+ end
128
91
  end
92
+ end
93
+ @option_parser
94
+ end
95
+
96
+ def show_help
97
+ $stdout.puts option_parser
98
+ exit 1
99
+ end
100
+
101
+ def show_version
102
+ $stdout.puts "#{option_parser.program_name}: version #{HTAuth::VERSION}"
103
+ exit 1
104
+ end
105
+
106
+ def parse_options(argv)
107
+ begin
108
+ option_parser.parse!(argv)
109
+ show_version if options.show_version
110
+ show_help if options.show_help
111
+
112
+ raise ::OptionParser::ParseError, "Unable to send to stdout AND create a new file" if options.send_to_stdout and (options.file_mode == File::CREATE)
113
+ raise ::OptionParser::ParseError, "a username is needed" if options.send_to_stdout and argv.size < 1
114
+ raise ::OptionParser::ParseError, "a username and password are needed" if options.send_to_stdout and options.batch_mode and ( argv.size < 2 )
115
+ raise ::OptionParser::ParseError, "a passwordfile, username and password are needed " if not options.send_to_stdout and options.batch_mode and ( argv.size < 3 )
116
+ raise ::OptionParser::ParseError, "a passwordfile and username are needed" if argv.size < 2
117
+
118
+ options.passwdfile = argv.shift unless options.send_to_stdout
119
+ options.username = argv.shift
120
+ options.password = argv.shift if options.batch_mode
121
+
122
+ rescue ::OptionParser::ParseError => pe
123
+ $stderr.puts "ERROR: #{option_parser.program_name} - #{pe}"
124
+ show_help
125
+ exit 1
126
+ end
127
+ end
129
128
 
130
- def run(argv)
131
- begin
132
- parse_options(argv)
133
- passwd_file = PasswdFile.new(options.passwdfile, options.file_mode)
134
-
135
- if options.delete_entry then
136
- passwd_file.delete(options.username)
137
- else
138
- unless options.batch_mode
139
- # initialize here so that if $stdin is overwritten it gest picked up
140
- hl = ::HighLine.new
141
-
142
- action = passwd_file.has_entry?(options.username) ? "Changing" : "Adding"
143
-
144
- $stdout.puts "#{action} password for #{options.username}."
145
-
146
- pw_in = hl.ask(" New password: ") { |q| q.echo = '*' }
147
- raise PasswordError, "password '#{pw_in}' too long" if pw_in.length >= MAX_PASSWD_LENGTH
148
-
149
- pw_validate = hl.ask("Re-type new password: ") { |q| q.echo = '*' }
150
- raise PasswordError, "They don't match, sorry." unless pw_in == pw_validate
151
- options.password = pw_in
152
- end
153
- passwd_file.add_or_update(options.username, options.password, options.algorithm)
154
- end
155
-
156
- passwd_file.save!
157
-
158
- rescue HTAuth::FileAccessError => fae
159
- msg = "Password file failure (#{options.passwdfile}) "
160
- $stderr.puts "#{msg}: #{fae.message}"
161
- exit 1
162
- rescue HTAuth::PasswordError => pe
163
- $stderr.puts "#{pe.message}"
164
- exit 1
165
- rescue HTAuth::PasswdFileError => fe
166
- $stderr.puts "#{fe.message}"
167
- exit 1
168
- rescue SignalException => se
169
- $stderr.puts
170
- $stderr.puts "Interrupted"
171
- exit 1
172
- end
173
- exit 0
129
+ def run(argv, env = ENV)
130
+ begin
131
+ parse_options(argv)
132
+ passwd_file = PasswdFile.new(options.passwdfile, options.file_mode)
133
+
134
+ if options.delete_entry then
135
+ passwd_file.delete(options.username)
136
+ else
137
+ unless options.batch_mode
138
+ # initialize here so that if $stdin is overwritten it gest picked up
139
+ hl = ::HighLine.new
140
+
141
+ action = passwd_file.has_entry?(options.username) ? "Changing" : "Adding"
142
+
143
+ $stdout.puts "#{action} password for #{options.username}."
144
+
145
+ pw_in = hl.ask(" New password: ") { |q| q.echo = '*' }
146
+ raise PasswordError, "password '#{pw_in}' too long" if pw_in.length >= MAX_PASSWD_LENGTH
147
+
148
+ pw_validate = hl.ask("Re-type new password: ") { |q| q.echo = '*' }
149
+ raise PasswordError, "They don't match, sorry." unless pw_in == pw_validate
150
+ options.password = pw_in
151
+ end
152
+ passwd_file.add_or_update(options.username, options.password, options.algorithm)
174
153
  end
154
+
155
+ passwd_file.save!
156
+
157
+ rescue HTAuth::FileAccessError => fae
158
+ msg = "Password file failure (#{options.passwdfile}) "
159
+ $stderr.puts "#{msg}: #{fae.message}"
160
+ exit 1
161
+ rescue HTAuth::PasswordError => pe
162
+ $stderr.puts "#{pe.message}"
163
+ exit 1
164
+ rescue HTAuth::PasswdFileError => fe
165
+ $stderr.puts "#{fe.message}"
166
+ exit 1
167
+ rescue SignalException => se
168
+ $stderr.puts
169
+ $stderr.puts "Interrupted"
170
+ exit 1
171
+ end
172
+ exit 0
175
173
  end
174
+ end
176
175
  end