- initial implementation of a GLSA-based security tool

git-svn-id: http://svn.sabayonlinux.org/projects/entropy/trunk@1131 cd1c1023-2f26-0410-ae45-c471fc1f0318
This commit is contained in:
(no author)
2008-02-05 01:48:12 +00:00
parent 5f5b6eda7f
commit d630249146
5 changed files with 284 additions and 1 deletions
+1
View File
@@ -2,6 +2,7 @@ TODO list:
- glsa utility (*)
- 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)
- Community repositories
- split RDEPEND and PDEPEND
- find a way to better handle real smartapps deps (need split PDEPEND?)
+7
View File
@@ -53,6 +53,9 @@ def print_help():
print_info(" \t\t"+red("--upgrade")+"\t\t upgrade "+etpConst['systemname']+" to the specified release (3.5,3.6...)")
print_info(" \t\t"+red("--nochecksum")+"\t\t disable package integrity check")
print_info(" \t"+blue("security")+brown("\t\t security infrastructure tools"))
print_info(" \t\t"+darkgreen("update")+red("\t\t\t download the latest Security Advisories"))
print_info(" \t"+blue("install")+brown("\t\t install one or more packages or .tbz2"))
print_info(" \t\t"+red("--ask")+"\t\t\t ask before making any changes")
print_info(" \t\t"+red("--pretend")+"\t\t just show what would be done")
@@ -252,6 +255,10 @@ try:
rc = text_ui.package(options)
load_conf_cache()
elif options[0] == "security":
import text_security
rc = text_security.security(options[1:])
elif (options[0] == "query"):
import text_query
rc = text_query.query(options[1:])
+11
View File
@@ -71,3 +71,14 @@ conntestlink|http://svn.sabayonlinux.org
# downloadspeedlimit|30
#
# downloadspeedlimit|
#
# syntax for securityurl:
#
# securityurl: Entropy compatible Security Advisories (GLSAs) package URL
# securityurl|<HTTP/FTP download URL>
#
# example:
# securityurl|http://packages.sabayonlinux.org/security/security-advisories.tar.bz2
#
# securityurl|
+256 -1
View File
@@ -93,6 +93,9 @@ class EquoInterface(TextInterface):
self.FileUpdates = self.__FileUpdates()
self.repoDbCache = {}
# security interface
self.Security = SecurityInterface(self)
def switchChroot(self, chroot = ""):
# clean caches
self.purge_cache()
@@ -3586,7 +3589,7 @@ class RepoInterface:
# Test network connectivity
conntest = self.Entropy.entropyTools.get_remote_data(etpConst['conntestlink'])
if not conntest:
raise exceptionTools.OnlineMirrorError("OnlineMirrorError: Cannot connect to ")
raise exceptionTools.OnlineMirrorError("OnlineMirrorError: Cannot connect to %s" % (etpConst['conntestlink'],))
if not self.reponames:
for x in etpRepositories:
@@ -3923,6 +3926,8 @@ class RepoInterface:
# clean caches
if self.dbupdated:
self.Entropy.generate_cache(depcache = True, configcache = False)
# update Security Advisories
self.Entropy.Security.fetch_advisories()
if self.syncErrors:
self.Entropy.updateProgress( red("Something bad happened. Please have a look."),
@@ -6135,3 +6140,253 @@ class ErrorReportInterface:
else:
raise exceptionTools.PermissionDenied("PermissionDenied: not yet prepared")
'''
I wrote this after having drunk a Red Bull,
so I think the code will look nice and sharp
~~ GIVES YOU WINGS ~~
'''
class SecurityInterface:
def __init__(self, EquoInstance):
self.Entropy = EquoInstance
try:
self.Entropy.instanceTest()
except:
raise exceptionTools.IncorrectParameter("IncorrectParameter: a valid Entropy Instance is needed")
self.lastfetch = None
self.previous_checksum = "0"
self.advisories_changed = None
self.unpackdir = os.path.join(etpConst['entropyunpackdir'],"security-"+str(self.Entropy.entropyTools.getRandomNumber()))
self.security_url = etpConst['securityurl']
self.unpacked_package = os.path.join(self.unpackdir,"glsa_package")
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.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):
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
def __prepare_unpack(self):
if os.path.isfile(self.unpackdir) or os.path.islink(self.unpackdir):
os.remove(self.unpackdir)
if os.path.isdir(self.unpackdir):
shutil.rmtree(self.unpackdir,True)
try:
os.rmdir(self.unpackdir)
except OSError:
pass
os.makedirs(self.unpackdir)
def __download_glsa_package(self):
return self.__generic_download(self.security_url, self.download_package)
def __download_glsa_package_checksum(self):
return self.__generic_download(self.security_url_checksum, self.download_package_checksum, showSpeed = False)
def __generic_download(self, url, save_to, showSpeed = True):
fetchConn = self.Entropy.urlFetcher(url, save_to, resume = False, showSpeed = showSpeed)
fetchConn.progress = self.Entropy.progress
rc = fetchConn.download()
del fetchConn
status = True
if rc in ("-1","-2","-3"):
status = False
return status
def __verify_checksum(self):
# read checksum
if not os.path.isfile(self.download_package_checksum) or not os.access(self.download_package_checksum,os.R_OK):
return 1
f = open(self.download_package_checksum)
try:
checksum = f.readline().strip().split()[0]
f.close()
except:
return 2
if checksum == self.previous_checksum:
self.advisories_changed = False
else:
self.advisories_changed = True
md5res = self.Entropy.entropyTools.compareMd5(self.download_package,checksum)
if not md5res:
return 3
return 0
def __unpack_advisories(self):
rc = self.Entropy.entropyTools.uncompressTarBz2(
self.download_package,
self.unpacked_package,
catchEmpty = True
)
return rc
def __clear_previous_advisories(self):
if os.listdir(etpConst['securitydir']):
shutil.rmtree(etpConst['securitydir'],True)
if not os.path.isdir(etpConst['securitydir']):
os.makedirs(etpConst['securitydir'])
def __put_advisories_in_place(self):
for advfile in os.listdir(self.unpacked_package):
from_file = os.path.join(self.unpacked_package,advfile)
to_file = os.path.join(etpConst['securitydir'],advfile)
shutil.move(from_file,to_file)
def __cleanup_garbage(self):
shutil.rmtree(self.unpackdir,True)
def fetch_advisories(self):
self.Entropy.updateProgress(
blue("Testing ")+bold("Security Advisories")+blue(" service connection"),
importance = 2,
type = "info",
header = red("@@ "),
footer = red(" ...")
)
# Test network connectivity
conntest = self.Entropy.entropyTools.get_remote_data(etpConst['conntestlink'])
if not conntest:
raise exceptionTools.OnlineMirrorError("OnlineMirrorError: Cannot connect to %s" % (etpConst['conntestlink'],))
self.Entropy.updateProgress(
blue("Getting the latest ")+bold("Security Advisories")+darkgreen(" (GLSAs)"),
importance = 2,
type = "info",
header = red("@@ "),
footer = red(" ...")
)
# prepare directories
self.__prepare_unpack()
# download package
status = self.__download_glsa_package()
self.lastfetch = status
if not status:
self.Entropy.updateProgress(
blue("Security Advisories: unable to download package, sorry."),
importance = 2,
type = "error",
header = red(" ## ")
)
return 1
self.Entropy.updateProgress(
blue("Verifying checksum"),
importance = 1,
type = "info",
header = red(" # "),
footer = red(" ..."),
back = True
)
# download digest
status = self.__download_glsa_package_checksum()
if not status:
self.Entropy.updateProgress(
blue("Security Advisories: cannot download checksum, sorry."),
importance = 2,
type = "error",
header = red(" ## ")
)
return 2
# verify digest
status = self.__verify_checksum()
if status == 1:
self.Entropy.updateProgress(
blue("Security Advisories: cannot open packages, sorry."),
importance = 2,
type = "error",
header = red(" ## ")
)
return 3
elif status == 2:
self.Entropy.updateProgress(
blue("Security Advisories: cannot read checksum, sorry."),
importance = 2,
type = "error",
header = red(" ## ")
)
return 4
elif status == 3:
self.Entropy.updateProgress(
blue("Security Advisories: digest verification failed, sorry."),
importance = 2,
type = "error",
header = red(" ## ")
)
return 5
elif status == 0:
self.Entropy.updateProgress(
darkgreen("Verification Successful"),
importance = 1,
type = "info",
header = red(" # ")
)
else:
raise exceptionTools.InvalidData("InvalidData: return status not valid.")
# now unpack in place
status = self.__unpack_advisories()
if status != 0:
self.Entropy.updateProgress(
blue("Security Advisories: digest verification failed, try again later."),
importance = 2,
type = "error",
header = red(" ## ")
)
return 6
self.Entropy.updateProgress(
darkgreen("Installing Security Advisories"),
importance = 1,
type = "info",
header = red(" # "),
footer = red(" ...")
)
# clear previous
self.__clear_previous_advisories()
# copy over
self.__put_advisories_in_place()
# remove temp stuff
self.__cleanup_garbage()
if self.advisories_changed:
advtext = darkgreen("Security Advisories: updated successfully")
else:
advtext = darkred("Security Advisories: already up to date")
self.Entropy.updateProgress(
advtext,
importance = 2,
type = "info",
header = red(" @@ ")
)
return 0
+9
View File
@@ -517,6 +517,7 @@ def initConfig_entropyConstants(rootdir):
ETP_SMARTAPPSDIR = "/smartapps/"+ETP_ARCH_CONST
ETP_SMARTPACKAGESDIR = "/smartpackages/"+ETP_ARCH_CONST
ETP_CACHESDIR = "/caches/"
ETP_SECURITYDIR = "/glsa/"
ETP_LOG_DIR = ETP_DIR+"/"+"logs"
ETP_CONF_DIR = rootdir+"/etc/entropy"
ETP_SYSLOG_DIR = rootdir+"/var/log/entropy/"
@@ -651,6 +652,8 @@ def initConfig_entropyConstants(rootdir):
'downloadspeedlimit': None, # equo packages download speed limit (in kb/sec)
'dumpstoragedir': ETP_DIR+ETP_CACHESDIR, # data storage directory, useful to speed up equo across multiple issued commands
'securitydir': ETP_DIR+ETP_SECURITYDIR, # where GLSAs are stored
'securityurl': "http://packages.sabayonlinux.org/security/security-advisories.tar.bz2",
# packages keywords/mask/unmask settings
'packagemasking': {}, # package masking information dictionary filled by maskingparser.py
@@ -871,6 +874,12 @@ def initConfig_entropyConstants(rootdir):
except:
etpConst['downloadspeedlimit'] = None
elif (line.find("securityurl|") != -1) and (not line.startswith("#")) and (len(line.split("|")) == 2):
try:
url = line.split("|")[1]
etpConst['securityurl'] = url
except:
pass
# align etpConst['binaryurirelativepath'] and etpConst['etpurirelativepath'] with etpConst['product']
etpConst['binaryurirelativepath'] = etpConst['product']+"/"+etpConst['binaryurirelativepath']