diff options
-rw-r--r-- | python/samba/tests/dcerpc/samr_change_password.py | 188 | ||||
-rw-r--r-- | selftest/tests.py | 2 |
2 files changed, 190 insertions, 0 deletions
diff --git a/python/samba/tests/dcerpc/samr_change_password.py b/python/samba/tests/dcerpc/samr_change_password.py new file mode 100644 index 00000000000..109eeea98cc --- /dev/null +++ b/python/samba/tests/dcerpc/samr_change_password.py @@ -0,0 +1,188 @@ +# Unix SMB/CIFS implementation. +# +# Copyright © 2020 Andreas Schneider <asn@samba.org> +# +# 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/>. +# + +"""Tests for samba.dcerpc.samr.password""" + +import os +import ctypes +import samba.tests + +from samba import crypto, generate_random_password, generate_random_bytes, ntstatus +from samba.auth import system_session +from samba.credentials import Credentials +from samba.credentials import SMB_ENCRYPTION_REQUIRED +from samba.dcerpc import samr, security, lsa +from samba.samdb import SamDB +from samba.tests import RpcInterfaceTestCase + + +class SamrPasswordTests(RpcInterfaceTestCase): + def setUp(self): + super(SamrPasswordTests, self).setUp() + self.open_samdb() + + self.create_user_account(10000) + + self.remote_server = samba.tests.env_get_var_value('SERVER') + self.remote_domain = samba.tests.env_get_var_value('DOMAIN') + self.remote_user = samba.tests.env_get_var_value('USERNAME') + self.remote_password = samba.tests.env_get_var_value('PASSWORD') + self.remote_binding_string = "ncacn_np:%s[krb5]" % (self.remote_server) + + self.remote_creds = Credentials() + self.remote_creds.guess(self.lp) + self.remote_creds.set_username(self.remote_user) + self.remote_creds.set_password(self.remote_password) + + def tearDown(self): + super(SamrPasswordTests, self).tearDown() + + samr.Close(self.user_handle) + samr.Close(self.domain_handle) + samr.Close(self.handle) + + samba.tests.delete_force(self.samdb, self.user_dn) + + # + # Open the samba database + # + def open_samdb(self): + self.lp = samba.tests.env_loadparm() + + self.local_creds = Credentials() + self.local_creds.guess(self.lp) + self.session = system_session() + self.samdb = SamDB(session_info=self.session, + credentials=self.local_creds, + lp=self.lp) + + # + # Open a SAMR Domain handle + # + def open_domain_handle(self): + self.handle = self.conn.Connect2(None, + security.SEC_FLAG_MAXIMUM_ALLOWED) + + self.domain_sid = self.conn.LookupDomain(self.handle, + lsa.String(self.remote_domain)) + + self.domain_handle = self.conn.OpenDomain(self.handle, + security.SEC_FLAG_MAXIMUM_ALLOWED, + self.domain_sid) + + def open_user_handle(self): + name = lsa.String(self.user_name) + + rids = self.conn.LookupNames(self.domain_handle, [name]) + + self.user_handle = self.conn.OpenUser(self.domain_handle, + security.SEC_FLAG_MAXIMUM_ALLOWED, + rids[0].ids[0]) + # + # Create a test user account + # + def create_user_account(self, user_id): + self.user_name = ("SAMR_USER_%d" % user_id) + self.user_pass = generate_random_password(32, 32) + self.user_dn = "cn=%s,cn=users,%s" % (self.user_name, self.samdb.domain_dn()) + + samba.tests.delete_force(self.samdb, self.user_dn) + + self.samdb.newuser(self.user_name, + self.user_pass, + description="Password for " + self.user_name + " is " + self.user_pass, + givenname=self.user_name, + surname=self.user_name) + + + def init_samr_CryptPassword(self, password, session_key): + + def encode_pw_buffer(password): + data = bytearray([0] * 516) + + p = samba.string_to_byte_array(password.encode('utf-16-le')) + plen = len(p) + + b = generate_random_bytes(512 - plen) + + i = 512 - plen + data[0:i] = b + data[i:i+plen] = p + data[512:516] = plen.to_bytes(4, byteorder='little') + + return bytes(data) + + # This is a test, so always allow to encrypt using RC4 + try: + crypto.set_relax_mode() + encrypted_blob = samba.arcfour_encrypt(session_key, encode_pw_buffer(password)) + finally: + crypto.set_strict_mode() + + out_blob = samr.CryptPassword() + out_blob.data = list(encrypted_blob) + + return out_blob + + + def test_setUserInfo2_Password(self, password='P@ssw0rd'): + self.conn = samr.samr(self.remote_binding_string, + self.get_loadparm(), + self.remote_creds) + self.open_domain_handle() + self.open_user_handle() + + password='P@ssw0rd' + + level = 24 + info = samr.UserInfo24() + + info.password_expired = 0 + info.password = self.init_samr_CryptPassword(password, self.conn.session_key) + + # If the server is in FIPS mode, it should reject the password change! + try: + self.conn.SetUserInfo2(self.user_handle, level, info) + except samba.NTSTATUSError as e: + code = ctypes.c_uint32(e.args[0]).value + print(code) + if ((code == ntstatus.NT_STATUS_ACCESS_DENIED) and + (self.lp.weak_crypto == 'disallowed')): + pass + else: + raise + + + def test_setUserInfo2_Password_Encrypted(self, password='P@ssw0rd'): + self.remote_creds.set_smb_encryption(SMB_ENCRYPTION_REQUIRED) + + self.conn = samr.samr(self.remote_binding_string, + self.get_loadparm(), + self.remote_creds) + self.open_domain_handle() + self.open_user_handle() + + password='P@ssw0rd' + + level = 24 + info = samr.UserInfo24() + + info.password_expired = 0 + info.password = self.init_samr_CryptPassword(password, self.conn.session_key) + + self.conn.SetUserInfo2(self.user_handle, level, info) diff --git a/selftest/tests.py b/selftest/tests.py index adcb5b53189..86cab3f8046 100644 --- a/selftest/tests.py +++ b/selftest/tests.py @@ -93,6 +93,8 @@ planpythontestsuite( os.path.join(samba4srcdir, "..", "third_party", "waf")]) planpythontestsuite("fileserver", "samba.tests.smbd_fuzztest") planpythontestsuite("nt4_dc_smb1", "samba.tests.dcerpc.binding") +for env in [ 'ad_dc:local', 'ad_dc_fips:local' ]: + planpythontestsuite(env, "samba.tests.dcerpc.samr_change_password") def cmdline(script, *args): |