diff --git a/satori/smb.py b/satori/smb.py index 6914184..f62f49f 100644 --- a/satori/smb.py +++ b/satori/smb.py @@ -9,6 +9,7 @@ # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. +# pylint: disable=W0703 """Windows remote client module implemented using psexec.py.""" @@ -203,12 +204,15 @@ class SMBClient(object): # pylint: disable=R0902 stderr=subprocess.STDOUT, stdin=subprocess.PIPE, close_fds=True, - bufsize=0) + universal_newlines=True, + bufsize=-1) output = '' while not self._prompt_pattern.findall(output): output += self._get_output() self._connected = True except Exception: + LOG.error("Failed to connect to host %s over smb", + self.host, exc_info=True) self.close() raise @@ -237,10 +241,10 @@ class SMBClient(object): # pylint: disable=R0902 str(exc)) del exc finally: - if self._process: - LOG.warning("Killing process: %s", self._process) - subprocess.call(['pkill', '-STOP', '-P', - str(self._process.pid)]) + try: + self._process.kill() + except OSError: + LOG.exception("Tried killing psexec subprocess.") def remote_execute(self, command, powershell=True, retry=0, **kwargs): """Execute a command on a remote host. @@ -256,19 +260,24 @@ class SMBClient(object): # pylint: disable=R0902 if powershell: command = ('powershell -EncodedCommand %s' % _posh_encode(command)) + LOG.info("Executing command: %s", command) self._process.stdin.write('%s\n' % command) + self._process.stdin.flush() try: output = self._get_output() + LOG.debug("Stdout produced: %s", output) output = "\n".join(output.splitlines()[:-1]).strip() return output - except SubprocessError: + except Exception: + LOG.error("Error while reading output from command %s on %s", + command, self.host, exc_info=True) if not retry: raise else: return self.remote_execute(command, powershell=powershell, retry=retry - 1) - def _get_output(self, prompt_expected=True, wait=200): + def _get_output(self, prompt_expected=True, wait=500): """Retrieve output from _process. This method will wait until output is started to be received and then @@ -285,16 +294,20 @@ class SMBClient(object): # pylint: disable=R0902 # leave loop if underlying process has a return code # obviously meaning that it has terminated if self._process.poll() is not None: - import json - error = {"error": tmp_out} + if self._process.returncode == 0: + break + error = tmp_out raise SubprocessError("subprocess with pid: %s has terminated " - "unexpectedly with return code: %s\n%s" + "unexpectedly with return code: %s | %s" % (self._process.pid, - self._process.poll(), - json.dumps(error))) - time.sleep(wait / 1000) + self._process.poll(), error)) + time.sleep(0) + time.sleep(float(wait) / 1000) + else: + LOG.debug("Loop 1 - stdout read: %s", tmp_out) + stdout = tmp_out - while (not tmp_out == '' or + while (tmp_out != '' or (not self._prompt_pattern.findall(stdout) and prompt_expected)): self._file_read.seek(0, 1) @@ -303,14 +316,18 @@ class SMBClient(object): # pylint: disable=R0902 # leave loop if underlying process has a return code # obviously meaning that it has terminated if self._process.poll() is not None: - import json - error = {"error": stdout} + if self._process.returncode == 0: + break + error = tmp_out raise SubprocessError("subprocess with pid: %s has terminated " - "unexpectedly with return code: %s\n%s" + "unexpectedly with return code: %s | %s" % (self._process.pid, - self._process.poll(), - json.dumps(error))) - time.sleep(wait / 1000) + self._process.poll(), error)) + time.sleep(0) + time.sleep(float(wait) / 1000) + else: + LOG.debug("Loop 2 - stdout read: %s", tmp_out) + self._output += stdout stdout = stdout.replace('\r', '').replace('\x08', '') return stdout