From 267a6f6526a540a1383441aab65e13c1e7fbc8a4 Mon Sep 17 00:00:00 2001 From: philipp Date: Thu, 20 Oct 2016 20:22:20 +0000 Subject: [PATCH] Added support of MediaWiki 1.27 pbkdf2 hashes. git-svn-id: http://www.winterrodeln.org/svn/wradmin/trunk@2504 7aebc617-e5e2-0310-91dc-80fb5f6d2477 --- wradmin/lib/mediawiki.py | 48 +++++++++++++++++++++++++++++---------- wradmin/tests/test_lib.py | 21 +++++++++++++---- 2 files changed, 53 insertions(+), 16 deletions(-) diff --git a/wradmin/lib/mediawiki.py b/wradmin/lib/mediawiki.py index 5a31cf0..8f7cbf4 100644 --- a/wradmin/lib/mediawiki.py +++ b/wradmin/lib/mediawiki.py @@ -51,21 +51,45 @@ class MediaWikiUsers(UsersReadOnly): con.close() log.info("%d users loaded from the MediaWiki database" % len(self.usernames)) - + @staticmethod + def password_is_correct(password_plain, password_db): + """Returns true if a plain text password corresponds to the hash of the password as stored in the MediaWiki db. + + :param password_plain: plain text password, e.g. 'abc' + :param password_db: complete password line as stored in the database, e.g. ':pbkdf2:sha256:10000:128:EXgVGhc2mAs710feKvkiaw==:J5fYth9pg/R2d0F8bSsYfTR8SBpTBNIcdv/DgJ0tOPC1rtajl2Dr0RLqOozLb8O0XpDhtv4a3JJd/M0b58WebfNWAcdJBJI9nNeC0EYYD7OCYZGVAaRhiYtK4m53KZBBL6x/k2j4RjHPT1NmgV8Fr1DPqBNOlOHxUIh5z5oslM4=' + """ + if not password_db.startswith(':'): + raise AuthKitError("Password entry in the database does have an unexpected format (does not start with ':').") + pwd_parts = password_db[1:].split(':') + pwd_type = pwd_parts[0] + if pwd_type == 'B': + # legacy + # example: password_db == ':B:d25b2886:41e46c952790b1b442aac4f24f7ea7a8' + # pwd_parts == ['B', 'd25b2886', '41e46c952790b1b442aac4f24f7ea7a8'] + if len(pwd_parts) != 3: + raise AuthKitError("Password entry in the database does have an unexpected format (too few ':').") + salt, pwd_md5 = tuple(pwd_parts[1:3]) # salt = 'd25b2886'; pwd_md5 = '41e46c952790b1b442aac4f24f7ea7a8' + # log.info("user: '%s'; md5 of salt+' '+entered_pwd: '%s'; md5-part of DB-pwd: %s" % (username, md5(salt + '-' + md5(password)), pwd_md5)) + return md5(salt + '-' + md5(password_plain)) == pwd_md5 + elif pwd_type == 'pbkdf2': + if len(pwd_parts) != 6: + raise AuthKitError("Password entry in the database does have an unexpected format (too few ':').") + _, algorithm, rounds, num_bit, salt, pwd_hash = pwd_parts + from base64 import b64decode, b64encode + from hashlib import pbkdf2_hmac + salt = b64decode(salt) + hash = pbkdf2_hmac(algorithm, password_plain, salt, int(rounds), int(num_bit)) + hash = b64encode(hash) + return hash == pwd_hash + + def user_has_password(self, username, password): """ Passwords are case sensitive. - Returns ``True`` if the user has the password specified, ``False`` otherwise. + Returns ``True`` if the user has the password specified, ``False`` otherwise. Raises an exception if the user doesn't exist. - + See http://www.winterrodeln.org/trac/wiki/MediaWikiAuthorization """ - pwd = self.user_password(username) - # Example: pwd = ':B:d25b2886:41e46c952790b1b442aac4f24f7ea7a8' - pwd_parts = pwd.split(':') # password_parts = ['', 'B', 'd25b2886', '41e46c952790b1b442aac4f24f7ea7a8'] - if len(pwd_parts) == 4 and pwd_parts[1] == 'B': - salt, pwd_md5 = tuple(pwd_parts[2:4]) # salt = 'd25b2886'; pwd_md5 = '41e46c952790b1b442aac4f24f7ea7a8' - else: - raise AuthKitError("Password in the MediaWiki database format has an unexpected format ('%s' instead of e.g. ':B:d25b2886:41e46c952790b1b442aac4f24f7ea7a8')" % pwd) - # log.info("user: '%s'; md5 of salt+' '+entered_pwd: '%s'; md5-part of DB-pwd: %s" % (username, md5(salt + '-' + md5(password)), pwd_md5)) - return md5(salt + '-' + md5(password)) == pwd_md5 + password_db = self.user_password(username) + return self.password_is_correct(password, password_db) diff --git a/wradmin/tests/test_lib.py b/wradmin/tests/test_lib.py index 347c1e4..196a04a 100644 --- a/wradmin/tests/test_lib.py +++ b/wradmin/tests/test_lib.py @@ -1,12 +1,25 @@ -#!/usr/bin/python2.6 +#!/usr/bin/python2.7 # -*- coding: iso-8859-15 -*- # $Id$ +import unittest import wradmin.lib import wradmin.lib.mediawiki import wradmin.model +class TestMediaWikiUsers(unittest.TestCase): -def _test_mediawiki_users(): - users = wradmin.lib.mediawiki.MediaWikiUsers(True) - assert len(users.usernames) >= 1 # We have at least one user + @unittest.skip + def test_mediawiki_users(self): + users = wradmin.lib.mediawiki.MediaWikiUsers(True) + assert len(users.usernames) >= 1 # We have at least one user + + def test_mediawiki_users_has_password_is_correct_b(self): + password_db = ':B:d25b2886:41e46c952790b1b442aac4f24f7ea7a8' # 'abc' + self.assertTrue(wradmin.lib.mediawiki.MediaWikiUsers.password_is_correct('abc', password_db)) + self.assertFalse(wradmin.lib.mediawiki.MediaWikiUsers.password_is_correct('abcd', password_db)) + + def test_mediawiki_users_has_password_is_correct_pbkdf2(self): + password_db = ':pbkdf2:sha256:10000:128:EXgVGhc2mAs710feKvkiaw==:J5fYth9pg/R2d0F8bSsYfTR8SBpTBNIcdv/DgJ0tOPC1rtajl2Dr0RLqOozLb8O0XpDhtv4a3JJd/M0b58WebfNWAcdJBJI9nNeC0EYYD7OCYZGVAaRhiYtK4m53KZBBL6x/k2j4RjHPT1NmgV8Fr1DPqBNOlOHxUIh5z5oslM4=' # 'abc' + self.assertTrue(wradmin.lib.mediawiki.MediaWikiUsers.password_is_correct('abc', password_db)) + self.assertFalse(wradmin.lib.mediawiki.MediaWikiUsers.password_is_correct('abcd', password_db)) -- 2.39.5