1   
  2   
  3   
  4   
  5   
  6   
  7   
  8   
  9   
 10   
 11   
 12   
 13   
 14   
 15   
 16   
 17  """Base classes for PyXMPP SASL implementation. 
 18   
 19  Normative reference: 
 20    - `RFC 2222 <http://www.ietf.org/rfc/rfc2222.txt>`__ 
 21  """ 
 22  __revision__="$Id: core.py 647 2006-08-26 18:27:39Z jajcus $" 
 23  __docformat__="restructuredtext en" 
 24   
 25  import random 
 26  import logging 
 27  from binascii import b2a_base64 
 28   
 30      """Base class for password managers. 
 31   
 32      Password manager is an object responsible for providing or verification 
 33      of authentication credentials. 
 34   
 35      All the methods of `PasswordManager` class may be overriden in derived 
 36      classes for specific authentication and authorization policy.""" 
 38          """Initialize a `PasswordManager` object.""" 
 39          pass 
  40   
 41 -    def get_password(self,username,realm=None,acceptable_formats=("plain",)): 
  42          """Get the password for user authentication. 
 43   
 44          [both client or server] 
 45   
 46          By default returns (None, None) providing no password. Should be 
 47          overriden in derived classes. 
 48   
 49          :Parameters: 
 50              - `username`: the username for which the password is requested. 
 51              - `realm`: the authentication realm for which the password is 
 52                requested. 
 53              - `acceptable_formats`: a sequence of acceptable formats of the 
 54                password data. Could be "plain", "md5:user:realm:password" or any 
 55                other mechanism-specific encoding. This allows non-plain-text 
 56                storage of passwords. But only "plain" format will work with 
 57                all password authentication mechanisms. 
 58          :Types: 
 59              - `username`: `unicode` 
 60              - `realm`: `unicode` 
 61              - `acceptable_formats`: sequence of `str` 
 62   
 63          :return: the password and its encoding (format). 
 64          :returntype: `unicode`,`str` tuple.""" 
 65          _unused, _unused, _unused = username, realm, acceptable_formats 
 66          return None,None 
  67   
 69          """Check the password validity. 
 70   
 71          [server only] 
 72   
 73          Used by plain-text authentication mechanisms. 
 74   
 75          Retrieve a "plain" password for the `username` and `realm` using 
 76          `self.get_password` and compare it with the password provided. 
 77   
 78          May be overrided e.g. to check the password against some external 
 79          authentication mechanism (PAM, LDAP, etc.). 
 80   
 81          :Parameters: 
 82              - `username`: the username for which the password verification is 
 83                requested. 
 84              - `password`: the password to verify. 
 85              - `realm`: the authentication realm for which the password 
 86                verification is requested. 
 87          :Types: 
 88              - `username`: `unicode` 
 89              - `password`: `unicode` 
 90              - `realm`: `unicode` 
 91   
 92          :return: `True` if the password is valid. 
 93          :returntype: `bool`""" 
 94          pw,format=self.get_password(username,realm,("plain",)) 
 95          if pw and format=="plain" and pw==password: 
 96              return True 
 97          return False 
  98   
100          """Get available realms list. 
101   
102          [server only] 
103   
104          :return: a list of realms available for authentication. May be empty -- 
105              the client may choose its own realm then or use no realm at all. 
106          :returntype: `list` of `unicode`""" 
107          return [] 
 108   
110          """Choose an authentication realm from the list provided by the server. 
111   
112          [client only] 
113   
114          By default return the first realm from the list or `None` if the list 
115          is empty. 
116   
117          :Parameters: 
118              - `realm_list`: the list of realms provided by a server. 
119          :Types: 
120              - `realm_list`: sequence of `unicode` 
121   
122          :return: the realm chosen. 
123          :returntype: `unicode`""" 
124          if realm_list: 
125              return realm_list[0] 
126          else: 
127              return None 
 128   
130          """Check if the authenticated entity is allowed to use given 
131          authorization id. 
132   
133          [server only] 
134   
135          By default return `True` if the `authzid` is `None` or empty or it is 
136          equal to extra_info["username"] (if the latter is present). 
137   
138          :Parameters: 
139              - `authzid`: an authorization id. 
140              - `extra_info`: information about an entity got during the 
141                authentication process. This is a mapping with arbitrary, 
142                mechanism-dependent items. Common keys are 'username' or 
143                'realm'. 
144          :Types: 
145              - `authzid`: `unicode` 
146              - `extra_info`: mapping 
147   
148          :return: `True` if the authenticated entity is authorized to use 
149              the provided authorization id. 
150          :returntype: `bool`""" 
151          if not extra_info: 
152              extra_info={} 
153          return (not authzid 
154                  or extra_info.has_key("username") 
155                          and extra_info["username"]==authzid) 
 156   
158          """Return the service type for DIGEST-MD5 'digest-uri' field. 
159   
160          Should be overriden in derived classes. 
161   
162          :return: the service type ("unknown" by default)""" 
163          return "unknown" 
 164   
166          """Return the host name for DIGEST-MD5 'digest-uri' field. 
167   
168          Should be overriden in derived classes. 
169   
170          :return: the host name ("unknown" by default)""" 
171          return "unknown" 
 172   
174          """Return the service name for DIGEST-MD5 'digest-uri' field. 
175   
176          Should be overriden in derived classes. 
177   
178          :return: the service name or `None` (which is the default).""" 
179          return None 
 180   
182          """Generate a random string for digest authentication challenges. 
183   
184          The string should be cryptographicaly secure random pattern. 
185   
186          :return: the string generated. 
187          :returntype: `str`""" 
188           
189          r1=str(random.random())[2:] 
190          r2=str(random.random())[2:] 
191          return r1+r2 
  192   
194      """Base class for SASL authentication reply objects. 
195   
196      :Ivariables: 
197          - `data`: optional reply data. 
198      :Types: 
199          - `data`: `str`""" 
201          """Initialize the `Reply` object. 
202   
203          :Parameters: 
204              - `data`: optional reply data. 
205          :Types: 
206              - `data`: `str`""" 
207          self.data=data 
 208   
210          """Base64-encode the data contained in the reply. 
211   
212          :return: base64-encoded data. 
213          :returntype: `str`""" 
214          if self.data is not None: 
215              ret=b2a_base64(self.data) 
216              if ret[-1]=='\n': 
217                  ret=ret[:-1] 
218              return ret 
219          else: 
220              return None 
  221   
223      """The challenge SASL message (server's challenge for the client).""" 
225          """Initialize the `Challenge` object.""" 
226          Reply.__init__(self,data) 
 228          return "<sasl.Challenge: %r>" % (self.data,) 
  229   
231      """The response SASL message (clients's reply the the server's challenge).""" 
233          """Initialize the `Response` object.""" 
234          Reply.__init__(self,data) 
 236          return "<sasl.Response: %r>" % (self.data,) 
  237   
239      """The failure SASL message. 
240   
241      :Ivariables: 
242          - `reason`: the failure reason. 
243      :Types: 
244          - `reason`: unicode.""" 
246          """Initialize the `Failure` object. 
247   
248          :Parameters: 
249              - `reason`: the failure reason. 
250          :Types: 
251              - `reason`: unicode.""" 
252          Reply.__init__(self,"") 
253          self.reason=reason 
 255          return "<sasl.Failure: %r>" % (self.reason,) 
  256   
258      """The success SASL message (sent by the server on authentication success).""" 
259 -    def __init__(self,username,realm=None,authzid=None,data=None): 
 260          """Initialize the `Success` object. 
261   
262          :Parameters: 
263              - `username`: authenticated username (authentication id). 
264              - `realm`: authentication realm used. 
265              - `authzid`: authorization id. 
266              - `data`: the success data to be sent to the client. 
267          :Types: 
268              - `username`: `unicode` 
269              - `realm`: `unicode` 
270              - `authzid`: `unicode` 
271              - `data`: `str` 
272          """ 
273          Reply.__init__(self,data) 
274          self.username=username 
275          self.realm=realm 
276          self.authzid=authzid 
 278          return "<sasl.Success: authzid: %r data: %r>" % (self.authzid,self.data) 
  279   
281      """Base class for client authenticators. 
282   
283      A client authenticator class is a client-side implementation of a SASL 
284      mechanism. One `ClientAuthenticator` object may be used for one 
285      client authentication process.""" 
286   
288          """Initialize a `ClientAuthenticator` object. 
289   
290          :Parameters: 
291              - `password_manager`: a password manager providing authentication 
292                credentials. 
293          :Types: 
294              - `password_manager`: `PasswordManager`""" 
295          self.password_manager=password_manager 
296          self.__logger=logging.getLogger("pyxmpp.sasl.ClientAuthenticator") 
 297   
298 -    def start(self,username,authzid): 
 299          """Start the authentication process. 
300   
301          :Parameters: 
302              - `username`: the username (authentication id). 
303              - `authzid`: the authorization id requester. 
304          :Types: 
305              - `username`: `unicode` 
306              - `authzid`: `unicode` 
307   
308          :return: the initial response to send to the server or a failuer 
309              indicator. 
310          :returntype: `Response` or `Failure`""" 
311          _unused, _unused = username, authzid 
312          return Failure("Not implemented") 
 313   
315          """Process the server's challenge. 
316   
317          :Parameters: 
318              - `challenge`: the challenge. 
319          :Types: 
320              - `challenge`: `str` 
321   
322          :return: the response or a failure indicator. 
323          :returntype: `Response` or `Failure`""" 
324          _unused = challenge 
325          return Failure("Not implemented") 
 326   
328          """Handle authentication succes information from the server. 
329   
330          :Parameters: 
331              - `data`: the optional additional data returned with the success. 
332          :Types: 
333              - `data`: `str` 
334   
335          :return: success or failure indicator. 
336          :returntype: `Success` or `Failure`""" 
337          _unused = data 
338          return Failure("Not implemented") 
  339   
341      """Base class for server authenticators. 
342   
343      A server authenticator class is a server-side implementation of a SASL 
344      mechanism. One `ServerAuthenticator` object may be used for one 
345      client authentication process.""" 
346   
348          """Initialize a `ServerAuthenticator` object. 
349   
350          :Parameters: 
351              - `password_manager`: a password manager providing authentication 
352                credential verfication. 
353          :Types: 
354              - `password_manager`: `PasswordManager`""" 
355          self.password_manager=password_manager 
356          self.__logger=logging.getLogger("pyxmpp.sasl.ServerAuthenticator") 
 357   
358 -    def start(self,initial_response): 
 359          """Start the authentication process. 
360   
361          :Parameters: 
362              - `initial_response`: the initial response send by the client with 
363                the authentication request. 
364   
365          :Types: 
366              - `initial_response`: `str` 
367   
368          :return: a challenge, a success or a failure indicator. 
369          :returntype: `Challenge` or `Failure` or `Success`""" 
370          _unused = initial_response 
371          return Failure("not-authorized") 
 372   
374          """Process a response from a client. 
375   
376          :Parameters: 
377              - `response`: the response from the client to our challenge. 
378          :Types: 
379              - `response`: `str` 
380   
381          :return: a challenge, a success or a failure indicator. 
382          :returntype: `Challenge` or `Success` or `Failure`""" 
383          _unused = response 
384          return Failure("not-authorized") 
  385   
386   
387