Line | Count | Source (jump to first uncovered line) |
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 |
22 | | void SetExtraInfo20(CommandData *Cmd,Archive &Arc,wchar *Name) |
23 | 0 | { |
24 | 0 | if (Cmd->Test) |
25 | 0 | return; |
26 | 0 | switch(Arc.SubBlockHead.SubType) |
27 | 0 | { |
28 | 0 | #ifdef _UNIX |
29 | 0 | case UO_HEAD: |
30 | 0 | if (Cmd->ProcessOwners) |
31 | 0 | ExtractUnixOwner20(Arc,Name); |
32 | 0 | break; |
33 | 0 | #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 | 0 | } |
44 | 0 | } |
45 | | #endif |
46 | | |
47 | | |
48 | | // RAR3 and RAR5 service header extra records. |
49 | | void SetExtraInfo(CommandData *Cmd,Archive &Arc,wchar *Name) |
50 | 197 | { |
51 | 197 | #ifdef _UNIX |
52 | 197 | if (!Cmd->Test && Cmd->ProcessOwners && Arc.Format==RARFMT15 && |
53 | 197 | Arc.SubHead.CmpName(SUBHEAD_TYPE_UOWNER)) |
54 | 0 | ExtractUnixOwner30(Arc,Name); |
55 | 197 | #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 | 197 | } |
63 | | |
64 | | |
65 | | // Extra data stored directly in file header. |
66 | | void SetFileHeaderExtra(CommandData *Cmd,Archive &Arc,wchar *Name) |
67 | 1.64k | { |
68 | 1.64k | #ifdef _UNIX |
69 | 1.64k | if (Cmd->ProcessOwners && Arc.Format==RARFMT50 && Arc.FileHead.UnixOwnerSet) |
70 | 0 | SetUnixOwner(Arc,Name); |
71 | 1.64k | #endif |
72 | 1.64k | } |
73 | | |
74 | | |
75 | | |
76 | | |
77 | | // Calculate a number of path components except \. and \.. |
78 | | static int CalcAllowedDepth(const wchar *Name) |
79 | 8 | { |
80 | 8 | int AllowedDepth=0; |
81 | 36 | while (*Name!=0) |
82 | 28 | { |
83 | 28 | if (IsPathDiv(Name[0]) && Name[1]!=0 && !IsPathDiv(Name[1])) |
84 | 0 | { |
85 | 0 | bool Dot=Name[1]=='.' && (IsPathDiv(Name[2]) || Name[2]==0); |
86 | 0 | bool Dot2=Name[1]=='.' && Name[2]=='.' && (IsPathDiv(Name[3]) || Name[3]==0); |
87 | 0 | if (!Dot && !Dot2) |
88 | 0 | AllowedDepth++; |
89 | 0 | } |
90 | 28 | Name++; |
91 | 28 | } |
92 | 8 | return AllowedDepth; |
93 | 8 | } |
94 | | |
95 | | |
96 | | // Check if all existing path components are directories and not links. |
97 | | static bool LinkInPath(const wchar *Name) |
98 | 1 | { |
99 | 1 | wchar Path[NM]; |
100 | 1 | if (wcslen(Name)>=ASIZE(Path)) |
101 | 0 | return true; // It should not be that long, skip. |
102 | 1 | wcsncpyz(Path,Name,ASIZE(Path)); |
103 | 3 | for (wchar *s=Path+wcslen(Path)-1;s>Path;s--) |
104 | 2 | if (IsPathDiv(*s)) |
105 | 0 | { |
106 | 0 | *s=0; |
107 | 0 | FindData FD; |
108 | 0 | if (FindFile::FastFind(Path,&FD,true) && (FD.IsLink || !FD.IsDir)) |
109 | 0 | return true; |
110 | 0 | } |
111 | 1 | return false; |
112 | 1 | } |
113 | | |
114 | | |
115 | | bool IsRelativeSymlinkSafe(CommandData *Cmd,const wchar *SrcName,const wchar *PrepSrcName,const wchar *TargetName) |
116 | 6 | { |
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 | 6 | if (IsFullRootPath(SrcName) || IsFullRootPath(TargetName)) |
121 | 2 | return false; |
122 | | |
123 | | // Number of ".." in link target. |
124 | 4 | int UpLevels=0; |
125 | 38 | for (int Pos=0;*TargetName!=0;Pos++) |
126 | 34 | { |
127 | 34 | bool Dot2=TargetName[0]=='.' && TargetName[1]=='.' && |
128 | 34 | (IsPathDiv(TargetName[2]) || TargetName[2]==0) && |
129 | 34 | (Pos==0 || IsPathDiv(*(TargetName-1))); |
130 | 34 | if (Dot2) |
131 | 1 | UpLevels++; |
132 | 34 | TargetName++; |
133 | 34 | } |
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 | 4 | if (UpLevels>0 && LinkInPath(PrepSrcName)) |
139 | 0 | 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 | 4 | 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 | 4 | size_t ExtrPathLength=wcslen(Cmd->ExtrPath); |
151 | 4 | if (ExtrPathLength>0 && wcsncmp(PrepSrcName,Cmd->ExtrPath,ExtrPathLength)==0) |
152 | 0 | { |
153 | 0 | PrepSrcName+=ExtrPathLength; |
154 | 0 | while (IsPathDiv(*PrepSrcName)) |
155 | 0 | PrepSrcName++; |
156 | 0 | } |
157 | 4 | int PrepAllowedDepth=CalcAllowedDepth(PrepSrcName); |
158 | | |
159 | 4 | return AllowedDepth>=UpLevels && PrepAllowedDepth>=UpLevels; |
160 | 4 | } |
161 | | |
162 | | |
163 | | bool ExtractSymlink(CommandData *Cmd,ComprDataIO &DataIO,Archive &Arc,const wchar *LinkName) |
164 | 11 | { |
165 | 11 | #if defined(SAVE_LINKS) && defined(_UNIX) |
166 | | // For RAR 3.x archives we process links even in test mode to skip link data. |
167 | 11 | if (Arc.Format==RARFMT15) |
168 | 7 | return ExtractUnixLink30(Cmd,DataIO,Arc,LinkName); |
169 | 4 | if (Arc.Format==RARFMT50) |
170 | 4 | 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 | 0 | return false; |
178 | 4 | } |