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
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)
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
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)
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)
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)
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")
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)
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)
def terminate(message): message += "\n" + _("The installation should be aborted.") raise NonCriticalInstallationError(message)
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)