From 99085a3ad122f986811769448959bc1ed5257bbc Mon Sep 17 00:00:00 2001 From: Michael Wallner Date: Tue, 1 Sep 2015 19:28:02 +0200 Subject: [PATCH] more failure checks --- bin/pharext | Bin 89746 -> 84497 bytes src/pharext/Archive.php | 139 +++++++++++++++++++++++++--------------- 2 files changed, 88 insertions(+), 51 deletions(-) diff --git a/bin/pharext b/bin/pharext index 321039ee1d5d6cace5c5162a40bef6c0a884a922..b464635584534dc679a2dcced3f27219c49fe2b5 100755 GIT binary patch delta 1745 zcmbtVUuaWj6u)Th#(_WtWKwGN;K%u}E+cnYccuif;HU(Kml6B)$TF zBxa%e#s;|mIQlZ(CqX$g245sM)b}Q@yX)~ScSTs&e_m{b+bFP;V3eQ4L@($#ABz<7 zaV1j5hN{d+t*v@NiCLBs(v+%6=)YJbk4&U!O2Nu=!~+Tiqp@(-z(*=LXX3o_`dedR zb&ys$Mt=mGHDV|%dw$L`%e@rS)Q~yy7sP4ggpCZ>@DmVowcA?PuZ5fjxf5 zBfbLoYGC7W+iOXZ%CO6ePytZ&U-GV4M?*y(K84wBJIYTq=6b5)$_#oCM8_7ze)xHP zU;W;;YhpT5pl|GRvsy11Y}KZkO!ckSJ>r-u-TNYKf;lwT>ln#C+VEtdjEdFdzRVOV z>N)Go8l%OPt&rIMNOdGNIm+ee3=BuJ&GvOV_)atTgJ?n=t|v!aVzZ}RT@O!e-tqnB n9pdg_=jYmMiN~KjJo8KF?|s`RPD$zP*$-c$pQ@dsdq)2O`%84b delta 4114 zcmeHKYiv_h9BVT-n%Ev zqLN_zU?Ru3peCZ(7co)ef{Pk3>IaBlBz_SCWD*mCF`_1DoC(DLIp^N4rNba<{9=>l z-*e9Y{rH{J7hcRbekSAHC(<%L&NZ28a5(Md^w!#@t&6tw$&A=WlgShbicx7=tA8Ns z3(AvHeXpp<0ZG;BBEd+58P1Ph-bbi2>)gQmK*?t_eD9RoD~Wz7THgoii0BU-6Qwlm zwQ@tWG(j|G*krOq?2B|JMrdE>mX#=%_Gr5K| zV8lB(fzlvXN9hTDeO_O$=xg3uOV1y-Zl?x5f$%D?^_3C>_#JB=-Tc~VG97hX`35`f z4!mqFrV=aGCQ9pankmz7`6ko1*((?DIOo87ax4zw)|quIctV?(D(n!<2E3G$Mt<2-A3g~>@Ac&W@mgy?JOrt z_N{dPN4tm8vOJcfNAg$>W_5ZhkJ-7TudVrvuPxt`?bU0H&9_TqoG&D*Dl!_AJH%Nc zQF&6-B;*cBN_)amnHWo4iV-<+83>7^YT||%lEtKrF-a2>86-uEX37%b!AFXU_leP{ zID_0{`iywcA+Mi%EaqHf0*ZaAv+<91HZ37Iwo(<>={6){ng9$eF!NPep2%H8mu5*}q-@F^eT~fD>ww^entg0st-=8NF)q~MlR_uHDtyW|PS@Vt4p_lK1rxh2 zeh2byRURF3QBPgN&;dbF4YKZg$!adkN&-}IMAf3APeTk@_fiZXz~Kuks)hz1?t_MK z_3iOaH#Eby#RXl=j=Bb$$Whl|aCNU|kZgIU8w1|y))C-ug);)mVNLRBk{`JP0Me@) znPkV`K0m(GT!2qrv<(x80Up+rao7Q+gpJH5!_9y~w+P^q5+#ikPi}%yEeIS9pe!5+ zsFFq(^UK@J8wyFQ8!S74huXpywm5NxP+H&p=!0yQ3>KI=sgIndud5N~;EjJZJU5cZhv68JV1loa3UeCQC8Iz=oXRzeGBiPtPC?tX1z zgu>C<$8ofe5j|`3thNyFa28b)HQ9&aRszr`VZb~$nG;1ElUA|A3H5;-cQSm6Gr%lG zYerlK>TAJhcxryG)Lc|W64Qd59SGVFl<}+BpodR7p~W*FDK(eHc-QXmM97bnFuk~t z$R4PO-B`w?vAezV(*kBs-x;zDKCI$V6-cJN*=xYZZny}cPI zfHKw&0BdykFv3^(F{1`Uo~r-)^{`G8@y`PVx%w+*XqZ9={>=0IUwt+F{x=6;@uZMu vrfvGn>Dr~!wdT%^8_#ZiyYai*<6qXfd = @fopen($this->file = $file, "r")) { + if (!$this->fd = @fopen($file, "r")) { throw new Exception; } + $this->file = $file; $this->stub = $this->readStub(); $this->manifest = $this->readManifest(); $this->signature = $this->readSignature(); } + function getIterator() { + return new RecursiveDirectoryIterator($this->extract()); + } + function extract() { return $this->extracted ?: $this->extractTo(new Tempdir("archive")); } @@ -80,30 +89,11 @@ class Archive implements ArrayAccess } foreach ($this->manifest["entries"] as $file => $entry) { fseek($this->fd, $this->manifest["offset"]+$entry["offset"]); - $path = $dir."/$file"; - $dirn = dirname($path); - if (!is_dir($dirn) && !@mkdir($dirn, 0777, true)) { - throw new Exception; - } - if (!$fd = @fopen($path, "w")) { - throw new Exception; - } - switch ($entry["flags"] & self::COMP_FILE_MASK) { - case self::COMP_GZ_FILE: - if (!@stream_filter_append($fd, "zlib.inflate")) { - throw new Exception; - } - break; - case self::COMP_BZ2_FILE: - if (!@stream_filter_append($fd, "bz2.decompress")) { - throw new Exception; - } - break; + $path = "$dir/$file"; + $copy = stream_copy_to_stream($this->fd, $this->outFd($path, $entry["flags"]), $entry["csize"]); + if ($entry["osize"] != $copy) { + throw new Exception("Copied '$copy' of '$file', expected '{$entry["osize"]}' from '{$entry["csize"]}"); } - if ($entry["osize"] != ($copied = stream_copy_to_stream($this->fd, $fd, $entry["csize"]))) { - throw new Exception("Copied '$copied' of '$file', expected '{$entry["osize"]}' from '{$entry["csize"]}"); - } - fclose($fd); $crc = hexdec(hash_file("crc32b", $path)); if ($crc !== $entry["crc32"]) { @@ -122,7 +112,7 @@ class Archive implements ArrayAccess function offsetGet($o) { $this->extract(); - return new \SplFileInfo($this->extracted."/$o"); + return new SplFileInfo($this->extracted."/$o"); } function offsetSet($o, $v) { @@ -143,7 +133,7 @@ class Archive implements ArrayAccess function getPath() { /* compatible with Phar::getPath() */ - return new \SplFileInfo($this->file); + return new SplFileInfo($this->file); } function getMetadata($key = null) { @@ -153,6 +143,63 @@ class Archive implements ArrayAccess return $this->manifest["meta"]; } + private function outFd($path, $flags) { + $dirn = dirname($path); + if (!is_dir($dirn) && !@mkdir($dirn, 0777, true)) { + throw new Exception; + } + if (!$fd = @fopen($path, "w")) { + throw new Exception; + } + switch ($flags & self::COMP_FILE_MASK) { + case self::COMP_GZ_FILE: + if (!@stream_filter_append($fd, "zlib.inflate")) { + throw new Exception; + } + break; + case self::COMP_BZ2_FILE: + if (!@stream_filter_append($fd, "bz2.decompress")) { + throw new Exception; + } + break; + } + + } + private function readVerified($fd, $len) { + if ($len != strlen($data = fread($fd, $len))) { + throw new Exception("Unexpected EOF"); + } + return $data; + } + + private function readFormat($format, $fd, $len) { + if (false === ($data = @unpack($format, $this->readVerified($fd, $len)))) { + throw new Exception; + } + return $data; + } + + private function readSingleFormat($format, $fd, $len) { + return current($this->readFormat($format, $fd, $len)); + } + + private function readStringBinary($fd) { + if (($length = $this->readSingleFormat("V", $fd, 4))) { + return $this->readVerified($this->fd, $length); + } + return null; + } + + private function readSerializedBinary($fd) { + if (($length = $this->readSingleFormat("V", $fd, 4))) { + if (false === ($data = unserialize($this->readVerified($fd, $length)))) { + throw new Exception; + } + return $data; + } + return null; + } + private function readStub() { $stub = ""; while (!feof($this->fd)) { @@ -160,7 +207,7 @@ class Archive implements ArrayAccess $stub .= $line; if (false !== stripos($line, self::HALT_COMPILER)) { /* check for '?>' on a separate line */ - if ('?>' === fread($this->fd, 2)) { + if ('?>' === $this->readVerified($this->fd, 2)) { $stub .= '?>' . fgets($this->fd); } else { fseek($this->fd, -2, SEEK_CUR); @@ -173,13 +220,9 @@ class Archive implements ArrayAccess private function readManifest() { $current = ftell($this->fd); - $header = unpack("Vlen/Vnum/napi/Vflags", fread($this->fd, 14)); - if (($alias = current(unpack("V", fread($this->fd, 4))))) { - $alias = fread($this->fd, $alias); - } - if (($meta = current(unpack("V", fread($this->fd, 4))))) { - $meta = unserialize(fread($this->fd, $meta)); - } + $header = $this->readFormat("Vlen/Vnum/napi/Vflags", $this->fd, 14); + $alias = $this->readStringBinary($this->fd); + $meta = $this->readSerializedBinary($this->fd); $entries = []; for ($i = 0; $i < $header["num"]; ++$i) { $this->readEntry($entries); @@ -198,24 +241,18 @@ class Archive implements ArrayAccess $last = end($entries); $offset = $last["offset"] + $last["csize"]; } - if (($file = current(unpack("V", fread($this->fd, 4))))) { - $file = fread($this->fd, $file); - } - if ($file === 0 || !strlen($file)) { + $file = $this->readStringBinary($this->fd); + if (!strlen($file)) { throw new Exception("Empty file name encountered at offset '$offset'"); } - $header = unpack("Vosize/Vstamp/Vcsize/Vcrc32/Vflags", fread($this->fd, 20)); - if (($meta = current(unpack("V", fread($this->fd, 4))))) { - $meta = unserialize(fread($this->fd, $meta)); - } else { - $meta = []; - } + $header = $this->readFormat("Vosize/Vstamp/Vcsize/Vcrc32/Vflags", $this->fd, 20); + $meta = $this->readSerializedBinary($this->fd); $entries[$file] = $header + compact("meta", "offset"); } private function readSignature() { fseek($this->fd, -8, SEEK_END); - $sig = unpack("Vflags/Z4magic", fread($this->fd, 8)); + $sig = $this->readFormat("Vflags/Z4magic", $this->fd, 8); $end = ftell($this->fd); if ($sig["magic"] !== "GBMB") { @@ -225,13 +262,13 @@ class Archive implements ArrayAccess switch ($sig["flags"]) { case self::SIG_OPENSSL: fseek($this->fd, -12, SEEK_END); - if (($hash = current(unpack("V", fread($this->fd, 4))))) { + if (($hash = $this->readSingleFormat("V", $this->fd, 4))) { $offset = 4 + $hash; fseek($this->fd, -$offset, SEEK_CUR); - $hash = fread($this->fd, $hash); + $hash = $this->readVerified($this->fd, $hash); fseek($this->fd, 0, SEEK_SET); - $valid = openssl_verify(fread($this->fd, $end - $offset - 8), - $hash, file_get_contents($this->file.".pubkey")) === 1; + $valid = openssl_verify($this->readVerified($this->fd, $end - $offset - 8), + $hash, @file_get_contents($this->file.".pubkey")) === 1; } break; @@ -241,7 +278,7 @@ class Archive implements ArrayAccess case self::SIG_SHA512: $offset = 8 + self::$siglen[$sig["flags"]]; fseek($this->fd, -$offset, SEEK_END); - $hash = fread($this->fd, self::$siglen[$sig["flags"]]); + $hash = $this->readVerified($this->fd, self::$siglen[$sig["flags"]]); $algo = hash_init(self::$sigalg[$sig["flags"]]); fseek($this->fd, 0, SEEK_SET); hash_update_stream($algo, $this->fd, $end - $offset); -- 2.30.2