htauth 1.0.1 → 1.0.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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