1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131
|
Subject: 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.
Origin: http://svn.php.net/viewvc?view=revision&revision=309042
CVE-2011-1144
Index: PEAR/REST.php
===================================================================
--- PEAR/REST.php (revision 309041)
+++ PEAR/REST.php (revision 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
+}
|