From e67a753aebdbc2d44b5e9a9744ff62712d2ffbc7 Mon Sep 17 00:00:00 2001 From: "(no author)" <(no author)@cd1c1023-2f26-0410-ae45-c471fc1f0318> Date: Thu, 7 Feb 2008 14:29:37 +0000 Subject: [PATCH] GLSA tool: - fixed atom matching when slotted packages are found - added info tool, that shows glsa information for the provided identifier - misc bug fixes git-svn-id: http://svn.sabayonlinux.org/projects/entropy/trunk@1164 cd1c1023-2f26-0410-ae45-c471fc1f0318 --- TODO | 5 +- client/equo | 7 +- client/text_security.py | 144 ++++++++++++++++++++++++++++++++++++++++ libraries/entropy.py | 72 +++++++++++++------- 4 files changed, 200 insertions(+), 28 deletions(-) diff --git a/TODO b/TODO index fd13ff477..cdfa0304a 100644 --- a/TODO +++ b/TODO @@ -1,8 +1,5 @@ TODO list: - - glsa utility (*) - - self.advisories_changed FIXME - - tool that prints glsa information - - tool that installs all the security updates + - improve dependencies order resolution - migrate server code to ServerInterface [] write a tool that helps keeping packages updated (also supporting injected ones) - packages.sabayonlinux.org interactivity (comments + images upload + connection to phpbb user db) diff --git a/client/equo b/client/equo index 38a39bec7..c6103544a 100644 --- a/client/equo +++ b/client/equo @@ -43,7 +43,7 @@ def print_help(): print_info(" \t"+blue("world")+brown("\t\t update system with the latest available packages")) print_info(" \t\t"+red("--ask")+"\t\t\t ask before making any changes") - print_info(" \t\t"+red("--fetch")+"\t\t\t just download packages without doing the install") + print_info(" \t\t"+red("--fetch")+"\t\t\t just download packages") print_info(" \t\t"+red("--pretend")+"\t\t just show what would be done") print_info(" \t\t"+red("--verbose")+"\t\t show more details about what's going on") print_info(" \t\t"+red("--replay")+"\t\t reinstall all the packages and their dependencies") @@ -58,6 +58,11 @@ def print_help(): print_info(" \t\t"+darkgreen("list")+red("\t\t\t list all the available Security Advisories")) print_info(" \t\t\t"+red("--affected")+"\t list only affected") print_info(" \t\t\t"+red("--unaffected")+"\t list only unaffected") + print_info(" \t\t"+darkgreen("info")+red("\t\t\t show information about provided advisories identifiers")) + print_info(" \t\t"+darkgreen("install")+red("\t\t\t automaticly install all the available security updates")) + print_info(" \t\t\t"+red("--ask")+"\t\t\t ask before making any changes") + print_info(" \t\t\t"+red("--fetch")+"\t\t\t just download packages") + print_info(" \t\t\t"+red("--pretend")+"\t\t just show what would be done") print_info(" \t\t"+red("--quiet")+"\t\t\t show less details (useful for scripting)") print_info(" \t"+blue("install")+brown("\t\t install one or more packages or .tbz2")) diff --git a/client/text_security.py b/client/text_security.py index f81f926ab..e71ed95c3 100644 --- a/client/text_security.py +++ b/client/text_security.py @@ -32,21 +32,141 @@ def security(options): only_affected = False only_unaffected = False + fetch = False for opt in options: if opt == "--affected": only_affected = True elif opt == "--unaffected": only_unaffected = True + elif opt == "--fetch": + fetch = True if options[0] == "update": rc = Equo.Security.fetch_advisories() elif options[0] == "list": rc = list_advisories(only_affected = only_affected, only_unaffected = only_unaffected) + elif options[0] == "install": + Equo.load_cache() + rc = install_packages(fetch = fetch) + Equo.save_cache() + elif options[0] == "info": + rc = show_advisories_info(options[1:]) else: rc = -10 return rc +def show_advisories_info(advisories): + if not advisories: + print_error(brown(" :: ")+darkgreen("No advisories provided.")) + return 1 + + adv_metadata = Equo.Security.get_advisories_metadata() + for advisory in advisories: + if advisory not in adv_metadata: + print_warning(brown(" :: ")+darkred("Advisory ")+blue(advisory)+darkred(" does not exist.")) + continue + print_advisory_information(adv_metadata[advisory], key = advisory) + + return 0 + +def print_advisory_information(advisory_data, key): + + # print advisory code + glsa_url = "http://www.gentoo.org/security/en/glsa/%s" % (advisory_data['filename'],) + print_info(blue(" @@ ")+red("GLSA Identifier ")+bold(key)+red(" | ")+blue(glsa_url)) + + # title + print_info("\t"+darkgreen("Title:\t\t")+darkred(advisory_data['title'])) + + # description + description = advisory_data['description'].split("\n") + desc_text = "\t"+darkgreen("Description:\t") + for x in description: + print_info(desc_text+x.strip()) + desc_text = "\t\t\t" + + # background + if advisory_data['background']: + background = advisory_data['background'].split("\n") + bg_text = "\t"+darkgreen("Background:\t") + for x in background: + print_info(bg_text+purple(x.strip())) + bg_text = "\t\t\t" + + # access + if advisory_data['access']: + print_info("\t"+darkgreen("Exploitable:\t")+bold(advisory_data['access'])) + + # impact + if advisory_data['impact']: + impact = advisory_data['impact'].split("\n") + imp_text = "\t"+darkgreen("Impact:\t\t") + for x in impact: + print_info(imp_text+brown(x.strip())) + imp_text = "\t\t\t" + + # impact type + if advisory_data['impacttype']: + print_info("\t"+darkgreen("Impact type:\t")+bold(advisory_data['impacttype'])) + + # revised + if advisory_data['revised']: + print_info("\t"+darkgreen("Revised:\t")+brown(advisory_data['revised'])) + + # announced + if advisory_data['announced']: + print_info("\t"+darkgreen("Announced:\t")+brown(advisory_data['announced'])) + + # synopsis + synopsis = advisory_data['synopsis'].split("\n") + syn_text = "\t"+darkgreen("Synopsis:\t") + for x in synopsis: + print_info(syn_text+x.strip()) + syn_text = "\t\t\t" + + # references + if advisory_data['references']: + print_info("\t"+darkgreen("References:")) + for reference in advisory_data['references']: + print_info("\t\t\t"+darkblue(reference)) + + # gentoo bugs + if advisory_data['bugs']: + print_info("\t"+darkgreen("Gentoo bugs:")) + for bug in advisory_data['bugs']: + bug = "https://bugs.gentoo.org/show_bug.cgi?id=%s" % (bug,) + print_info("\t\t\t"+darkblue(bug)) + + # affected + if advisory_data['affected']: + print_info("\t"+darkgreen("Affected:")) + for key in advisory_data['affected']: + print_info("\t\t\t"+darkred(key)) + affected_data = advisory_data['affected'][key][0] + vul_vers = affected_data['vul_vers'] + unaff_vers = affected_data['unaff_vers'] + if vul_vers: + print_info("\t\t\t "+brown("vulnerable versions: ")+", ".join(vul_vers)) + if unaff_vers: + print_info("\t\t\t "+brown("unaffected versions: ")+", ".join(unaff_vers)) + #print affected_data + + # workaround + if advisory_data['workaround']: + print_info("\t"+darkgreen("Workaround:\t")+darkred(advisory_data['workaround'])) + + # resolution + if advisory_data['resolution']: + res_text = "\t"+darkgreen("Resolution:\t") + resolutions = advisory_data['resolution'] + for resolution in resolutions: + for x in resolution.split("\n"): + print_info(res_text+x.strip()) + res_text = "\t\t\t" + +{'background': u''} + def list_advisories(only_affected = False, only_unaffected = False): if (not only_affected and not only_unaffected) or (only_affected and only_unaffected): @@ -85,4 +205,28 @@ def list_advisories(only_affected = False, only_unaffected = False): print_info(description) return 0 +def install_packages(fetch = False): + import text_ui + + print_info(red(" @@ ")+blue("Calculating security updates...")) + affected_atoms = Equo.Security.get_affected_atoms() + # match in client database + valid_matches = set() + for atom in affected_atoms: + match = Equo.clientDbconn.atomMatch(atom) + if match[0] == -1: + continue + # get key + slot + key, slot = Equo.clientDbconn.retrieveKeySlot(match[0]) + # match in repos + match = Equo.atomMatch(key, matchSlot = slot) + if match[0] != -1: + valid_matches.add(match) + + if not valid_matches: + print_info(red(" @@ ")+blue("All the available updates have been already installed.")) + return 0 + + rc, stat = text_ui.installPackages(atomsdata = valid_matches, onlyfetch = fetch) + return rc diff --git a/libraries/entropy.py b/libraries/entropy.py index 29ac6d67e..8e2e43747 100644 --- a/libraries/entropy.py +++ b/libraries/entropy.py @@ -4853,10 +4853,8 @@ class TriggerInterface: functions.add('addbootablekernel') if x.startswith('/usr/src/'): functions.add('createkernelsym') - ''' if x.startswith('/etc/env.d/'): functions.add('env_update') - ''' if os.path.dirname(x) in ldpaths: if x.find(".so") > -1: functions.add('run_ldconfig') @@ -6215,7 +6213,7 @@ class SecurityInterface: "ge": ">=", "rge": ">=", # >=~ "rle": "<=", # <=~ - "rgt": " >", # >~ + "rgt": ">", # >~ "rlt": "<" # <~ } @@ -6225,24 +6223,23 @@ class SecurityInterface: self.security_url_checksum = etpConst['securityurl']+etpConst['packageshashfileext'] self.download_package = os.path.join(self.unpackdir,os.path.basename(etpConst['securityurl'])) self.download_package_checksum = self.download_package+etpConst['packageshashfileext'] + self.old_download_package_checksum = os.path.join(etpConst['dumpstoragedir'],os.path.basename(etpConst['securityurl']))+etpConst['packageshashfileext'] self.security_package = os.path.join(etpConst['securitydir'],os.path.basename(etpConst['securityurl'])) self.security_package_checksum = self.security_package+etpConst['packageshashfileext'] + if os.path.isfile(etpConst['securitydir']) or os.path.islink(etpConst['securitydir']): os.remove(etpConst['securitydir']) if not os.path.isdir(etpConst['securitydir']): os.makedirs(etpConst['securitydir']) - elif os.path.isfile(self.security_package_checksum): + elif os.path.isfile(self.old_download_package_checksum): + f = open(self.old_download_package_checksum) try: - f = open(self.security_package_checksum) self.previous_checksum = f.readline().strip().split()[0] - f.close() - except: # FIXME really horrible exception trapping - try: - f.close() - except: - pass + except: + pass + f.close() def __prepare_unpack(self): @@ -6457,12 +6454,15 @@ class SecurityInterface: vul_atoms = mydata['affected'][key][0]['vul_atoms'] unaff_atoms = mydata['affected'][key][0]['unaff_atoms'] unaffected_atoms = set() - for atom in unaff_atoms: - match = self.Entropy.clientDbconn.atomMatch(atom) - if match[0] != -1: - unaffected_atoms.add(match) if not vul_atoms: return False + # XXX: does multimatch work correctly? + for atom in unaff_atoms: + matches = self.Entropy.clientDbconn.atomMatch(atom, multiMatch = True) + if matches[1] == 0: + for idpackage in matches[0]: + unaffected_atoms.add((idpackage,0)) + for atom in vul_atoms: match = self.Entropy.clientDbconn.atomMatch(atom) if (match[0] != -1) and (match not in unaffected_atoms): @@ -6555,16 +6555,38 @@ class SecurityInterface: references = glsa_tree.getElementsByTagName("references")[0] xml_data['references'] = [x.getAttribute("link").strip() for x in references.getElementsByTagName("uri")] - xml_data['description'] = glsa_tree.getElementsByTagName("description")[0].firstChild.data.strip() - xml_data['workaround'] = glsa_tree.getElementsByTagName("workaround")[0].firstChild.data.strip() - xml_data['resolution'] = glsa_tree.getElementsByTagName("resolution")[0].firstChild.data.strip() - xml_data['impact'] = glsa_tree.getElementsByTagName("impact")[0].firstChild.data.strip() - xml_data['impacttype'] = glsa_tree.getElementsByTagName("impact")[0].getAttribute("type").strip() - xml_data['background'] = "" try: - xml_data['background'] = glsa_tree.getElementsByTagName("background")[0].firstChild.data.strip() + description = glsa_tree.getElementsByTagName("description")[0] + xml_data['description'] = description.getElementsByTagName("p")[0].firstChild.data.strip() except IndexError: - pass + xml_data['description'] = "" + try: + workaround = glsa_tree.getElementsByTagName("workaround")[0] + xml_data['workaround'] = workaround.getElementsByTagName("p")[0].firstChild.data.strip() + except IndexError: + xml_data['workaround'] = "" + + try: + xml_data['resolution'] = [] + resolution = glsa_tree.getElementsByTagName("resolution")[0] + p_elements = resolution.getElementsByTagName("p") + for p_elem in p_elements: + xml_data['resolution'].append(p_elem.firstChild.data.strip()) + except IndexError: + xml_data['resolution'] = [] + + try: + impact = glsa_tree.getElementsByTagName("impact")[0] + xml_data['impact'] = impact.getElementsByTagName("p")[0].firstChild.data.strip() + except IndexError: + xml_data['impact'] = "" + xml_data['impacttype'] = glsa_tree.getElementsByTagName("impact")[0].getAttribute("type").strip() + + try: + background = glsa_tree.getElementsByTagName("background")[0] + xml_data['background'] = background.getElementsByTagName("p")[0].firstChild.data.strip() + except IndexError: + xml_data['background'] = "" # affection information affected = glsa_tree.getElementsByTagName("affected")[0] @@ -6720,6 +6742,10 @@ class SecurityInterface: else: raise exceptionTools.InvalidData("InvalidData: return status not valid.") + # save downloaded md5 + if os.path.isfile(self.download_package_checksum) and os.path.isdir(etpConst['dumpstoragedir']): + shutil.copy2(self.download_package_checksum,self.old_download_package_checksum) + # now unpack in place status = self.__unpack_advisories() if status != 0: