Class Jabber::SASL::DigestMD5
In: lib/xmpp4r/sasl.rb
Parent: Base

SASL DIGEST-MD5 authentication helper (RFC2831)

Methods

auth   decode_challenge   new  

Public Class methods

Sends the wished auth mechanism and wait for a challenge

(proceed with DigestMD5#auth)

[Source]

     # File lib/xmpp4r/sasl.rb, line 99
 99:       def initialize(stream)
100:         super
101: 
102:         challenge = {}
103:         error = nil
104:         @stream.send(generate_auth('DIGEST-MD5')) { |reply|
105:           if reply.name == 'challenge' and reply.namespace == NS_SASL
106:             challenge = decode_challenge(reply.text)
107:           else
108:             error = reply.first_element(nil).name
109:           end
110:           true
111:         }
112:         raise error if error
113: 
114:         @nonce = challenge['nonce']
115:         @realm = challenge['realm']
116:       end

Public Instance methods

  • Send a response
  • Wait for the server‘s challenge (which aren‘t checked)
  • Send a blind response to the server‘s challenge

[Source]

     # File lib/xmpp4r/sasl.rb, line 170
170:       def auth(password)
171:         response = {}
172:         response['nonce'] = @nonce
173:         response['charset'] = 'utf-8'
174:         response['username'] = @stream.jid.node
175:         response['realm'] = @realm || @stream.jid.domain
176:         response['cnonce'] = generate_nonce
177:         response['nc'] = '00000001'
178:         response['qop'] = 'auth'
179:         response['digest-uri'] = "xmpp/#{@stream.jid.domain}"
180:         response['response'] = response_value(@stream.jid.node, @stream.jid.domain, response['digest-uri'], password, @nonce, response['cnonce'], response['qop'], response['authzid'])
181:         response.each { |key,value|
182:           unless %w(nc qop response charset).include? key
183:             response[key] = "\"#{value}\""
184:           end
185:         }
186: 
187:         response_text = response.collect { |k,v| "#{k}=#{v}" }.join(',')
188:         Jabber::debuglog("SASL DIGEST-MD5 response:\n#{response_text}\n#{response.inspect}")
189: 
190:         r = REXML::Element.new('response')
191:         r.add_namespace NS_SASL
192:         r.text = Base64::encode64(response_text).gsub(/\s/, '')
193: 
194:         success_already = false
195:         error = nil
196:         @stream.send(r) { |reply|
197:           if reply.name == 'success'
198:             success_already = true
199:           elsif reply.name != 'challenge'
200:             error = reply.first_element(nil).name
201:           end
202:           true
203:         }
204: 
205:         return if success_already
206:         raise error if error
207: 
208:         # TODO: check the challenge from the server
209: 
210:         r.text = nil
211:         @stream.send(r) { |reply|
212:           if reply.name != 'success'
213:             error = reply.first_element(nil).name
214:           end
215:           true
216:         }
217: 
218:         raise error if error
219:       end

[Source]

     # File lib/xmpp4r/sasl.rb, line 118
118:       def decode_challenge(challenge)
119:         text = Base64::decode64(challenge)
120:         res = {}
121: 
122:         state = :key
123:         key = ''
124:         value = ''
125:         text.scan(/./) do |ch|
126:           if state == :key
127:             if ch == '='
128:               state = :value
129:             else
130:               key += ch
131:             end
132: 
133:           elsif state == :value
134:             if ch == ','
135:               # due to our home-made parsing of the challenge, the key could have
136:               # leading whitespace. strip it, or that would break jabberd2 support.
137:               key = key.strip
138:               res[key] = value
139:               key = ''
140:               value = ''
141:               state = :key
142:             elsif ch == '"' and value == ''
143:               state = :quote
144:             else
145:               value += ch
146:             end
147: 
148:           elsif state == :quote
149:             if ch == '"'
150:               state = :value
151:             else
152:               value += ch
153:             end
154:           end
155:         end
156:         # due to our home-made parsing of the challenge, the key could have
157:         # leading whitespace. strip it, or that would break jabberd2 support.
158:         key = key.strip
159:         res[key] = value unless key == ''
160: 
161:         Jabber::debuglog("SASL DIGEST-MD5 challenge:\n#{text}\n#{res.inspect}")
162: 
163:         res
164:       end

[Validate]