Exemplo n.º 1
0
    def run(self):
        """Run the task."""
        try:
            common.assert_scanner_works(chroot=self._sysroot,
                                        executable="oscap")
        except Exception as exc:
            msg_lines = [
                _("The 'oscap' scanner doesn't work in the installed system: {error}"
                  .format(error=str(exc)))
            ]
            msg_lines.append(
                _("As a result, the installed system can't be hardened."))
            terminate("\n".join(msg_lines))
            return

        try:
            common.schedule_firstboot_remediation(
                self._sysroot,
                self._policy_data.profile_id,
                self._target_content_path,
                self._policy_data.datastream_id,
                self._policy_data.xccdf_id,
                self._target_tailoring_path,
            )
        except Exception as exc:
            msg = _(
                "Something went wrong when scheduling the first-boot remediation: {exc}."
                .format(exc=str(exc)))
            terminate(msg)
            return
Exemplo n.º 2
0
    def process_kickstart(self, data):
        """Process the kickstart data."""
        preferred_section_header = f"%addon {self.canonical_addon_name}"
        all_addon_data = [
            getattr(data.addons, name) for name in common.ADDON_NAMES
        ]
        relevant_data = [d for d in all_addon_data if d.addon_section_present]
        if len(relevant_data) > 1:
            msg = common._(
                "You have used more than one oscap addon sections in the kickstart. "
                f"Please use only one, preferably '{preferred_section_header}'."
            )
            raise KickstartParseError(msg)
        if len(relevant_data) == 0:
            addon_data = all_addon_data[0]
        else:
            addon_data = relevant_data[0]

        self.policy_data = addon_data.policy_data

        if (common.COMPLAIN_ABOUT_NON_CANONICAL_NAMES
                and addon_data.name != self.canonical_addon_name):
            used_section_header = f"%addon {addon_data.name}"
            msg = common._(
                f"You have configured the oscap addon using '{used_section_header}' section. "
                f"Please update your configuration and use '{preferred_section_header}'. "
                "Support for legacy sections will be removed in the future major version."
            )
            warnings.warn(msg, KickstartDeprecationWarning)
Exemplo n.º 3
0
def wait_and_fetch_net_data(url, out_file, ca_certs_path=None):
    """
    Function that waits for network connection and starts a thread that fetches
    data over network.

    :see: org_fedora_oscap.data_fetch.fetch_data
    :return: the name of the thread running fetch_data
    :rtype: str

    """

    # get thread that tries to establish a network connection
    nm_conn_thread = threadMgr.get(constants.THREAD_WAIT_FOR_CONNECTING_NM)
    if nm_conn_thread:
        # NM still connecting, wait for it to finish
        nm_conn_thread.join()

    network_proxy = NETWORK.get_proxy()
    if not network_proxy.Connected:
        raise common.OSCAPaddonNetworkError(
            _("Network connection needed to fetch data."))

    log.info(f"Fetching data from {url}")
    fetch_data_thread = AnacondaThread(name=common.THREAD_FETCH_DATA,
                                       target=fetch_data,
                                       args=(url, out_file, ca_certs_path),
                                       fatal=False)

    # register and run the thread
    threadMgr.add(fetch_data_thread)

    return common.THREAD_FETCH_DATA
Exemplo n.º 4
0
def _handle_error(exception):
    log.error("OSCAP Addon: Failed to fetch and initialize SCAP content!")

    if isinstance(exception, ContentCheckError):
        msg = _("The integrity check of the security content failed.")
        terminate(msg)
    elif (isinstance(exception, common.OSCAPaddonError)
          or isinstance(exception, data_fetch.DataFetchError)):
        msg = _(
            "There was an error fetching and loading the security content:\n" +
            f"{str(exception)}")
        terminate(msg)

    else:
        msg = _("There was an unexpected problem with the supplied content.")
        terminate(msg)
Exemplo n.º 5
0
    def run(self):
        """Run the task."""
        target_content_dir = utils.join_paths(self._sysroot,
                                              self._target_directory)

        utils.ensure_dir_exists(target_content_dir)

        if self._policy_data.content_type == "scap-security-guide":
            pass  # nothing needed
        elif self._policy_data.content_type == "datastream":
            shutil.copy2(self._content_path, target_content_dir)
        elif self._policy_data.content_type == "rpm":
            # copy the RPM to the target system
            shutil.copy2(self._file_path, target_content_dir)

            # get the path of the RPM
            content_name = common.get_content_name(self._policy_data)
            package_path = utils.join_paths(self._target_directory,
                                            content_name)

            # and install it with yum
            ret = util.execInSysroot(
                "yum", ["-y", "--nogpg", "install", package_path])

            if ret != 0:
                msg = _(f"Failed to install content RPM to the target system.")
                terminate(msg)
                return
        else:
            pattern = utils.join_paths(common.INSTALLATION_CONTENT_DIR, "*")
            utils.universal_copy(pattern, target_content_dir)

        if os.path.exists(self._tailoring_path):
            shutil.copy2(self._tailoring_path, target_content_dir)
Exemplo n.º 6
0
    def _evaluate_rules(self, rule_data):
        # evaluate rules, do automatic fixes and stop if something that cannot
        # be fixed automatically is wrong
        all_messages = rule_data.eval_rules(None, None)
        fatal_messages = [
            message for message in all_messages
            if message.type == common.MESSAGE_TYPE_FATAL
        ]
        if any(fatal_messages):
            msg_lines = [_("Wrong configuration detected!")]
            msg_lines.extend([m.text for m in fatal_messages])
            terminate("\n".join(msg_lines))
            return

        # add packages needed on the target system to the list of packages
        # that are requested to be installed
        packages_data = get_packages_data()
        pkgs_to_install = list(REQUIRED_PACKAGES)

        if self._policy_data.content_type == "scap-security-guide":
            pkgs_to_install.append("scap-security-guide")

        for pkg in pkgs_to_install:
            if pkg not in packages_data.packages:
                packages_data.packages.append(pkg)

        set_packages_data(packages_data)
Exemplo n.º 7
0
    def _verify_fingerprint(self, dest_filename, fingerprint=""):
        if not fingerprint:
            log.info(
                "OSCAP Addon: No fingerprint provided, skipping integrity check"
            )
            return

        hash_obj = utils.get_hashing_algorithm(fingerprint)
        digest = utils.get_file_fingerprint(dest_filename, hash_obj)
        if digest != fingerprint:
            log.error(
                "OSCAP Addon: "
                f"File {dest_filename} failed integrity check - assumed a "
                f"{hash_obj.name} hash and '{fingerprint}', got '{digest}'")
            msg = _(
                f"OSCAP Addon: Integrity check of the content failed - {hash_obj.name} hash didn't match"
            )
            raise content_handling.ContentCheckError(msg)
        log.info(f"Integrity check passed using {hash_obj.name} hash")
Exemplo n.º 8
0
    def setup(self, storage, ksdata, instclass, payload):
        """
        The setup method that should make changes to the runtime environment
        according to the data stored in this object.

        :param storage: object storing storage-related information
                        (disks, partitioning, bootloader, etc.)
        :type storage: blivet.Blivet instance
        :param ksdata: data parsed from the kickstart file and set in the
                       installation process
        :type ksdata: pykickstart.base.BaseHandler instance
        :param instclass: distribution-specific information
        :type instclass: pyanaconda.installclass.BaseInstallClass

        """

        if self.dry_run or not self.profile_id:
            # nothing more to be done in the dry-run mode or if no profile is
            # selected
            return

        if not os.path.exists(self.preinst_content_path) and not os.path.exists(self.raw_preinst_content_path):
            # content not available/fetched yet
            try:
                self._fetch_content_and_initialize()
            except (common.OSCAPaddonError, data_fetch.DataFetchError) as e:
                log.error("Failed to fetch and initialize SCAP content!")
                msg = _("There was an error fetching and loading the security content:\n" +
                        "%s\n" +
                        "The installation should be aborted. Do you wish to continue anyway?") % e

                if flags.flags.automatedInstall and not flags.flags.ksprompt:
                    # cannot have ask in a non-interactive kickstart
                    # installation
                    raise errors.CmdlineError(msg)

                answ = errors.errorHandler.ui.showYesNoQuestion(msg)
                if answ == errors.ERROR_CONTINUE:
                    # prevent any futher actions here by switching to the dry
                    # run mode and let things go on
                    self.dry_run = True
                    return
                else:
                    # Let's sleep forever to prevent any further actions and
                    # wait for the main thread to quit the process.
                    progressQ.send_quit(1)
                    while True:
                        time.sleep(100000)

        # check fingerprint if given
        if self.fingerprint:
            hash_obj = utils.get_hashing_algorithm(self.fingerprint)
            digest = utils.get_file_fingerprint(self.raw_preinst_content_path,
                                                hash_obj)
            if digest != self.fingerprint:
                log.error("Failed to fetch and initialize SCAP content!")
                msg = _("The integrity check of the security content failed.\n" +
                        "The installation should be aborted. Do you wish to continue anyway?")

                if flags.flags.automatedInstall and not flags.flags.ksprompt:
                    # cannot have ask in a non-interactive kickstart
                    # installation
                    raise errors.CmdlineError(msg)

                answ = errors.errorHandler.ui.showYesNoQuestion(msg)
                if answ == errors.ERROR_CONTINUE:
                    # prevent any futher actions here by switching to the dry
                    # run mode and let things go on
                    self.dry_run = True
                    return
                else:
                    # Let's sleep forever to prevent any further actions and
                    # wait for the main thread to quit the process.
                    progressQ.send_quit(1)
                    while True:
                        time.sleep(100000)

        # evaluate rules, do automatic fixes and stop if something that cannot
        # be fixed automatically is wrong
        fatal_messages = [message for message in self.rule_data.eval_rules(ksdata, storage)
                          if message.type == common.MESSAGE_TYPE_FATAL]
        if any(fatal_messages):
            msg = "Wrong configuration detected!\n"
            msg += "\n".join(message.text for message in fatal_messages)
            msg += "\nThe installation should be aborted. Do you wish to continue anyway?"
            if flags.flags.automatedInstall and not flags.flags.ksprompt:
                # cannot have ask in a non-interactive kickstart installation
                raise errors.CmdlineError(msg)

            answ = errors.errorHandler.ui.showYesNoQuestion(msg)
            if answ == errors.ERROR_CONTINUE:
                # prevent any futher actions here by switching to the dry
                # run mode and let things go on
                self.dry_run = True
                return
            else:
                # Let's sleep forever to prevent any further actions and wait
                # for the main thread to quit the process.
                progressQ.send_quit(1)
                while True:
                    time.sleep(100000)

        # add packages needed on the target system to the list of packages
        # that are requested to be installed
        pkgs_to_install = list(REQUIRED_PACKAGES)
        if self.content_type == "scap-security-guide":
            pkgs_to_install.append("scap-security-guide")
        for pkg in pkgs_to_install:
            if pkg not in ksdata.packages.packageList:
                ksdata.packages.packageList.append(pkg)
Exemplo n.º 9
0
def _curl_fetch(url, out_file, ca_certs_path=None):
    """
    Function that fetches data and writes it out to the given file path. If a
    path to the file with CA certificates is given and the url starts with
    'https', the server certificate is validated.

    :param url: url of the data that has to start with 'http://' or "https://"
    :type url: str
    :param out_file: path to the output file
    :type out_file: str
    :param ca_certs_path: path to the file with CA certificates for server
                     certificate validation
    :type ca_certs_path: str
    :raise WrongRequestError: if a wrong combination of arguments is passed
                              (ca_certs_path file path given and url starting with
                              http://) or arguments don't have required format
    :raise CertificateValidationError: if server certificate validation fails
    :raise FetchError: if data fetching fails (usually due to I/O errors)

    """

    if url.startswith("ftp"):
        match = FTP_URL_RE.match(url)
        if not match:
            msg = "Wrong url not matching '%s'" % FTP_URL_RE_STR
            raise WrongRequestError(msg)
        else:
            protocol, path = match.groups()
            if '@' not in path:
                # no user:pass given -> use anonymous login to the FTP server
                url = protocol + "://anonymous:@" + path
    elif url.startswith("file"):
        match = FILE_URL_RE.match(url)
        if not match:
            msg = "Wrong url not matching '%s'" % FILE_URL_RE_STR
            raise WrongRequestError(msg)
    else:
        match = HTTP_URL_RE.match(url)
        if not match:
            msg = "Wrong url not matching '%s'" % HTTP_URL_RE_STR
            raise WrongRequestError(msg)

    # the first group contains the protocol, the second one the rest
    protocol = match.groups()[0]

    if not out_file:
        raise WrongRequestError("out_file cannot be an empty string")

    if ca_certs_path and protocol != "https":
        msg = "Cannot verify server certificate when using plain HTTP"
        raise WrongRequestError(msg)

    curl = pycurl.Curl()
    curl.setopt(pycurl.URL, url)

    if ca_certs_path and protocol == "https":
        # the strictest verification
        curl.setopt(pycurl.SSL_VERIFYHOST, 2)
        curl.setopt(pycurl.SSL_VERIFYPEER, 1)
        curl.setopt(pycurl.CAINFO, ca_certs_path)

    # may be turned off by flags (specified on command line, take precedence)
    if not conf.payload.verify_ssl:
        log.warning("Disabling SSL verification due to the noverifyssl flag")
        curl.setopt(pycurl.SSL_VERIFYHOST, 0)
        curl.setopt(pycurl.SSL_VERIFYPEER, 0)

    try:
        with open(out_file, "wb") as fobj:
            curl.setopt(pycurl.WRITEDATA, fobj)
            curl.perform()
    except pycurl.error as err:
        # first arg is the error code
        if err.args[0] == pycurl.E_SSL_CACERT:
            msg = "Failed to connect to server and validate its "\
                  "certificate: %s" % err
            raise CertificateValidationError(msg)
        else:
            msg = "Failed to fetch data: %s" % err
            raise FetchError(msg)

    if protocol in ("http", "https"):
        return_code = curl.getinfo(pycurl.HTTP_CODE)
        if 400 <= return_code < 600:
            msg = _(
                f"Failed to fetch data - the request returned HTTP error code {return_code}"
            )
            raise FetchError(msg)
Exemplo n.º 10
0
def terminate(message):
    message += "\n" + _("The installation should be aborted.")
    raise NonCriticalInstallationError(message)
Exemplo n.º 11
0
    def setup(self, storage, ksdata, payload):
        """
        The setup method that should make changes to the runtime environment
        according to the data stored in this object.

        :param storage: object storing storage-related information
                        (disks, partitioning, bootloader, etc.)
        :type storage: blivet.Blivet instance
        :param ksdata: data parsed from the kickstart file and set in the
                       installation process
        :type ksdata: pykickstart.base.BaseHandler instance

        """

        if self.dry_run or not self.profile_id:
            # nothing more to be done in the dry-run mode or if no profile is
            # selected
            return

        if not os.path.exists(self.preinst_content_path) and not os.path.exists(self.raw_preinst_content_path):
            # content not available/fetched yet
            try:
                self._fetch_content_and_initialize()
            except (common.OSCAPaddonError, data_fetch.DataFetchError) as e:
                log.error("Failed to fetch and initialize SCAP content!")
                msg = _("There was an error fetching and loading the security content:\n" +
                        "%s\n" +
                        "The installation should be aborted. Do you wish to continue anyway?") % e

                if flags.flags.automatedInstall and not flags.flags.ksprompt:
                    # cannot have ask in a non-interactive kickstart
                    # installation
                    raise errors.CmdlineError(msg)

                answ = errors.errorHandler.ui.showYesNoQuestion(msg)
                if answ == errors.ERROR_CONTINUE:
                    # prevent any futher actions here by switching to the dry
                    # run mode and let things go on
                    self.dry_run = True
                    return
                else:
                    # Let's sleep forever to prevent any further actions and
                    # wait for the main thread to quit the process.
                    progressQ.send_quit(1)
                    while True:
                        time.sleep(100000)

        # check fingerprint if given
        if self.fingerprint:
            hash_obj = utils.get_hashing_algorithm(self.fingerprint)
            digest = utils.get_file_fingerprint(self.raw_preinst_content_path,
                                                hash_obj)
            if digest != self.fingerprint:
                log.error("Failed to fetch and initialize SCAP content!")
                msg = _("The integrity check of the security content failed.\n" +
                        "The installation should be aborted. Do you wish to continue anyway?")

                if flags.flags.automatedInstall and not flags.flags.ksprompt:
                    # cannot have ask in a non-interactive kickstart
                    # installation
                    raise errors.CmdlineError(msg)

                answ = errors.errorHandler.ui.showYesNoQuestion(msg)
                if answ == errors.ERROR_CONTINUE:
                    # prevent any futher actions here by switching to the dry
                    # run mode and let things go on
                    self.dry_run = True
                    return
                else:
                    # Let's sleep forever to prevent any further actions and
                    # wait for the main thread to quit the process.
                    progressQ.send_quit(1)
                    while True:
                        time.sleep(100000)

        # evaluate rules, do automatic fixes and stop if something that cannot
        # be fixed automatically is wrong
        fatal_messages = [message for message in self.rule_data.eval_rules(ksdata, storage)
                          if message.type == common.MESSAGE_TYPE_FATAL]
        if any(fatal_messages):
            msg = "Wrong configuration detected!\n"
            msg += "\n".join(message.text for message in fatal_messages)
            msg += "\nThe installation should be aborted. Do you wish to continue anyway?"
            if flags.flags.automatedInstall and not flags.flags.ksprompt:
                # cannot have ask in a non-interactive kickstart installation
                raise errors.CmdlineError(msg)

            answ = errors.errorHandler.ui.showYesNoQuestion(msg)
            if answ == errors.ERROR_CONTINUE:
                # prevent any futher actions here by switching to the dry
                # run mode and let things go on
                self.dry_run = True
                return
            else:
                # Let's sleep forever to prevent any further actions and wait
                # for the main thread to quit the process.
                progressQ.send_quit(1)
                while True:
                    time.sleep(100000)

        # add packages needed on the target system to the list of packages
        # that are requested to be installed
        pkgs_to_install = list(REQUIRED_PACKAGES)
        if self.content_type == "scap-security-guide":
            pkgs_to_install.append("scap-security-guide")
        for pkg in pkgs_to_install:
            if pkg not in ksdata.packages.packageList:
                ksdata.packages.packageList.append(pkg)