mirror of
https://github.com/the-djmaze/snappymail.git
synced 2026-08-28 19:49:20 +03:00
Drop my generateFileNameOld() compatibility due security exploit.
This commit is contained in:
parent
7020345739
commit
32699c5446
3 changed files with 51 additions and 147 deletions
|
|
@ -55,16 +55,6 @@ class FileStorage implements \RainLoop\Providers\Files\IFiles
|
|||
$bCreate = !!\preg_match('/[wac]/', $sOpenMode);
|
||||
|
||||
$sFileName = $this->generateFullFileName($oAccount, $sKey, $bCreate);
|
||||
if (!\file_exists($sFileName)) {
|
||||
$sOldFileName = $this->generateFullFileNameOld($oAccount, $sKey);
|
||||
if (\file_exists($sOldFileName)
|
||||
&& (
|
||||
(!\is_dir(\dirname($sOldFileName)) && !\mkdir(\dirname($sOldFileName), 0700, true))
|
||||
|| !\rename($sOldFileName, $sFileName)
|
||||
)) {
|
||||
$sFileName = $sOldFileName;
|
||||
}
|
||||
}
|
||||
if ($bCreate || \file_exists($sFileName))
|
||||
{
|
||||
$mResult = \fopen($sFileName, $sOpenMode);
|
||||
|
|
@ -81,18 +71,12 @@ class FileStorage implements \RainLoop\Providers\Files\IFiles
|
|||
public function GetFileName(\RainLoop\Model\Account $oAccount, string $sKey) /*: string|false*/
|
||||
{
|
||||
$sFileName = $this->generateFullFileName($oAccount, $sKey);
|
||||
if (!\file_exists($sFileName)) {
|
||||
$sFileName = $this->generateFullFileNameOld($oAccount, $sKey);
|
||||
}
|
||||
return \file_exists($sFileName) ? $sFileName : false;
|
||||
}
|
||||
|
||||
public function Clear(\RainLoop\Model\Account $oAccount, string $sKey) : bool
|
||||
{
|
||||
$sFileName = $this->generateFullFileName($oAccount, $sKey);
|
||||
if (!\file_exists($sFileName)) {
|
||||
$sFileName = $this->generateFullFileNameOld($oAccount, $sKey);
|
||||
}
|
||||
if (\file_exists($sFileName)) {
|
||||
if (isset($this->aResources[$sFileName]) && \is_resource($this->aResources[$sFileName])) {
|
||||
\fclose($this->aResources[$sFileName]);
|
||||
|
|
@ -105,16 +89,12 @@ class FileStorage implements \RainLoop\Providers\Files\IFiles
|
|||
public function FileSize(\RainLoop\Model\Account $oAccount, string $sKey) /*: int|false*/
|
||||
{
|
||||
$sFileName = $this->generateFullFileName($oAccount, $sKey);
|
||||
if (!\file_exists($sFileName)) {
|
||||
$sFileName = $this->generateFullFileNameOld($oAccount, $sKey);
|
||||
}
|
||||
return \file_exists($sFileName) ? \filesize($sFileName) : false;
|
||||
}
|
||||
|
||||
public function FileExists(\RainLoop\Model\Account $oAccount, string $sKey) : bool
|
||||
{
|
||||
return \file_exists($this->generateFullFileName($oAccount, $sKey))
|
||||
|| \file_exists($this->generateFullFileNameOld($oAccount, $sKey));
|
||||
return \file_exists($this->generateFullFileName($oAccount, $sKey));
|
||||
}
|
||||
|
||||
public function GC(int $iTimeToClearInHours = 24) : bool
|
||||
|
|
@ -180,39 +160,4 @@ class FileStorage implements \RainLoop\Providers\Files\IFiles
|
|||
return $sFilePath;
|
||||
}
|
||||
|
||||
private function generateFullFileNameOld(\RainLoop\Model\Account $oAccount, string $sKey, bool $bMkDir = false) : string
|
||||
{
|
||||
$sEmail = $sSubEmail = '';
|
||||
if ($oAccount instanceof \RainLoop\Model\Account)
|
||||
{
|
||||
$sEmail = \preg_replace('/[^a-z0-9\-\.@]+/', '_', $oAccount->ParentEmailHelper());
|
||||
if ($oAccount->IsAdditionalAccount())
|
||||
{
|
||||
$sSubEmail = \preg_replace('/[^a-z0-9\-\.@]+/', '_', $oAccount->Email());
|
||||
}
|
||||
}
|
||||
|
||||
if (empty($sEmail))
|
||||
{
|
||||
$sEmail = '__unknown__';
|
||||
}
|
||||
|
||||
$sKeyPath = \sha1($sKey);
|
||||
$sKeyPath = \substr($sKeyPath, 0, 2).'/'.\substr($sKeyPath, 2, 2).'/'.$sKeyPath;
|
||||
|
||||
$sFilePath = $this->sDataPath.'/'.
|
||||
\str_pad(\rtrim(\substr($sEmail, 0, 2), '@'), 2, '_').'/'.$sEmail.'/'.
|
||||
(0 < \strlen($sSubEmail) ? $sSubEmail.'/' : '').
|
||||
$sKeyPath;
|
||||
|
||||
if ($bMkDir && !empty($sFilePath) && !\is_dir(\dirname($sFilePath)))
|
||||
{
|
||||
if (!\mkdir(\dirname($sFilePath), 0700, true))
|
||||
{
|
||||
throw new \RainLoop\Exceptions\Exception('Can\'t make storage directory "'.$sFilePath.'"');
|
||||
}
|
||||
}
|
||||
|
||||
return $sFilePath;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -49,13 +49,6 @@ class FileStorage implements \RainLoop\Providers\Storage\IStorage
|
|||
$sFileName = $this->generateFileName($oAccount, $iStorageType, $sKey);
|
||||
if ($sFileName && \file_exists($sFileName)) {
|
||||
$mValue = \file_get_contents($sFileName);
|
||||
} else {
|
||||
$sFileName = $this->generateFileNameOld($oAccount, $iStorageType, $sKey);
|
||||
if ($sFileName && \file_exists($sFileName)) {
|
||||
$mValue = \file_get_contents($sFileName);
|
||||
$this->Put($oAccount, $iStorageType, $sKey, $mValue);
|
||||
\unlink($sFileName);
|
||||
}
|
||||
}
|
||||
|
||||
return false === $mValue ? $mDefault : $mValue;
|
||||
|
|
@ -70,11 +63,6 @@ class FileStorage implements \RainLoop\Providers\Storage\IStorage
|
|||
$sFileName = $this->generateFileName($oAccount, $iStorageType, $sKey);
|
||||
if ($sFileName && \file_exists($sFileName)) {
|
||||
$mResult = \unlink($sFileName);
|
||||
} else {
|
||||
$sFileName = $this->generateFileNameOld($oAccount, $iStorageType, $sKey);
|
||||
if ($sFileName && \file_exists($sFileName)) {
|
||||
$mResult = \unlink($sFileName);
|
||||
}
|
||||
}
|
||||
|
||||
return $mResult;
|
||||
|
|
@ -89,17 +77,6 @@ class FileStorage implements \RainLoop\Providers\Storage\IStorage
|
|||
if ($sPath && \is_dir($sPath)) {
|
||||
\MailSo\Base\Utils::RecRmDir($sPath);
|
||||
}
|
||||
|
||||
$sPath = $this->generateFileNameOld($oAccount, StorageType::USER, '', false, true);
|
||||
if ($sPath && \is_dir($sPath)) {
|
||||
\MailSo\Base\Utils::RecRmDir($sPath);
|
||||
}
|
||||
|
||||
$sPath = $this->generateFileNameOld($oAccount, StorageType::CONFIG, '', false, true);
|
||||
if ($sPath && \is_dir($sPath)) {
|
||||
\MailSo\Base\Utils::RecRmDir($sPath);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
@ -147,6 +124,9 @@ class FileStorage implements \RainLoop\Providers\Storage\IStorage
|
|||
if (empty($sEmail)) {
|
||||
return '';
|
||||
}
|
||||
if (\is_dir("{$this->sDataPath}/cfg")) {
|
||||
FixFileStorage::FixIt($this->sDataPath);
|
||||
}
|
||||
$aEmail = \explode('@', $sEmail ?: 'nobody@unknown.tld');
|
||||
$sDomain = \trim(1 < \count($aEmail) ? \array_pop($aEmail) : '');
|
||||
$sFilePath = $this->sDataPath
|
||||
|
|
@ -175,74 +155,6 @@ class FileStorage implements \RainLoop\Providers\Storage\IStorage
|
|||
return $sFilePath;
|
||||
}
|
||||
|
||||
/**
|
||||
* Old RainLoop structure
|
||||
*/
|
||||
private function generateFileNameOld($mAccount, int $iStorageType, string $sKey, bool $bMkDir = false, bool $bForDeleteAction = false) : string
|
||||
{
|
||||
if (null === $mAccount)
|
||||
{
|
||||
$iStorageType = StorageType::NOBODY;
|
||||
}
|
||||
|
||||
$sEmail = $sSubEmail = '';
|
||||
if ($mAccount instanceof \RainLoop\Model\Account)
|
||||
{
|
||||
$sEmail = $mAccount->ParentEmailHelper();
|
||||
if ($this->bLocal && $mAccount->IsAdditionalAccount() && !$bForDeleteAction)
|
||||
{
|
||||
$sSubEmail = $mAccount->Email();
|
||||
}
|
||||
}
|
||||
|
||||
if (\is_string($mAccount) && empty($sEmail))
|
||||
{
|
||||
$sEmail = $mAccount;
|
||||
}
|
||||
|
||||
$sEmail = \preg_replace('/[^a-z0-9\-\.@]+/i', '_', $sEmail);
|
||||
$sSubEmail = \preg_replace('/[^a-z0-9\-\.@]+/i', '_', $sSubEmail);
|
||||
|
||||
$sTypePath = $sKeyPath = '';
|
||||
switch ($iStorageType)
|
||||
{
|
||||
default:
|
||||
case StorageType::USER:
|
||||
case StorageType::NOBODY:
|
||||
$sTypePath = 'data';
|
||||
$sKeyPath = \md5($sKey);
|
||||
$sKeyPath = \substr($sKeyPath, 0, 2).'/'.$sKeyPath;
|
||||
break;
|
||||
case StorageType::CONFIG:
|
||||
$sTypePath = 'cfg';
|
||||
$sKeyPath = \preg_replace('/[_]+/', '_', \preg_replace('/[^a-zA-Z0-9\/]/', '_', $sKey));
|
||||
break;
|
||||
}
|
||||
|
||||
$sFilePath = '';
|
||||
if (StorageType::NOBODY === $iStorageType)
|
||||
{
|
||||
$sFilePath = $this->sDataPath.'/'.$sTypePath.'/__nobody__/'.$sKeyPath;
|
||||
}
|
||||
else if (!empty($sEmail))
|
||||
{
|
||||
$sFilePath = $this->sDataPath.'/'.$sTypePath.'/'.
|
||||
\str_pad(\rtrim(\substr($sEmail, 0, 2), '@'), 2, '_').'/'.$sEmail.'/'.
|
||||
(\strlen($sSubEmail) ? $sSubEmail.'/' : '').
|
||||
($bForDeleteAction ? '' : $sKeyPath);
|
||||
}
|
||||
|
||||
if ($bMkDir && !$bForDeleteAction && !empty($sFilePath) && !\is_dir(\dirname($sFilePath)))
|
||||
{
|
||||
if (!\mkdir(\dirname($sFilePath), 0700, true))
|
||||
{
|
||||
throw new \RainLoop\Exceptions\Exception('Can\'t make storage directory "'.$sFilePath.'"');
|
||||
}
|
||||
}
|
||||
|
||||
return $sFilePath;
|
||||
}
|
||||
|
||||
public function SetLogger(?\MailSo\Log\Logger $oLogger)
|
||||
{
|
||||
$this->oLogger = $oLogger;
|
||||
|
|
|
|||
|
|
@ -0,0 +1,47 @@
|
|||
<?php
|
||||
|
||||
namespace RainLoop\Providers\Storage;
|
||||
|
||||
abstract class FixFileStorage
|
||||
{
|
||||
|
||||
/**
|
||||
* Replace control characters, ampersand, spaces and reserved characters (based on Win95 VFAT)
|
||||
* en.wikipedia.org/wiki/Filename#Reserved_characters_and_words
|
||||
*/
|
||||
private static function fixName($filename)
|
||||
{
|
||||
return \preg_replace('#[|\\\\?*<":>+\\[\\]/&\\s\\pC]#su', '-', $filename);
|
||||
}
|
||||
|
||||
public static function FixIt(string $sDataPath)
|
||||
{
|
||||
// /cfg/ex/example@example.com
|
||||
foreach (\glob("{$sDataPath}/cfg/*", GLOB_ONLYDIR) as $sOldDir) {
|
||||
foreach (\glob("{$sOldDir}/*", GLOB_ONLYDIR) as $sDomainDir) {
|
||||
$aEmail = \explode('@', \basename($sDomainDir));
|
||||
$sDomain = \trim(1 < \count($aEmail) ? \array_pop($aEmail) : '');
|
||||
$sNewDir = $sDataPath
|
||||
.'/'.static::fixName($sDomain ?: 'unknown.tld')
|
||||
.'/'.static::fixName(\implode('@', $aEmail) ?: '.unknown');
|
||||
if (\is_dir($sNewDir) || \mkdir($sNewDir, 0700, true)) {
|
||||
foreach (\glob("{$sDomainDir}/*") as $sItem) {
|
||||
$sName = \basename($sItem);
|
||||
if ('sign_me' === $sName) {
|
||||
// Security issue
|
||||
// https://github.com/RainLoop/rainloop-webmail/issues/2133
|
||||
\unlink($sItem);
|
||||
} else {
|
||||
\rename($sItem, "{$sNewDir}/{$sName}");
|
||||
}
|
||||
}
|
||||
\MailSo\Base\Utils::RecRmDir($sDomainDir);
|
||||
}
|
||||
}
|
||||
}
|
||||
\MailSo\Base\Utils::RecRmDir("{$sDataPath}/cfg");
|
||||
\MailSo\Base\Utils::RecRmDir("{$sDataPath}/data");
|
||||
\MailSo\Base\Utils::RecRmDir("{$sDataPath}/files");
|
||||
}
|
||||
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue