libclamunrar/extinfo.cpp
01eebc13
 #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,wchar *Name)
 {
   if (Cmd->Test)
     return;
   switch(Arc.SubBlockHead.SubType)
   {
 #ifdef _UNIX
     case UO_HEAD:
       if (Cmd->ProcessOwners)
         ExtractUnixOwner20(Arc,Name);
       break;
 #endif
 #ifdef _WIN_ALL
     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,wchar *Name)
 {
 #ifdef _UNIX
   if (!Cmd->Test && Cmd->ProcessOwners && Arc.Format==RARFMT15 &&
       Arc.SubHead.CmpName(SUBHEAD_TYPE_UOWNER))
     ExtractUnixOwner30(Arc,Name);
 #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,wchar *Name)
 {
 #ifdef _UNIX
    if (Cmd->ProcessOwners && Arc.Format==RARFMT50 && Arc.FileHead.UnixOwnerSet)
      SetUnixOwner(Arc,Name);
 #endif
 }
 
 
 
 
 // Calculate a number of path components except \. and \..
 static int CalcAllowedDepth(const wchar *Name)
 {
   int AllowedDepth=0;
   while (*Name!=0)
   {
     if (IsPathDiv(Name[0]) && Name[1]!=0 && !IsPathDiv(Name[1]))
     {
       bool Dot=Name[1]=='.' && (IsPathDiv(Name[2]) || Name[2]==0);
       bool Dot2=Name[1]=='.' && Name[2]=='.' && (IsPathDiv(Name[3]) || Name[3]==0);
       if (!Dot && !Dot2)
         AllowedDepth++;
     }
     Name++;
   }
   return AllowedDepth;
 }
 
 
 // Check if all existing path components are directories and not links.
 static bool LinkInPath(const wchar *Name)
 {
   wchar Path[NM];
   if (wcslen(Name)>=ASIZE(Path))
     return true;  // It should not be that long, skip.
   wcsncpyz(Path,Name,ASIZE(Path));
   for (wchar *s=Path+wcslen(Path)-1;s>Path;s--)
     if (IsPathDiv(*s))
     {
       *s=0;
       FindData FD;
       if (FindFile::FastFind(Path,&FD,true) && (FD.IsLink || !FD.IsDir))
         return true;
     }
   return false;
 }
 
 
 bool IsRelativeSymlinkSafe(CommandData *Cmd,const wchar *SrcName,const wchar *PrepSrcName,const wchar *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 (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)
       UpLevels++;
     TargetName++;
   }
   // If link target includes "..", it must not have another links
   // in the 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.
   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=wcslen(Cmd->ExtrPath);
   if (ExtrPathLength>0 && wcsncmp(PrepSrcName,Cmd->ExtrPath,ExtrPathLength)==0)
   {
     PrepSrcName+=ExtrPathLength;
     while (IsPathDiv(*PrepSrcName))
       PrepSrcName++;
   }
   int PrepAllowedDepth=CalcAllowedDepth(PrepSrcName);
 
   return AllowedDepth>=UpLevels && PrepAllowedDepth>=UpLevels;
 }
 
 
 bool ExtractSymlink(CommandData *Cmd,ComprDataIO &DataIO,Archive &Arc,const wchar *LinkName)
 {
 #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);
   if (Arc.Format==RARFMT50)
     return ExtractUnixLink50(Cmd,LinkName,&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,&Arc.FileHead);
 #endif
   return false;
 }