def test_dialect_3_1_1(self, smb_real): connection = Connection(uuid.uuid4(), smb_real[2], smb_real[3]) connection.connect(Dialects.SMB_3_1_1) session = Session(connection, smb_real[0], smb_real[1]) try: session.connect() assert len(session.application_key) == 16 assert session.application_key != session.session_key assert len(session.decryption_key) == 16 assert session.decryption_key != session.session_key assert session.encrypt_data assert len(session.encryption_key) == 16 assert session.encryption_key != session.session_key assert len(session.connection.preauth_integrity_hash_value) == 2 assert len(session.preauth_integrity_hash_value) == 3 assert session.require_encryption assert session.session_id is not None assert len(session.session_key) == 16 assert len(session.signing_key) == 16 assert session.signing_key != session.session_key assert not session.signing_required finally: connection.disconnect(True) # test that disconnect can be run multiple times session.disconnect()
def test_change_notify_underlying_close(self, smb_real): connection = Connection(uuid.uuid4(), smb_real[2], smb_real[3]) connection.connect() session = Session(connection, smb_real[0], smb_real[1]) tree = TreeConnect(session, smb_real[4]) open = Open(tree, "directory-watch") try: session.connect() tree.connect() open.create( ImpersonationLevel.Impersonation, DirectoryAccessMask.MAXIMUM_ALLOWED, FileAttributes.FILE_ATTRIBUTE_DIRECTORY, ShareAccess.FILE_SHARE_READ | ShareAccess.FILE_SHARE_WRITE | ShareAccess.FILE_SHARE_DELETE, CreateDisposition.FILE_OPEN_IF, CreateOptions.FILE_DIRECTORY_FILE) watcher = FileSystemWatcher(open) watcher.start(CompletionFilter.FILE_NOTIFY_CHANGE_FILE_NAME) assert watcher.result is None assert watcher.response_event.is_set() is False open.close() expected = "Received unexpected status from the server: (267) STATUS_NOTIFY_CLEANUP" with pytest.raises(SMBResponseException, match=re.escape(expected)): watcher.wait() finally: connection.disconnect(True)
def test_change_notify_on_a_file(self, smb_real): connection = Connection(uuid.uuid4(), smb_real[2], smb_real[3]) connection.connect() session = Session(connection, smb_real[0], smb_real[1]) tree = TreeConnect(session, smb_real[4]) open = Open(tree, "file-watch.txt") try: session.connect() tree.connect() open.create( ImpersonationLevel.Impersonation, FilePipePrinterAccessMask.MAXIMUM_ALLOWED, FileAttributes.FILE_ATTRIBUTE_NORMAL, ShareAccess.FILE_SHARE_READ | ShareAccess.FILE_SHARE_WRITE | ShareAccess.FILE_SHARE_DELETE, CreateDisposition.FILE_OPEN_IF, CreateOptions.FILE_NON_DIRECTORY_FILE) watcher = FileSystemWatcher(open) watcher.start(CompletionFilter.FILE_NOTIFY_CHANGE_FILE_NAME) expected = "Received unexpected status from the server: (3221225485) STATUS_INVALID_PARAMETER" with pytest.raises(SMBResponseException, match=re.escape(expected)): watcher.wait() finally: connection.disconnect(True)
def test_change_notify_on_a_file(self, smb_real): connection = Connection(uuid.uuid4(), smb_real[2], smb_real[3]) connection.connect() session = Session(connection, smb_real[0], smb_real[1]) tree = TreeConnect(session, smb_real[4]) open = Open(tree, "file-watch.txt") try: session.connect() tree.connect() open.create( ImpersonationLevel.Impersonation, FilePipePrinterAccessMask.MAXIMUM_ALLOWED, FileAttributes.FILE_ATTRIBUTE_NORMAL, ShareAccess.FILE_SHARE_READ | ShareAccess.FILE_SHARE_WRITE | ShareAccess.FILE_SHARE_DELETE, CreateDisposition.FILE_OPEN_IF, CreateOptions.FILE_NON_DIRECTORY_FILE) watcher = FileSystemWatcher(open) watcher.start(CompletionFilter.FILE_NOTIFY_CHANGE_FILE_NAME) with pytest.raises(InvalidParameter): watcher.wait() finally: connection.disconnect(True)
def check(self, ip_addr, port=445): """Checks whether a SMB server allows login without credentials""" try: # connect and attempt authentication connection = Connection(uuid.uuid4(), ip_addr, port, require_signing=False) connection.connect(Dialects.SMB_2_0_2, timeout=TIMEOUT) try: session = Session( connection, "", "", require_encryption=False ) # dont require encryption or signing to support o2 HomeBox try: session.connect() except SMBAuthenticationError: return False return ("", "") finally: connection.disconnect() except Exception as e: print("SMB connection failed") print(e) return False
def test_setup_session_with_ntlm_only(self, smb_real): connection = Connection(uuid.uuid4(), smb_real[2], smb_real[3]) connection.connect() session = Session(connection, smb_real[0], smb_real[1], False, auth_protocol='ntlm') try: session.connect() assert len(session.application_key) == 16 assert session.application_key != session.session_key assert len(session.decryption_key) == 16 assert session.decryption_key != session.session_key assert not session.encrypt_data assert len(session.encryption_key) == 16 assert session.encryption_key != session.session_key assert len(session.connection.preauth_integrity_hash_value) == 2 assert len(session.preauth_integrity_hash_value) == 3 assert not session.require_encryption assert session.session_id is not None assert len(session.session_key) == 16 assert len(session.signing_key) == 16 assert session.signing_key != session.session_key assert session.signing_required finally: connection.disconnect()
def register_session(server, username=None, password=None, port=445, encrypt=None, connection_timeout=60, connection_cache=None, auth_protocol='negotiate'): """ Creates an active connection and session to the server specified. This can be manually called to register the credentials of a specific server instead of defining it on the first function connecting to the server. The opened connection is registered in a pool and re-used if a connection is made to the same server with the same credentials. :param server: The server name to register. :param username: Optional username to connect with. Required if no session has been registered for the server and Kerberos auth is not being used. :param password: Optional password to connect with. :param port: The port to connect with. :param encrypt: Whether to force encryption or not, once this has been set to True the session cannot be changed back to False. :param connection_timeout: Override the timeout used for the initial connection. :param connection_cache: Connection cache to be used with :param auth_protocol: The protocol to use for authentication. Possible values are 'negotiate', 'ntlm' or 'kerberos'. Defaults to 'negotiate'. :return: The Session that was registered or already existed in the pool. """ connection_key = "%s:%s" % (server.lower(), port) if connection_cache is None: connection_cache = _SMB_CONNECTIONS connection = connection_cache.get(connection_key, None) # Make sure we ignore any connections that may have had a closed connection if not connection or not connection.transport.connected: connection = Connection(ClientConfig().client_guid, server, port) connection.connect(timeout=connection_timeout) connection_cache[connection_key] = connection # Find the first session in the connection session list that match the username specified, if not username then # just use the first session found or fall back to creating a new one with implicit auth/kerberos. session = next((s for s in connection.session_table.values() if username is None or s.username == username), None) if not session: session = Session(connection, username=username, password=password, require_encryption=(encrypt is True), auth_protocol=auth_protocol) session.connect() elif encrypt is not None: # We cannot go from encryption to no encryption on an existing session but we can do the opposite. if session.encrypt_data and not encrypt: raise ValueError( "Cannot disable encryption on an already negotiated session.") elif not session.encrypt_data and encrypt: session.encrypt = True return session
def fingerprint(self, ip_addr, port=445): try: # connect and attempt authentication connection = Connection(uuid.uuid4(), ip_addr, port, require_signing=False) connection.connect(Dialects.SMB_2_0_2, timeout=TIMEOUT) try: session = Session( connection, "", "", require_encryption=False ) # dont require encryption or signing to support o2 HomeBox try: session.connect() except SMBAuthenticationError: # ignore authentication error # the result doesn't matter as long as there was an attempt pass for packet in session.preauth_integrity_hash_value: # find a STATUS_MORE_PROCESSING_REQUIRED response if not isinstance(packet, SMB2HeaderResponse) or not packet[ "status"].value == NtStatus.STATUS_MORE_PROCESSING_REQUIRED: continue sess_resp_bytes = packet["data"].value # parse session setup response sess_resp = SMB2SessionSetupResponse() sess_resp.unpack(sess_resp_bytes) chlg_mesg_bytes = sess_resp["buffer"].value # skip if this is not a NTLMSSP challenge # https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-nlmp/801a4681-8809-4be9-ab0d-61dcfe762786 if not chlg_mesg_bytes.startswith(NTLMSSP_MAGIC_NUMBER): continue # parse NTLMSSP challenge # (labelled "security blob" in wireshark) chlg_mesg = ChallengeMessage(chlg_mesg_bytes) # parse the version field # https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-nlmp/b1a6ceb2-f8ad-462b-b5af-f18527c48175 return struct.unpack( NTLM_VERSION_STRUCT, chlg_mesg.version.to_bytes(8, byteorder='little')) return (None, None, None, None) finally: connection.disconnect() except Exception as e: print("SMB connection failed") print(e) return (None, None, None, None)
def test_connection_echo(self, smb_real): connection = Connection(uuid.uuid4(), smb_real[2], smb_real[3]) connection.connect() session = Session(connection, smb_real[0], smb_real[1]) session.connect() try: actual = connection.echo(sid=session.session_id, credit_request=2) assert actual == 2 finally: connection.disconnect(True)
def test_invalid_user(self, smb_real): connection = Connection(uuid.uuid4(), smb_real[2], smb_real[3]) connection.connect() try: session = Session(connection, "fakeuser", "fakepass") with pytest.raises(SMBAuthenticationError) as exc: session.connect() assert "Failed to authenticate with server: " in str(exc.value) finally: connection.disconnect(True)
def test_dialect_2_encrypted_share(self, smb_real): connection = Connection(uuid.uuid4(), smb_real[2], smb_real[3]) connection.connect(Dialects.SMB_2_1_0) session = Session(connection, smb_real[0], smb_real[1], False) tree = TreeConnect(session, smb_real[5]) try: session.connect() with pytest.raises(AccessDenied) as exc: tree.connect() finally: connection.disconnect(True)
def test_require_encryption_not_supported(self, smb_real): connection = Connection(uuid.uuid4(), smb_real[2], smb_real[3]) connection.connect(Dialects.SMB_2_1_0) try: session = Session(connection, smb_real[0], smb_real[1]) with pytest.raises(SMBException) as exc: session.connect() assert str(exc.value) == "SMB encryption is required but the " \ "connection does not support it" finally: connection.disconnect(True)
def test_send_invalid_tree_id(self, smb_real): connection = Connection(uuid.uuid4(), smb_real[2], smb_real[3]) session = Session(connection, smb_real[0], smb_real[1]) connection.connect() try: session.connect() msg = SMB2IOCTLRequest() msg['file_id'] = b"\xff" * 16 with pytest.raises(SMBException) as exc: connection.send(msg, session.session_id, 10) assert str(exc.value) == "Cannot find Tree with the ID 10 in " \ "the session tree table" finally: connection.disconnect()
def test_secure_negotiation_verification_failed(self, smb_real): connection = Connection(uuid.uuid4(), smb_real[2], smb_real[3]) connection.connect(Dialects.SMB_3_0_2) session = Session(connection, smb_real[0], smb_real[1]) connection.dialect = Dialects.SMB_3_0_0 tree = TreeConnect(session, smb_real[4]) try: session.connect() with pytest.raises(SMBException) as exc: tree.connect() assert "Secure negotiate failed to verify server dialect, " \ "Actual: 770, Expected: 768" in str(exc.value) finally: connection.disconnect(True)
def test_dialect_2_encrypted_share(self, smb_real): connection = Connection(uuid.uuid4(), smb_real[2], smb_real[3]) connection.connect(Dialects.SMB_2_1_0) session = Session(connection, smb_real[0], smb_real[1], False) tree = TreeConnect(session, smb_real[5]) try: session.connect() with pytest.raises(SMBResponseException) as exc: tree.connect() assert str(exc.value) == "Received unexpected status from the " \ "server: (3221225506) " \ "STATUS_ACCESS_DENIED: 0xc0000022" finally: connection.disconnect(True)
def test_dialect_3_encrypted_share(self, smb_real): connection = Connection(uuid.uuid4(), smb_real[2], smb_real[3]) connection.connect(Dialects.SMB_3_1_1) session = Session(connection, smb_real[0], smb_real[1]) tree = TreeConnect(session, smb_real[5]) try: session.connect() tree.connect() assert tree.encrypt_data assert not tree.is_ca_share assert not tree.is_dfs_share assert not tree.is_scaleout_share assert isinstance(tree.tree_connect_id, int) finally: connection.disconnect(True)
def test_connect_fail(self, smb_real, monkeypatch, mocker): connection = Connection(uuid.uuid4(), smb_real[2], smb_real[3]) connection.connect() try: monkeypatch.setattr( pyspnego, 'client', mocker.MagicMock( side_effect=pyspnego.exceptions.NoCredentialError())) session = Session(connection, smb_real[0], smb_real[1]) with pytest.raises(SMBAuthenticationError, match="Failed to authenticate with server"): session.connect() finally: connection.disconnect(True)
def test_decrypt_invalid_flag(self, smb_real): connection = Connection(uuid.uuid4(), smb_real[2], smb_real[3], True) session = Session(connection, smb_real[0], smb_real[1]) connection.connect() try: session.connect() # just get some random message header = connection.preauth_integrity_hash_value[-1] enc_header = connection._encrypt(header.pack(), session) assert isinstance(enc_header, SMB2TransformHeader) enc_header['flags'] = 5 with pytest.raises(SMBException) as exc: connection._decrypt(enc_header) assert str(exc.value) == "Expecting flag of 0x0001 but got 5 in " \ "the SMB Transform Header Response" finally: connection.disconnect(True)
def test_decrypt_invalid_session_id(self, smb_real): connection = Connection(uuid.uuid4(), smb_real[2], smb_real[3], True) session = Session(connection, smb_real[0], smb_real[1]) connection.connect() try: session.connect() # just get some random message header = connection.preauth_integrity_hash_value[-1] enc_header = connection._encrypt(header.pack(), session) assert isinstance(enc_header, SMB2TransformHeader) enc_header['session_id'] = 100 with pytest.raises(SMBException) as exc: connection._decrypt(enc_header) assert str(exc.value) == "Failed to find valid session 100 for " \ "message decryption" finally: connection.disconnect(True)
def test_secure_ignore_negotiation_verification_failed(self, smb_real): connection = Connection(uuid.uuid4(), smb_real[2], smb_real[3]) connection.connect(Dialects.SMB_3_0_2) session = Session(connection, smb_real[0], smb_real[1]) connection.dialect = Dialects.SMB_3_0_0 tree = TreeConnect(session, smb_real[4]) try: session.connect() tree.connect(False) assert not tree.encrypt_data assert not tree.is_ca_share assert not tree.is_dfs_share assert not tree.is_scaleout_share assert isinstance(tree.tree_connect_id, int) finally: connection.disconnect(True) tree.disconnect() # test that disconnect can be run mutliple times
def bule_screen(IP, username=None, password=None, port=445, encode=None, connectionTimeout=10): _SMB_CONNECTIONS = {} connection_key = "%s:%s" %(IP, port) connection = _SMB_CONNECTIONS.get(connection_key, None) if not connection: connection = Connection(uuid.uuid4(), IP, port) connection.connect(timeout=connectionTimeout) _SMB_CONNECTIONS[connection_key] = connection session = next((s for s in connection.session_table.values() if username is None or s.username == username), None) if not session: session = Session(connection, username=username, password=password, require_encryption=(encode is True)) session.connect() elif encode is not None: if session.encrypt_data and not encode: print("[\033[33m-\033[0m]Cannot disable encryption on an already negotiated session.") elif not session.encrypt_data and encode: session.encrypt = True return session
def test_verify_mistmatch(self, smb_real): connection = Connection(uuid.uuid4(), smb_real[2], smb_real[3], True) session = Session(connection, smb_real[0], smb_real[1]) connection.connect() try: session.connect() header = connection.preauth_integrity_hash_value[-2] # just set some random values for verifiation failure header['flags'].set_flag(Smb2Flags.SMB2_FLAGS_SIGNED) header['signature'] = b"\xff" * 16 with pytest.raises(SMBException) as exc: connection._verify(header, header['session_id'].get_value(), verify_session=True) assert "Server message signature could not be verified:" in \ str(exc.value) finally: connection.disconnect(True)
def test_change_notify_cancel(self, smb_real): connection = Connection(uuid.uuid4(), smb_real[2], smb_real[3]) connection.connect() session = Session(connection, smb_real[0], smb_real[1], require_encryption=False) tree = TreeConnect(session, smb_real[4]) open = Open(tree, "directory-watch") try: session.connect() tree.connect() open.create( ImpersonationLevel.Impersonation, DirectoryAccessMask.MAXIMUM_ALLOWED, FileAttributes.FILE_ATTRIBUTE_DIRECTORY, ShareAccess.FILE_SHARE_READ | ShareAccess.FILE_SHARE_WRITE | ShareAccess.FILE_SHARE_DELETE, CreateDisposition.FILE_OPEN_IF, CreateOptions.FILE_DIRECTORY_FILE) watcher = FileSystemWatcher(open) watcher.start(CompletionFilter.FILE_NOTIFY_CHANGE_FILE_NAME) assert watcher.result is None assert watcher.response_event.is_set() is False # Makes sure that we cancel after the async response has been returned from the server. while watcher._request.async_id is None: pass assert watcher.result is None watcher.cancel() watcher.wait() assert watcher.cancelled is True assert watcher.result is None # Make sure it doesn't cause any weird errors when calling it again watcher.cancel() finally: connection.disconnect(True)
def test_dialect_2_1_0(self, smb_real): connection = Connection(uuid.uuid4(), smb_real[2], smb_real[3]) connection.connect(Dialects.SMB_2_1_0) session = Session(connection, smb_real[0], smb_real[1], require_encryption=False) try: session.connect() assert len(session.application_key) == 16 assert session.decryption_key is None assert not session.encrypt_data assert session.encryption_key is None assert len(session.preauth_integrity_hash_value) == 5 assert not session.require_encryption assert session.session_id is not None assert session.session_key == session.application_key assert session.signing_key == session.signing_key assert session.signing_required finally: connection.disconnect(True)
def session(self): server = os.environ.get('PYPSEXEC_SERVER', None) username = os.environ.get('PYPSEXEC_USERNAME', None) password = os.environ.get('PYPSEXEC_PASSWORD', None) if server: connection = Connection(uuid.uuid4(), server, 445) session = Session(connection, username, password) tree = TreeConnect(session, r"\\%s\ADMIN$" % server) paexec_file = Open(tree, "PAExec.exe") connection.connect() try: session.connect() tree.connect() paexec_file.create(ImpersonationLevel.Impersonation, FilePipePrinterAccessMask.FILE_WRITE_DATA, FileAttributes.FILE_ATTRIBUTE_NORMAL, ShareAccess.FILE_SHARE_READ, CreateDisposition.FILE_OVERWRITE_IF, CreateOptions.FILE_NON_DIRECTORY_FILE) paexec_file.write(pkgutil.get_data('pypsexec', 'paexec.exe'), 0) paexec_file.close(get_attributes=False) yield session finally: paexec_file.create(ImpersonationLevel.Impersonation, FilePipePrinterAccessMask.DELETE, FileAttributes.FILE_ATTRIBUTE_NORMAL, ShareAccess.FILE_SHARE_DELETE, CreateDisposition.FILE_OVERWRITE_IF, CreateOptions.FILE_DELETE_ON_CLOSE) paexec_file.close(get_attributes=False) connection.disconnect(True) else: pytest.skip("PYPSEXEC_SERVER, PYPSEXEC_USERNAME, PYPSEXEC_PASSWORD" " environment variables were not set. Integration " "tests will be skipped")
def register_session(server, username=None, password=None, port=445, encrypt=None, connection_timeout=60): """ Creates an active connection and session to the server specified. This can be manually called to register the credentials of a specific server instead of defining it on the first function connecting to the server. The opened connection is registered in a pool and re-used if a connection is made to the same server with the same credentials. :param server: The server name to register. :param username: Optional username to connect with. Required if no session has been registered for the server and Kerberos auth is not being used. :param password: Optional password to connect with. :param port: The port to connect with. :param encrypt: Whether to force encryption or not, once this has been set to True the session cannot be changed back to False. :param connection_timeout: Override the timeout used for the initial connection. :return: The Session that was registered or already existed in the pool. """ connection_key = "%s:%s" % (server, port) global _SMB_CONNECTIONS connection = _SMB_CONNECTIONS.get(connection_key, None) if not connection: connection = Connection(_CLIENT_GUID, server, port) connection.connect(timeout=connection_timeout) _SMB_CONNECTIONS[connection_key] = connection # Find the first session in the connection session list that match the username specified, if not username then # just use the first session found or fall back to creating a new one with implicit auth/kerberos. session = next((s for s in connection.session_table.values() if username is None or s.username == username), None) if not session: session = Session(connection, username=username, password=password, require_encryption=(encrypt is True)) session.connect() elif encrypt is not None: # We cannot go from encryption to no encryption on an existing session but we can do the opposite. if session.encrypt_data and not encrypt: raise ValueError("Cannot disable encryption on an already negotiated session.") elif not session.encrypt_data and encrypt: session.encrypt = True return session
def test_verify_mistmatch(self, smb_real): connection = Connection(uuid.uuid4(), smb_real[2], smb_real[3], True) session = Session(connection, smb_real[0], smb_real[1]) connection.connect() try: session.connect() b_header = connection.preauth_integrity_hash_value[-2] header = SMB2HeaderResponse() header.unpack(b_header) # just set some random values for verification failure header['flags'].set_flag(Smb2Flags.SMB2_FLAGS_SIGNED) header['signature'] = b"\xff" * 16 with pytest.raises(SMBException) as exc: connection.verify_signature( header, list(connection.session_table.keys())[0], force=True) assert "Server message signature could not be verified:" in str( exc.value) finally: connection.disconnect(True)
def test_setup_session_with_ms_gss_token(self, smb_real): connection = Connection(uuid.uuid4(), smb_real[2], smb_real[3]) connection.connect() connection.gss_negotiate_token = b"\x60\x76\x06\x06\x2b\x06\x01\x05" \ b"\x05\x02\xa0\x6c\x30\x6a\xa0\x3c" \ b"\x30\x3a\x06\x0a\x2b\x06\x01\x04" \ b"\x01\x82\x37\x02\x02\x1e\x06\x09" \ b"\x2a\x86\x48\x82\xf7\x12\x01\x02" \ b"\x02\x06\x09\x2a\x86\x48\x86\xf7" \ b"\x12\x01\x02\x02\x06\x0a\x2a\x86" \ b"\x48\x86\xf7\x12\x01\x02\x02\x03" \ b"\x06\x0a\x2b\x06\x01\x04\x01\x82" \ b"\x37\x02\x02\x0a\xa3\x2a\x30\x28" \ b"\xa0\x26\x1b\x24\x6e\x6f\x74\x5f" \ b"\x64\x65\x66\x69\x6e\x65\x64\x5f" \ b"\x69\x6e\x5f\x52\x46\x43\x34\x31" \ b"\x37\x38\x40\x70\x6c\x65\x61\x73" \ b"\x65\x5f\x69\x67\x6e\x6f\x72\x65" session = Session(connection, smb_real[0], smb_real[1], False) try: session.connect() assert len(session.application_key) == 16 assert session.application_key != session.session_key assert len(session.decryption_key) == 16 assert session.decryption_key != session.session_key assert not session.encrypt_data assert len(session.encryption_key) == 16 assert session.encryption_key != session.session_key assert len(session.connection.preauth_integrity_hash_value) == 2 assert len(session.preauth_integrity_hash_value) == 3 assert not session.require_encryption assert session.session_id is not None assert len(session.session_key) == 16 assert len(session.signing_key) == 16 assert session.signing_key != session.session_key assert session.signing_required finally: connection.disconnect(True)
def test_change_notify_on_dir_compound(self, smb_real): connection = Connection(uuid.uuid4(), smb_real[2], smb_real[3]) connection.connect() # Cannot use encryption as Samba has a bug where the transform response has the wrong Session Id. Also there's # a special edge case of testing the Session Id of the plaintext response with signatures so don't use # encryption. # https://bugzilla.samba.org/show_bug.cgi?id=14189 session = Session(connection, smb_real[0], smb_real[1], require_encryption=False) tree = TreeConnect(session, smb_real[4]) open = Open(tree, "directory-watch") try: session.connect() tree.connect() # Ensure the dir is clean of files. open.create( ImpersonationLevel.Impersonation, DirectoryAccessMask.MAXIMUM_ALLOWED, FileAttributes.FILE_ATTRIBUTE_DIRECTORY, ShareAccess.FILE_SHARE_READ | ShareAccess.FILE_SHARE_WRITE | ShareAccess.FILE_SHARE_DELETE, CreateDisposition.FILE_OPEN_IF, CreateOptions.FILE_DIRECTORY_FILE) self._remove_file(tree, "directory-watch\\created file") open.close() watcher = FileSystemWatcher(open) messages = [ open.create(ImpersonationLevel.Impersonation, DirectoryAccessMask.MAXIMUM_ALLOWED, FileAttributes.FILE_ATTRIBUTE_DIRECTORY, ShareAccess.FILE_SHARE_READ | ShareAccess.FILE_SHARE_WRITE | ShareAccess.FILE_SHARE_DELETE, CreateDisposition.FILE_OPEN_IF, CreateOptions.FILE_DIRECTORY_FILE, send=False), watcher.start(CompletionFilter.FILE_NOTIFY_CHANGE_FILE_NAME, send=False) ] assert watcher.result is None assert watcher.response_event.is_set() is False requests = connection.send_compound([m[0] for m in messages], sid=session.session_id, tid=tree.tree_connect_id, related=True) [messages[i][1](req) for i, req in enumerate(requests)] # Run the wait in a separate thread so we can create the dir def watcher_wait(): watcher.wait() watcher_wait_thread = threading.Thread(target=watcher_wait) watcher_wait_thread.daemon = True watcher_wait_thread.start() def watcher_event(): watcher.response_event.wait() watcher_event_thread = threading.Thread(target=watcher_event) watcher_event_thread.daemon = True watcher_event_thread.start() # Create the new file file_open = Open(tree, "directory-watch\\created file") file_open.create( ImpersonationLevel.Impersonation, FilePipePrinterAccessMask.MAXIMUM_ALLOWED, FileAttributes.FILE_ATTRIBUTE_NORMAL, ShareAccess.FILE_SHARE_READ | ShareAccess.FILE_SHARE_WRITE | ShareAccess.FILE_SHARE_DELETE, CreateDisposition.FILE_OPEN_IF, CreateOptions.FILE_NON_DIRECTORY_FILE) file_open.close() watcher_wait_thread.join(timeout=2) watcher_event_thread.join(timeout=2) assert watcher_wait_thread.is_alive() is False assert watcher_event_thread.is_alive() is False assert watcher.response_event.is_set() assert len(watcher.result) == 1 assert watcher.result[0]['file_name'].get_value( ) == u"created file" assert watcher.result[0]['action'].get_value( ) == FileAction.FILE_ACTION_ADDED open.close() finally: connection.disconnect(True)
def test_change_notify_no_data(self, smb_real): connection = Connection(uuid.uuid4(), smb_real[2], smb_real[3]) connection.connect() session = Session(connection, smb_real[0], smb_real[1]) tree = TreeConnect(session, smb_real[4]) open = Open(tree, "directory-watch") try: session.connect() tree.connect() open.create( ImpersonationLevel.Impersonation, DirectoryAccessMask.MAXIMUM_ALLOWED, FileAttributes.FILE_ATTRIBUTE_DIRECTORY, ShareAccess.FILE_SHARE_READ | ShareAccess.FILE_SHARE_WRITE | ShareAccess.FILE_SHARE_DELETE, CreateDisposition.FILE_OPEN_IF, CreateOptions.FILE_DIRECTORY_FILE) self._remove_file(tree, "directory-watch\\created file") watcher = FileSystemWatcher(open) watcher.start(CompletionFilter.FILE_NOTIFY_CHANGE_FILE_NAME, output_buffer_length=0) assert watcher.result is None assert watcher.response_event.is_set() is False # Run the wait in a separate thread so we can create the dir def watcher_wait(): watcher.wait() watcher_wait_thread = threading.Thread(target=watcher_wait) watcher_wait_thread.daemon = True watcher_wait_thread.start() def watcher_event(): watcher.response_event.wait() watcher_event_thread = threading.Thread(target=watcher_event) watcher_event_thread.daemon = True watcher_event_thread.start() # Create the new file file_open = Open(tree, "directory-watch\\created file") file_open.create( ImpersonationLevel.Impersonation, FilePipePrinterAccessMask.MAXIMUM_ALLOWED, FileAttributes.FILE_ATTRIBUTE_NORMAL, ShareAccess.FILE_SHARE_READ | ShareAccess.FILE_SHARE_WRITE | ShareAccess.FILE_SHARE_DELETE, CreateDisposition.FILE_OPEN_IF, CreateOptions.FILE_NON_DIRECTORY_FILE) file_open.close() watcher_wait_thread.join(timeout=2) watcher_event_thread.join(timeout=2) assert watcher_wait_thread.is_alive() is False assert watcher_event_thread.is_alive() is False assert watcher.response_event.is_set() assert watcher.result == [] open.close() finally: connection.disconnect(True)