1 #include "rar.hpp"
2 
3 #include "hardlinks.cpp"
4 #include "win32stm.cpp"
5 
6 #ifdef _WIN_ALL
7 #include "win32acl.cpp"
8 #include "win32lnk.cpp"
9 #endif
10 
11 #ifdef _UNIX
12 #include "uowners.cpp"
13 #ifdef SAVE_LINKS
14 #include "ulinks.cpp"
15 #endif
16 #endif
17 
18 
19 
20 // RAR2 service header extra records.
21 #ifndef SFX_MODULE
SetExtraInfo20(CommandData * Cmd,Archive & Arc,wchar * Name)22 void SetExtraInfo20(CommandData *Cmd,Archive &Arc,wchar *Name)
23 {
24   if (Cmd->Test)
25     return;
26   switch(Arc.SubBlockHead.SubType)
27   {
28 #ifdef _UNIX
29     case UO_HEAD:
30       if (Cmd->ProcessOwners)
31         ExtractUnixOwner20(Arc,Name);
32       break;
33 #endif
34 #ifdef _WIN_ALL
35     case NTACL_HEAD:
36       if (Cmd->ProcessOwners)
37         ExtractACL20(Arc,Name);
38       break;
39     case STREAM_HEAD:
40       ExtractStreams20(Arc,Name);
41       break;
42 #endif
43   }
44 }
45 #endif
46 
47 
48 // RAR3 and RAR5 service header extra records.
SetExtraInfo(CommandData * Cmd,Archive & Arc,wchar * Name)49 void SetExtraInfo(CommandData *Cmd,Archive &Arc,wchar *Name)
50 {
51 #ifdef _UNIX
52   if (!Cmd->Test && Cmd->ProcessOwners && Arc.Format==RARFMT15 &&
53       Arc.SubHead.CmpName(SUBHEAD_TYPE_UOWNER))
54     ExtractUnixOwner30(Arc,Name);
55 #endif
56 #ifdef _WIN_ALL
57   if (!Cmd->Test && Cmd->ProcessOwners && Arc.SubHead.CmpName(SUBHEAD_TYPE_ACL))
58     ExtractACL(Arc,Name);
59   if (Arc.SubHead.CmpName(SUBHEAD_TYPE_STREAM))
60     ExtractStreams(Arc,Name,Cmd->Test);
61 #endif
62 }
63 
64 
65 // Extra data stored directly in file header.
SetFileHeaderExtra(CommandData * Cmd,Archive & Arc,wchar * Name)66 void SetFileHeaderExtra(CommandData *Cmd,Archive &Arc,wchar *Name)
67 {
68 #ifdef _UNIX
69    if (Cmd->ProcessOwners && Arc.Format==RARFMT50 && Arc.FileHead.UnixOwnerSet)
70      SetUnixOwner(Arc,Name);
71 #endif
72 }
73 
74 
75 
76 
77 // Calculate a number of path components except \. and \..
CalcAllowedDepth(const wchar * Name)78 static int CalcAllowedDepth(const wchar *Name)
79 {
80   int AllowedDepth=0;
81   while (*Name!=0)
82   {
83     if (IsPathDiv(Name[0]) && Name[1]!=0 && !IsPathDiv(Name[1]))
84     {
85       bool Dot=Name[1]=='.' && (IsPathDiv(Name[2]) || Name[2]==0);
86       bool Dot2=Name[1]=='.' && Name[2]=='.' && (IsPathDiv(Name[3]) || Name[3]==0);
87       if (!Dot && !Dot2)
88         AllowedDepth++;
89     }
90     Name++;
91   }
92   return AllowedDepth;
93 }
94 
95 
96 // Check if all existing path components are directories and not links.
LinkInPath(const wchar * Name)97 static bool LinkInPath(const wchar *Name)
98 {
99   wchar Path[NM];
100   if (wcslen(Name)>=ASIZE(Path))
101     return true;  // It should not be that long, skip.
102   wcsncpyz(Path,Name,ASIZE(Path));
103   for (wchar *s=Path+wcslen(Path)-1;s>Path;s--)
104     if (IsPathDiv(*s))
105     {
106       *s=0;
107       FindData FD;
108       if (FindFile::FastFind(Path,&FD,true) && (FD.IsLink || !FD.IsDir))
109         return true;
110     }
111   return false;
112 }
113 
114 
IsRelativeSymlinkSafe(CommandData * Cmd,const wchar * SrcName,const wchar * PrepSrcName,const wchar * TargetName)115 bool IsRelativeSymlinkSafe(CommandData *Cmd,const wchar *SrcName,const wchar *PrepSrcName,const wchar *TargetName)
116 {
117   // Catch root dir based /path/file paths also as stuff like \\?\.
118   // Do not check PrepSrcName here, it can be root based if destination path
119   // is a root based.
120   if (IsFullRootPath(SrcName) || IsFullRootPath(TargetName))
121     return false;
122 
123   // Number of ".." in link target.
124   int UpLevels=0;
125   for (int Pos=0;*TargetName!=0;Pos++)
126   {
127     bool Dot2=TargetName[0]=='.' && TargetName[1]=='.' &&
128               (IsPathDiv(TargetName[2]) || TargetName[2]==0) &&
129               (Pos==0 || IsPathDiv(*(TargetName-1)));
130     if (Dot2)
131       UpLevels++;
132     TargetName++;
133   }
134   // If link target includes "..", it must not have another links
135   // in the path, because they can bypass our safety check. For example,
136   // suppose we extracted "lnk1" -> "." first and "lnk1/lnk2" -> ".." next
137   // or "dir/lnk1" -> ".." first and "dir/lnk1/lnk2" -> ".." next.
138   if (UpLevels>0 && LinkInPath(PrepSrcName))
139     return false;
140 
141   // We could check just prepared src name, but for extra safety
142   // we check both original (as from archive header) and prepared
143   // (after applying the destination path and -ep switches) names.
144 
145   int AllowedDepth=CalcAllowedDepth(SrcName); // Original name depth.
146 
147   // Remove the destination path from prepared name if any. We should not
148   // count the destination path depth, because the link target must point
149   // inside of this path, not outside of it.
150   size_t ExtrPathLength=wcslen(Cmd->ExtrPath);
151   if (ExtrPathLength>0 && wcsncmp(PrepSrcName,Cmd->ExtrPath,ExtrPathLength)==0)
152   {
153     PrepSrcName+=ExtrPathLength;
154     while (IsPathDiv(*PrepSrcName))
155       PrepSrcName++;
156   }
157   int PrepAllowedDepth=CalcAllowedDepth(PrepSrcName);
158 
159   return AllowedDepth>=UpLevels && PrepAllowedDepth>=UpLevels;
160 }
161 
162 
ExtractSymlink(CommandData * Cmd,ComprDataIO & DataIO,Archive & Arc,const wchar * LinkName)163 bool ExtractSymlink(CommandData *Cmd,ComprDataIO &DataIO,Archive &Arc,const wchar *LinkName)
164 {
165 #if defined(SAVE_LINKS) && defined(_UNIX)
166   // For RAR 3.x archives we process links even in test mode to skip link data.
167   if (Arc.Format==RARFMT15)
168     return ExtractUnixLink30(Cmd,DataIO,Arc,LinkName);
169   if (Arc.Format==RARFMT50)
170     return ExtractUnixLink50(Cmd,LinkName,&Arc.FileHead);
171 #elif defined _WIN_ALL
172   // RAR 5.0 archives store link information in file header, so there is
173   // no need to additionally test it if we do not create a file.
174   if (Arc.Format==RARFMT50)
175     return CreateReparsePoint(Cmd,LinkName,&Arc.FileHead);
176 #endif
177   return false;
178 }
179