mirror of
https://github.com/samba-team/samba.git
synced 2025-01-10 01:18:15 +03:00
f0860de5bb
Signed-off-by: Douglas Bagnall <douglas.bagnall@catalyst.net.nz> Reviewed-by: Noel Power <npower@samba.org>
467 lines
16 KiB
Python
467 lines
16 KiB
Python
# Tests for the samba-tool user sub command reading Primary:WDigest
|
|
#
|
|
# Copyright (C) Andrew Bartlett <abartlet@samba.org> 2017
|
|
#
|
|
#
|
|
# This program is free software; you can redistribute it and/or modify
|
|
# it under the terms of the GNU General Public License as published by
|
|
# the Free Software Foundation; either version 3 of the License, or
|
|
# (at your option) any later version.
|
|
#
|
|
# This program is distributed in the hope that it will be useful,
|
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
# GNU General Public License for more details.
|
|
#
|
|
# You should have received a copy of the GNU General Public License
|
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
#
|
|
|
|
import os
|
|
import time
|
|
import base64
|
|
import ldb
|
|
import samba
|
|
from samba.tests.samba_tool.base import SambaToolCmdTest
|
|
from samba import (
|
|
credentials,
|
|
nttime2unix,
|
|
dsdb
|
|
)
|
|
from samba.ndr import ndr_unpack
|
|
from samba.dcerpc import drsblobs
|
|
from hashlib import md5
|
|
import random
|
|
import string
|
|
|
|
|
|
USER_NAME = "WdigestTestUser"
|
|
# Create a random 32 character password, containing only letters and
|
|
# digits to avoid issues when used on the command line.
|
|
USER_PASS = ''.join(random.choice(string.ascii_uppercase +
|
|
string.ascii_lowercase +
|
|
string.digits) for _ in range(32))
|
|
|
|
# Calculate the MD5 password digest from the supplied user, realm and password
|
|
#
|
|
|
|
|
|
def calc_digest(user, realm, password):
|
|
data = "%s:%s:%s" % (user, realm, password)
|
|
if isinstance(data, str):
|
|
data = data.encode('utf8')
|
|
|
|
return "%s:%s:%s" % (user, realm, md5(data).hexdigest())
|
|
|
|
|
|
class UserCmdWdigestTestCase(SambaToolCmdTest):
|
|
"""Tests for samba-tool user subcommands extraction of the wdigest values
|
|
Test results validated against Windows Server 2012 R2.
|
|
NOTE: That as at 22-05-2017 the values Documented at
|
|
3.1.1.8.11.3.1 WDIGEST_CREDENTIALS Construction
|
|
are incorrect.
|
|
"""
|
|
users = []
|
|
samdb = None
|
|
|
|
def setUp(self):
|
|
super(UserCmdWdigestTestCase, self).setUp()
|
|
self.lp = samba.tests.env_loadparm()
|
|
self.samdb = self.getSamDB(
|
|
"-H", "ldap://%s" % os.environ["DC_SERVER"],
|
|
"-U%s%%%s" % (os.environ["DC_USERNAME"],
|
|
os.environ["DC_PASSWORD"]))
|
|
self.dns_domain = self.samdb.domain_dns_name()
|
|
res = self.samdb.search(
|
|
base=self.samdb.get_config_basedn(),
|
|
expression="ncName=%s" % self.samdb.get_default_basedn(),
|
|
attrs=["nETBIOSName"])
|
|
self.netbios_domain = str(res[0]["nETBIOSName"][0])
|
|
self.runsubcmd("user",
|
|
"create",
|
|
USER_NAME,
|
|
USER_PASS,
|
|
"-H",
|
|
"ldap://%s" % os.environ["DC_SERVER"],
|
|
"-U%s%%%s" % (
|
|
os.environ["DC_USERNAME"],
|
|
os.environ["DC_PASSWORD"]))
|
|
|
|
def tearDown(self):
|
|
super(UserCmdWdigestTestCase, self).tearDown()
|
|
self.runsubcmd("user", "delete", USER_NAME)
|
|
|
|
def _testWDigest(self, attribute, expected, missing=False):
|
|
|
|
(result, out, err) = self.runsubcmd("user",
|
|
"getpassword",
|
|
USER_NAME,
|
|
"--attributes",
|
|
attribute)
|
|
self.assertCmdSuccess(result,
|
|
out,
|
|
err,
|
|
"Ensure getpassword runs")
|
|
self.assertEqual(err, "", "getpassword")
|
|
self.assertMatch(out,
|
|
"Got password OK",
|
|
"getpassword out[%s]" % out)
|
|
|
|
if missing:
|
|
self.assertTrue(attribute not in out)
|
|
else:
|
|
self.assertMatch(out.replace('\n ', ''),
|
|
"%s: %s" % (attribute, expected))
|
|
|
|
def test_Wdigest_no_suffix(self):
|
|
attribute = "virtualWDigest"
|
|
self._testWDigest(attribute, None, True)
|
|
|
|
def test_Wdigest_non_numeric_suffix(self):
|
|
attribute = "virtualWDigestss"
|
|
self._testWDigest(attribute, None, True)
|
|
|
|
def test_Wdigest00(self):
|
|
attribute = "virtualWDigest00"
|
|
self._testWDigest(attribute, None, True)
|
|
|
|
# Hash01 MD5(sAMAccountName,
|
|
# NETBIOSDomainName,
|
|
# password)
|
|
#
|
|
def test_Wdigest01(self):
|
|
attribute = "virtualWDigest01"
|
|
expected = calc_digest(USER_NAME,
|
|
self.netbios_domain,
|
|
USER_PASS)
|
|
self._testWDigest(attribute, expected)
|
|
|
|
# Hash02 MD5(LOWER(sAMAccountName),
|
|
# LOWER(NETBIOSDomainName),
|
|
# password)
|
|
#
|
|
def test_Wdigest02(self):
|
|
attribute = "virtualWDigest02"
|
|
expected = calc_digest(USER_NAME.lower(),
|
|
self.netbios_domain.lower(),
|
|
USER_PASS)
|
|
self._testWDigest(attribute, expected)
|
|
|
|
# Hash03 MD5(UPPER(sAMAccountName),
|
|
# UPPER(NETBIOSDomainName),
|
|
# password)
|
|
#
|
|
def test_Wdigest03(self):
|
|
attribute = "virtualWDigest03"
|
|
expected = calc_digest(USER_NAME.upper(),
|
|
self.netbios_domain.upper(),
|
|
USER_PASS)
|
|
self._testWDigest(attribute, expected)
|
|
|
|
# Hash04 MD5(sAMAccountName,
|
|
# UPPER(NETBIOSDomainName),
|
|
# password)
|
|
#
|
|
def test_Wdigest04(self):
|
|
attribute = "virtualWDigest04"
|
|
expected = calc_digest(USER_NAME,
|
|
self.netbios_domain.upper(),
|
|
USER_PASS)
|
|
self._testWDigest(attribute, expected)
|
|
|
|
# Hash05 MD5(sAMAccountName,
|
|
# LOWER(NETBIOSDomainName),
|
|
# password)
|
|
#
|
|
def test_Wdigest05(self):
|
|
attribute = "virtualWDigest05"
|
|
expected = calc_digest(USER_NAME,
|
|
self.netbios_domain.lower(),
|
|
USER_PASS)
|
|
self._testWDigest(attribute, expected)
|
|
|
|
# Hash06 MD5(UPPER(sAMAccountName),
|
|
# LOWER(NETBIOSDomainName),
|
|
# password)
|
|
#
|
|
def test_Wdigest06(self):
|
|
attribute = "virtualWDigest06"
|
|
expected = calc_digest(USER_NAME.upper(),
|
|
self.netbios_domain.lower(),
|
|
USER_PASS)
|
|
self._testWDigest(attribute, expected)
|
|
|
|
# Hash07 MD5(LOWER(sAMAccountName),
|
|
# UPPER(NETBIOSDomainName),
|
|
# password)
|
|
#
|
|
def test_Wdigest07(self):
|
|
attribute = "virtualWDigest07"
|
|
expected = calc_digest(USER_NAME.lower(),
|
|
self.netbios_domain.upper(),
|
|
USER_PASS)
|
|
self._testWDigest(attribute, expected)
|
|
|
|
# Hash08 MD5(sAMAccountName,
|
|
# DNSDomainName,
|
|
# password)
|
|
#
|
|
# Note: Samba lowercases the DNSDomainName at provision time,
|
|
# Windows preserves the case. This means that the WDigest08 values
|
|
# calculated byt Samba and Windows differ.
|
|
#
|
|
def test_Wdigest08(self):
|
|
attribute = "virtualWDigest08"
|
|
expected = calc_digest(USER_NAME,
|
|
self.dns_domain,
|
|
USER_PASS)
|
|
self._testWDigest(attribute, expected)
|
|
|
|
# Hash09 MD5(LOWER(sAMAccountName),
|
|
# LOWER(DNSDomainName),
|
|
# password)
|
|
#
|
|
def test_Wdigest09(self):
|
|
attribute = "virtualWDigest09"
|
|
expected = calc_digest(USER_NAME.lower(),
|
|
self.dns_domain.lower(),
|
|
USER_PASS)
|
|
self._testWDigest(attribute, expected)
|
|
|
|
# Hash10 MD5(UPPER(sAMAccountName),
|
|
# UPPER(DNSDomainName),
|
|
# password)
|
|
#
|
|
def test_Wdigest10(self):
|
|
attribute = "virtualWDigest10"
|
|
expected = calc_digest(USER_NAME.upper(),
|
|
self.dns_domain.upper(),
|
|
USER_PASS)
|
|
self._testWDigest(attribute, expected)
|
|
|
|
# Hash11 MD5(sAMAccountName,
|
|
# UPPER(DNSDomainName),
|
|
# password)
|
|
#
|
|
def test_Wdigest11(self):
|
|
attribute = "virtualWDigest11"
|
|
expected = calc_digest(USER_NAME,
|
|
self.dns_domain.upper(),
|
|
USER_PASS)
|
|
self._testWDigest(attribute, expected)
|
|
|
|
# Hash12 MD5(sAMAccountName,
|
|
# LOWER(DNSDomainName),
|
|
# password)
|
|
#
|
|
def test_Wdigest12(self):
|
|
attribute = "virtualWDigest12"
|
|
expected = calc_digest(USER_NAME,
|
|
self.dns_domain.lower(),
|
|
USER_PASS)
|
|
self._testWDigest(attribute, expected)
|
|
|
|
# Hash13 MD5(UPPER(sAMAccountName),
|
|
# LOWER(DNSDomainName),
|
|
# password)
|
|
#
|
|
def test_Wdigest13(self):
|
|
attribute = "virtualWDigest13"
|
|
expected = calc_digest(USER_NAME.upper(),
|
|
self.dns_domain.lower(),
|
|
USER_PASS)
|
|
self._testWDigest(attribute, expected)
|
|
|
|
# Hash14 MD5(LOWER(sAMAccountName),
|
|
# UPPER(DNSDomainName),
|
|
# password)
|
|
#
|
|
|
|
def test_Wdigest14(self):
|
|
attribute = "virtualWDigest14"
|
|
expected = calc_digest(USER_NAME.lower(),
|
|
self.dns_domain.upper(),
|
|
USER_PASS)
|
|
self._testWDigest(attribute, expected)
|
|
|
|
# Hash15 MD5(userPrincipalName,
|
|
# password)
|
|
#
|
|
def test_Wdigest15(self):
|
|
attribute = "virtualWDigest15"
|
|
name = "%s@%s" % (USER_NAME, self.dns_domain)
|
|
expected = calc_digest(name,
|
|
"",
|
|
USER_PASS)
|
|
self._testWDigest(attribute, expected)
|
|
|
|
# Hash16 MD5(LOWER(userPrincipalName),
|
|
# password)
|
|
#
|
|
def test_Wdigest16(self):
|
|
attribute = "virtualWDigest16"
|
|
name = "%s@%s" % (USER_NAME.lower(), self.dns_domain.lower())
|
|
expected = calc_digest(name,
|
|
"",
|
|
USER_PASS)
|
|
self._testWDigest(attribute, expected)
|
|
|
|
# Hash17 MD5(UPPER(userPrincipalName),
|
|
# password)
|
|
#
|
|
def test_Wdigest17(self):
|
|
attribute = "virtualWDigest17"
|
|
name = "%s@%s" % (USER_NAME.upper(), self.dns_domain.upper())
|
|
expected = calc_digest(name,
|
|
"",
|
|
USER_PASS)
|
|
self._testWDigest(attribute, expected)
|
|
|
|
# Hash18 MD5(NETBIOSDomainName\sAMAccountName,
|
|
# password)
|
|
#
|
|
def test_Wdigest18(self):
|
|
attribute = "virtualWDigest18"
|
|
name = "%s\\%s" % (self.netbios_domain, USER_NAME)
|
|
expected = calc_digest(name,
|
|
"",
|
|
USER_PASS)
|
|
self._testWDigest(attribute, expected)
|
|
|
|
# Hash19 MD5(LOWER(NETBIOSDomainName\sAMAccountName),
|
|
# password)
|
|
#
|
|
def test_Wdigest19(self):
|
|
attribute = "virtualWDigest19"
|
|
name = "%s\\%s" % (self.netbios_domain, USER_NAME)
|
|
expected = calc_digest(name.lower(),
|
|
"",
|
|
USER_PASS)
|
|
self._testWDigest(attribute, expected)
|
|
|
|
# Hash20 MD5(UPPER(NETBIOSDomainName\sAMAccountName),
|
|
# password)
|
|
#
|
|
def test_Wdigest20(self):
|
|
attribute = "virtualWDigest20"
|
|
name = "%s\\%s" % (self.netbios_domain, USER_NAME)
|
|
expected = calc_digest(name.upper(),
|
|
"",
|
|
USER_PASS)
|
|
self._testWDigest(attribute, expected)
|
|
|
|
# Hash21 MD5(sAMAccountName,
|
|
# "Digest",
|
|
# password)
|
|
#
|
|
def test_Wdigest21(self):
|
|
attribute = "virtualWDigest21"
|
|
expected = calc_digest(USER_NAME,
|
|
"Digest",
|
|
USER_PASS)
|
|
self._testWDigest(attribute, expected)
|
|
|
|
# Hash22 MD5(LOWER(sAMAccountName),
|
|
# "Digest",
|
|
# password)
|
|
#
|
|
def test_Wdigest22(self):
|
|
attribute = "virtualWDigest22"
|
|
expected = calc_digest(USER_NAME.lower(),
|
|
"Digest",
|
|
USER_PASS)
|
|
self._testWDigest(attribute, expected)
|
|
|
|
# Hash23 MD5(UPPER(sAMAccountName),
|
|
# "Digest",
|
|
# password)
|
|
#
|
|
def test_Wdigest23(self):
|
|
attribute = "virtualWDigest23"
|
|
expected = calc_digest(USER_NAME.upper(),
|
|
"Digest",
|
|
USER_PASS)
|
|
self._testWDigest(attribute, expected)
|
|
|
|
# Hash24 MD5(userPrincipalName),
|
|
# "Digest",
|
|
# password)
|
|
#
|
|
def test_Wdigest24(self):
|
|
attribute = "virtualWDigest24"
|
|
name = "%s@%s" % (USER_NAME, self.dns_domain)
|
|
expected = calc_digest(name,
|
|
"Digest",
|
|
USER_PASS)
|
|
self._testWDigest(attribute, expected)
|
|
|
|
# Hash25 MD5(LOWER(userPrincipalName),
|
|
# "Digest",
|
|
# password)
|
|
#
|
|
def test_Wdigest25(self):
|
|
attribute = "virtualWDigest25"
|
|
name = "%s@%s" % (USER_NAME, self.dns_domain.lower())
|
|
expected = calc_digest(name.lower(),
|
|
"Digest",
|
|
USER_PASS)
|
|
self._testWDigest(attribute, expected)
|
|
|
|
# Hash26 MD5(UPPER(userPrincipalName),
|
|
# "Digest",
|
|
# password)
|
|
#
|
|
def test_Wdigest26(self):
|
|
attribute = "virtualWDigest26"
|
|
name = "%s@%s" % (USER_NAME, self.dns_domain.lower())
|
|
expected = calc_digest(name.upper(),
|
|
"Digest",
|
|
USER_PASS)
|
|
self._testWDigest(attribute, expected)
|
|
# Hash27 MD5(NETBIOSDomainName\sAMAccountName,
|
|
# "Digest",
|
|
# password)
|
|
#
|
|
|
|
def test_Wdigest27(self):
|
|
attribute = "virtualWDigest27"
|
|
name = "%s\\%s" % (self.netbios_domain, USER_NAME)
|
|
expected = calc_digest(name,
|
|
"Digest",
|
|
USER_PASS)
|
|
self._testWDigest(attribute, expected)
|
|
|
|
# Hash28 MD5(LOWER(NETBIOSDomainName\sAMAccountName),
|
|
# "Digest",
|
|
# password)
|
|
#
|
|
def test_Wdigest28(self):
|
|
attribute = "virtualWDigest28"
|
|
name = "%s\\%s" % (self.netbios_domain.lower(), USER_NAME.lower())
|
|
expected = calc_digest(name,
|
|
"Digest",
|
|
USER_PASS)
|
|
self._testWDigest(attribute, expected)
|
|
|
|
# Hash29 MD5(UPPER(NETBIOSDomainName\sAMAccountName),
|
|
# "Digest",
|
|
# password)
|
|
#
|
|
def test_Wdigest29(self):
|
|
attribute = "virtualWDigest29"
|
|
name = "%s\\%s" % (self.netbios_domain.upper(), USER_NAME.upper())
|
|
expected = calc_digest(name,
|
|
"Digest",
|
|
USER_PASS)
|
|
self._testWDigest(attribute, expected)
|
|
|
|
def test_Wdigest30(self):
|
|
attribute = "virtualWDigest30"
|
|
self._testWDigest(attribute, None, True)
|
|
|
|
# Check digest calculation against an known htdigest value
|
|
def test_calc_digest(self):
|
|
htdigest = "gary:fred:2204fcc247cb47ded249ef2fe0013255"
|
|
digest = calc_digest("gary", "fred", "password")
|
|
self.assertEqual(htdigest, digest)
|