svn: /pear/pear-core/tags/PEAR-1.9.3/PEAR/ REST.php
[email protected] (Helgi Þormar Þorbjörnsson) Tue, 08 Mar 2011 23:16:30 +0000
| Newsgroups | php.pear.cvs,php.pear.core |
|---|---|
| Message-ID | <[email protected]> |
dufuz Tue, 08 Mar 2011 23:16:30 +0000
Revision: http://svn.php.net/viewvc?view=revision&revision=309042
Log:
Introduce a time-of-check-time-of-use (TOCTOU) save file handler, this makes sure that from the time we check it is available until we get the file handler that no one attempted to put in a soft / hard link instead of what we checked for.
Further improves on #18056
Bug: http://pear.php.net/bugs/18056 (unknown)
Changed paths:
U pear/pear-core/tags/PEAR-1.9.3/PEAR/REST.php
Modified: pear/pear-core/tags/PEAR-1.9.3/PEAR/REST.php
===================================================================
--- pear/pear-core/tags/PEAR-1.9.3/PEAR/REST.php 2011-03-08 22:46:27 UTC (rev 309041)
+++ pear/pear-core/tags/PEAR-1.9.3/PEAR/REST.php 2011-03-08 23:16:30 UTC (rev 309042)
@@ -228,59 +228,75 @@
$cacheidfile = $d . 'rest.cacheid';
$cachefile = $d . 'rest.cachefile';
+ if (!is_dir($cache_dir)) {
+ if (System::mkdir(array('-p', $cache_dir) === false)) {
+ return PEAR::raiseError("The value of config option cache_dir ($cache_dir) is not a directory and attempts to create the directory failed.");
+ }
+ }
+
if ($cacheid === null && $nochange) {
$cacheid = unserialize(implode('', file($cacheidfile)));
}
- if (is_link($cacheidfile)) {
- return PEAR::raiseError('SECURITY ERROR: Will not write to ' . $cacheidfile . ' as it is symlinked to ' . readlink($cacheidfile) . ' - Possible symlink attack');
- }
+ $idData = serialize(array(
+ 'age' => time(),
+ 'lastChange' => ($nochange ? $cacheid['lastChange'] : $lastmodified),
+ ));
- if (is_link($cachefile)) {
- return PEAR::raiseError('SECURITY ERROR: Will not write to ' . $cacheidfile . ' as it is symlinked to ' . readlink($cacheidfile) . ' - Possible symlink attack');
+ $result = $this->saveCacheFile($cacheidfile, $idData);
+ if (PEAR::isError($result)) {
+ return $result;
+ } elseif ($nochange) {
+ return true;
}
- $cacheidfile_fp = @fopen($cacheidfile, 'wb');
- if (!$cacheidfile_fp) {
- if (is_dir($cache_dir)) {
- return PEAR::raiseError("The value of config option cache_dir ($cache_dir) is not a directory. ");
+ $result = $this->saveCacheFile($cachefile, serialize($contents));
+ if (PEAR::isError($result)) {
+ if (file_exists($cacheidfile)) {
+ @unlink($cacheidfile);
}
- System::mkdir(array('-p', $cache_dir));
- $cacheidfile_fp = @fopen($cacheidfile, 'wb');
- if (!$cacheidfile_fp) {
- return PEAR::raiseError("Could not open $cacheidfile for writing.");
- }
+ return $result;
}
- if ($nochange) {
- fwrite($cacheidfile_fp, serialize(array(
- 'age' => time(),
- 'lastChange' => $cacheid['lastChange'],
- ))
- );
+ return true;
+ }
- fclose($cacheidfile_fp);
- return true;
- }
+ function saveCacheFile($file, $contents)
+ {
+ $len = strlen($contents);
- fwrite($cacheidfile_fp, serialize(array(
- 'age' => time(),
- 'lastChange' => $lastmodified,
- ))
- );
- fclose($cacheidfile_fp);
+ $cachefile_fp = @fopen($file, 'xb'); // x is the O_CREAT|O_EXCL mode
+ if ($cachefile_fp !== false) { // create file
+ if (fwrite($cachefile_fp, $contents, $len) < $len) {
+ fclose($cachefile_fp);
+ return PEAR::raiseError("Could not write $file.");
+ }
+ } else { // update file
+ $cachefile_lstat = lstat($file);
+ $cachefile_fp = @fopen($file, 'wb');
+ if (!$cachefile_fp) {
+ return PEAR::raiseError("Could not open $file for writing.");
+ }
- $cachefile_fp = @fopen($cachefile, 'wb');
- if (!$cachefile_fp) {
- if (file_exists($cacheidfile)) {
- @unlink($cacheidfile);
+ $cachefile_fstat = fstat($cachefile_fp);
+ if (
+ $cachefile_lstat['mode'] == $cachefile_fstat['mode'] &&
+ $cachefile_lstat['ino'] == $cachefile_fstat['ino'] &&
+ $cachefile_lstat['dev'] == $cachefile_fstat['dev'] &&
+ $cachefile_fstat['nlink'] === 1
+ ) {
+ if (fwrite($cachefile_fp, $contents, $len) < $len) {
+ fclose($cachefile_fp);
+ return PEAR::raiseError("Could not write $file.");
+ }
+ } else {
+ fclose($cachefile_fp);
+ $link = function_exists('readlink') ? readlink($file) : $file;
+ return PEAR::raiseError('SECURITY ERROR: Will not write to ' . $file . ' as it is symlinked to ' . $link . ' - Possible symlink attack');
}
-
- return PEAR::raiseError("Could not open $cacheidfile for writing.");
}
- fwrite($cachefile_fp, serialize($contents));
fclose($cachefile_fp);
return true;
}
@@ -464,4 +480,4 @@
return $data;
}
-}
+}
\ No newline at end of file