class Net::IMAP::SASL::DigestMD5Authenticator
Net::IMAP
authenticator for the “‘DIGEST-MD5`” 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.
Constants
- STAGE_DONE
- STAGE_ONE
- STAGE_TWO
Attributes
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"
A password or passphrase that matches the username
.
The password
will be used to create the response digest.
Public Class Methods
Creates an Authenticator for the “DIGEST-MD5
” SASL mechanism.
Called by Net::IMAP#authenticate
and similar methods on other clients.
Parameters¶ ↑
-
authcid
― Authentication identity that is associated withpassword
.username
― An alias forauthcid
. -
password
― A password or passphrase associated with thisauthcid
. -
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 net-imap-0.4.4/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
Public Instance Methods
# File net-imap-0.4.4/lib/net/imap/sasl/digest_md5_authenticator.rb, line 156 def done?; @stage == STAGE_DONE end
# File net-imap-0.4.4/lib/net/imap/sasl/digest_md5_authenticator.rb, line 88 def initial_response?; false end
Responds to server challenge in two stages.
# File net-imap-0.4.4/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
Private Instance Methods
# File net-imap-0.4.4/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
some responses need quoting
# File net-imap-0.4.4/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