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 78 78: def initialize(stream) 79: super 80: 81: challenge = {} 82: error = nil 83: @stream.send(generate_auth('DIGEST-MD5')) { |reply| 84: if reply.name == 'challenge' and reply.namespace == NS_SASL 85: challenge = decode_challenge(reply.text) 86: else 87: error = reply.first_element(nil).name 88: end 89: true 90: } 91: raise error if error 92: 93: @nonce = challenge['nonce'] 94: @realm = challenge['realm'] 95: end
# File lib/xmpp4r/sasl.rb, line 144 144: def auth(password) 145: response = {} 146: response['nonce'] = @nonce 147: response['charset'] = 'utf-8' 148: response['username'] = @stream.jid.node 149: response['realm'] = @realm || @stream.jid.domain 150: response['cnonce'] = generate_nonce 151: response['nc'] = '00000001' 152: response['qop'] = 'auth' 153: response['digest-uri'] = "xmpp/#{@stream.jid.domain}" 154: response['response'] = response_value(@stream.jid.node, @stream.jid.domain, response['digest-uri'], password, @nonce, response['cnonce'], response['qop']) 155: response.each { |key,value| 156: unless %w(nc qop response charset).include? key 157: response[key] = "\"#{value}\"" 158: end 159: } 160: 161: response_text = response.collect { |k,v| "#{k}=#{v}" }.join(',') 162: Jabber::debuglog("SASL DIGEST-MD5 response:\n#{response_text}") 163: 164: r = REXML::Element.new('response') 165: r.add_namespace NS_SASL 166: r.text = Base64::encode64(response_text).gsub(/\s/, '') 167: 168: success_already = false 169: error = nil 170: @stream.send(r) { |reply| 171: if reply.name == 'success' 172: success_already = true 173: elsif reply.name != 'challenge' 174: error = reply.first_element(nil).name 175: end 176: true 177: } 178: 179: return if success_already 180: raise error if error 181: 182: # TODO: check the challenge from the server 183: 184: r.text = nil 185: @stream.send(r) { |reply| 186: if reply.name != 'success' 187: error = reply.first_element(nil).name 188: end 189: true 190: } 191: 192: raise error if error 193: end
# File lib/xmpp4r/sasl.rb, line 97 97: def decode_challenge(challenge) 98: text = Base64::decode64(challenge) 99: res = {} 100: 101: state = :key 102: key = '' 103: value = '' 104: 105: text.scan(/./) do |ch| 106: if state == :key 107: if ch == '=' 108: state = :value 109: else 110: key += ch 111: end 112: 113: elsif state == :value 114: if ch == ',' 115: res[key] = value 116: key = '' 117: value = '' 118: state = :key 119: elsif ch == '"' and value == '' 120: state = :quote 121: else 122: value += ch 123: end 124: 125: elsif state == :quote 126: if ch == '"' 127: state = :value 128: else 129: value += ch 130: end 131: end 132: end 133: res[key] = value unless key == '' 134: 135: Jabber::debuglog("SASL DIGEST-MD5 challenge:\n#{text.inspect}\n#{res.inspect}") 136: 137: res 138: end
Function from RFC2831
# File lib/xmpp4r/sasl.rb, line 202 202: def hh(s); Digest::MD5.hexdigest(s); end
Calculate the value for the response field
# File lib/xmpp4r/sasl.rb, line 206 206: def response_value(username, realm, digest_uri, passwd, nonce, cnonce, qop) 207: a1_h = h("#{username}:#{realm}:#{passwd}") 208: a1 = "#{a1_h}:#{nonce}:#{cnonce}" 209: #a2 = "AUTHENTICATE:#{digest_uri}#{(qop == 'auth') ? '' : ':00000000000000000000000000000000'}" 210: a2 = "AUTHENTICATE:#{digest_uri}" 211: 212: hh("#{hh(a1)}:#{nonce}:00000001:#{cnonce}:#{qop}:#{hh(a2)}") 213: end