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 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184
|
#include "rar.hpp"
#include "hardlinks.cpp"
#include "win32stm.cpp"
#ifdef _WIN_ALL
#include "win32acl.cpp"
#include "win32lnk.cpp"
#endif
#ifdef _UNIX
#include "uowners.cpp"
#ifdef SAVE_LINKS
#include "ulinks.cpp"
#endif
#endif
// RAR2 service header extra records.
#ifndef SFX_MODULE
void SetExtraInfo20(CommandData *Cmd,Archive &Arc,const std::wstring &Name)
{
#ifdef _WIN_ALL
if (Cmd->Test)
return;
switch(Arc.SubBlockHead.SubType)
{
case NTACL_HEAD:
if (Cmd->ProcessOwners)
ExtractACL20(Arc,Name);
break;
case STREAM_HEAD:
ExtractStreams20(Arc,Name);
break;
}
#endif
}
#endif
// RAR3 and RAR5 service header extra records.
void SetExtraInfo(CommandData *Cmd,Archive &Arc,const std::wstring &Name)
{
#ifdef _UNIX
if (!Cmd->Test && Cmd->ProcessOwners && Arc.Format==RARFMT15 &&
Arc.SubHead.CmpName(SUBHEAD_TYPE_UOWNER))
ExtractUnixOwner30(Arc,Name.c_str());
#endif
#ifdef _WIN_ALL
if (!Cmd->Test && Cmd->ProcessOwners && Arc.SubHead.CmpName(SUBHEAD_TYPE_ACL))
ExtractACL(Arc,Name);
if (Arc.SubHead.CmpName(SUBHEAD_TYPE_STREAM))
ExtractStreams(Arc,Name,Cmd->Test);
#endif
}
// Extra data stored directly in file header.
void SetFileHeaderExtra(CommandData *Cmd,Archive &Arc,const std::wstring &Name)
{
#ifdef _UNIX
if (Cmd->ProcessOwners && Arc.Format==RARFMT50 && Arc.FileHead.UnixOwnerSet)
SetUnixOwner(Arc,Name);
#endif
}
// Calculate the number of path components except \. and \..
static int CalcAllowedDepth(const std::wstring &Name)
{
int AllowedDepth=0;
for (size_t I=0;I<Name.size();I++)
if (IsPathDiv(Name[I]))
{
bool Dot=Name[I+1]=='.' && (IsPathDiv(Name[I+2]) || Name[I+2]==0);
bool Dot2=Name[I+1]=='.' && Name[I+2]=='.' && (IsPathDiv(Name[I+3]) || Name[I+3]==0);
if (!Dot && !Dot2)
AllowedDepth++;
else
if (Dot2)
AllowedDepth--;
}
return AllowedDepth < 0 ? 0 : AllowedDepth;
}
// Check if all existing path components are directories and not links.
static bool LinkInPath(std::wstring Path)
{
if (Path.empty()) // So we can safely use Path.size()-1 below.
return false;
for (size_t I=Path.size()-1;I>0;I--)
if (IsPathDiv(Path[I]))
{
Path.erase(I);
FindData FD;
if (FindFile::FastFind(Path,&FD,true) && (FD.IsLink || !FD.IsDir))
return true;
}
return false;
}
bool IsRelativeSymlinkSafe(CommandData *Cmd,const std::wstring &SrcName,std::wstring PrepSrcName,const std::wstring &TargetName)
{
// Catch root dir based /path/file paths also as stuff like \\?\.
// Do not check PrepSrcName here, it can be root based if destination path
// is a root based.
if (IsFullRootPath(SrcName) || IsFullRootPath(TargetName))
return false;
// Number of ".." in link target.
int UpLevels=0;
for (uint Pos=0;Pos<TargetName.size();Pos++)
{
bool Dot2=TargetName[Pos]=='.' && TargetName[Pos+1]=='.' &&
(IsPathDiv(TargetName[Pos+2]) || TargetName[Pos+2]==0) &&
(Pos==0 || IsPathDiv(TargetName[Pos-1]));
if (Dot2)
UpLevels++;
}
// 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, "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;
// We could check just prepared src name, but for extra safety
// we check both original (as from archive header) and prepared
// (after applying the destination path and -ep switches) names.
int AllowedDepth=CalcAllowedDepth(SrcName); // Original name depth.
// Remove the destination path from prepared name if any. We should not
// count the destination path depth, because the link target must point
// inside of this path, not outside of it.
size_t ExtrPathLength=Cmd->ExtrPath.size();
if (ExtrPathLength>0 && PrepSrcName.compare(0,ExtrPathLength,Cmd->ExtrPath)==0)
{
while (IsPathDiv(PrepSrcName[ExtrPathLength]))
ExtrPathLength++;
PrepSrcName.erase(0,ExtrPathLength);
}
int PrepAllowedDepth=CalcAllowedDepth(PrepSrcName);
return AllowedDepth>=UpLevels && PrepAllowedDepth>=UpLevels;
}
bool ExtractSymlink(CommandData *Cmd,ComprDataIO &DataIO,Archive &Arc,const std::wstring &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=Arc.FileHead.RedirName.find(L"..")!=std::wstring::npos;
#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.c_str(),UpLink);
if (Arc.Format==RARFMT50)
return ExtractUnixLink50(Cmd,LinkName.c_str(),&Arc.FileHead);
#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)
return CreateReparsePoint(Cmd,LinkName.c_str(),&Arc.FileHead);
#endif
return false;
}
|