Class: Net::IMAP::SASL::DigestMD5Authenticator
Relationships & Source Files | |
Inherits: | Object |
Defined in: | lib/net/imap/sasl/digest_md5_authenticator.rb |
Overview
::Net::IMAP
authenticator for the “‘DIGEST-MD5`” ::Net::IMAP::SASL
mechanism type, specified in RFC-2831. See Net::IMAP#authenticate.
Deprecated
“DIGEST-MD5
” has been deprecated by RFC-6331 and should not be relied on for security. It is included for compatibility with existing servers.
Constant Summary
-
STAGE_DONE =
private
# File 'lib/net/imap/sasl/digest_md5_authenticator.rb', line 14:stage_done
-
STAGE_ONE =
private
# File 'lib/net/imap/sasl/digest_md5_authenticator.rb', line 12:stage_one
-
STAGE_TWO =
private
# File 'lib/net/imap/sasl/digest_md5_authenticator.rb', line 13:stage_two
Class Method Summary
-
.new(username, password, authzid = nil, **options) ⇒ authenticator
constructor
Creates an Authenticator for the “
DIGEST-MD5
”::Net::IMAP::SASL
mechanism.
Instance Attribute Summary
-
#authcid
readonly
Alias for #username.
-
#authzid
readonly
Authorization identity: an identity to act as or on behalf of.
- #done? ⇒ Boolean readonly
- #initial_response? ⇒ Boolean readonly
-
#password
readonly
A password or passphrase that matches the #username.
-
#username
(also: #authcid)
readonly
Authentication identity: the identity that matches the #password.
Instance Method Summary
-
#process(challenge)
Responds to server challenge in two stages.
- #nc(nonce) private
-
#qdval(k, v)
private
some responses need quoting.
Constructor Details
.new(username, password, authzid = nil, **options) ⇒ authenticator
.new(username:, password:, authzid: nil, **options) ⇒ authenticator
.new(authcid:, password:, authzid: nil, **options) ⇒ authenticator
Creates an Authenticator for the “DIGEST-MD5
” ::Net::IMAP::SASL
mechanism.
Called by Net::IMAP#authenticate and similar methods on other clients.
Parameters
-
#authcid ― Authentication identity that is associated with #password.
#username ― An alias for #authcid.
-
#password ― A password or passphrase associated with this #authcid.
-
optional #authzid ― Authorization identity to act as or on behalf of.
When #authzid is not set, the server should derive the authorization identity from the authentication identity.
-
optional
warn_deprecation
— Set tofalse
to silence the warning.
Any other keyword arguments are silently ignored.
# File 'lib/net/imap/sasl/digest_md5_authenticator.rb', line 70
def initialize(user = nil, pass = nil, authz = nil, username: nil, password: nil, authzid: nil, authcid: nil, secret: nil, warn_deprecation: true, **) username = authcid || username || user or raise ArgumentError, "missing username (authcid)" password ||= secret || pass or raise ArgumentError, "missing password" authzid ||= authz if warn_deprecation warn "WARNING: DIGEST-MD5 SASL mechanism was deprecated by RFC6331." # TODO: recommend SCRAM instead. end require "digest/md5" require "strscan" @username, @password, @authzid = username, password, authzid @nc, @stage = {}, STAGE_ONE end
Instance Attribute Details
#authcid (readonly)
Alias for #username.
# File 'lib/net/imap/sasl/digest_md5_authenticator.rb', line 25
alias authcid username
#authzid (readonly)
Authorization identity: an identity to act as or on behalf of. The identity form is application protocol specific. If not provided or left blank, the server derives an authorization identity from the authentication identity. The server is responsible for verifying the client’s credentials and verifying that the identity it associates with the client’s authentication identity is allowed to act as (or on behalf of) the authorization identity.
For example, an administrator or superuser might take on another role:
imap.authenticate "DIGEST-MD5", "root", ->{passwd}, authzid: "user"
# File 'lib/net/imap/sasl/digest_md5_authenticator.rb', line 43
attr_reader :authzid
#done? ⇒ Boolean
(readonly)
[ GitHub ]
# File 'lib/net/imap/sasl/digest_md5_authenticator.rb', line 156
def done?; @stage == STAGE_DONE end
#initial_response? ⇒ Boolean
(readonly)
[ GitHub ]
# File 'lib/net/imap/sasl/digest_md5_authenticator.rb', line 88
def initial_response?; false end
#password (readonly)
A password or passphrase that matches the #username.
The password
will be used to create the response digest.
# File 'lib/net/imap/sasl/digest_md5_authenticator.rb', line 30
attr_reader :password
#username (readonly) Also known as: #authcid
# File 'lib/net/imap/sasl/digest_md5_authenticator.rb', line 24
attr_reader :username
Instance Method Details
#nc(nonce) (private)
[ GitHub ]# File 'lib/net/imap/sasl/digest_md5_authenticator.rb', line 160
def nc(nonce) if @nc.has_key? nonce @nc[nonce] = @nc[nonce] + 1 else @nc[nonce] = 1 end return @nc[nonce] end
#process(challenge)
Responds to server challenge in two stages.
# File 'lib/net/imap/sasl/digest_md5_authenticator.rb', line 91
def process(challenge) case @stage when STAGE_ONE @stage = STAGE_TWO sparams = {} c = StringScanner.new(challenge) while c.scan(/(?:\s*,)?\s*(\w)=("(?:[^\\"]|\\.)*"|[^,])\s*/) k, v = c[1], c[2] if v =~ /^"(.*)"$/ v = $1 if v =~ /,/ v = v.split(',') end end sparams[k] = v end raise Net::IMAP::DataFormatError, "Bad Challenge: '#{challenge}'" unless c.eos? and sparams['qop'] raise Net::IMAP::Error, "Server does not support auth (qop = #{sparams['qop'].join(',')})" unless sparams['qop'].include?("auth") response = { :nonce => sparams['nonce'], :username => @username, :realm => sparams['realm'], :cnonce => Digest::MD5.hexdigest("%.15f:%.15f:%d" % [Time.now.to_f, rand, Process.pid.to_s]), :'digest-uri' => 'imap/' + sparams['realm'], :qop => 'auth', :maxbuf => 65535, :nc => "%08d" % nc(sparams['nonce']), :charset => sparams['charset'], } response[:authzid] = @authzid unless @authzid.nil? # now, the real thing a0 = Digest::MD5.digest( [ response.values_at(:username, :realm), @password ].join(':') ) a1 = [ a0, response.values_at(:nonce,:cnonce) ].join(':') a1 << ':' + response[:authzid] unless response[:authzid].nil? a2 = "AUTHENTICATE:" + response[:'digest-uri'] a2 << ":00000000000000000000000000000000" if response[:qop] and response[:qop] =~ /^auth-(?:conf|int)$/ response[:response] = Digest::MD5.hexdigest( [ Digest::MD5.hexdigest(a1), response.values_at(:nonce, :nc, :cnonce, :qop), Digest::MD5.hexdigest(a2) ].join(':') ) return response.keys.map {|key| qdval(key.to_s, response[key]) }.join(',') when STAGE_TWO @stage = STAGE_DONE # if at the second stage, return an empty string if challenge =~ /rspauth=/ return '' else raise ResponseParseError, challenge end else raise ResponseParseError, challenge end end
#qdval(k, v) (private)
some responses need quoting
# File 'lib/net/imap/sasl/digest_md5_authenticator.rb', line 170
def qdval(k, v) return if k.nil? or v.nil? if %w"username authzid realm nonce cnonce digest-uri qop".include? k v = v.gsub(/([\\"])/, "\\\1") return '%s="%s"' % [k, v] else return '%s=%s' % [k, v] end end