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 _resolve_dfs(raw_io): """ Resolves a DFS path for a failed Open request. :param raw_io: The SMBRawIO to resolve the DFS path for. :return: A new Open for each DFS target that was resolved. """ if not raw_io.fd.tree_connect.is_dfs_share: return # Path is on a DFS root that is linked to another server. client_config = ClientConfig() raw_path = raw_io.name referral = dfs_request(raw_io.fd.tree_connect, raw_path[1:]) client_config.cache_referral(referral) info = client_config.lookup_referral( [p for p in raw_path.split("\\") if p]) connection_kwargs = getattr(raw_io, '_%s__kwargs' % type(raw_io).__name__, {}) for target in info: new_path = raw_path.replace(info.dfs_path, target.target_path, 1) try: tree, fd_path = get_smb_tree(new_path, **connection_kwargs) except SMBResponseException as link_exc: log.warning("Failed to connect to DFS link target %s: %s" % (str(target), link_exc)) continue # Record the target that worked for future reference. info.target_hint = target yield Open(tree, fd_path)
def _delete_file_smbprotocol(path, share='C$', conn=None, host=None, username=None, password=None): if conn is None: conn = get_conn(host, username, password) if conn is False: return False tree = conn.tree_connect(share) file_open = Open(tree, path) delete_msgs = [ file_open.create( ImpersonationLevel.Impersonation, FilePipePrinterAccessMask.GENERIC_READ | FilePipePrinterAccessMask.DELETE, FileAttributes.FILE_ATTRIBUTE_NORMAL, ShareAccess.FILE_SHARE_READ | ShareAccess.FILE_SHARE_WRITE, CreateDisposition.FILE_OPEN, CreateOptions.FILE_NON_DIRECTORY_FILE | CreateOptions.FILE_DELETE_ON_CLOSE, send=False ), file_open.close(False, send=False) ] requests = conn.connection.send_compound([x[0] for x in delete_msgs], conn.session.session_id, tree.tree_connect_id, related=True) responses = [] for i, request in enumerate(requests): # A SMBResponseException will be raised if something went wrong response = delete_msgs[i][1](request) responses.append(response)
def _delete_directory_smbprotocol(path, share='C$', conn=None, host=None, username=None, password=None): if conn is None: conn = get_conn(host, username, password) if conn is False: return False log.warn("PATH: %s %s", share, path) tree = conn.tree_connect(share) dir_open = Open(tree, path) delete_msgs = [ dir_open.create( ImpersonationLevel.Impersonation, DirectoryAccessMask.DELETE, FileAttributes.FILE_ATTRIBUTE_DIRECTORY, 0, CreateDisposition.FILE_OPEN, CreateOptions.FILE_DIRECTORY_FILE | CreateOptions.FILE_DELETE_ON_CLOSE, send=False ), dir_open.close(False, send=False) ] delete_reqs = conn.connection.send_compound([x[0] for x in delete_msgs], sid=conn.session.session_id, tid=tree.tree_connect_id, related=True) for i, request in enumerate(delete_reqs): # A SMBResponseException will be raised if something went wrong response = delete_msgs[i][1](request)
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 cleanup(self): """ Cleans up any old services or payloads that may have been left behind on a previous failure. This will search C:\\Windows for any files starting with PAExec-*.exe and delete them. It will also stop and remove any services that start with PAExec-* if they exist. Before calling this function, the connect() function must have already been called. """ scmr = self._service._scmr services = scmr.enum_services_status_w( self._service._scmr_handle, ServiceType.SERVICE_WIN32_OWN_PROCESS, EnumServiceState.SERVICE_STATE_ALL) for service in services: if service['service_name'].lower().startswith("paexec"): svc = Service(service['service_name'], self.session) svc.open() svc.delete() smb_tree = TreeConnect(self.session, r"\\%s\ADMIN$" % self.connection.server_name) smb_tree.connect() share = Open(smb_tree, "") query_msgs = [ share.create(ImpersonationLevel.Impersonation, DirectoryAccessMask.FILE_READ_ATTRIBUTES | DirectoryAccessMask.SYNCHRONIZE | DirectoryAccessMask.FILE_LIST_DIRECTORY, FileAttributes.FILE_ATTRIBUTE_DIRECTORY, ShareAccess.FILE_SHARE_READ | ShareAccess.FILE_SHARE_WRITE | ShareAccess.FILE_SHARE_DELETE, CreateDisposition.FILE_OPEN, CreateOptions.FILE_DIRECTORY_FILE, send=False), share.query_directory("PAExec-*.exe", FileInformationClass.FILE_NAMES_INFORMATION, send=False), share.close(False, send=False) ] query_reqs = self.connection.send_compound([x[0] for x in query_msgs], self.session.session_id, smb_tree.tree_connect_id, related=True) # receive response for open and close query_msgs[0][1](query_reqs[0]) query_msgs[2][1](query_reqs[2]) try: # receive the response for query_directory files = query_msgs[1][1](query_reqs[1]) except SMBResponseException as exc: if exc.status != NtStatus.STATUS_NO_SUCH_FILE: raise exc files = [] for file in files: file_name = file['file_name'].get_value().decode('utf-16-le') self._delete_file(smb_tree, file_name)
def create_service(self): # check if the service exists and delete it log.debug("Ensuring service is deleted before starting") self._service.delete() # copy across the PAExec payload to C:\Windows\ smb_tree = TreeConnect(self.session, r"\\%s\ADMIN$" % self.connection.server_name) log.info("Connecting to SMB Tree %s" % smb_tree.share_name) smb_tree.connect() paexec_file = Open(smb_tree, self._exe_file) log.debug("Creating open to PAExec file") paexec_file.open(ImpersonationLevel.Impersonation, FilePipePrinterAccessMask.FILE_WRITE_DATA, FileAttributes.FILE_ATTRIBUTE_NORMAL, ShareAccess.FILE_SHARE_READ, CreateDisposition.FILE_OVERWRITE_IF, CreateOptions.FILE_NON_DIRECTORY_FILE) log.info("Creating PAExec executable at %s\\%s" % (smb_tree.share_name, self._exe_file)) for (data, o) in paexec_out_stream(self.connection.max_write_size): paexec_file.write(data, o) log.debug("Closing open to PAExec file") paexec_file.close(False) log.info("Disconnecting from SMB Tree %s" % smb_tree.share_name) smb_tree.disconnect() # create the PAExec service service_path = r'"%SystemRoot%\{0}" -service'.format(self._exe_file) log.info("Creating PAExec service %s" % self.service_name) self._service.create(service_path)
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 open_file(cls, tree, file): file = cls.normalize_filename(file) # ensure file is created, get maximal access, and set everybody read access max_req = SMB2CreateContextRequest() max_req[ "buffer_name"] = CreateContextName.SMB2_CREATE_QUERY_MAXIMAL_ACCESS_REQUEST max_req["buffer_data"] = SMB2CreateQueryMaximalAccessRequest() # create security buffer that sets the ACL for everyone to have read access everyone_sid = SIDPacket() everyone_sid.from_string("S-1-1-0") ace = AccessAllowedAce() ace["mask"] = AccessMask.GENERIC_ALL ace["sid"] = everyone_sid acl = AclPacket() acl["aces"] = [ace] sec_desc = SMB2CreateSDBuffer() sec_desc["control"].set_flag(SDControl.SELF_RELATIVE) sec_desc.set_dacl(acl) sd_buffer = SMB2CreateContextRequest() sd_buffer["buffer_name"] = CreateContextName.SMB2_CREATE_SD_BUFFER sd_buffer["buffer_data"] = sec_desc create_contexts = [max_req, sd_buffer] file_open = Open(tree, file) open_info = file_open.create( ImpersonationLevel.Impersonation, FilePipePrinterAccessMask.GENERIC_READ | FilePipePrinterAccessMask.GENERIC_WRITE, FileAttributes.FILE_ATTRIBUTE_NORMAL, ShareAccess.FILE_SHARE_READ | ShareAccess.FILE_SHARE_WRITE, CreateDisposition.FILE_OVERWRITE_IF, CreateOptions.FILE_NON_DIRECTORY_FILE, ) return file_open
def _delete_file(self, tree, name): file_open = Open(tree, name) file_open.open( ImpersonationLevel.Impersonation, FilePipePrinterAccessMask.DELETE, FileAttributes.FILE_ATTRIBUTE_NORMAL, 0, CreateDisposition.FILE_OPEN_IF, CreateOptions.FILE_NON_DIRECTORY_FILE | CreateOptions.FILE_DELETE_ON_CLOSE) file_open.close(get_attributes=False)
def _remove_file(self, tree, name): file_open = Open(tree, name) file_open.create( ImpersonationLevel.Impersonation, FilePipePrinterAccessMask.DELETE, 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 | CreateOptions.FILE_DELETE_ON_CLOSE), file_open.close()
def open_directory(tree, name, create=False): # ensure directory is created dir_open = Open(tree, name) if create: dir_open.create( ImpersonationLevel.Impersonation, DirectoryAccessMask.GENERIC_READ | DirectoryAccessMask.GENERIC_WRITE, FileAttributes.FILE_ATTRIBUTE_DIRECTORY, ShareAccess.FILE_SHARE_READ | ShareAccess.FILE_SHARE_WRITE, CreateDisposition.FILE_OPEN_IF, CreateOptions.FILE_DIRECTORY_FILE ) return dir_open
def _get_paexec_files_and_services(self, client): server = os.environ['PYPSEXEC_SERVER'] username = os.environ.get('PYPSEXEC_USERNAME', None) password = os.environ.get('PYPSEXEC_PASSWORD', None) paexec_services = [] # need to close and reopen the connection to ensure deletes are # processed client.disconnect() client = Client(server, username=username, password=password) client.connect() scmr = client._service._scmr scmr_handle = client._service._scmr_handle services = scmr.enum_services_status_w(scmr_handle, ServiceType. SERVICE_WIN32_OWN_PROCESS, EnumServiceState. SERVICE_STATE_ALL) for service in services: if service['service_name'].lower().startswith("paexec"): paexec_services.append(service['service_name']) smb_tree = TreeConnect(client.session, r"\\%s\ADMIN$" % client.connection.server_name) smb_tree.connect() share = Open(smb_tree, "") share.create(ImpersonationLevel.Impersonation, DirectoryAccessMask.FILE_READ_ATTRIBUTES | DirectoryAccessMask.SYNCHRONIZE | DirectoryAccessMask.FILE_LIST_DIRECTORY, FileAttributes.FILE_ATTRIBUTE_DIRECTORY, ShareAccess.FILE_SHARE_READ | ShareAccess.FILE_SHARE_WRITE | ShareAccess.FILE_SHARE_DELETE, CreateDisposition.FILE_OPEN, CreateOptions.FILE_DIRECTORY_FILE) try: paexec_files = share.query_directory("PAExec-*.exe", FileInformationClass. FILE_NAMES_INFORMATION) except SMBResponseException as exc: if exc.status != NtStatus.STATUS_NO_SUCH_FILE: raise exc paexec_files = [] return client, paexec_services, paexec_files
def open_pipe(tree, name, access_mask, fsctl_wait=False): """ Opened the requested pipe with the access mask specified. Will attempt to connect 3 times before failing in case the pipe's don't exist at the time. :param tree: The SMB TreeConnect of IPC$ to connect to :param name: The name of the pipe to connect to :param access_mask: The access mask to apply to the Open :param fsctl_wait: Runs the FSCTL_PIPE_WAIT command over an SMB2IOCTLRequest :return: A connected Open() object of the pipe """ log.info("Creating SMB Open for pipe: %s" % name) pipe = Open(tree, name) if fsctl_wait: wait_pipe = SMB2IOCTLRequest() wait_pipe['ctl_code'] = CtlCode.FSCTL_PIPE_WAIT wait_pipe['file_id'] = b"\xff" * 16 wait_pipe['flags'] = IOCTLFlags.SMB2_0_IOCTL_IS_FSCTL fsctl_data = FSCTLPipeWait() fsctl_data['name'] = name.encode('utf-16-le') wait_pipe['buffer'] = fsctl_data log.info("Sending FSCTL_PIPE_WAIT for pipe %s" % name) log.debug(str(fsctl_data)) request = tree.session.connection.send( wait_pipe, sid=tree.session.session_id, tid=tree.tree_connect_id ) log.info("Receiving FSCTL_PIPE_WAIT response for pipe: %s" % name) tree.session.connection.receive(request) pipe.create(ImpersonationLevel.Impersonation, access_mask, FileAttributes.FILE_ATTRIBUTE_NORMAL, 0, CreateDisposition.FILE_OPEN, CreateOptions.FILE_NON_DIRECTORY_FILE | CreateOptions.FILE_SYNCHRONOUS_IO_NONALERT) return pipe
def __init__(self, path, mode='r', share_access=None, desired_access=None, file_attributes=None, create_options=0, buffer_size=MAX_PAYLOAD_SIZE, **kwargs): tree, fd_path = get_smb_tree(path, **kwargs) self.share_access = share_access self.fd = Open(tree, fd_path) self._mode = mode self._name = path self._offset = 0 self._flush = False self._buffer_size = buffer_size self.__kwargs = kwargs # Used in open for DFS referrals if desired_access is None: desired_access = 0 # While we can open a directory, the values for FilePipePrinterAccessMask also apply to Dirs so just use # the same enum to simplify code. if 'r' in self.mode or '+' in self.mode: desired_access |= FilePipePrinterAccessMask.FILE_READ_DATA | \ FilePipePrinterAccessMask.FILE_READ_ATTRIBUTES | \ FilePipePrinterAccessMask.FILE_READ_EA if 'w' in self.mode or 'x' in self.mode or 'a' in self.mode or '+' in self.mode: desired_access |= FilePipePrinterAccessMask.FILE_WRITE_DATA | \ FilePipePrinterAccessMask.FILE_WRITE_ATTRIBUTES | \ FilePipePrinterAccessMask.FILE_WRITE_EA self._desired_access = desired_access if file_attributes is None: file_attributes = FileAttributes.FILE_ATTRIBUTE_DIRECTORY if self.FILE_TYPE == 'dir' \ else FileAttributes.FILE_ATTRIBUTE_NORMAL self._file_attributes = file_attributes self._create_options = create_options self._create_options |= { 'dir': CreateOptions.FILE_DIRECTORY_FILE, 'file': CreateOptions.FILE_NON_DIRECTORY_FILE, }.get(self.FILE_TYPE, 0) super(SMBRawIO, self).__init__()
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 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 _delete_file(self, tree, name): file_open = Open(tree, name) msgs = [ file_open.create(ImpersonationLevel.Impersonation, FilePipePrinterAccessMask.DELETE, FileAttributes.FILE_ATTRIBUTE_NORMAL, 0, CreateDisposition.FILE_OPEN_IF, CreateOptions.FILE_NON_DIRECTORY_FILE | CreateOptions.FILE_DELETE_ON_CLOSE, send=False), file_open.close(get_attributes=False, send=False) ] reqs = self.connection.send_compound([x[0] for x in msgs], sid=self.session.session_id, tid=tree.tree_connect_id, related=True) # remove the responses from the SMB outstanding requests msgs[0][1](reqs[0]) msgs[1][1](reqs[1])
def __init__(self, smb_session): # connect to the IPC tree and open a handle at svcctl self.tree = TreeConnect( smb_session, r"\\%s\IPC$" % smb_session.connection.server_name) self.handle = Open(self.tree, "svcctl") self.call_id = 0
ace['mask'] = AccessMask.GENERIC_ALL ace['sid'] = everyone_sid acl = AclPacket() acl['aces'] = [ace] sec_desc = SMB2CreateSDBuffer() sec_desc['control'].set_flag(SDControl.SELF_RELATIVE) sec_desc.set_dacl(acl) sd_buffer = SMB2CreateContextRequest() sd_buffer['buffer_name'] = CreateContextName.SMB2_CREATE_SD_BUFFER sd_buffer['buffer_data'] = sec_desc create_contexts = [max_req, sd_buffer] file_open = Open(tree, file_name) open_info = file_open.create( ImpersonationLevel.Impersonation, FilePipePrinterAccessMask.GENERIC_READ | FilePipePrinterAccessMask.GENERIC_WRITE, FileAttributes.FILE_ATTRIBUTE_NORMAL, ShareAccess.FILE_SHARE_READ | ShareAccess.FILE_SHARE_WRITE, CreateDisposition.FILE_OVERWRITE_IF, CreateOptions.FILE_NON_DIRECTORY_FILE, create_contexts) # as the raw structure 'maximal_access' is an IntField, we create our own # flag field, set the value and get the human readble string max_access = FlagField(size=4, flag_type=FilePipePrinterAccessMask, flag_strict=False) max_access.set_value(open_info[0]['maximal_access'].get_value())
def open(self, transaction=None): if not self.closed: return share_access = _parse_share_access(self.share_access, self.mode) create_disposition = _parse_mode(self.mode, invalid=self._INVALID_MODE) try: # https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-wpo/feeb3122-cfe0-4b34-821d-e31c036d763c # Impersonation on SMB has little meaning when opening files but is important if using RPC so set to a sane # default of Impersonation. open_result = self.fd.create( ImpersonationLevel.Impersonation, self._desired_access, self._file_attributes, share_access, create_disposition, self._create_options, send=(transaction is None), ) except (PathNotCovered, ObjectNameNotFound, ObjectPathNotFound) as exc: # The MS-DFSC docs status that STATUS_PATH_NOT_COVERED is used when encountering a link to a different # server but Samba seems to return the generic name or path not found. if not self.fd.tree_connect.is_dfs_share: raise SMBOSError(exc.status, self.name) # Path is on a DFS root that is linked to another server. client_config = ClientConfig() referral = dfs_request(self.fd.tree_connect, self.name[1:]) client_config.cache_referral(referral) info = client_config.lookup_referral([p for p in self.name.split("\\") if p]) for target in info: new_path = self.name.replace(info.dfs_path, target.target_path, 1) try: tree, fd_path = get_smb_tree(new_path, **self.__kwargs) self.fd = Open(tree, fd_path) self.open(transaction=transaction) except SMBResponseException as link_exc: log.warning("Failed to connect to DFS link target %s: %s" % (str(target), link_exc)) else: # Record the target that worked for future reference. info.target_hint = target break else: # None of the targets worked so raise the original error. raise SMBOSError(exc.status, self.name) return except SMBResponseException as exc: raise SMBOSError(exc.status, self.name) if transaction is not None: transaction += open_result elif 'a' in self.mode and self.FILE_TYPE != 'pipe': self._offset = self.fd.end_of_file
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)
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)
username = "******" password = "******" share = r"\\%s\share" % server dir_name = "directory" connection = Connection(uuid.uuid4(), server, port) connection.connect() try: session = Session(connection, username, password) session.connect() tree = TreeConnect(session, share) tree.connect() # ensure directory is created dir_open = Open(tree, dir_name) dir_open.create( ImpersonationLevel.Impersonation, DirectoryAccessMask.GENERIC_READ | DirectoryAccessMask.GENERIC_WRITE, FileAttributes.FILE_ATTRIBUTE_DIRECTORY, ShareAccess.FILE_SHARE_READ | ShareAccess.FILE_SHARE_WRITE, CreateDisposition.FILE_OPEN_IF, CreateOptions.FILE_DIRECTORY_FILE) # create some files in dir and query the contents as part of a compound # request directory_file = Open(tree, r"%s\file.txt" % dir_name) directory_file.create( ImpersonationLevel.Impersonation, FilePipePrinterAccessMask.GENERIC_WRITE | FilePipePrinterAccessMask.DELETE, FileAttributes.FILE_ATTRIBUTE_NORMAL, ShareAccess.FILE_SHARE_READ,