Revision: 8125
          http://svn.sourceforge.net/mailman/?rev=8125&view=rev
Author:   bwarsaw
Date:     2006-12-29 16:32:41 -0800 (Fri, 29 Dec 2006)

Log Message:
-----------
Added the ability to specify a password hashing scheme for output of
user passwords.  Default is 'none' which disables passwords altogether
in the resulting XML.  Other choices include:

plain -- output passwords in plain text
sha   -- sha1 hash of password
ssha  -- sha1 hash salted with 4 random bytes (only available if
         os.urandom() works)

Modified Paths:
--------------
    branches/Release_2_1-maint/mailman/bin/export.py

Modified: branches/Release_2_1-maint/mailman/bin/export.py
===================================================================
--- branches/Release_2_1-maint/mailman/bin/export.py    2006-12-29 22:21:54 UTC 
(rev 8124)
+++ branches/Release_2_1-maint/mailman/bin/export.py    2006-12-30 00:32:41 UTC 
(rev 8125)
@@ -19,7 +19,10 @@
 
 """Export an XML representation of a mailing list."""
 
+import os
 import sys
+import sha
+import base64
 import datetime
 import optparse
 
@@ -42,6 +45,7 @@
                    'autoresponse_postings_text',
                    'autoresponse_admin_text',
                    'autoresponse_request_text')
+SALT_LENGTH     = 4 # bytes
 
 
 
@@ -59,7 +63,8 @@
         assert self._indent >= 0
 
     def write(self, s):
-        self._fp.write(self._indent * self._width * ' ')
+        if s <> '\n':
+            self._fp.write(self._indent * self._width * ' ')
         self._fp.write(s)
 
 
@@ -160,7 +165,7 @@
             else:
                 self._element('option', value, name=varname)
 
-    def _dump_list(self, mlist, with_passwords):
+    def _dump_list(self, mlist, password_scheme):
         # Write list configuration values
         self._push_element('list', name=mlist._internal_name)
         self._push_element('configuration')
@@ -185,8 +190,8 @@
                 attrs['original'] = cased
             self._push_element('member', **attrs)
             self._element('realname', mlist.getMemberName(member))
-            if with_passwords:
-                self._element('password', mlist.getMemberPassword(member))
+            self._element('password',
+                          password_scheme(mlist.getMemberPassword(member)))
             self._element('language', mlist.getMemberLanguage(member))
             # Delivery status, combined with the type of delivery
             attrs = {}
@@ -230,7 +235,7 @@
         self._pop_element('roster')
         self._pop_element('list')
 
-    def dump(self, listnames, with_passwords=False):
+    def dump(self, listnames, password_scheme):
         print >> self._fp, '<?xml version="1.0" encoding="UTF-8"?>'
         self._push_element('mailman', **{
             'xmlns:xsi': 'http://www.w3.org/2001/XMLSchema-instance',
@@ -242,7 +247,7 @@
             except Errors.MMUnknownListError:
                 print >> sys.stderr, _('No such list: %(listname)s')
                 continue
-            self._dump_list(mlist, with_passwords)
+            self._dump_list(mlist, password_scheme)
         self._pop_element('mailman')
 
     def close(self):
@@ -251,6 +256,41 @@
 
 
 
+def no_password(password):
+    return '{NONE}'
+
+
+def plaintext_password(password):
+    return '{PLAIN}' + password
+
+
+def sha_password(password):
+    h = sha.new(password)
+    return '{SHA}' + base64.b64encode(h.digest())
+
+
+def ssha_password(password):
+    salt = os.urandom(SALT_LENGTH)
+    h = sha.new(password)
+    h.update(salt)
+    return '{SSHA}' + base64.b64encode(h.digest() + salt)
+
+
+SCHEMES = {
+    'none'  : no_password,
+    'plain' : plaintext_password,
+    'sha'   : sha_password,
+    }
+
+try:
+    os.urandom(1)
+except NotImplementedError:
+    pass
+else:
+    SCHEMES['ssha'] = ssha_password
+
+
+
 def parseargs():
     parser = optparse.OptionParser(version=mm_cfg.VERSION,
                                    usage=_("""\
@@ -262,10 +302,15 @@
                       help=_("""\
 Output XML to FILENAME.  If not given, or if FILENAME is '-', standard out is
 used."""))
-    parser.add_option('-p', '--with-passwords',
+    parser.add_option('-p', '--password-scheme',
+                      default='none', type='string', help=_("""\
+Specify the RFC 2307 style hashing scheme for passwords included in the
+output.  Use -P to get a list of supported schemes, which are
+case-insensitive."""))
+    parser.add_option('-P', '--list-hash-schemes',
                       default=False, action='store_true', help=_("""\
-With this option, user passwords are included in cleartext.  For this reason,
-the default is to not include passwords."""))
+List the supported password hashing schemes and exit.  The scheme labels are
+case-insensitive."""))
     parser.add_option('-l', '--listname',
                       default=[], action='append', type='string',
                       metavar='LISTNAME', dest='listnames', help=_("""\
@@ -275,6 +320,12 @@
     if args:
         parser.print_help()
         parser.error(_('Unexpected arguments'))
+    if opts.list_hash_schemes:
+        for label in SCHEMES:
+            print label.upper()
+        sys.exit(0)
+    if opts.password_scheme.lower() not in SCHEMES:
+        parser.error(_('Invalid password scheme'))
     return parser, opts, args
 
 
@@ -293,7 +344,7 @@
             listnames = opts.listnames
         else:
             listnames = Utils.list_names()
-        dumper.dump(listnames, opts.with_passwords)
+        dumper.dump(listnames, SCHEMES[opts.password_scheme])
         dumper.close()
     finally:
         if fp is not sys.stdout:


This was sent by the SourceForge.net collaborative development platform, the 
world's largest Open Source development site.
_______________________________________________
Mailman-checkins mailing list
[email protected]
Unsubscribe: 
http://mail.python.org/mailman/options/mailman-checkins/archive%40jab.org

Reply via email to