Ejemplo n.º 1
0
    def _fetch_from_io(self, mode: Mode) -> AgentRawData:
        if mode is not Mode.CHECKING:
            raise MKFetcherError(
                f"Refusing to fetch live data during {mode.name.lower()}")

        agent_data, protocol = self._get_agent_data()
        return self._validate_decrypted_data(
            self._decrypt(protocol, agent_data))
Ejemplo n.º 2
0
    def _fetch_from_io(self, mode: Mode) -> AgentRawData:
        if self.use_only_cache:
            raise MKFetcherError(
                "Got no data: No usable cache file present at %s" % self.file_cache.base_path
            )

        agent_data, protocol = self._get_agent_data()
        return self._validate_decrypted_data(self._decrypt(protocol, agent_data))
Ejemplo n.º 3
0
    def _decrypt_agent_data(self, output: AgentRawData) -> AgentRawData:
        salt_start = len(OPENSSL_SALTED_MARKER)

        try:
            # simply check if the protocol is an actual number
            protocol = int(output[:2])
        except ValueError:
            raise MKFetcherError(
                f"Unsupported protocol version: {output[:2]!r}")
        encrypted_pkg = output[2:]
        password = self.encryption_settings["passphrase"]

        if protocol == 3:
            return AgentRawData(
                decrypt_aes_256_cbc_pbkdf2(
                    ciphertext=encrypted_pkg[salt_start:],
                    password=password,
                ))
        if protocol == 2:
            return AgentRawData(
                decrypt_aes_256_cbc_legacy(
                    ciphertext=encrypted_pkg,
                    password=password,
                    digest=sha256,
                ))
        if protocol == 0:
            return AgentRawData(
                decrypt_aes_256_cbc_legacy(
                    ciphertext=encrypted_pkg,
                    password=password,
                    digest=md5,
                ))
        # Support encrypted agent data with "99" header.
        # This was not intended, but the Windows agent accidentally sent this header
        # instead of "00" up to 2.0.0p1, so we keep this for a while.
        # Caution: "99" for real-time check data means "unencrypted"!
        if protocol == 99:
            return AgentRawData(
                decrypt_aes_256_cbc_legacy(
                    ciphertext=encrypted_pkg,
                    password=password,
                    digest=md5,
                ))

        raise MKFetcherError(f"Unsupported protocol version: {protocol}")
Ejemplo n.º 4
0
    def read(self, mode: Mode) -> Optional[TRawData]:
        if not (self.simulation or self.cache_read(mode)):
            return None

        raw_data = self._read(self.make_path(mode))
        if raw_data is None and self.simulation:
            raise MKFetcherError(
                "Got no data (Simulation mode enabled and no cachefile present)"
            )
        return raw_data
Ejemplo n.º 5
0
 def _detect_transport_protocol(self,
                                raw_protocol: bytes) -> TransportProtocol:
     try:
         protocol = TransportProtocol(raw_protocol)
         self._logger.debug(
             "Detected transport protocol: {protocol} ({raw_protocol!r})")
         return protocol
     except ValueError:
         raise MKFetcherError(
             f"Unknown transport protocol: {raw_protocol!r}")
Ejemplo n.º 6
0
    def _validate_protocol(self, protocol: TransportProtocol) -> None:
        if protocol is TransportProtocol.TLS:
            return

        enc_setting = self.encryption_settings["use_regular"]
        if enc_setting == "tls":
            raise MKFetcherError("Refused: TLS not supported by agent")

        if protocol is TransportProtocol.PLAIN:
            if enc_setting in ("disable", "allow"):
                return
            raise MKFetcherError(
                "Agent output is plaintext but encryption is enforced by configuration"
            )

        if enc_setting == "disable":
            raise MKFetcherError(
                "Agent output is encrypted but encryption is disabled by configuration"
            )
Ejemplo n.º 7
0
 def _fetch_from_io(self, mode: Mode) -> AgentRawData:
     if self._process is None:
         raise MKFetcherError("No process")
     # ? do they have the default byte type, because in open() none of the "text", "encoding",
     #  "errors", "universal_newlines" were specified?
     stdout, stderr = self._process.communicate(
         input=self.stdin.encode() if self.stdin else None)
     if self._process.returncode == 127:
         exepath = self.cmdline.split()[
             0]  # for error message, hide options!
         # ? exepath is AnyStr
         raise MKFetcherError("Program '%s' not found (exit code 127)" %
                              ensure_str(exepath)  # pylint: disable= six-ensure-str-bin-call
                              )
     if self._process.returncode:
         raise MKFetcherError("Agent exited with code %d: %s" % (
             self._process.returncode,
             ensure_str(stderr).strip(),  # pylint: disable= six-ensure-str-bin-call
         ))
     return stdout
Ejemplo n.º 8
0
 def __enter__(self) -> 'Fetcher':
     """Prepare the data source."""
     try:
         self.open()
     except MKFetcherError:
         raise
     except Exception as exc:
         if cmk.utils.debug.enabled():
             raise
         raise MKFetcherError(repr(exc) if any(exc.args) else type(exc).__name__) from exc
     return self
Ejemplo n.º 9
0
    def __enter__(self) -> "Fetcher":
        """Prepare the data source. Only needed if simulation mode is
        disabled"""
        if self.file_cache.simulation:
            return self

        try:
            self.open()
        except MKFetcherError:
            raise
        except Exception as exc:
            raise MKFetcherError(repr(exc) if any(exc.args) else type(exc).__name__) from exc
        return self
Ejemplo n.º 10
0
    def _recvall(self, sock: socket.socket, flags: int = 0) -> bytes:
        self._logger.debug("Reading data from agent")
        buffer: List[bytes] = []
        try:
            while True:
                data = sock.recv(4096, flags)
                if not data:
                    break
                buffer.append(data)
        except socket.error as e:
            if cmk.utils.debug.enabled():
                raise
            raise MKFetcherError("Communication failed: %s" % e)

        return b"".join(buffer)
Ejemplo n.º 11
0
    def _real_decrypt(self, output: AgentRawData) -> AgentRawData:
        SALTED_MARKER = b"Salted__"
        salt_start = len(SALTED_MARKER)

        try:
            # simply check if the protocol is an actual number
            protocol = int(output[:2])
        except ValueError:
            raise MKFetcherError("Unsupported protocol version: %r" %
                                 output[:2])
        encrypted_pkg = output[2:]
        encryption_key = self.encryption_settings["passphrase"]

        if protocol == 3:
            return self._decrypt_aes_256_cbc_pbkdf2(encrypted_pkg[salt_start:],
                                                    encryption_key)
        if protocol == 2:
            return self._decrypt_aes_256_cbc_legacy(encrypted_pkg,
                                                    encryption_key, sha256)
        if protocol == 0:
            return self._decrypt_aes_256_cbc_legacy(encrypted_pkg,
                                                    encryption_key, md5)

        raise MKFetcherError(f"Unsupported protocol version: {protocol}")
Ejemplo n.º 12
0
    def open(self) -> None:
        self._logger.debug(
            "Connecting to %s:623 (User: %s, Privlevel: 2)",
            self.address or "local",
            self.username or "no user",
        )

        # Performance: See header.
        import pyghmi.ipmi.command as ipmi_cmd  # type: ignore[import]
        try:
            self._command = ipmi_cmd.Command(
                bmc=self.address,
                userid=self.username,
                password=self.password,
                privlevel=2,
            )
        except IpmiException as exc:
            raise MKFetcherError("IPMI connection failed") from exc
Ejemplo n.º 13
0
    def _decrypt(self, protocol: TransportProtocol, output: AgentRawData) -> AgentRawData:
        if not output:
            return output  # nothing to to, validation will fail

        if protocol is TransportProtocol.PLAIN:
            return protocol.value + output  # bring back stolen bytes

        self._logger.debug("Try to decrypt output")
        try:
            return AgentRawData(
                decrypt_by_agent_protocol(
                    self.encryption_settings["passphrase"],
                    protocol,
                    output,
                )
            )
        except Exception as e:
            raise MKFetcherError("Failed to decrypt agent output: %s" % e) from e
Ejemplo n.º 14
0
    def _firmware_section(self) -> AgentRawData:
        if self._command is None:
            raise MKFetcherError("Not connected")

        self._logger.debug("Fetching firmware information via UDP from %s:623", self._command.bmc)
        try:
            firmware_entries = self._command.get_firmware()
        except Exception as e:
            self._logger.log(VERBOSE, "Failed to fetch firmware information: %r", e)
            self._logger.debug("Exception", exc_info=True)
            return AgentRawData(b"")

        output = b"<<<mgmt_ipmi_firmware:sep(124)>>>\n"
        for entity_name, attributes in firmware_entries:
            for attribute_name, value in attributes.items():
                output += b"|".join(f.encode("utf8") for f in (entity_name, attribute_name, value))
                output += b"\n"

        return AgentRawData(output)
Ejemplo n.º 15
0
    def open(self) -> None:
        verify_ipaddress(self.address[0])
        self._logger.debug(
            "Connecting via TCP to %s:%d (%ss timeout)",
            self.address[0],
            self.address[1],
            self.timeout,
        )
        self._opt_socket = socket.socket(self.family, socket.SOCK_STREAM)
        try:
            self._socket.settimeout(self.timeout)
            self._socket.connect(self.address)
            self._socket.settimeout(None)
        except socket.error as e:
            self._close_socket()

            if cmk.utils.debug.enabled():
                raise
            raise MKFetcherError("Communication failed: %s" % e)
Ejemplo n.º 16
0
    def _fetch(self, mode: Mode) -> TRawData:
        raw_data = self.file_cache.read(mode)
        if raw_data is not None:
            return raw_data

        self._logger.log(VERBOSE, "[%s] Execute data source",
                         self.__class__.__name__)

        try:
            self.open()
            raw_data = self._fetch_from_io(mode)
        except MKFetcherError:
            raise
        except Exception as exc:
            raise MKFetcherError(
                repr(exc) if any(exc.args) else type(exc).__name__) from exc

        self.file_cache.write(raw_data, mode)
        return raw_data
Ejemplo n.º 17
0
    def _get_agent_data(self) -> Tuple[AgentRawData, TransportProtocol]:
        try:
            raw_protocol = self._socket.recv(2, socket.MSG_WAITALL)
        except socket.error as e:
            raise MKFetcherError(f"Communication failed: {e}") from e

        protocol = self._detect_transport_protocol(raw_protocol)

        self._validate_protocol(protocol)

        if protocol is TransportProtocol.TLS:
            with self._wrap_tls() as ssock:
                agent_data = self._recvall(ssock)
            return AgentRawData(
                agent_data[2:]), self._detect_transport_protocol(
                    agent_data[:2])

        return AgentRawData(self._recvall(self._socket,
                                          socket.MSG_WAITALL)), protocol
Ejemplo n.º 18
0
    def _raw_data(self) -> AgentRawData:
        self._logger.debug("Reading data from agent")
        if not self._socket:
            return AgentRawData(b"")

        def recvall(sock: socket.socket) -> bytes:
            buffer: List[bytes] = []
            while True:
                data = sock.recv(4096, socket.MSG_WAITALL)
                if not data:
                    break
                buffer.append(data)
            return b"".join(buffer)

        try:
            return AgentRawData(recvall(self._socket))
        except socket.error as e:
            if cmk.utils.debug.enabled():
                raise
            raise MKFetcherError("Communication failed: %s" % e)
Ejemplo n.º 19
0
    def _sensors_section(self) -> AgentRawData:
        if self._command is None:
            raise MKFetcherError("Not connected")

        self._logger.debug("Fetching sensor data via UDP from %s:623",
                           self._command.bmc)

        # Performance: See header.
        import pyghmi.ipmi.sdr as ipmi_sdr  # type: ignore[import]
        try:
            sdr = ipmi_sdr.SDR(self._command)
        except NotImplementedError as e:
            self._logger.log(VERBOSE, "Failed to fetch sensor data: %r", e)
            self._logger.debug("Exception", exc_info=True)
            return AgentRawData(b"")

        sensors = []
        has_no_gpu = not self._has_gpu()
        for ident in sdr.get_sensor_numbers():
            sensor = sdr.sensors[ident]
            rsp = self._command.raw_command(command=0x2d,
                                            netfn=4,
                                            rslun=sensor.sensor_lun,
                                            data=(sensor.sensor_number, ))
            if 'error' in rsp:
                continue

            reading = sensor.decode_sensor_reading(rsp['data'])
            if reading is not None:
                # sometimes (wrong) data for GPU sensors is reported, even if
                # not installed
                if "GPU" in reading.name and has_no_gpu:
                    continue
                sensors.append(
                    IPMIFetcher._parse_sensor_reading(sensor.sensor_number,
                                                      reading))

        return AgentRawData(b"<<<ipmi_sensors:sep(124)>>>\n" +
                            b"".join(b"|".join(sensor) + b"\n"
                                     for sensor in sensors))
Ejemplo n.º 20
0
    def read(self, mode: Mode) -> Optional[TRawData]:
        if self.disabled:
            self._logger.debug("Not using cache (Cache usage disabled)")
            return None

        path = self.make_path(mode)
        if not path.exists():
            self._logger.debug("Not using cache (Does not exist)")
            return None

        if not (self.simulation or mode is not Mode.FORCE_SECTIONS):
            self._logger.debug("Not using cache (Mode %s)", mode)
            return None

        raw_data = self._read(path)
        if raw_data is None and self.simulation:
            raise MKFetcherError(
                "Got no data (Simulation mode enabled and no cachefile present)"
            )

        if raw_data is not None:
            self._logger.debug("Got %r bytes data from cache", len(raw_data))
        return raw_data
Ejemplo n.º 21
0
 def _validate_decrypted_data(self, output: AgentRawData) -> AgentRawData:
     if len(output) < 16:
         raise MKFetcherError(
             f"Too short payload from agent at {self.address[0]}:{self.address[1]}: {output!r}"
         )
     return output
Ejemplo n.º 22
0
    def _fetch_from_io(self, mode: Mode) -> AgentRawData:
        if self._command is None:
            raise MKFetcherError("Not connected")

        return AgentRawData(b"" + self._sensors_section() +
                            self._firmware_section())
Ejemplo n.º 23
0
 def _socket(self) -> socket.socket:
     if self._opt_socket is None:
         raise MKFetcherError("Not connected")
     return self._opt_socket
Ejemplo n.º 24
0
 def read(self) -> Optional[TRawData]:
     raw_data = self._read()
     if raw_data is None and self.simulation:
         raise MKFetcherError("Got no data (Simulation mode enabled and no cachefile present)")
     return raw_data