1   
  2   
  3   
  4   
  5   
  6   
  7   
  8   
  9   
 10   
 11   
 12   
 13   
 14   
 15   
 16   
 17  """ 
 18  Provides library with all Non-SASL and SASL authentication mechanisms. 
 19  Can be used both for client and transport authentication. 
 20  """ 
 21   
 22  from protocol import * 
 23  from client import PlugIn 
 24  import sha,base64,random,dispatcher,re 
 25   
 26  import md5 
 27 -def HH(some): return md5.new(some).hexdigest() 
  28 -def H(some): return md5.new(some).digest() 
  29 -def C(some): return ':'.join(some) 
  30   
 32      """ Implements old Non-SASL (JEP-0078) authentication used in jabberd1.4 and transport authentication.""" 
 33 -    def __init__(self,user,password,resource): 
  34          """ Caches username, password and resource for auth. """ 
 35          PlugIn.__init__(self) 
 36          self.DBG_LINE='gen_auth' 
 37          self.user=user 
 38          self.password=password 
 39          self.resource=resource 
  40   
 42          """ Determine the best auth method (digest/0k/plain) and use it for auth. 
 43              Returns used method name on success. Used internally. """ 
 44          if not self.resource: return self.authComponent(owner) 
 45          self.DEBUG('Querying server about possible auth methods','start') 
 46          resp=owner.Dispatcher.SendAndWaitForResponse(Iq('get',NS_AUTH,payload=[Node('username',payload=[self.user])])) 
 47          if not isResultNode(resp): 
 48              self.DEBUG('No result node arrived! Aborting...','error') 
 49              return 
 50          iq=Iq(typ='set',node=resp) 
 51          query=iq.getTag('query') 
 52          query.setTagData('username',self.user) 
 53          query.setTagData('resource',self.resource) 
 54   
 55          if query.getTag('digest'): 
 56              self.DEBUG("Performing digest authentication",'ok') 
 57              query.setTagData('digest',sha.new(owner.Dispatcher.Stream._document_attrs['id']+self.password).hexdigest()) 
 58              if query.getTag('password'): query.delChild('password') 
 59              method='digest' 
 60          elif query.getTag('token'): 
 61              token=query.getTagData('token') 
 62              seq=query.getTagData('sequence') 
 63              self.DEBUG("Performing zero-k authentication",'ok') 
 64              hash = sha.new(sha.new(self.password).hexdigest()+token).hexdigest() 
 65              for foo in xrange(int(seq)): hash = sha.new(hash).hexdigest() 
 66              query.setTagData('hash',hash) 
 67              method='0k' 
 68          else: 
 69              self.DEBUG("Sequre methods unsupported, performing plain text authentication",'warn') 
 70              query.setTagData('password',self.password) 
 71              method='plain' 
 72          resp=owner.Dispatcher.SendAndWaitForResponse(iq) 
 73          if isResultNode(resp): 
 74              self.DEBUG('Sucessfully authenticated with remove host.','ok') 
 75              owner.User=self.user 
 76              owner.Resource=self.resource 
 77              owner._registered_name=owner.User+'@'+owner.Server+'/'+owner.Resource 
 78              return method 
 79          self.DEBUG('Authentication failed!','error') 
  80   
 82          """ Authenticate component. Send handshake stanza and wait for result. Returns "ok" on success. """ 
 83          self.handshake=0 
 84          owner.send(Node(NS_COMPONENT_ACCEPT+' handshake',payload=[sha.new(owner.Dispatcher.Stream._document_attrs['id']+self.password).hexdigest()])) 
 85          owner.RegisterHandler('handshake',self.handshakeHandler,xmlns=NS_COMPONENT_ACCEPT) 
 86          while not self.handshake: 
 87              self.DEBUG("waiting on handshake",'notify') 
 88              owner.Process(1) 
 89          owner._registered_name=self.user 
 90          if self.handshake+1: return 'ok' 
  91   
 93          """ Handler for registering in dispatcher for accepting transport authentication. """ 
 94          if stanza.getName()=='handshake': self.handshake=1 
 95          else: self.handshake=-1 
   96   
 98      """ Implements SASL authentication. """ 
100          PlugIn.__init__(self) 
101          self.username=username 
102          self.password=password 
 103   
110   
120   
127   
129          """ Used to determine if server supports SASL auth. Used internally. """ 
130          if not feats.getTag('mechanisms',namespace=NS_SASL): 
131              self.startsasl='not-supported' 
132              self.DEBUG('SASL not supported by server','error') 
133              return 
134          mecs=[] 
135          for mec in feats.getTag('mechanisms',namespace=NS_SASL).getTags('mechanism'): 
136              mecs.append(mec.getData()) 
137          self._owner.RegisterHandler('challenge',self.SASLHandler,xmlns=NS_SASL) 
138          self._owner.RegisterHandler('failure',self.SASLHandler,xmlns=NS_SASL) 
139          self._owner.RegisterHandler('success',self.SASLHandler,xmlns=NS_SASL) 
140          if "DIGEST-MD5" in mecs: 
141              node=Node('auth',attrs={'xmlns':NS_SASL,'mechanism':'DIGEST-MD5'}) 
142          elif "PLAIN" in mecs: 
143              sasl_data='%s\x00%s\x00%s'%(self.username+'@'+self._owner.Server,self.username,self.password) 
144              node=Node('auth',attrs={'xmlns':NS_SASL,'mechanism':'PLAIN'},payload=[base64.encodestring(sasl_data)]) 
145          else: 
146              self.startsasl='failure' 
147              self.DEBUG('I can only use DIGEST-MD5 and PLAIN mecanisms.','error') 
148              return 
149          self.startsasl='in-process' 
150          self._owner.send(node.__str__()) 
151          raise NodeProcessed 
 152   
154          """ Perform next SASL auth step. Used internally. """ 
155          if challenge.getNamespace()<>NS_SASL: return 
156          if challenge.getName()=='failure': 
157              self.startsasl='failure' 
158              try: reason=challenge.getChildren()[0] 
159              except: reason=challenge 
160              self.DEBUG('Failed SASL authentification: %s'%reason,'error') 
161              raise NodeProcessed 
162          elif challenge.getName()=='success': 
163              self.startsasl='success' 
164              self.DEBUG('Successfully authenticated with remote server.','ok') 
165              handlers=self._owner.Dispatcher.dumpHandlers() 
166              self._owner.Dispatcher.PlugOut() 
167              dispatcher.Dispatcher().PlugIn(self._owner) 
168              self._owner.Dispatcher.restoreHandlers(handlers) 
169              self._owner.User=self.username 
170              raise NodeProcessed 
171   
172          incoming_data=challenge.getData() 
173          chal={} 
174          data=base64.decodestring(incoming_data) 
175          self.DEBUG('Got challenge:'+data,'ok') 
176          for pair in re.findall('(\w+=(?:"[^"]+")|(?:[^,]+))',data): 
177              key,value=pair.split('=', 1) 
178              if value[:1]=='"' and value[-1:]=='"': value=value[1:-1] 
179              chal[key]=value 
180          if chal.has_key('qop') and 'auth' in chal['qop'].split(','): 
181              resp={} 
182              resp['username']=self.username 
183              resp['realm']=self._owner.Server 
184              resp['nonce']=chal['nonce'] 
185              cnonce='' 
186              for i in range(7): 
187                  cnonce+=hex(int(random.random()*65536*4096))[2:] 
188              resp['cnonce']=cnonce 
189              resp['nc']=('00000001') 
190              resp['qop']='auth' 
191              resp['digest-uri']='xmpp/'+self._owner.Server 
192              A1=C([H(C([resp['username'],resp['realm'],self.password])),resp['nonce'],resp['cnonce']]) 
193              A2=C(['AUTHENTICATE',resp['digest-uri']]) 
194              response= HH(C([HH(A1),resp['nonce'],resp['nc'],resp['cnonce'],resp['qop'],HH(A2)])) 
195              resp['response']=response 
196              resp['charset']='utf-8' 
197              sasl_data='' 
198              for key in ['charset','username','realm','nonce','nc','cnonce','digest-uri','response','qop']: 
199                  if key in ['nc','qop','response','charset']: sasl_data+="%s=%s,"%(key,resp[key]) 
200                  else: sasl_data+='%s="%s",'%(key,resp[key]) 
201   
202              node=Node('response',attrs={'xmlns':NS_SASL},payload=[base64.encodestring(sasl_data[:-1]).replace('\r','').replace('\n','')]) 
203              self._owner.send(node.__str__()) 
204          elif chal.has_key('rspauth'): self._owner.send(Node('response',attrs={'xmlns':NS_SASL}).__str__()) 
205          else:  
206              self.startsasl='failure' 
207              self.DEBUG('Failed SASL authentification: unknown challenge','error') 
208          raise NodeProcessed 
  209   
211      """ Bind some JID to the current connection to allow router know of our location.""" 
213          PlugIn.__init__(self) 
214          self.DBG_LINE='bind' 
215          self.bound=None 
 216   
223   
227   
229          """ Determine if server supports resource binding and set some internal attributes accordingly. """ 
230          if not feats.getTag('bind',namespace=NS_BIND): 
231              self.bound='failure' 
232              self.DEBUG('Server does not requested binding.','error') 
233              return 
234          if feats.getTag('session',namespace=NS_SESSION): self.session=1 
235          else: self.session=-1 
236          self.bound=[] 
 237   
238 -    def Bind(self,resource=None): 
 239          """ Perform binding. Use provided resource name or random (if not provided). """ 
240          while self.bound is None and self._owner.Process(1): pass 
241          if resource: resource=[Node('resource',payload=[resource])] 
242          else: resource=[] 
243          resp=self._owner.SendAndWaitForResponse(Protocol('iq',typ='set',payload=[Node('bind',attrs={'xmlns':NS_BIND},payload=resource)])) 
244          if isResultNode(resp): 
245              self.bound.append(resp.getTag('bind').getTagData('jid')) 
246              self.DEBUG('Successfully bound %s.'%self.bound[-1],'ok') 
247              jid=JID(resp.getTag('bind').getTagData('jid')) 
248              self._owner.User=jid.getNode() 
249              self._owner.Resource=jid.getResource() 
250              resp=self._owner.SendAndWaitForResponse(Protocol('iq',typ='set',payload=[Node('session',attrs={'xmlns':NS_SESSION})])) 
251              if isResultNode(resp): 
252                  self.DEBUG('Successfully opened session.','ok') 
253                  self.session=1 
254                  return 'ok' 
255              else: 
256                  self.DEBUG('Session open failed.','error') 
257                  self.session=0 
258          elif resp: self.DEBUG('Binding failed: %s.'%resp.getTag('error'),'error') 
259          else: 
260              self.DEBUG('Binding failed: timeout expired.','error') 
261              return '' 
  262   
264      """ ComponentBind some JID to the current connection to allow router know of our location.""" 
266          PlugIn.__init__(self) 
267          self.DBG_LINE='bind' 
268          self.bound=None 
269          self.needsUnregister=None 
270          self.sasl = sasl 
 271   
283   
288   
290          """ Determine if server supports resource binding and set some internal attributes accordingly. """ 
291          if not feats.getTag('bind',namespace=NS_BIND): 
292              self.bound='failure' 
293              self.DEBUG('Server does not requested binding.','error') 
294              return 
295          if feats.getTag('session',namespace=NS_SESSION): self.session=1 
296          else: self.session=-1 
297          self.bound=[] 
 298   
299 -    def Bind(self,domain=None): 
 300          """ Perform binding. Use provided domain name (if not provided). """ 
301          while self.bound is None and self._owner.Process(1): pass 
302          if self.sasl: 
303              xmlns = NS_COMPONENT_1 
304          else: 
305              xmlns = None 
306          self.bindresponse = None 
307          ttl = dispatcher.DefaultTimeout 
308          self._owner.RegisterHandler('bind',self.BindHandler,xmlns=xmlns) 
309          self._owner.send(Protocol('bind',attrs={'name':domain},xmlns=NS_COMPONENT_1)) 
310          while self.bindresponse is None and self._owner.Process(1) and ttl > 0: ttl-=1 
311          self._owner.UnregisterHandler('bind',self.BindHandler,xmlns=xmlns) 
312          resp=self.bindresponse 
313          if resp and resp.getAttr('error'): 
314              self.DEBUG('Binding failed: %s.'%resp.getAttr('error'),'error') 
315          elif resp: 
316              self.DEBUG('Successfully bound.','ok') 
317              return 'ok' 
318          else: 
319              self.DEBUG('Binding failed: timeout expired.','error') 
320              return '' 
 321   
323          self.bindresponse = bind 
324          pass 
  325