On 23.03.2015 23:15, Felix Geyer wrote:
> It looks like upstream attempted to fix / work around the issue.
> 
> From the WinRAR Version 5.21 changelog:
> 
>>    4. Now by default WinRAR skips symbolic links with absolute paths
>>       in link target when extracting. You can enable creating such links
>>       with "Allow absolute paths in symbolic links" option on "Advanced"
>>       page of extraction dialog or with -ola command line switch.
>>
>>       Such links pointing to folders outside of extraction destination
>>       folder can present a security risk. Enable their extraction only
>>       if you are sure that archive contents is safe, such as your own backup.
> 
> The -ola switch and related changes are part of 5.2.5 but I can still 
> reproduce
> the problem with this version.

I've notified upstream about the incomplete fix.
Attached are patches for 5.0.10 and 5.2.5.
Review would be appreciated.

Cheers,
Felix
--- unrar-nonfree-5.0.10.orig/extinfo.cpp
+++ unrar-nonfree-5.0.10/extinfo.cpp
@@ -60,6 +60,37 @@ void SetExtraInfo(CommandData *Cmd,Archi
 
 
 
+bool IsRelativeSymlinkSafe(const wchar *SrcName,const wchar *TargetName)
+{
+  if (IsFullRootPath(SrcName))
+    return false;
+  int AllowedDepth=0;
+  while (*SrcName!=0)
+  {
+    if (IsPathDiv(SrcName[0]) && SrcName[1]!=0 && !IsPathDiv(SrcName[1]))
+    {
+      bool Dot=SrcName[1]=='.' && (IsPathDiv(SrcName[2]) || SrcName[2]==0);
+      bool Dot2=SrcName[1]=='.' && SrcName[2]=='.' && (IsPathDiv(SrcName[3]) || SrcName[3]==0);
+      if (!Dot && !Dot2)
+        AllowedDepth++;
+    }
+    SrcName++;
+  }
+  if (IsFullRootPath(TargetName)) // Catch root dir based /path/file paths.
+    return false;
+  for (int Pos=0;*TargetName!=0;Pos++)
+  {
+    bool Dot2=TargetName[0]=='.' && TargetName[1]=='.' && 
+              (IsPathDiv(TargetName[2]) || TargetName[2]==0) &&
+              (Pos==0 || IsPathDiv(*(TargetName-1)));
+    if (Dot2)
+      AllowedDepth--;
+    TargetName++;
+  }
+  return AllowedDepth>=0;
+}
+
+
 bool ExtractSymlink(CommandData *Cmd,ComprDataIO &DataIO,Archive &Arc,const wchar *LinkName)
 {
 #if defined(SAVE_LINKS) && defined(_UNIX)
--- unrar-nonfree-5.0.10.orig/extinfo.hpp
+++ unrar-nonfree-5.0.10/extinfo.hpp
@@ -1,6 +1,7 @@
 #ifndef _RAR_EXTINFO_
 #define _RAR_EXTINFO_
 
+bool IsRelativeSymlinkSafe(const wchar *SrcName,const wchar *TargetName);
 bool ExtractSymlink(CommandData *Cmd,ComprDataIO &DataIO,Archive &Arc,const wchar *LinkName);
 #ifdef _UNIX
 void SetUnixOwner(Archive &Arc,const wchar *FileName);
--- unrar-nonfree-5.0.10.orig/pathfn.cpp
+++ unrar-nonfree-5.0.10/pathfn.cpp
@@ -593,6 +593,12 @@ bool IsFullPath(const wchar *Path)
 }
 
 
+bool IsFullRootPath(const wchar *Path)
+{
+  return IsFullPath(Path) || IsPathDiv(Path[0]);
+}
+
+
 bool IsDiskLetter(const wchar *Path)
 {
   wchar Letter=etoupperw(Path[0]);
--- unrar-nonfree-5.0.10.orig/pathfn.hpp
+++ unrar-nonfree-5.0.10/pathfn.hpp
@@ -31,6 +31,7 @@ wchar* UnixSlashToDos(wchar *SrcName,wch
 wchar* DosSlashToUnix(wchar *SrcName,wchar *DestName=NULL,size_t MaxLength=NM);
 void ConvertNameToFull(const wchar *Src,wchar *Dest,size_t MaxSize);
 bool IsFullPath(const wchar *Path);
+bool IsFullRootPath(const wchar *Path);
 bool IsDiskLetter(const wchar *Path);
 void GetPathRoot(const wchar *Path,wchar *Root,size_t MaxSize);
 int ParseVersionFileName(wchar *Name,bool Truncate);
--- unrar-nonfree-5.0.10.orig/ulinks.cpp
+++ unrar-nonfree-5.0.10/ulinks.cpp
@@ -24,6 +24,12 @@ static bool UnixSymlink(const char *Targ
 }
 
 
+static bool IsFullPath(const char *PathA) // Unix ASCII version.
+{
+  return *PathA==CPATHDIVIDER;
+}
+
+
 bool ExtractUnixLink30(ComprDataIO &DataIO,Archive &Arc,const wchar *LinkName)
 {
   char Target[NM];
@@ -42,6 +48,9 @@ bool ExtractUnixLink30(ComprDataIO &Data
     if (!DataIO.UnpHash.Cmp(&Arc.FileHead.FileHash,Arc.FileHead.UseHashKey ? Arc.FileHead.HashKey:NULL))
       return true;
 
+    if (IsFullPath(Target) || !IsRelativeSymlinkSafe(Arc.FileHead.FileName,Arc.FileHead.RedirName))
+      return false;
+
     return UnixSymlink(Target,LinkName);
   }
   return false;
@@ -55,10 +64,14 @@ bool ExtractUnixLink50(const wchar *Name
   if (hd->RedirType==FSREDIR_WINSYMLINK || hd->RedirType==FSREDIR_JUNCTION)
   {
     // Cannot create Windows absolute path symlinks in Unix. Only relative path
-    // Windows symlinks can be created here.
-    if (strncmp(Target,"\\??\\",4)==0)
+    // Windows symlinks can be created here. RAR 5.0 used \??\ prefix
+    // for Windows absolute symlinks, since RAR 5.1 /??/ is used.
+    // We escape ? as \? to avoid "trigraph" warning
+    if (strncmp(Target,"\\??\\",4)==0 || strncmp(Target,"/\?\?/",4)==0)
       return false;
     DosSlashToUnix(Target,Target,ASIZE(Target));
   }
+  if (IsFullPath(Target) || !IsRelativeSymlinkSafe(hd->FileName,hd->RedirName))
+    return false;
   return UnixSymlink(Target,Name);
 }
--- unrar-nonfree-5.2.5.orig/ulinks.cpp
+++ unrar-nonfree-5.2.5/ulinks.cpp
@@ -56,6 +56,7 @@ bool ExtractUnixLink30(CommandData *Cmd,
 
     if (!Cmd->AbsoluteLinks && (IsFullPath(Target) ||
         !IsRelativeSymlinkSafe(Arc.FileHead.FileName,Arc.FileHead.RedirName)))
+      return false;
 
     return UnixSymlink(Target,LinkName,&Arc.FileHead.mtime,&Arc.FileHead.atime);
   }

Reply via email to