11# frozen_string_literal: true
22
33# Net::IMAP authenticator for the "`DIGEST-MD5`" SASL mechanism type, specified
4- # in RFC2831( https://tools.ietf.org/html/rfc2831) . See Net::IMAP#authenticate.
4+ # in RFC-2831[ https://tools.ietf.org/html/rfc2831] . See Net::IMAP#authenticate.
55#
66# == Deprecated
77#
88# "+DIGEST-MD5+" has been deprecated by
9- # {RFC6331} [https://tools.ietf.org/html/rfc6331] and should not be relied on for
9+ # RFC-6331 [https://tools.ietf.org/html/rfc6331] and should not be relied on for
1010# security. It is included for compatibility with existing servers.
1111class Net ::IMAP ::SASL ::DigestMD5Authenticator
12+ STAGE_ONE = :stage_one
13+ STAGE_TWO = :stage_two
14+ private_constant :STAGE_ONE , :STAGE_TWO
15+
16+ # Authentication identity: the identity that matches the #password.
17+ #
18+ # RFC-2831[https://tools.ietf.org/html/rfc2831] uses the term +username+.
19+ # "Authentication identity" is the generic term used by
20+ # RFC-4422[https://tools.ietf.org/html/rfc4422].
21+ # RFC-4616[https://tools.ietf.org/html/rfc4616] and many later RFCs abbreviate
22+ # that to +authcid+. So +authcid+ is available as an alias for #username.
23+ attr_reader :username
24+
25+ # A password or passphrase that matches the #username.
26+ #
27+ # The +password+ will be used to create the response digest.
28+ attr_reader :password
29+
30+ # Authorization identity: an identity to act as or on behalf of. The identity
31+ # form is application protocol specific. If not provided or left blank, the
32+ # server derives an authorization identity from the authentication identity.
33+ # The server is responsible for verifying the client's credentials and
34+ # verifying that the identity it associates with the client's authentication
35+ # identity is allowed to act as (or on behalf of) the authorization identity.
36+ #
37+ # For example, an administrator or superuser might take on another role:
38+ #
39+ # imap.authenticate "DIGEST-MD5", "root", ->{passwd}, authzid: "user"
40+ #
41+ attr_reader :authzid
42+
43+ # :call-seq:
44+ # new(username, password, authzid = nil) -> authenticator
45+ #
46+ # Creates an Authenticator for the "+DIGEST-MD5+" SASL mechanism.
47+ #
48+ # Called by Net::IMAP#authenticate and similar methods on other clients.
49+ #
50+ # ==== Parameters
51+ #
52+ # * #username — Identity whose #password is used.
53+ # * #password — A password or passphrase associated with this #username.
54+ # * #authzid ― Alternate identity to act as or on behalf of. Optional.
55+ # * +warn_deprecation+ — Set to +false+ to silence the warning.
56+ #
57+ # See the documentation for each attribute for more details.
58+ def initialize ( username , password , authzid = nil , warn_deprecation : true )
59+ if warn_deprecation
60+ warn "WARNING: DIGEST-MD5 SASL mechanism was deprecated by RFC6331."
61+ # TODO: recommend SCRAM instead.
62+ end
63+ require "digest/md5"
64+ require "strscan"
65+ @username , @password , @authzid = username , password , authzid
66+ @nc , @stage = { } , STAGE_ONE
67+ end
68+
69+ # Responds to server challenge in two stages.
1270 def process ( challenge )
1371 case @stage
1472 when STAGE_ONE
@@ -31,7 +89,7 @@ def process(challenge)
3189
3290 response = {
3391 :nonce => sparams [ 'nonce' ] ,
34- :username => @user ,
92+ :username => @username ,
3593 :realm => sparams [ 'realm' ] ,
3694 :cnonce => Digest ::MD5 . hexdigest ( "%.15f:%.15f:%d" % [ Time . now . to_f , rand , Process . pid . to_s ] ) ,
3795 :'digest-uri' => 'imap/' + sparams [ 'realm' ] ,
@@ -41,7 +99,7 @@ def process(challenge)
4199 :charset => sparams [ 'charset' ] ,
42100 }
43101
44- response [ :authzid ] = @authname unless @authname . nil?
102+ response [ :authzid ] = @authzid unless @authzid . nil?
45103
46104 # now, the real thing
47105 a0 = Digest ::MD5 . digest ( [ response . values_at ( :username , :realm ) , @password ] . join ( ':' ) )
@@ -74,23 +132,8 @@ def process(challenge)
74132 end
75133 end
76134
77- def initialize ( user , password , authname = nil , warn_deprecation : true )
78- if warn_deprecation
79- warn "WARNING: DIGEST-MD5 SASL mechanism was deprecated by RFC6331."
80- # TODO: recommend SCRAM instead.
81- end
82- require "digest/md5"
83- require "strscan"
84- @user , @password , @authname = user , password , authname
85- @nc , @stage = { } , STAGE_ONE
86- end
87-
88-
89135 private
90136
91- STAGE_ONE = :stage_one
92- STAGE_TWO = :stage_two
93-
94137 def nc ( nonce )
95138 if @nc . has_key? nonce
96139 @nc [ nonce ] = @nc [ nonce ] + 1
0 commit comments