mirror of
https://github.com/samba-team/samba.git
synced 2024-12-22 13:34:15 +03:00
samba-tool user: add rounds option to virtualCryptSHAxxx
Allow the number of rounds to be specified when calculating the virtualCryptSHA256 and virtualCryptSHA512 attributes. i.e. --attributes="virtualCryptSHA256;rounds=3000" will calculate the hash using 3,000 rounds. Signed-off-by: Gary Lockyer <gary@catalyst.net.nz> Reviewed-by: Garming Sam <garming@catalyst.net.nz> Reviewed-by: Andrew Bartlett <abartlet@samba.org>
This commit is contained in:
parent
d51253609d
commit
d4bc91a964
@ -103,7 +103,7 @@ def get_random_bytes(num):
|
||||
raise ImportError(random_reason)
|
||||
return get_random_bytes_fn(num)
|
||||
|
||||
def get_crypt_value(alg, utf8pw):
|
||||
def get_crypt_value(alg, utf8pw, rounds=0):
|
||||
algs = {
|
||||
"5": {"length": 43},
|
||||
"6": {"length": 86},
|
||||
@ -116,8 +116,13 @@ def get_crypt_value(alg, utf8pw):
|
||||
# we can ignore the possible == at the end
|
||||
# of the base64 string
|
||||
# we just need to replace '+' by '.'
|
||||
b64salt = base64.b64encode(salt)
|
||||
crypt_salt = "$%s$%s$" % (alg, b64salt[0:16].replace('+', '.'))
|
||||
b64salt = base64.b64encode(salt)[0:16].replace('+', '.')
|
||||
crypt_salt = ""
|
||||
if rounds != 0:
|
||||
crypt_salt = "$%s$rounds=%s$%s$" % (alg, rounds, b64salt)
|
||||
else:
|
||||
crypt_salt = "$%s$%s$" % (alg, b64salt)
|
||||
|
||||
crypt_value = crypt.crypt(utf8pw, crypt_salt)
|
||||
if crypt_value is None:
|
||||
raise NotImplementedError("crypt.crypt(%s) returned None" % (crypt_salt))
|
||||
@ -127,6 +132,24 @@ def get_crypt_value(alg, utf8pw):
|
||||
crypt_salt, len(crypt_value), expected_len))
|
||||
return crypt_value
|
||||
|
||||
# Extract the rounds value from the options of a virtualCrypt attribute
|
||||
# i.e. options = "rounds=20;other=ignored;" will return 20
|
||||
# if the rounds option is not found or the value is not a number, 0 is returned
|
||||
# which indicates that the default number of rounds should be used.
|
||||
def get_rounds(options):
|
||||
if not options:
|
||||
return 0
|
||||
|
||||
opts = options.split(';')
|
||||
for o in opts:
|
||||
if o.lower().startswith("rounds="):
|
||||
(key, _, val) = o.partition('=')
|
||||
try:
|
||||
return int(val)
|
||||
except ValueError:
|
||||
return 0
|
||||
return 0
|
||||
|
||||
try:
|
||||
random_reason = check_random()
|
||||
if random_reason is not None:
|
||||
@ -882,9 +905,19 @@ class GetPasswordCommand(Command):
|
||||
def get_account_attributes(self, samdb, username, basedn, filter, scope,
|
||||
attrs, decrypt):
|
||||
|
||||
require_supplementalCredentials = False
|
||||
search_attrs = attrs[:]
|
||||
raw_attrs = attrs[:]
|
||||
search_attrs = []
|
||||
attr_opts = {}
|
||||
for a in raw_attrs:
|
||||
(attr, _, opts) = a.partition(';')
|
||||
if opts:
|
||||
attr_opts[attr] = opts
|
||||
else:
|
||||
attr_opts[attr] = None
|
||||
search_attrs.append(attr)
|
||||
lower_attrs = [x.lower() for x in search_attrs]
|
||||
|
||||
require_supplementalCredentials = False
|
||||
for a in virtual_attributes.keys():
|
||||
if a.lower() in lower_attrs:
|
||||
require_supplementalCredentials = True
|
||||
@ -1124,6 +1157,22 @@ class GetPasswordCommand(Command):
|
||||
except IndexError:
|
||||
return None
|
||||
|
||||
def get_userPassword_hash(blob, scheme, prefix):
|
||||
up = ndr_unpack(drsblobs.package_PrimaryUserPasswordBlob, blob)
|
||||
|
||||
# Check that the NT hash has not been changed without updating
|
||||
# the user password hashes.
|
||||
if unicodePwd != bytearray(up.current_nt_hash.hash):
|
||||
return None
|
||||
|
||||
|
||||
# Return the first hash that matches scheme
|
||||
for h in up.hashes:
|
||||
if h.scheme == scheme and h.value.startswith(prefix):
|
||||
return h.value
|
||||
|
||||
return None
|
||||
|
||||
# We use sort here in order to have a predictable processing order
|
||||
for a in sorted(virtual_attributes.keys()):
|
||||
if not a.lower() in lower_attrs:
|
||||
@ -1161,7 +1210,8 @@ class GetPasswordCommand(Command):
|
||||
u8 = get_utf8(a, b, username or account_name)
|
||||
if u8 is None:
|
||||
continue
|
||||
sv = get_crypt_value("5", u8)
|
||||
rounds = get_rounds(attr_opts[a])
|
||||
sv = get_crypt_value("5", u8, rounds)
|
||||
v = "{CRYPT}" + sv
|
||||
elif a == "virtualCryptSHA512":
|
||||
b = get_package("Primary:CLEARTEXT")
|
||||
@ -1170,7 +1220,8 @@ class GetPasswordCommand(Command):
|
||||
u8 = get_utf8(a, b, username or account_name)
|
||||
if u8 is None:
|
||||
continue
|
||||
sv = get_crypt_value("6", u8)
|
||||
rounds = get_rounds(attr_opts[a])
|
||||
sv = get_crypt_value("6", u8, rounds)
|
||||
v = "{CRYPT}" + sv
|
||||
elif a == "virtualSambaGPG":
|
||||
# Samba adds 'Primary:SambaGPG' at the end.
|
||||
@ -1257,10 +1308,20 @@ for which virtual attributes are supported in your environment):
|
||||
virtualCryptSHA256: As virtualClearTextUTF8, but a salted SHA256
|
||||
checksum, useful for OpenLDAP's '{CRYPT}' algorithm,
|
||||
with a $5$... salt, see crypt(3) on modern systems.
|
||||
The number of rounds used to calculate the hash can
|
||||
also be specified. By appending ";rounds=x" to the
|
||||
attribute name i.e. virtualCryptSHA256;rounds=10000
|
||||
will calculate a SHA256 hash with 10,000 rounds.
|
||||
non numeric values for rounds are silently ignored
|
||||
|
||||
virtualCryptSHA512: As virtualClearTextUTF8, but a salted SHA512
|
||||
checksum, useful for OpenLDAP's '{CRYPT}' algorithm,
|
||||
with a $6$... salt, see crypt(3) on modern systems.
|
||||
The number of rounds used to calculate the hash can
|
||||
also be specified. By appending ";rounds=x" to the
|
||||
attribute name i.e. virtualCryptSHA512;rounds=10000
|
||||
will calculate a SHA512 hash with 10,000 rounds.
|
||||
non numeric values for rounds are silently ignored
|
||||
|
||||
virtualWDigestNN: The individual hash values stored in
|
||||
'Primary:WDigest' where NN is the hash number in
|
||||
@ -1398,10 +1459,20 @@ for supported virtual attributes in your environment):
|
||||
virtualCryptSHA256: As virtualClearTextUTF8, but a salted SHA256
|
||||
checksum, useful for OpenLDAP's '{CRYPT}' algorithm,
|
||||
with a $5$... salt, see crypt(3) on modern systems.
|
||||
The number of rounds used to calculate the hash can
|
||||
also be specified. By appending ";rounds=x" to the
|
||||
attribute name i.e. virtualCryptSHA256;rounds=10000
|
||||
will calculate a SHA256 hash with 10,000 rounds.
|
||||
non numeric values for rounds are silently ignored
|
||||
|
||||
virtualCryptSHA512: As virtualClearTextUTF8, but a salted SHA512
|
||||
checksum, useful for OpenLDAP's '{CRYPT}' algorithm,
|
||||
with a $6$... salt, see crypt(3) on modern systems.
|
||||
The number of rounds used to calculate the hash can
|
||||
also be specified. By appending ";rounds=x" to the
|
||||
attribute name i.e. virtualCryptSHA512;rounds=10000
|
||||
will calculate a SHA512 hash with 10,000 rounds.
|
||||
non numeric values for rounds are silently ignored
|
||||
|
||||
virtualWDigestNN: The individual hash values stored in
|
||||
'Primary:WDigest' where NN is the hash number in
|
||||
|
@ -330,6 +330,3 @@
|
||||
# We currently don't send referrals for LDAP modify of non-replicated attrs
|
||||
^samba4.ldap.rodc.python\(rodc\).__main__.RodcTests.test_modify_nonreplicated.*
|
||||
^samba4.ldap.rodc_rwdc.python.*.__main__.RodcRwdcTests.test_change_password_reveal_on_demand_kerberos
|
||||
# rounds tests for samba_tool.user_virtualCryptSHA should fail until the rounds functionality is implemented
|
||||
^samba.tests.samba_tool.user_virtualCryptSHA.*.test_gpg_both_hashes_sha256_rounds_invalid\(.*
|
||||
^samba.tests.samba_tool.user_virtualCryptSHA.*.gpg_both_hashes_both_rounds\(.*
|
||||
|
Loading…
Reference in New Issue
Block a user