The branch, master has been updated via 6c95e467817 smbd/notify: add option "honor change notify privilege" via bcfce0ecd31 selftest: add tests for smb notify, using the a special share via 3e9f0e97255 selftest: add option to pass args to tests to planpythontestsuite() via 0133c17c099 python/tests: add tests for smb notify and the dependency to the TRAVERSE privilege via f4e578aa24b python/ntacls.py: add SMBHelper.set_acl() helper function via b082cf321e4 python/ntacls.py: let SMBHelper.get_acl() use the default values of self.smb_conn.get_acl() via 18dd953d83a libsmb_samba_internal: calculate the access_mask for {g,s}et_acl() based on the secinfo flags via 0ccdce67d3a libsmb_samba_internal: don't send SECINFO_[UN]PROTECTED_{S,D}ACL by default via 752a8f870de s3:pylibsmb: remove unused SECINFO_DEFAULT_FLAGS via 3ffb817506c s3:pylibsmb: add notify() support via fde65c2f293 s3:pylibsmb: add echo() support via 084c22403f0 s3:pylibsmb: PyErr_NTSTATUS_IS_ERR_RAISE => PyErr_NTSTATUS_NOT_OK_RAISE via 560e4b1b32f libcli/smb: add smbXcli_conn_send_queue() via 76121ae7cf4 s3:libsmb: set correct min and max smb protocol when smb2 is enforced on connect via f40da74e145 s3:libsmb: set min smb protocol when enforcing smb1 on connect via 98119189cfe blackbox/test_samba-tool_ntacl.sh: script requires two arguments via 577d4f1a60c docs:smbdotconf: fix a typo in oldpasswordallowedperiod.xml from 8004cf7a4af pep8 tidy up config
https://git.samba.org/?p=samba.git;a=shortlog;h=master - Log ----------------------------------------------------------------- commit 6c95e467817b246a0eab626cac10b6120f6c88f8 Author: Björn Baumbach <b...@sernet.de> Date: Tue Nov 6 15:21:37 2018 +0100 smbd/notify: add option "honor change notify privilege" This option can be used to make use of the change notify privilege. By default notify results are not checked against the file system permissions. If "honor change notify privilege" is enabled, a user will only receive notify results, if he has change notify privilege or sufficient file system permissions. If a user has the change notify privilege, he will receive all requested notify results, even if the user does not have the permissions on the file system. Pair-Programmed-With: Stefan Metzmacher <me...@samba.org> Signed-off-by: Björn Baumbach <b...@sernet.de> Signed-off-by: Stefan Metzmacher <me...@samba.org> Reviewed-by: Jeremy Allison <j...@samba.org> Autobuild-User(master): Stefan Metzmacher <me...@samba.org> Autobuild-Date(master): Thu Dec 17 15:01:53 UTC 2020 on sn-devel-184 commit bcfce0ecd3153d158f712ff548a1ccd005031bc8 Author: Björn Baumbach <b...@sernet.de> Date: Mon Jul 20 16:49:39 2020 +0200 selftest: add tests for smb notify, using the a special share That share will get the "honor change notify privilege = yes" option once it's implemented. For now it's marked as knownfail. Pair-Programmed-With: Stefan Metzmacher <me...@samba.org> Signed-off-by: Björn Baumbach <b...@sernet.de> Signed-off-by: Stefan Metzmacher <me...@samba.org> Reviewed-by: Jeremy Allison <j...@samba.org> commit 3e9f0e97255de1b4235c4dca6912635386328746 Author: Björn Baumbach <b...@sernet.de> Date: Fri Jul 24 12:18:11 2020 +0200 selftest: add option to pass args to tests to planpythontestsuite() The logic is basically a copy from planoldpythontestsuite(). Pair-Programmed-With: Stefan Metzmacher <me...@samba.org> Signed-off-by: Björn Baumbach <b...@sernet.de> Signed-off-by: Stefan Metzmacher <me...@samba.org> Reviewed-by: Jeremy Allison <j...@samba.org> commit 0133c17c099f6a482e2941a2254c983ec0188592 Author: Björn Baumbach <b...@sernet.de> Date: Mon Jul 20 16:49:39 2020 +0200 python/tests: add tests for smb notify and the dependency to the TRAVERSE privilege The easiest way to run this against Windows was to use a domain controller and configure an enforce group policy and grant the "Bypass Traverse Checking" only to the "BUILTIN\Administrators" group. (Note that "LOCAL SERVICE" and "NETWORK SERVICE" are always added in the local security policy. The test runs like this: SMB_CONF_PATH=/dev/null \ SERVER=172.31.9.188 \ TARGET_HOSTNAME=w2012r2-188.w2012r2-l6.base \ USERNAME=administrator \ PASSWORD=A1b2C3d4 \ NOTIFY_SHARE=torture \ USERNAME_UNPRIV=ldaptestuser \ PASSWORD_UNPRIV=a1B2c3D4 \ python/samba/tests/smb-notify.py -v -f SMBNotifyTests Pair-Programmed-With: Stefan Metzmacher <me...@samba.org> Signed-off-by: Björn Baumbach <b...@sernet.de> Signed-off-by: Stefan Metzmacher <me...@samba.org> Reviewed-by: Jeremy Allison <j...@samba.org> commit f4e578aa24bc020d87716915ea5037d7677b75e9 Author: Björn Baumbach <b...@sernet.de> Date: Tue Jul 21 12:34:19 2020 +0200 python/ntacls.py: add SMBHelper.set_acl() helper function Pair-Programmed-With: Stefan Metzmacher <me...@samba.org> Signed-off-by: Björn Baumbach <b...@sernet.de> Signed-off-by: Stefan Metzmacher <me...@samba.org> Reviewed-by: Jeremy Allison <j...@samba.org> commit b082cf321e4267cc214f2a74550847585275e25a Author: Stefan Metzmacher <me...@samba.org> Date: Thu Dec 3 13:59:58 2020 +0100 python/ntacls.py: let SMBHelper.get_acl() use the default values of self.smb_conn.get_acl() Now that self.smb_conn.get_acl() has sane default values for secinfo and access_mask we can remove any additional logic in SMBHelper. The resulting values are the same. Signed-off-by: Stefan Metzmacher <me...@samba.org> Reviewed-by: Jeremy Allison <j...@samba.org> commit 18dd953d83a5d43b243047d9dd622620034ee6c7 Author: Stefan Metzmacher <me...@samba.org> Date: Thu Dec 3 14:51:52 2020 +0100 libsmb_samba_internal: calculate the access_mask for {g,s}et_acl() based on the secinfo flags SEC_FLAG_MAXIMUM_ALLOWED will never result in SEC_FLAG_SYSTEM_SECURITY being granted. As SECINFO_SACL is part of the default secinfo value (SECINFO_DEFAULT_FLAGS), {g,s}et_acl() will always return NT_STATUS_ACCESS_DENIED by default. Signed-off-by: Stefan Metzmacher <me...@samba.org> Reviewed-by: Jeremy Allison <j...@samba.org> commit 0ccdce67d3adfc06b6d8235b53d677da1526ba4b Author: Stefan Metzmacher <me...@samba.org> Date: Thu Dec 3 13:58:53 2020 +0100 libsmb_samba_internal: don't send SECINFO_[UN]PROTECTED_{S,D}ACL by default We want to get the default behavior. It's also pointless to set PROTECTED and UNPROTECTED at the same time. These are defined in MS-DTYP 2.4.7 SECURITY_INFORMATION with a brief description, but they aren't referenced in anywhere in MS-DTYP itself, nor in MS-FSA are any other document. Signed-off-by: Stefan Metzmacher <me...@samba.org> Reviewed-by: Jeremy Allison <j...@samba.org> commit 752a8f870de2bb087802a1287d7fb6c7624ac631 Author: Stefan Metzmacher <me...@samba.org> Date: Thu Dec 3 13:19:58 2020 +0100 s3:pylibsmb: remove unused SECINFO_DEFAULT_FLAGS commit 42be033b0b0c02413a74f984c8622b5baed2689a removed the last reference. Signed-off-by: Stefan Metzmacher <me...@samba.org> Reviewed-by: Jeremy Allison <j...@samba.org> commit 3ffb817506c032cdc05064abfbffb9f364e09a22 Author: Stefan Metzmacher <me...@samba.org> Date: Tue Aug 27 13:49:04 2019 +0000 s3:pylibsmb: add notify() support The operation is a bit different from others, as results are returned in an async fashion. It returns a request handle. notify_req = conn.notify(fnum=fnum, buffer_size=0xffff, completion_filter=libsmb.FILE_NOTIFY_CHANGE_ALL, recursive=True) # ... do other operations on conn.*() ... changes = notify_req.get_changes(wait=False) # changes is likely to be None if no result arrived yet # ... do other operations on conn.*() ... changes = notify_req.get_changes(wait=True) # changes is a list of change dictionaries # each containing "name" (a string) and # "action" (an integer, e.g. libsmb.NOTIFY_ACTION_REMOVED) Signed-off-by: Stefan Metzmacher <me...@samba.org> Reviewed-by: Jeremy Allison <j...@samba.org> commit fde65c2f293ae3038c0052ea9d0dcdf3261d3fbc Author: Stefan Metzmacher <me...@samba.org> Date: Mon Dec 7 17:52:49 2020 +0100 s3:pylibsmb: add echo() support In tests it's sometimes to have a no-op in order to check the transport is still alive. Signed-off-by: Stefan Metzmacher <me...@samba.org> Reviewed-by: Jeremy Allison <j...@samba.org> commit 084c22403f0042f9cb6c5af6651cc9ef8b5f3bd0 Author: Stefan Metzmacher <me...@samba.org> Date: Wed Dec 9 10:13:54 2020 +0100 s3:pylibsmb: PyErr_NTSTATUS_IS_ERR_RAISE => PyErr_NTSTATUS_NOT_OK_RAISE We want to raise an exception for everything that's not NT_STATUS_OK. Signed-off-by: Stefan Metzmacher <me...@samba.org> Reviewed-by: Jeremy Allison <j...@samba.org> commit 560e4b1b32f56ed4cfffc60efd9199ed435409ba Author: Stefan Metzmacher <me...@samba.org> Date: Thu Aug 29 10:28:14 2019 +0000 libcli/smb: add smbXcli_conn_send_queue() This is useful in order to test async requests tevent_queue_wait_send/recv() can be used to block the queue between requests or wait for the queue to be flushed. Signed-off-by: Stefan Metzmacher <me...@samba.org> Reviewed-by: Jeremy Allison <j...@samba.org> commit 76121ae7cf4967329b9745137999fde00309c987 Author: Björn Baumbach <b...@sernet.de> Date: Thu Aug 29 12:13:50 2019 +0200 s3:libsmb: set correct min and max smb protocol when smb2 is enforced on connect BUG: https://bugzilla.samba.org/show_bug.cgi?id=14105 Pair-programmed-with: Stefan Metzmacher <me...@samba.org> Signed-off-by: Björn Baumbach <b...@sernet.de> Signed-off-by: Stefan Metzmacher <me...@samba.org> Reviewed-by: Jeremy Allison <j...@samba.org> commit f40da74e1452e73799f8643d93b8b9e572df1088 Author: Björn Baumbach <b...@sernet.de> Date: Wed Aug 28 14:11:02 2019 +0200 s3:libsmb: set min smb protocol when enforcing smb1 on connect Otherwise the connect fails if the configured client min protocol is higher than NT1. BUG: https://bugzilla.samba.org/show_bug.cgi?id=14105 Signed-off-by: Björn Baumbach <b...@sernet.de> Reviewed-by: Stefan Metzmacher <me...@samba.org> Reviewed-by: Jeremy Allison <j...@samba.org> commit 98119189cfe7b76322253d30170528eb0bf33eb7 Author: Björn Baumbach <b...@sernet.de> Date: Tue Aug 27 11:19:42 2019 +0200 blackbox/test_samba-tool_ntacl.sh: script requires two arguments Signed-off-by: Björn Baumbach <b...@sernet.de> Reviewed-by: Stefan Metzmacher <me...@samba.org> Reviewed-by: Jeremy Allison <j...@samba.org> commit 577d4f1a60c41df82da8711dabde9361780a9144 Author: Björn Baumbach <b...@sernet.de> Date: Wed Mar 18 10:31:04 2020 +0100 docs:smbdotconf: fix a typo in oldpasswordallowedperiod.xml Signed-off-by: Björn Baumbach <b...@sernet.de> Reviewed-by: Stefan Metzmacher <me...@samba.org> Reviewed-by: Jeremy Allison <j...@samba.org> ----------------------------------------------------------------------- Summary of changes: .../smbdotconf/misc/honorchangenotifyprivilege.xml | 20 + .../security/oldpasswordallowedperiod.xml | 2 +- libcli/smb/smbXcli_base.c | 5 + libcli/smb/smbXcli_base.h | 1 + python/samba/ntacls.py | 28 +- python/samba/samba3/libsmb_samba_internal.py | 83 +++- python/samba/tests/smb-notify.py | 430 +++++++++++++++++++++ python/samba/tests/usage.py | 1 + selftest/selftesthelpers.py | 16 +- selftest/target/Samba3.pm | 4 + source3/libsmb/cliconnect.c | 11 +- source3/libsmb/pylibsmb.c | 309 ++++++++++++++- source3/param/loadparm.c | 1 + source3/smbd/notify.c | 129 +++++++ source4/selftest/tests.py | 8 + testprogs/blackbox/test_samba-tool_ntacl.sh | 8 +- 16 files changed, 1008 insertions(+), 48 deletions(-) create mode 100644 docs-xml/smbdotconf/misc/honorchangenotifyprivilege.xml create mode 100755 python/samba/tests/smb-notify.py Changeset truncated at 500 lines: diff --git a/docs-xml/smbdotconf/misc/honorchangenotifyprivilege.xml b/docs-xml/smbdotconf/misc/honorchangenotifyprivilege.xml new file mode 100644 index 00000000000..a9c880ce467 --- /dev/null +++ b/docs-xml/smbdotconf/misc/honorchangenotifyprivilege.xml @@ -0,0 +1,20 @@ +<samba:parameter name="honor change notify privilege" + context="S" + type="boolean" + xmlns:samba="http://www.samba.org/samba/DTD/samba-doc"> +<description> + <para> + This option can be used to make use of the change notify privilege. + By default notify results are not checked against the file system + permissions. + </para> + <para> + If "honor change notify privilege" is enabled, a user will only + receive notify results, if he has change notify privilege or + sufficient file system permissions. If a user has the change notify + privilege, he will receive all requested notify results, even if the + user does not have the permissions on the file system. + </para> +</description> +<value type="default">no</value> +</samba:parameter> diff --git a/docs-xml/smbdotconf/security/oldpasswordallowedperiod.xml b/docs-xml/smbdotconf/security/oldpasswordallowedperiod.xml index 9cb607b11b3..78d6ff1d609 100644 --- a/docs-xml/smbdotconf/security/oldpasswordallowedperiod.xml +++ b/docs-xml/smbdotconf/security/oldpasswordallowedperiod.xml @@ -5,7 +5,7 @@ <description> <para>Number of minutes to permit an NTLM login after a password change or reset using the old password. This allows the user to re-cache the new password on multiple clients without disrupting a network reconnection in the meantime. </para> - <para>This parameter only applies when <smbconfoption name="server role"/> is set to Active Directory Domain Controller</para> + <para>This parameter only applies when <smbconfoption name="server role"/> is set to Active Directory Domain Controller.</para> </description> <value type="default">60</value> diff --git a/libcli/smb/smbXcli_base.c b/libcli/smb/smbXcli_base.c index df80be6bf16..0fc4aa4451a 100644 --- a/libcli/smb/smbXcli_base.c +++ b/libcli/smb/smbXcli_base.c @@ -2743,6 +2743,11 @@ NTSTATUS smb1cli_req_chain_submit(struct tevent_req **reqs, int num_reqs) return NT_STATUS_OK; } +struct tevent_queue *smbXcli_conn_send_queue(struct smbXcli_conn *conn) +{ + return conn->outgoing; +} + bool smbXcli_conn_has_async_calls(struct smbXcli_conn *conn) { return ((tevent_queue_length(conn->outgoing) != 0) diff --git a/libcli/smb/smbXcli_base.h b/libcli/smb/smbXcli_base.h index db5f5d58799..d9c3175bdf5 100644 --- a/libcli/smb/smbXcli_base.h +++ b/libcli/smb/smbXcli_base.h @@ -43,6 +43,7 @@ struct smbXcli_conn *smbXcli_conn_create(TALLOC_CTX *mem_ctx, bool smbXcli_conn_is_connected(struct smbXcli_conn *conn); void smbXcli_conn_disconnect(struct smbXcli_conn *conn, NTSTATUS status); +struct tevent_queue *smbXcli_conn_send_queue(struct smbXcli_conn *conn); bool smbXcli_conn_has_async_calls(struct smbXcli_conn *conn); bool smbXcli_conn_dfs_supported(struct smbXcli_conn *conn); diff --git a/python/samba/ntacls.py b/python/samba/ntacls.py index 0dcf958f727..4f189965d05 100644 --- a/python/samba/ntacls.py +++ b/python/samba/ntacls.py @@ -49,12 +49,6 @@ SECURITY_SECINFO_FLAGS = security.SECINFO_OWNER | \ security.SECINFO_DACL | \ security.SECINFO_SACL - -# SEC_FLAG_SYSTEM_SECURITY is required otherwise get Access Denied -SECURITY_SEC_FLAGS = security.SEC_FLAG_SYSTEM_SECURITY | \ - security.SEC_STD_READ_CONTROL - - class XattrBackendError(Exception): """A generic xattr backend error.""" @@ -335,14 +329,30 @@ class SMBHelper: self.smb_conn = smb_conn self.dom_sid = dom_sid - def get_acl(self, smb_path, as_sddl=False): + def get_acl(self, smb_path, as_sddl=False, + sinfo=None, access_mask=None): assert '/' not in smb_path - ntacl_sd = self.smb_conn.get_acl( - smb_path, SECURITY_SECINFO_FLAGS, SECURITY_SEC_FLAGS) + ntacl_sd = self.smb_conn.get_acl(smb_path, + sinfo=sinfo, + access_mask=access_mask) return ntacl_sd.as_sddl(self.dom_sid) if as_sddl else ntacl_sd + def set_acl(self, smb_path, ntacl_sd, + sinfo=None, access_mask=None): + assert '/' not in smb_path + + assert(isinstance(ntacl_sd, str) or isinstance(ntacl_sd, security.descriptor)) + if isinstance(ntacl_sd, str): + tmp_desc = security.descriptor.from_sddl(ntacl_sd, self.domain_sid) + elif isinstance(ntacl_sd, security.descriptor): + tmp_desc = ntacl_sd + + self.smb_conn.set_acl(smb_path, tmp_desc, + sinfo=sinfo, + access_mask=access_mask) + def list(self, smb_path=''): """ List file and dir base names in smb_path without recursive. diff --git a/python/samba/samba3/libsmb_samba_internal.py b/python/samba/samba3/libsmb_samba_internal.py index 84729b2041d..ef0b30d774b 100644 --- a/python/samba/samba3/libsmb_samba_internal.py +++ b/python/samba/samba3/libsmb_samba_internal.py @@ -29,17 +29,77 @@ class Conn(LibsmbCConn): security.SECINFO_OWNER | \ security.SECINFO_GROUP | \ security.SECINFO_DACL | \ - security.SECINFO_PROTECTED_DACL | \ - security.SECINFO_UNPROTECTED_DACL | \ - security.SECINFO_SACL | \ - security.SECINFO_PROTECTED_SACL | \ - security.SECINFO_UNPROTECTED_SACL + security.SECINFO_SACL + + def required_access_for_get_secinfo(self, secinfo): + access = 0 + + # + # This is based on MS-FSA + # 2.1.5.13 Server Requests a Query of Security Information + # + # Note that MS-SMB2 3.3.5.20.3 Handling SMB2_0_INFO_SECURITY + # doesn't specify any extra checks + # + + if secinfo & security.SECINFO_OWNER: + access |= security.SEC_STD_READ_CONTROL + if secinfo & security.SECINFO_GROUP: + access |= security.SEC_STD_READ_CONTROL + if secinfo & security.SECINFO_DACL: + access |= security.SEC_STD_READ_CONTROL + if secinfo & security.SECINFO_SACL: + access |= security.SEC_FLAG_SYSTEM_SECURITY + + if secinfo & security.SECINFO_LABEL: + access |= security.SEC_STD_READ_CONTROL + + return access + + def required_access_for_set_secinfo(self, secinfo): + access = 0 + + # + # This is based on MS-FSA + # 2.1.5.16 Server Requests Setting of Security Information + # and additional constraints from + # MS-SMB2 3.3.5.21.3 Handling SMB2_0_INFO_SECURITY + # + + if secinfo & security.SECINFO_OWNER: + access |= security.SEC_STD_WRITE_OWNER + if secinfo & security.SECINFO_GROUP: + access |= security.SEC_STD_WRITE_OWNER + if secinfo & security.SECINFO_DACL: + access |= security.SEC_STD_WRITE_DAC + if secinfo & security.SECINFO_SACL: + access |= security.SEC_FLAG_SYSTEM_SECURITY + + if secinfo & security.SECINFO_LABEL: + access |= security.SEC_STD_WRITE_OWNER + + if secinfo & security.SECINFO_ATTRIBUTE: + access |= security.SEC_STD_WRITE_DAC + + if secinfo & security.SECINFO_SCOPE: + access |= security.SEC_FLAG_SYSTEM_SECURITY + + if secinfo & security.SECINFO_BACKUP: + access |= security.SEC_STD_WRITE_OWNER + access |= security.SEC_STD_WRITE_DAC + access |= security.SEC_FLAG_SYSTEM_SECURITY + + return access def get_acl(self, filename, - sinfo = SECINFO_DEFAULT_FLAGS, - access_mask = security.SEC_FLAG_MAXIMUM_ALLOWED): + sinfo=None, + access_mask=None): """Get security descriptor for file.""" + if sinfo is None: + sinfo = self.SECINFO_DEFAULT_FLAGS + if access_mask is None: + access_mask = self.required_access_for_get_secinfo(sinfo) fnum = self.create( Name=filename, DesiredAccess=access_mask, @@ -53,11 +113,16 @@ class Conn(LibsmbCConn): def set_acl(self, filename, sd, - sinfo = SECINFO_DEFAULT_FLAGS): + sinfo=None, + access_mask=None): """Set security descriptor for file.""" + if sinfo is None: + sinfo = self.SECINFO_DEFAULT_FLAGS + if access_mask is None: + access_mask = self.required_access_for_set_secinfo(sinfo) fnum = self.create( Name=filename, - DesiredAccess=security.SEC_FLAG_MAXIMUM_ALLOWED, + DesiredAccess=access_mask, ShareAccess=(FILE_SHARE_READ|FILE_SHARE_WRITE)) try: self.set_sd(fnum, sd, sinfo) diff --git a/python/samba/tests/smb-notify.py b/python/samba/tests/smb-notify.py new file mode 100755 index 00000000000..2f42263be25 --- /dev/null +++ b/python/samba/tests/smb-notify.py @@ -0,0 +1,430 @@ +#!/usr/bin/env python3 +# Unix SMB/CIFS implementation. Tests for smb notify +# Copyright (C) Björn Baumbach <b...@samba.org> 2020 +# +# 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 sys +import os + +sys.path.insert(0, "bin/python") +os.environ["PYTHONUNBUFFERED"] = "1" + +import samba +import random +from samba.tests import TestCase +from samba import NTSTATUSError +from samba import credentials +from samba.ntstatus import NT_STATUS_NOTIFY_CLEANUP +from samba.samba3 import libsmb_samba_internal as libsmb +from samba.samba3 import param as s3param +from samba.dcerpc import security + +from samba import ntacls + +test_dir = os.path.join('notify_test_%d' % random.randint(0, 0xFFFF)) + +class SMBNotifyTests(TestCase): + def setUp(self): + super(SMBNotifyTests, self).setUp() + self.server = samba.tests.env_get_var_value("SERVER") + + # create an SMB connection to the server + self.lp = s3param.get_context() + self.lp.load(samba.tests.env_get_var_value("SMB_CONF_PATH")) + + self.share = samba.tests.env_get_var_value("NOTIFY_SHARE") + + creds = credentials.Credentials() + creds.guess(self.lp) + creds.set_username(samba.tests.env_get_var_value("USERNAME")) + creds.set_password(samba.tests.env_get_var_value("PASSWORD")) + + strict_checking = samba.tests.env_get_var_value('STRICT_CHECKING', allow_missing=True) + if strict_checking is None: + strict_checking = '1' + self.strict_checking = bool(int(strict_checking)) + + self.smb_conn = libsmb.Conn(self.server, self.share, self.lp, creds) + self.smb_conn_unpriv = None + + try: + self.smb_conn.deltree(test_dir) + except: + pass + self.smb_conn.mkdir(test_dir) + + def connect_unpriv(self): + creds_unpriv = credentials.Credentials() + creds_unpriv.guess(self.lp) + creds_unpriv.set_username(samba.tests.env_get_var_value("USERNAME_UNPRIV")) + creds_unpriv.set_password(samba.tests.env_get_var_value("PASSWORD_UNPRIV")) + + self.smb_conn_unpriv = libsmb.Conn(self.server, self.share, self.lp, creds_unpriv) + + def tearDown(self): + super(SMBNotifyTests, self).tearDown() + try: + self.smb_conn.deltree(test_dir) + except: + pass + + def make_path(self, dirpath, filename): + return os.path.join(dirpath, filename).replace('/', '\\') + + def test_notify(self): + # setup notification request on the share root + root_fnum = self.smb_conn.create(Name="", ShareAccess=1) + root_notify = self.smb_conn.notify(fnum=root_fnum, + buffer_size=0xffff, + completion_filter=libsmb.FILE_NOTIFY_CHANGE_ALL, + recursive=True) + # setup notification request on the test_dir + test_dir_fnum = self.smb_conn.create(Name=test_dir, ShareAccess=1) + test_dir_notify = self.smb_conn.notify(fnum=test_dir_fnum, + buffer_size=0xffff, + completion_filter=libsmb.FILE_NOTIFY_CHANGE_ALL, + recursive=True) + + # make sure we didn't receive any changes yet. + self.smb_conn.echo() + changes = root_notify.get_changes(wait=False) + self.assertIsNone(changes) + changes = test_dir_notify.get_changes(wait=False) + self.assertIsNone(changes) + + # create a test directory + dir_name = "dir" + dir_path = self.make_path(test_dir, dir_name) + self.smb_conn.mkdir(dir_path) + + # check for 'added' notifications + changes = root_notify.get_changes(wait=True) + self.assertIsNotNone(changes) + self.assertEqual(changes[0]['name'], dir_path) + self.assertEqual(changes[0]['action'], libsmb.NOTIFY_ACTION_ADDED) + self.assertEqual(len(changes), 1) + changes = test_dir_notify.get_changes(wait=True) + self.assertIsNotNone(changes) + self.assertEqual(changes[0]['name'], dir_name) + self.assertEqual(changes[0]['action'], libsmb.NOTIFY_ACTION_ADDED) + self.assertEqual(len(changes), 1) + + # readd notification requests + root_notify = self.smb_conn.notify(fnum=root_fnum, + buffer_size=0xffff, + completion_filter=libsmb.FILE_NOTIFY_CHANGE_ALL, + recursive=True) + test_dir_notify = self.smb_conn.notify(fnum=test_dir_fnum, + buffer_size=0xffff, + completion_filter=libsmb.FILE_NOTIFY_CHANGE_ALL, + recursive=True) + + # make sure we didn't receive any changes yet. + self.smb_conn.echo() + changes = root_notify.get_changes(wait=False) + self.assertIsNone(changes) + changes = test_dir_notify.get_changes(wait=False) + self.assertIsNone(changes) + + # create subdir and trigger notifications + sub_name = "subdir" + sub_path_rel = self.make_path(dir_name, sub_name) + sub_path_full = self.make_path(dir_path, sub_name) + self.smb_conn.mkdir(sub_path_full) + + # check for 'added' notifications + changes = root_notify.get_changes(wait=True) + self.assertIsNotNone(changes) + self.assertEqual(changes[0]['name'], sub_path_full) + self.assertEqual(changes[0]['action'], libsmb.NOTIFY_ACTION_ADDED) + self.assertEqual(len(changes), 1) + changes = test_dir_notify.get_changes(wait=True) + self.assertIsNotNone(changes) + self.assertEqual(changes[0]['name'], sub_path_rel) + self.assertEqual(changes[0]['action'], libsmb.NOTIFY_ACTION_ADDED) + self.assertEqual(len(changes), 1) + + # readd notification requests + root_notify = self.smb_conn.notify(fnum=root_fnum, + buffer_size=0xffff, + completion_filter=libsmb.FILE_NOTIFY_CHANGE_ALL, + recursive=True) + test_dir_notify = self.smb_conn.notify(fnum=test_dir_fnum, + buffer_size=0xffff, + completion_filter=libsmb.FILE_NOTIFY_CHANGE_ALL, + recursive=True) + + # make sure we didn't receive any changes yet. + self.smb_conn.echo() + changes = root_notify.get_changes(wait=False) + self.assertIsNone(changes) + changes = test_dir_notify.get_changes(wait=False) + self.assertIsNone(changes) + + # remove test dir and trigger notifications + self.smb_conn.rmdir(sub_path_full) + + # check for 'removed' notifications + changes = root_notify.get_changes(wait=True) + self.assertIsNotNone(changes) + self.assertEqual(changes[0]['name'], sub_path_full) + self.assertEqual(changes[0]['action'], libsmb.NOTIFY_ACTION_REMOVED) + self.assertEqual(len(changes), 1) + changes = test_dir_notify.get_changes(wait=True) + self.assertIsNotNone(changes) + self.assertEqual(changes[0]['name'], sub_path_rel) + self.assertEqual(changes[0]['action'], libsmb.NOTIFY_ACTION_REMOVED) + self.assertEqual(len(changes), 1) + + # readd notification requests + root_notify = self.smb_conn.notify(fnum=root_fnum, + buffer_size=0xffff, + completion_filter=libsmb.FILE_NOTIFY_CHANGE_ALL, + recursive=True) + test_dir_notify = self.smb_conn.notify(fnum=test_dir_fnum, + buffer_size=0xffff, + completion_filter=libsmb.FILE_NOTIFY_CHANGE_ALL, + recursive=True) + + # make sure we didn't receive any changes yet. + self.smb_conn.echo() + changes = root_notify.get_changes(wait=False) + self.assertIsNone(changes) + changes = test_dir_notify.get_changes(wait=False) + self.assertIsNone(changes) + + # closing the handle on test_dir will trigger + # a NOTIFY_CLEANUP on test_dir_notify and + # it also seems to update something on test_dir it self + # and post a MODIFIED on root_notify + # + # TODO: find out why windows generates ACTION_MODIFIED + # and why Samba doesn't + self.smb_conn.close(test_dir_fnum) + try: + changes = test_dir_notify.get_changes(wait=True) + self.fail() + except samba.NTSTATUSError as err: + self.assertEqual(err.args[0], NT_STATUS_NOTIFY_CLEANUP) + self.smb_conn.echo() + changes = root_notify.get_changes(wait=False) + if self.strict_checking: + self.assertIsNotNone(changes) + if changes is not None: + self.assertEqual(changes[0]['name'], test_dir) + self.assertEqual(changes[0]['action'], libsmb.NOTIFY_ACTION_MODIFIED) + self.assertEqual(len(changes), 1) + + # readd notification request + root_notify = self.smb_conn.notify(fnum=root_fnum, + buffer_size=0xffff, + completion_filter=libsmb.FILE_NOTIFY_CHANGE_ALL, + recursive=True) + + # make sure we didn't receive any changes yet. + self.smb_conn.echo() + changes = root_notify.get_changes(wait=False) + self.assertIsNone(changes) + + # remove test_dir + self.smb_conn.rmdir(dir_path) + + # check for 'removed' notifications + changes = root_notify.get_changes(wait=True) + self.assertIsNotNone(changes) + self.assertEqual(changes[0]['name'], dir_path) + self.assertEqual(changes[0]['action'], libsmb.NOTIFY_ACTION_REMOVED) + self.assertEqual(len(changes), 1) + + # readd notification request + root_notify = self.smb_conn.notify(fnum=root_fnum, + buffer_size=0xffff, + completion_filter=libsmb.FILE_NOTIFY_CHANGE_ALL, + recursive=True) + # closing the handle on test_dir will trigger + # a NOTIFY_CLEANUP on root_notify + self.smb_conn.close(root_fnum) + try: + changes = root_notify.get_changes(wait=True) + self.fail() + except samba.NTSTATUSError as err: + self.assertEqual(err.args[0], NT_STATUS_NOTIFY_CLEANUP) + + + def _test_notify_privileged_path(self, + monitor_path=None, + rel_prefix=None): -- Samba Shared Repository