Class | Jabber::SASL::DigestMD5 |
In: |
lib/xmpp4r/sasl.rb
|
Parent: | Base |
SASL DIGEST-MD5 authentication helper (RFC2831)
Sends the wished auth mechanism and wait for a challenge
(proceed with DigestMD5#auth)
# 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
# File lib/xmpp4r/sasl.rb, line 165 165: def auth(password) 166: response = {} 167: response['nonce'] = @nonce 168: response['charset'] = 'utf-8' 169: response['username'] = @stream.jid.node 170: response['realm'] = @realm || @stream.jid.domain 171: response['cnonce'] = generate_nonce 172: response['nc'] = '00000001' 173: response['qop'] = 'auth' 174: response['digest-uri'] = "xmpp/#{@stream.jid.domain}" 175: response['response'] = response_value(@stream.jid.node, @stream.jid.domain, response['digest-uri'], password, @nonce, response['cnonce'], response['qop']) 176: response.each { |key,value| 177: unless %w(nc qop response charset).include? key 178: response[key] = "\"#{value}\"" 179: end 180: } 181: 182: response_text = response.collect { |k,v| "#{k}=#{v}" }.join(',') 183: Jabber::debuglog("SASL DIGEST-MD5 response:\n#{response_text}") 184: 185: r = REXML::Element.new('response') 186: r.add_namespace NS_SASL 187: r.text = Base64::encode64(response_text).gsub(/\s/, '') 188: 189: success_already = false 190: error = nil 191: @stream.send(r) { |reply| 192: if reply.name == 'success' 193: success_already = true 194: elsif reply.name != 'challenge' 195: error = reply.first_element(nil).name 196: end 197: true 198: } 199: 200: return if success_already 201: raise error if error 202: 203: # TODO: check the challenge from the server 204: 205: r.text = nil 206: @stream.send(r) { |reply| 207: if reply.name != 'success' 208: error = reply.first_element(nil).name 209: end 210: true 211: } 212: 213: raise error if error 214: end
# 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: 126: text.scan(/./) do |ch| 127: if state == :key 128: if ch == '=' 129: state = :value 130: else 131: key += ch 132: end 133: 134: elsif state == :value 135: if ch == ',' 136: res[key] = value 137: key = '' 138: value = '' 139: state = :key 140: elsif ch == '"' and value == '' 141: state = :quote 142: else 143: value += ch 144: end 145: 146: elsif state == :quote 147: if ch == '"' 148: state = :value 149: else 150: value += ch 151: end 152: end 153: end 154: res[key] = value unless key == '' 155: 156: Jabber::debuglog("SASL DIGEST-MD5 challenge:\n#{text.inspect}\n#{res.inspect}") 157: 158: res 159: end
Function from RFC2831
# File lib/xmpp4r/sasl.rb, line 223 223: def hh(s); Digest::MD5.hexdigest(s); end
Calculate the value for the response field
# File lib/xmpp4r/sasl.rb, line 227 227: def response_value(username, realm, digest_uri, passwd, nonce, cnonce, qop) 228: a1_h = h("#{username}:#{realm}:#{passwd}") 229: a1 = "#{a1_h}:#{nonce}:#{cnonce}" 230: #a2 = "AUTHENTICATE:#{digest_uri}#{(qop == 'auth') ? '' : ':00000000000000000000000000000000'}" 231: a2 = "AUTHENTICATE:#{digest_uri}" 232: 233: hh("#{hh(a1)}:#{nonce}:00000001:#{cnonce}:#{qop}:#{hh(a2)}") 234: end