123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504 |
- #!/usr/bin/perl
- ###############################################################################
- ##
- ## Functions for downloading and parsing Debian Security Advisories (DSAs)
- ## Currently also includes some FreeBSD Advisory parsing (which sucks)
- ##
- ###############################################################################
- use strict;
- use warnings;
- #use Storable; # persistant storage
- use LWP::UserAgent;
- #use Config::Auto; # libconfig-auto-perl
- use Time::ParseDate; # libtime-modules-perl
- #use IO::Uncompress::Bunzip2 qw(bunzip2 $Bunzip2Error);
- #use IO::Uncompress::Gunzip qw(gunzip $GunzipError);
- #use Digest::MD5 qw(md5_hex);
- use POSIX qw(mktime);
- #use File::Path qw(make_path);
- use HTML::Parser;
- use feature "switch";
- # Track renamed packages here, easy but manual. We tried to automate it
- # using package metadata for distribution upgrades but that really sucked.
- sub unifySrcName {
- my $name = shift;
-
- given ($name) {
- ## TODO: it should map to the most recent version, not unversioned
- ## TODO: we can partially automate this..
- ## -> make all lower-case
- ## -> replace -X.Y version numbers by highest encounter(?)
- ## -> handle special cases like xfree86
- when ("proftp-dfsg") { $name = "proftp" };
- when ("mozilla-firefox") { $name = "iceweasel" };
- when ("mozilla-thunderbird") { $name = "icedove" };
- when ("squid3") { $name = "squid" };
- when ("squid/squid3") { $name = "squid" };
- when ("tk8.3") { $name = "tk8.4" };
- when ("tk8.4") { $name = "tk8.4" };
- when ("xpdf-i") { $name = "xpdf" };
- when ("zope2.10/zope2.9") { $name = "zope2.7" };
- when ("zope-cmfplone") { $name = "zope2.7" };
- when ("zope-ldapuserfolder") { $name = "zope2.7" };
- when ("librmagick-ruby") { $name = "ruby-rmagick" };
- when ("libcompass-ruby") { $name = "ruby-compass" };
- when ("bio-ruby") { $name = "ruby-bio" };
- when ("request-tracker3.4") { $name = "request-tracker3.8" };
- when ("request-tracker3.6") { $name = "request-tracker3.8" };
- when ("perl-5.005") { $name = "perl" };
- when ("otrs2") { $name = "otrs" };
- when ("openldap2.3") { $name = "openldap" };
- when ("openldap2") { $name = "openldap" };
- when ("libreoffice") { $name = "openoffice.org" };
- when ("nsd3") { $name = "nsd" };
- when ("network-manager/network-manager-applet") { $name = "network-manager" };
- when ("nagios3") { $name = "nagios" };
- when ("nagios2") { $name = "nagios" };
- when ("mysql-dfsg-4.1") { $name = "mysql" };
- when ("mysql-dfsg-5.0") { $name = "mysql" };
- when ("mysql-dfsg") { $name = "mysql" };
- when ("linux-2.6.24") { $name = "linux-2.6" };
- when ("linux-kernel-alpha") { $name = "linux-2.4" };
- when ("linux-kernel-i386") { $name = "linux-2.4" };
- when ("libmusicbrainz-2.0") { $name = "libmusicbrainz3" };
- when ("libmusicbrainz-2.1") { $name = "libmusicbrainz3" };
- when ("libgtop1") { $name = "libgtop2" };
- when ("libgd1") { $name = "libgd2" };
- when ("libast1") { $name = "libast" };
- when ("libmozjs0d") { $name = "libast" };
- when (/^kernel-source-2.2.*/) { $name = "linux-2.2" };
- when (/^kernel-patch-2.2.*/) { $name = "linux-2.2" };
- when ("kernel") { $name = "linux-2.4" };
- when (/^kernel-source-2.4.*/) { $name = "linux-2.4" };
- when (/^kernel-image-2.2.*/) { $name = "linux-2.2" };
- when (/^kernel-image-2.4.*/) { $name = "linux-2.4" };
- when (/^kernel-patch-2.4.*/) { $name = "linux-2.4" };
- when ("kernel-patch-benh") { $name = "linux-2.4" };
- when ("kernel-patch-vserver") { $name = "linux-2.4" };
- when (/^kernel-source-2.6.*/) { $name = "linux-2.6" };
- when ("gnutls11") { $name = "gnutls26" };
- when ("gnutls13") { $name = "gnutls26" };
- when ("gallery2") { $name = "gallery" };
- when ("firebird2") { $name = "firebird2.5" };
- when ("firebird2.0") { $name = "firebird2.5" };
- when ("firebird2.1") { $name = "firebird2.5" };
- when ("fltk1.1") { $name = "fltk1.3" };
- when ("fox1.4") { $name = "fox1.6" };
- when ("exim-tls") { $name = "exim" };
- when ("exim4") { $name = "exim" };
- when ("epic4") { $name = "epic" };
- when ("drupal6") { $name = "drupal" };
- when ("dhcp") { $name = "dhcp3" };
- when ("cyrus-sasl") { $name = "cyrus-sasl2" };
- when (/^cyrus-imapd.*/) { $name = "cyrus-imapd" };
- when (/^kolab-cyrus-imapd.*/) { $name = "cyrus-imapd" };
- when ("cfengine") { $name = "cfengine2" };
- when ("bind") { $name = "bind9" };
- when ("apache") { $name = "apache2" };
- when ("horde2") { $name = "horde3" };
- when ("mediawiki1.7") { $name = "mediawiki" };
- when ("ffmpeg-debian") { $name = "ffmpeg" };
- when ("xserver-xorg") { $name = "xorg-server" };
- when ("xfree86-1") { $name = "xorg-server" };
- when ("xfree86v3") { $name = "xorg-server" };
- when ("xfree86") { $name = "xorg-server" };
- when ("xfree86") { $name = "xorg-server" };
- when ("xorg") { $name = "xorg-server" };
- when ("typo3") { $name = "typo3-src" };
- when ("lvm10") { $name = "lvm2" };
- when ("cupsys") { $name = "cups" };
- when ("ethereal") { $name = "wireshark" };
- when ("libboost1.42") { $name = "libboost1.46" };
- when ("cinelerra-cv") { $name = "cinelerra" };
- when ("mplayer-dmo") { $name = "mplayer" };
- when ("libcap") { $name = "libgda2" };
- when ("xkb-data-legacy") { $name = "xkeyboard-config" };
- when ("boost-defaults") { $name = "boost" };
- when ("xen-3") { $name = "xen" };
- when ("kde-icons-gorilla") { $name = "kde-icons-korilla" };
- when ("kde4libs") { $name = "kdelibs" };
- when ("libcgi-application-extra-plugin-bundle-perl") { $name = "libcgi-application-plugins-perl"};
- when (/^openssl\d$/) { $name = "openssl" };
- when (/^tomcat\d/) { $name = "tomcat7" };
- when (/^tomcat\d.\d$/) { $name = "tomcat7" };
- when (/^libgda\d/) { $name = "libgda4" };
- when (/^readline\d/) { $name = "readline6" };
- when (/^libwnck\d/) { $name = "libwnck" };
- when (/^xerces-c\d/) { $name = "xerces-c" };
- when (/^libticalcs\d/) { $name = "libticals" };
- when (/^libtifiles\d/) { $name = "libtifiles" };
- when (/^db\d.\d$/) { $name = "db4.8" };
- when (/^gcc-.*/) { $name = "gcc" };
- when (/^automake\d+.*/) { $name = "automake" };
- when (/^sun-java\d/) { $name = "sun-java6" };
- when (/^open-jdk\d/) { $name = "open-jdk7" };
- when (/^mbrola-es\d/) { $name = "mbrola-es" };
- when (/^mgltools-.*/) { $name = "mgltools" };
- when (/^coin\d$/) { $name = "coin" };
- when (/^adobereader-\.*/) { $name = "adobereader" };
- when (/^picon-\.*/) { $name = "picon" };
- when (/^nvidia-graphics-drivers\.*/) { $name = "nvidia-graphics-drivers" };
- when (/^boost\d\.\d\d/) { $name = "boost" };
- when (/^llvm-\d.\d/) { $name = "llvm" };
- when (/^octave\d.\d/) { $name = "octave" };
- when (/^libjibx\d.\d-java/) { $name = "libjibx-java" };
- when (/^emacs2\d/) { $name = "emacs23" };
- when (/^emacs2\d-non-dfsg/) { $name = "emacs23" };
- when (/^libupnp\d/) { $name = "libupnp" };
- when (/^python\d.\d/) { $name = "python3.2" };
- when (/^postgresql-\d.\d/) { $name = "postgresql-9.0" };
- when (/^ruby\d.\d/) { $name = "ruby1.9" };
- when (/^php\d/) { $name = "php5" };
- when (/^PHP\d/) { $name = "php5" };
- };
- return $name;
- }
- ## Should this advisory be skipped?
- sub blacklistedDSA {
- my $dsa_id = shift or die "Advisory blacklist: no id given..";
-
- ## check if DSA is blacklisted..
- my @id_blacklist = ("DSA-1975", "DSA-2360");
-
- grep ($_ eq $dsa_id, @id_blacklist) and return "true";
-
- return; # FALSE
- }
- ## Static map to correct errors in DSAs
- ## Return fixed list of CVE IDs or 0 to skip DSA
- sub fixDSAquirks {
- my $dsa_id = shift;
- my $dsa_state = shift;
-
- my @new_names = @{@$dsa_state[0]};
- my $new_date = @$dsa_state[1];
- my @new_cves = @{@$dsa_state[2]};
-
- ## most of these have dupe/wrong CVE referenced
- given ($dsa_id) {
- when ($_ eq "085") {
- @new_cves = ("CVE-2001-1562", "LOCAL-03/04/05", "LOCAL-08/24/08");
- }
- when ($_ eq "745") {
- @new_cves = ("CVE-2005-1921", "CVE-2005-2106", "CVE-2005-1921");
- }
- when ($_ eq "1095") {
- @new_cves = ("CVE-2006-0747", "CVE-2006-1861", "CVE-2006-2661");
- }
- when ($_ eq "1284") {
- @new_cves = ("CVE-2007-1320", "CVE-2007-1321", "CVE-2007-1322",
- "CVE-2007-2893", "CVE-2007-1366");
- }
- when ($_ eq "1502") {
- @new_cves = ("CVE-2007-2821", "CVE-2007-3238", "CVE-2008-0193", "CVE-2008-0194");
- }
- when ($_ eq "1706") {
- @new_cves = ("CVE-2009-0135", "CVE-2009-0136");
- }
- when ($_ eq "1757") {
- @new_cves = ("CVE-2007-2383", "CVE-2008-7220", "CVE-2009-1208");
- }
- when ($_ eq "1896") {
- @new_cves = ("CVE-2009-3474", "CVE-2009-3475", "CVE-2009-3476");
- }
- when ($_ eq "1896") {
- @new_cves = ("CVE-2009-3474", "CVE-2009-3475", "CVE-2009-3476");
- }
-
- when ($_ eq "1931") {
- @new_cves = ("CVE-2009-0689", "CVE-2009-2463");
- }
- when ($_ eq "1989") {
- @new_cves = ("CVE-2010-0789");
- }
- when ($_ eq "1941") {
- @new_cves = ("CVE-2009-0755", "CVE-2009-3903", "CVE-2009-3904",
- "CVE-2009-3905", "CVE-2009-3606", "CVE-2009-3607", "CVE-2009-3608",
- "CVE-2009-3909", "CVE-2009-3938");
- }
- when ($_ eq "2004") {
- @new_cves = ("CVE-2010-0787", "CVE-2010-0547");
- }
- when ($_ eq "2008") {
- @new_cves = ("LOCAL-02/23/10", "LOCAL-02/23/10", "LOCAL-02/23/10",
- "LOCAL-02/23/10");
- }
- when ($_ eq "2043") {
- @new_cves = ("CVE-2010-2062");
- }
- when ($_ eq "2044") {
- @new_cves = ("CVE-2010-2062");
- }
- when ($_ eq "2056") {
- @new_cves = ("CVE-2010-2155", "CVE-2009-4882");
- }
- when ($_ eq "2092") {
- @new_cves = ("CVE-2010-1625", "CVE-2010-1448", "CVE-2009-4497");
- }
- when ($_ eq "2098") {
- @new_cves = ("CVE-2010-3659", "CVE-2010-3660", "CVE-2010-3661",
- "CVE-2010-3662", "CVE-2010-3663", "CVE-2010-3664", "CVE-2010-3665",
- "CVE-2010-3666", "CVE-2010-3667", "CVE-2010-3668", "CVE-2010-3669",
- "CVE-2010-3670", "CVE-2010-3671", "CVE-2010-3672", "CVE-2010-3673",
- "CVE-2010-3674");
- }
- when ($_ eq "2103") {
- @new_cves = ("CVE-2010-3076");
- }
- when ($_ eq "2218") {
- @new_cves = ("CVE-2011-1684");
- }
- when ($_ eq "2229") {
- @new_cves = ("CVE-2005-4494", "CVE-2006-0517", "CVE-2006-0518",
- "CVE-2006-0519", "CVE-2006-0625", "CVE-2006-0626", "CVE-2006-1295",
- "CVE-2006-1702", "CVE-2007-4525", "CVE-2008-5812", "CVE-2008-5813",
- "CVE-2009-3041");
- }
- when ($_ eq "2261") {
- @new_cves = ("CVE-2009-4078", "CVE-2009-4079", "CVE-2009-4059",
- "LOCAL-12/30/10", "LOCAL-12/30/10");
- }
- when ($_ eq "2262") {
- @new_cves = ("LOCAL-03/01/11", "LOCAL-03/01/11", "LOCAL-03/01/11",
- "LOCAL-03/01/11", "LOCAL-05/18/11", "LOCAL-05/18/11");
- }
- when ($_ eq "2286") {
- @new_names = ("phpmyadmin");
- }
- when ($_ eq "2289") {
- @new_cves = ( "LOCAL-07/27/11", "LOCAL-07/27/11", "LOCAL-07/27/11",
- "LOCAL-07/27/11", "LOCAL-07/27/11", "LOCAL-07/27/11", "LOCAL-07/27/11",
- "LOCAL-07/27/11", "LOCAL-07/27/11", "LOCAL-07/27/11", "LOCAL-07/27/11",
- "LOCAL-07/27/11");
- }
-
- };
-
- return (\@new_names, $new_date, \@new_cves);
- }
- ## Fetch DSA from debian archive. Can't use tracker since dates are missing.
- ## DSA started counting in November 2000. We'll simply bruteforce which DSA
- ## was in which year and start in 2000 til current.
- sub fetchDSA {
- my $dsa_id = shift;
- my $base_url = shift;
- my $year= 2000;
- my $current_year = (gmtime())[5] + 1900;
- my $dsa;
-
- trace " Fetching DSA-$dsa_id\n";
-
- given ($dsa_id) {
- when ($_ >= 2140) { $year=2011; }
- when ($_ >= 1965) { $year=2010; }
- when ($_ >= 1694) { $year=2009; }
- when ($_ >= 1443) { $year=2008; }
- when ($_ >= 1245) { $year=2007; }
- when ($_ >= 929) { $year=2006; }
- when ($_ >= 622) { $year=2005; }
- when ($_ >= 406) { $year=2004; }
- when ($_ >= 220) { $year=2003; }
- when ($_ >= 96) { $year=2002; }
- when ($_ >= 11) { $year=2001; }
- };
-
- until (defined $dsa) {
- #my $url= $config->{"dsa_base_url"} . $year . "/dsa-" . sprintf("%03d",$dsa_id);
- my $url= $base_url . $year . "/dsa-" . sprintf("%03d",$dsa_id);
- $dsa = get $url;
- return $dsa if (defined $dsa);
-
- if ($year >= $current_year) {
- note " Could not fetch DSA $dsa_id\n";
- return "";
- }
- $year++;
- }
- }
- # fetch FSAs from freeside. returns 3-tuple (("kfreebsd-8"), $date, @CVE) if
- # kernel-fsa and () if not
- our @__FSAINDEX;
- our $__FSAIDXTIMESTAMP = 0;
- sub fetchFSA($$) {
-
- my $id = shift;
- our $fsa_base_url = shift;
- sub getFSAurls() {
- $\ = "\n";
- #print $__FSAIDXTIMESTAMP;
- #print time;
- return @__FSAINDEX if $__FSAIDXTIMESTAMP + 60 >= time;
- #print "not caching";
- $__FSAIDXTIMESTAMP = time();
- our @fsaurls;
- sub start {
- my ($self, $tagname, $attr, $attrseq, $origtext) = @_;
- if ($tagname eq 'a') {
- if ($attr->{"href"} =~ /^FreeBSD-SA-/ && $attr->{"href"} ne "FreeBSD-SA-06%3a14-amd.txt") {
- # wtf? what douchbag added this as a FreeBSD-SA?
- push @fsaurls, $fsa_base_url . "/" . $attr->{href};
- }
- }
- }
-
- sub getIDfromFSA ($) {
- $_ = shift;
- /FreeBSD-SA-(\d\d)%3a(\d\d)/;
- my $year = $1;
- my $num = $2;
- $year += 2000 if $year < 90;
- $year *= 1000;
- return $year + $num;
- }
- my $p = HTML::Parser->new;
- $p->handler(start => \&start);
- #$p->parse(get ($fsabase));
- $p->parse(get ($_)) foreach @fsaurls;
- $p->eof;
-
-
- @__FSAINDEX = sort { getIDfromFSA $a > getIDfromFSA $b } @fsaurls;
- return @__FSAINDEX;
- }
- my @fsas = getFSAurls();
- # die "unexisting FSA" if $id > $#fsas;
- return "" if ($id > $#fsas);
- return get $fsas[$id];
- }
- ## Try to find new DSAs by iteration, return table of DSAs to process
- sub checkDSAs {
- my $state = shift;
- my $config = shift;
- my %dsatable;
- my $next_dsa = $state->{"next_adv"};
- trace "Checking for new DSAs..\n";
-
- if ($next_dsa < $config->{"first_dsa"}) {
- note "Cache was deleted, starting at DSA $next_dsa\n";
- $next_dsa = $config->{"first_dsa"};
- }
- $next_dsa++ if (blacklistedDSA("DSA-".$next_dsa));
- my $dsa = fetchDSA($next_dsa, $config->{"dsa_base_url"});
- until ($dsa eq "") {
- debug " Got DSA-$next_dsa\n";
- $dsa =~ s/.*<div\ id="inner">//si;
- $dsa =~ s/<div\ id="footer">.*//si;
- $dsatable{$next_dsa} = $dsa;
- $next_dsa++;
- $next_dsa++ if (blacklistedDSA("DSA-".$next_dsa));
- $dsa = fetchDSA($next_dsa, $config->{"dsa_base_url"});
- }
- my $next_fsa = $state->{"next_fsa"};
- if ($next_fsa < $config->{"first_fsa"}) {
- note "Cache was deleted, starting at FSA $next_fsa\n";
- $next_fsa = $config->{"first_fsa"};
- }
- my $fsa = fetchFSA($next_fsa, $config->{"fsa_base_url"});
- until ($fsa eq "") {
- debug " Got FSA-$next_fsa\n";
- $dsatable{"FSA-$next_fsa"} = $fsa;
- $next_fsa++;
- $fsa = fetchFSA($next_fsa, $config->{"fsa_base_url"});
- }
- $state->{"next_adv"} = $next_dsa;
- $state->{"next_fsa"} = $next_fsa;
-
- return \%dsatable;
- }
- ## Parse DSA html data and return array
- ## (src-pkg-name date (CVE-id)*)
- sub parseDSAhtml {
- my $dsa = shift;
-
- my @dsa_names;
- my @dsa_CVEs;
- my @tmp;
- my %tmp;
-
- # Date Reported -> $dsa_date
- $dsa =~ /<dt>Date\ Reported:<\/dt>\s+<dd>(\d+)\s+(\w+)\s+(\d+)<\/dd>/s;
- my $dsa_date = parsedate("$1-$2-$3");
- die " Unable to extract date from DSA" unless (defined $dsa_date);
-
- # Affected Packages -> @dsa_names
- $dsa =~ /<dt>Affected\ Packages:<\/dt>(.*)<\/dd>\s+<dt>Vulnerable:/s;
- die "Unable to find src package in DSA" unless defined $1;
-
- @tmp = split(/, /, $1);
- foreach my $tmpstr (@tmp) {
- if ($tmpstr =~ /<a href=.*>(.*)<\/a>/s) {
- #push(@dsa_names, $1);
- #push(@dsa_names, unifySrcName($1));
- $tmp{unifySrcName($1)} = 1;
- }
- }
- @dsa_names = keys %tmp;
-
- # Security database references (CVEs) -> @dsa_CVEs
- $dsa =~ /<dt>Security\ database\ references:<\/dt>\s+<dd>(.*)<\/dd>\s+<dt>More\ information:/s;
- die " Unable to find CVEs in DSA" unless defined $1;
-
- @tmp = split(/(,|<br>)/, $1);
- foreach my $tmpstr (@tmp) {
- if ($tmpstr =~ /<a href=.*mitre.org.*>(.*)<\/a>/s) {
- push(@dsa_CVEs, $1);
- }
- }
-
- return (\@dsa_names, $dsa_date, \@dsa_CVEs);
- }
- # parse freebsd security announcements
- sub parseFSA ($) {
- my $str = shift;
- my %FSA;
- my %details;
- ($_, %FSA) = split /\n[IVX]+\.\s+(.*)\n/, $str;
- $FSA{"Preamble"} = $_;
- my @src = ("kfreebsd");
- # get cve and reldate
- /\nCVE Name:\s*(.*)\n/;
- my @CVE = split /, /, $1 if defined $1;
- /\nAnnounced:\**(.*)\n/;
- my $reldate = 0;
- $reldate = parsedate $1 if defined $1;
- return (\@src, $reldate, \@CVE) if /\nModule:\s*kernel\n/;
-
- # make sure this is a kernel package
- return () if !defined $FSA{"Correction details"};
- foreach (split /\n/, $FSA{"Correction details"}) {
- return (\@src, $reldate, \@CVE) if /^ src\/sys\/(?!conf\/newvers\.sh)/;
- }
- return ();
- }
- 1;
|