Skip to content

Commit

Permalink
Updated to 6.2.3
Browse files Browse the repository at this point in the history
  • Loading branch information
pmachapman committed Dec 20, 2022
1 parent 82056a8 commit 2ecab6b
Show file tree
Hide file tree
Showing 14 changed files with 180 additions and 35 deletions.
4 changes: 3 additions & 1 deletion arcread.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1476,7 +1476,9 @@ bool Archive::ReadSubData(Array<byte> *UnpData,File *DestFile,bool TestMode)
{
if (SubHead.UnpSize>0x1000000)
{
// So huge allocation must never happen in valid archives.
// Prevent the excessive allocation. When reading to memory, normally
// this function operates with reasonably small blocks, such as
// the archive comment, NTFS ACL or "Zone.Identifier" NTFS stream.
uiMsg(UIERROR_SUBHEADERUNKNOWN,FileName);
return false;
}
Expand Down
5 changes: 1 addition & 4 deletions crypt.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -15,10 +15,7 @@ enum CRYPT_METHOD {
#define CRYPT_BLOCK_SIZE 16
#define CRYPT_BLOCK_MASK (CRYPT_BLOCK_SIZE-1) // 0xf

// 2013.04.29: set to 15 for RAR 5.00 beta 1.
// 2022.09.07: changed to 16 for upcoming RAR 6.20.
#define CRYPT5_KDF_LG2_COUNT 16 // LOG2 of PDKDF2 iteration count.

#define CRYPT5_KDF_LG2_COUNT 15 // LOG2 of PDKDF2 iteration count.
#define CRYPT5_KDF_LG2_COUNT_MAX 24 // LOG2 of maximum accepted iteration count.
#define CRYPT_VERSION 0 // Supported encryption version.

Expand Down
8 changes: 4 additions & 4 deletions dll.rc
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,8 @@
#include <commctrl.h>

VS_VERSION_INFO VERSIONINFO
FILEVERSION 6, 20, 2, 681
PRODUCTVERSION 6, 20, 2, 681
FILEVERSION 6, 20, 3, 714
PRODUCTVERSION 6, 20, 3, 714
FILEOS VOS__WINDOWS32
FILETYPE VFT_APP
{
Expand All @@ -14,8 +14,8 @@ FILETYPE VFT_APP
VALUE "CompanyName", "Alexander Roshal\0"
VALUE "ProductName", "RAR decompression library\0"
VALUE "FileDescription", "RAR decompression library\0"
VALUE "FileVersion", "6.20.2\0"
VALUE "ProductVersion", "6.20.2\0"
VALUE "FileVersion", "6.20.3\0"
VALUE "ProductVersion", "6.20.3\0"
VALUE "LegalCopyright", "Copyright � Alexander Roshal 1993-2022\0"
VALUE "OriginalFilename", "Unrar.dll\0"
}
Expand Down
89 changes: 83 additions & 6 deletions extinfo.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -112,6 +112,68 @@ static bool LinkInPath(const wchar *Name)
}


// Delete symbolic links in file path, if any, and replace them by directories.
// Prevents extracting files outside of destination folder with symlink chains.
bool LinksToDirs(const wchar *SrcName,const wchar *SkipPart,std::wstring &LastChecked)
{
// Unlike Unix, Windows doesn't expand lnk1 in symlink targets like
// "lnk1/../dir", but converts the path to "dir". In Unix we need to call
// this function to prevent placing unpacked files outside of destination
// folder if previously we unpacked "dir/lnk1" -> "..",
// "dir/lnk2" -> "lnk1/.." and "dir/lnk2/anypath/poc.txt".
// We may still need this function to prevent abusing symlink chains
// in link source path if we remove detection of such chains
// in IsRelativeSymlinkSafe. This function seems to make other symlink
// related safety checks redundant, but for now we prefer to keep them too.
//
// 2022.12.01: the performance impact is minimized after adding the check
// against the previous path and enabling this verification only after
// extracting a symlink with ".." in target. So we enabled it for Windows
// as well for extra safety.
//#ifdef _UNIX
wchar Path[NM];
if (wcslen(SrcName)>=ASIZE(Path))
return false; // It should not be that long, skip.
wcsncpyz(Path,SrcName,ASIZE(Path));

size_t SkipLength=wcslen(SkipPart);

if (SkipLength>0 && wcsncmp(Path,SkipPart,SkipLength)!=0)
SkipLength=0; // Parameter validation, not really needed now.

// Do not check parts already checked in previous path to improve performance.
for (uint I=0;Path[I]!=0 && I<LastChecked.size() && Path[I]==LastChecked[I];I++)
if (IsPathDiv(Path[I]) && I>SkipLength)
SkipLength=I;

wchar *Name=Path;
if (SkipLength>0)
{
// Avoid converting symlinks in destination path part specified by user.
Name+=SkipLength;
while (IsPathDiv(*Name))
Name++;
}

for (wchar *s=Path+wcslen(Path)-1;s>Name;s--)
if (IsPathDiv(*s))
{
*s=0;
FindData FD;
if (FindFile::FastFind(Path,&FD,true) && FD.IsLink)
#ifdef _WIN_ALL
if (!DelDir(Path))
#else
if (!DelFile(Path))
#endif
return false; // Couldn't delete the symlink to replace it with directory.
}
LastChecked=SrcName;
//#endif
return true;
}


bool IsRelativeSymlinkSafe(CommandData *Cmd,const wchar *SrcName,const wchar *PrepSrcName,const wchar *TargetName)
{
// Catch root dir based /path/file paths also as stuff like \\?\.
Expand All @@ -131,10 +193,14 @@ bool IsRelativeSymlinkSafe(CommandData *Cmd,const wchar *SrcName,const wchar *Pr
UpLevels++;
TargetName++;
}
// If link target includes "..", it must not have another links
// in the path, because they can bypass our safety check. For example,
// If link target includes "..", it must not have another links in its
// source path, because they can bypass our safety check. For example,
// suppose we extracted "lnk1" -> "." first and "lnk1/lnk2" -> ".." next
// or "dir/lnk1" -> ".." first and "dir/lnk1/lnk2" -> ".." next.
// or "dir/lnk1" -> ".." first, "dir/lnk1/lnk2" -> ".." next and
// file "dir/lnk1/lnk2/poc.txt" last.
// Do not confuse with link chains in target, this is in link source path.
// It is important for Windows too, though this check can be omitted
// if LinksToDirs is invoked in Windows as well.
if (UpLevels>0 && LinkInPath(PrepSrcName))
return false;

Expand All @@ -160,15 +226,26 @@ bool IsRelativeSymlinkSafe(CommandData *Cmd,const wchar *SrcName,const wchar *Pr
}


bool ExtractSymlink(CommandData *Cmd,ComprDataIO &DataIO,Archive &Arc,const wchar *LinkName)
bool ExtractSymlink(CommandData *Cmd,ComprDataIO &DataIO,Archive &Arc,const wchar *LinkName,bool &UpLink)
{
// Returning true in Uplink indicates that link target might include ".."
// and enables additional checks. It is ok to falsely return true here,
// as it implies only the minor performance penalty. But we shall always
// return true for links with ".." in target for security reason.

UpLink=true; // Assume the target might include potentially unsafe "..".
#if defined(SAVE_LINKS) && defined(_UNIX) || defined(_WIN_ALL)
if (Arc.Format==RARFMT50) // For RAR5 archives we can check RedirName for both Unix and Windows.
UpLink=wcsstr(Arc.FileHead.RedirName,L"..")!=NULL;
#endif

#if defined(SAVE_LINKS) && defined(_UNIX)
// For RAR 3.x archives we process links even in test mode to skip link data.
if (Arc.Format==RARFMT15)
return ExtractUnixLink30(Cmd,DataIO,Arc,LinkName);
return ExtractUnixLink30(Cmd,DataIO,Arc,LinkName,UpLink);
if (Arc.Format==RARFMT50)
return ExtractUnixLink50(Cmd,LinkName,&Arc.FileHead);
#elif defined _WIN_ALL
#elif defined(_WIN_ALL)
// RAR 5.0 archives store link information in file header, so there is
// no need to additionally test it if we do not create a file.
if (Arc.Format==RARFMT50)
Expand Down
3 changes: 2 additions & 1 deletion extinfo.hpp
Original file line number Diff line number Diff line change
@@ -1,8 +1,9 @@
#ifndef _RAR_EXTINFO_
#define _RAR_EXTINFO_

bool LinksToDirs(const wchar *SrcName,const wchar *SkipPart,std::wstring &LastChecked);
bool IsRelativeSymlinkSafe(CommandData *Cmd,const wchar *SrcName,const wchar *PrepSrcName,const wchar *TargetName);
bool ExtractSymlink(CommandData *Cmd,ComprDataIO &DataIO,Archive &Arc,const wchar *LinkName);
bool ExtractSymlink(CommandData *Cmd,ComprDataIO &DataIO,Archive &Arc,const wchar *LinkName,bool &UpLink);
#ifdef _UNIX
void SetUnixOwner(Archive &Arc,const wchar *FileName);
#endif
Expand Down
47 changes: 43 additions & 4 deletions extract.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,12 @@ CmdExtract::CmdExtract(CommandData *Cmd)
memset(Analyze,0,sizeof(*Analyze));

TotalFileCount=0;

// Common for all archives involved. Set here instead of DoExtract()
// to use in unrar.dll too. Allows to avoid LinksToDirs() calls
// and save CPU time in no symlinks including ".." in target were extracted.
UpLinkExtracted=false;

Unp=new Unpack(&DataIO);
#ifdef RAR_SMP
Unp->SetThreads(Cmd->Threads);
Expand Down Expand Up @@ -125,6 +131,8 @@ void CmdExtract::ExtractArchiveInit(Archive &Arc)
ArcAnalyzed=false;

StartTime.SetCurrentTime();

LastCheckedSymlink.clear();
}


Expand Down Expand Up @@ -618,6 +626,10 @@ bool CmdExtract::ExtractCurrentFile(Archive &Arc,size_t HeaderSize,bool &Repeat)
wcsncpyz(DestFileName,Cmd->DllDestName,ASIZE(DestFileName));
#endif

if (ExtrFile && Command!='P' && !Cmd->Test && !Cmd->AbsoluteLinks &&
UpLinkExtracted)
ExtrFile=LinksToDirs(DestFileName,Cmd->ExtrPath,LastCheckedSymlink);

File CurFile;

bool LinkEntry=Arc.FileHead.RedirType!=FSREDIR_NONE;
Expand Down Expand Up @@ -747,7 +759,17 @@ bool CmdExtract::ExtractCurrentFile(Archive &Arc,size_t HeaderSize,bool &Repeat)
if (Type==FSREDIR_HARDLINK || Type==FSREDIR_FILECOPY)
{
wchar RedirName[NM];
ConvertPath(Arc.FileHead.RedirName,RedirName,ASIZE(RedirName));

// 2022.11.15: Might be needed when unpacking WinRAR 5.0 links with
// Unix RAR. WinRAR 5.0 used \ path separators here, when beginning
// from 5.10 even Windows version uses / internally and converts
// them to \ when reading FHEXTRA_REDIR.
// We must perform this conversion before ConvertPath call,
// so paths mixing different slashes like \dir1/dir2\file are
// processed correctly.
SlashToNative(Arc.FileHead.RedirName,RedirName,ASIZE(RedirName));

ConvertPath(RedirName,RedirName,ASIZE(RedirName));

wchar NameExisting[NM];
ExtrPrepareName(Arc,RedirName,NameExisting,ASIZE(NameExisting));
Expand All @@ -761,7 +783,22 @@ bool CmdExtract::ExtractCurrentFile(Archive &Arc,size_t HeaderSize,bool &Repeat)
if (Type==FSREDIR_UNIXSYMLINK || Type==FSREDIR_WINSYMLINK || Type==FSREDIR_JUNCTION)
{
if (FileCreateMode)
LinkSuccess=ExtractSymlink(Cmd,DataIO,Arc,DestFileName);
{
bool UpLink;
LinkSuccess=ExtractSymlink(Cmd,DataIO,Arc,DestFileName,UpLink);
UpLinkExtracted|=LinkSuccess && UpLink;

// We do not actually need to reset the cache here if we cache
// only the single last checked path, because at this point
// it will always contain the link own path and link can't
// overwrite its parent folder. But if we ever decide to cache
// several already checked paths, we'll need to reset them here.
// Otherwise if no files were created in one of such paths,
// let's say because of file create error, it might be possible
// to overwrite the path with link and avoid checks. We keep this
// code here as a reminder in case of possible modifications.
LastCheckedSymlink.clear(); // Reset cache for safety reason.
}
}
else
{
Expand Down Expand Up @@ -948,8 +985,6 @@ void CmdExtract::UnstoreFile(ComprDataIO &DataIO,int64 DestUnpSize)

bool CmdExtract::ExtractFileCopy(File &New,wchar *ArcName,const wchar *RedirName,wchar *NameNew,wchar *NameExisting,size_t NameExistingSize,int64 UnpSize)
{
SlashToNative(NameExisting,NameExisting,NameExistingSize); // Not needed for RAR 5.1+ archives.

File Existing;
if (!Existing.Open(NameExisting))
{
Expand Down Expand Up @@ -1269,6 +1304,8 @@ void CmdExtract::ExtrCreateDir(Archive &Arc,const wchar *ArcFileName)
DirExist=FileExist(DestFileName) && IsDir(GetFileAttr(DestFileName));
if (!DirExist)
{
if (!Cmd->AbsoluteLinks && UpLinkExtracted)
LinksToDirs(DestFileName,Cmd->ExtrPath,LastCheckedSymlink);
CreatePath(DestFileName,true,Cmd->DisableNames);
MDCode=MakeDir(DestFileName,!Cmd->IgnoreGeneralAttr,Arc.FileHead.FileAttr);
}
Expand Down Expand Up @@ -1350,6 +1387,8 @@ bool CmdExtract::ExtrCreateFile(Archive &Arc,File &CurFile)

MakeNameUsable(DestFileName,true);

if (!Cmd->AbsoluteLinks && UpLinkExtracted)
LinksToDirs(DestFileName,Cmd->ExtrPath,LastCheckedSymlink);
CreatePath(DestFileName,true,Cmd->DisableNames);
if (FileCreate(Cmd,&CurFile,DestFileName,ASIZE(DestFileName),&UserReject,Arc.FileHead.UnpSize,&Arc.FileHead.mtime,true))
{
Expand Down
6 changes: 6 additions & 0 deletions extract.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,12 @@ class CmdExtract
bool PrevProcessed; // If previous file was successfully extracted or tested.
wchar DestFileName[NM];
bool PasswordCancelled;
bool UpLinkExtracted; // At least one symlink with ".." in target was extracted.

// Last path checked for symlinks. We use it to improve the performance,
// so we do not check recently checked folders again.
std::wstring LastCheckedSymlink;

#if defined(_WIN_ALL) && !defined(SFX_MODULE) && !defined(SILENT)
bool Fat32,NotFat32;
#endif
Expand Down
2 changes: 0 additions & 2 deletions hardlinks.cpp
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
bool ExtractHardlink(CommandData *Cmd,wchar *NameNew,wchar *NameExisting,size_t NameExistingSize)
{
SlashToNative(NameExisting,NameExisting,NameExistingSize); // Not needed for RAR 5.1+ archives.

if (!FileExist(NameExisting))
{
uiMsg(UIERROR_HLINKCREATE,NameNew);
Expand Down
6 changes: 4 additions & 2 deletions model.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -532,13 +532,15 @@ inline bool RARPPM_CONTEXT::decodeSymbol2(ModelPPM *Model)
Model->Coder.SubRange.LowCount=HiCnt;
Model->Coder.SubRange.HighCount=Model->Coder.SubRange.scale;
i=NumStats-Model->NumMasked;
pps--;

// 2022.12.02: we removed pps-- here and changed the code below to avoid
// "array subscript -1 is outside array bounds" warning in some compilers.
do
{
pps++;
if (pps>=ps+ASIZE(ps)) // Extra safety check.
return false;
Model->CharMask[(*pps)->Symbol]=Model->EscCount;
pps++;
} while ( --i );
psee2c->Summ += Model->Coder.SubRange.scale;
Model->NumMasked = NumStats;
Expand Down
14 changes: 10 additions & 4 deletions pathfn.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -31,11 +31,17 @@ wchar* ConvertPath(const wchar *SrcPath,wchar *DestPath,size_t DestSize)
const wchar *s=DestPtr;
if (s[0]!=0 && IsDriveDiv(s[1]))
s+=2;
if (s[0]=='\\' && s[1]=='\\')

// Skip UNC Windows \\server\share\ or Unix //server/share/
if (IsPathDiv(s[0]) && IsPathDiv(s[1]))
{
const wchar *Slash=wcschr(s+2,'\\');
if (Slash!=NULL && (Slash=wcschr(Slash+1,'\\'))!=NULL)
s=Slash+1;
uint SlashCount=0;
for (const wchar *t=s+2;*t!=0;t++)
if (IsPathDiv(*t) && ++SlashCount==2)
{
s=t+1; // Found two more path separators after leading two.
break;
}
}
for (const wchar *t=s;*t!=0;t++)
if (IsPathDiv(*t))
Expand Down
11 changes: 11 additions & 0 deletions timefn.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,17 @@ class RarTime

// Internal time representation in 1/TICKS_PER_SECOND since 01.01.1601.
// We use nanoseconds here to handle the high precision Unix time.
// It allows dates up to July 2185.
//
// If we'll ever need to extend the date range, we can define a lower
// precision Windows version of TICKS_PER_SECOND. But then Unix and Windows
// versions can differ in least significant digits of "lt" time output
// for Unix archives.
// Alternatively we can introduce 'bool HighPrecision' set to true
// in SetUnixNS() and TicksPerSecond() instead of constant above.
// It might be more reliable than defining TicksPerSecond variable,
// which wouldn't survive memset of any structure hosting RarTime.
// We would need to eliminate all such memsets in the entire code first.
uint64 itime;
public:
// RarLocalTime::Reminder precision. Must be equal to TICKS_PER_SECOND.
Expand Down
6 changes: 4 additions & 2 deletions ulinks.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,8 @@ static bool SafeCharToWide(const char *Src,wchar *Dest,size_t DestSize)
}


bool ExtractUnixLink30(CommandData *Cmd,ComprDataIO &DataIO,Archive &Arc,const wchar *LinkName)
static bool ExtractUnixLink30(CommandData *Cmd,ComprDataIO &DataIO,Archive &Arc,
const wchar *LinkName,bool &UpLink)
{
char Target[NM];
if (IsLink(Arc.FileHead.FileAttr))
Expand Down Expand Up @@ -100,13 +101,14 @@ bool ExtractUnixLink30(CommandData *Cmd,ComprDataIO &DataIO,Archive &Arc,const w
if (!Cmd->AbsoluteLinks && (IsFullPath(TargetW) ||
!IsRelativeSymlinkSafe(Cmd,Arc.FileHead.FileName,LinkName,TargetW)))
return false;
UpLink=strstr(Target,"..")!=NULL;
return UnixSymlink(Cmd,Target,LinkName,&Arc.FileHead.mtime,&Arc.FileHead.atime);
}
return false;
}


bool ExtractUnixLink50(CommandData *Cmd,const wchar *Name,FileHeader *hd)
static bool ExtractUnixLink50(CommandData *Cmd,const wchar *Name,FileHeader *hd)
{
char Target[NM];
WideToChar(hd->RedirName,Target,ASIZE(Target));
Expand Down
6 changes: 3 additions & 3 deletions version.hpp
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
#define RARVER_MAJOR 6
#define RARVER_MINOR 20
#define RARVER_BETA 2
#define RARVER_DAY 12
#define RARVER_MONTH 11
#define RARVER_BETA 3
#define RARVER_DAY 15
#define RARVER_MONTH 12
#define RARVER_YEAR 2022
Loading

0 comments on commit 2ecab6b

Please sign in to comment.