Line | Count | Source (jump to first uncovered line) |
1 | | |
2 | | |
3 | | #ifdef _WIN_ALL |
4 | | // StreamName must include the leading ':'. |
5 | | static bool IsNtfsProhibitedStream(const std::wstring &StreamName) |
6 | | { |
7 | | // 2024.03.14: We replaced the predefined names check with simpler |
8 | | // "no more than a single colon" check. Second colon could be used to |
9 | | // define the type of alternate stream, but RAR archives work only with |
10 | | // data streams and do not store :$DATA type in archive. It is assumed. |
11 | | // So there is no legitimate use for stream type inside of archive, |
12 | | // but it can be abused to hide the actual file data in file::$DATA |
13 | | // or hide the actual MOTW data in Zone.Identifier:$DATA. |
14 | | uint ColonCount=0; |
15 | | for (wchar Ch:StreamName) |
16 | | if (Ch==':' && ++ColonCount>1) |
17 | | return true; |
18 | | return false; |
19 | | /* |
20 | | const wchar *Reserved[]{ |
21 | | L"::$ATTRIBUTE_LIST",L"::$BITMAP",L"::$DATA",L"::$EA",L"::$EA_INFORMATION", |
22 | | L"::$FILE_NAME",L"::$INDEX_ALLOCATION",L":$I30:$INDEX_ALLOCATION", |
23 | | L"::$INDEX_ROOT",L"::$LOGGED_UTILITY_STREAM",L":$EFS:$LOGGED_UTILITY_STREAM", |
24 | | L":$TXF_DATA:$LOGGED_UTILITY_STREAM",L"::$OBJECT_ID",L"::$REPARSE_POINT" |
25 | | }; |
26 | | for (const wchar *Name : Reserved) |
27 | | if (wcsicomp(StreamName,Name)==0) |
28 | | return true; |
29 | | return false; |
30 | | */ |
31 | | } |
32 | | #endif |
33 | | |
34 | | |
35 | | #if !defined(SFX_MODULE) && defined(_WIN_ALL) |
36 | | void ExtractStreams20(Archive &Arc,const std::wstring &FileName) |
37 | | { |
38 | | if (Arc.BrokenHeader) |
39 | | { |
40 | | uiMsg(UIERROR_STREAMBROKEN,Arc.FileName,FileName); |
41 | | ErrHandler.SetErrorCode(RARX_CRC); |
42 | | return; |
43 | | } |
44 | | |
45 | | if (Arc.StreamHead.Method<0x31 || Arc.StreamHead.Method>0x35 || Arc.StreamHead.UnpVer>VER_PACK) |
46 | | { |
47 | | uiMsg(UIERROR_STREAMUNKNOWN,Arc.FileName,FileName); |
48 | | ErrHandler.SetErrorCode(RARX_WARNING); |
49 | | return; |
50 | | } |
51 | | |
52 | | std::wstring StreamName; |
53 | | CharToWide(Arc.StreamHead.StreamName,StreamName); |
54 | | |
55 | | if (StreamName[0]!=':') |
56 | | { |
57 | | uiMsg(UIERROR_STREAMBROKEN,Arc.FileName,FileName); |
58 | | ErrHandler.SetErrorCode(RARX_CRC); |
59 | | return; |
60 | | } |
61 | | |
62 | | // Convert single character names like f:stream to .\f:stream to |
63 | | // resolve the ambiguity with drive letters. |
64 | | std::wstring FullName=FileName.size()==1 ? L".\\"+FileName:FileName; |
65 | | FullName+=StreamName; |
66 | | |
67 | | #ifdef PROPAGATE_MOTW |
68 | | // 2022.10.31: Can't easily read RAR 2.0 stream data here, so if we already |
69 | | // propagated the archive Zone.Identifier stream, also known as Mark of |
70 | | // the Web, to extracted file, we do not overwrite it here. |
71 | | if (Arc.Motw.IsNameConflicting(StreamName)) |
72 | | return; |
73 | | |
74 | | // 2024.02.03: Prevent using Zone.Identifier:$DATA to overwrite Zone.Identifier |
75 | | // according to ZDI-CAN-23156 Trend Micro report. |
76 | | // 2024.03.14: Not needed after adding check for 2+ ':' in IsNtfsProhibitedStream((). |
77 | | // if (wcsnicomp(StreamName,L":Zone.Identifier:",17)==0) |
78 | | // return; |
79 | | #endif |
80 | | |
81 | | if (IsNtfsProhibitedStream(StreamName)) |
82 | | return; |
83 | | |
84 | | FindData FD; |
85 | | bool HostFound=FindFile::FastFind(FileName,&FD); |
86 | | |
87 | | if ((FD.FileAttr & FILE_ATTRIBUTE_READONLY)!=0) |
88 | | SetFileAttr(FileName,FD.FileAttr & ~FILE_ATTRIBUTE_READONLY); |
89 | | |
90 | | File CurFile; |
91 | | if (CurFile.WCreate(FullName)) |
92 | | { |
93 | | ComprDataIO DataIO; |
94 | | Unpack Unpack(&DataIO); |
95 | | Unpack.Init(0x10000,false); |
96 | | |
97 | | DataIO.SetPackedSizeToRead(Arc.StreamHead.DataSize); |
98 | | DataIO.EnableShowProgress(false); |
99 | | DataIO.SetFiles(&Arc,&CurFile); |
100 | | DataIO.UnpHash.Init(HASH_CRC32,1); |
101 | | Unpack.SetDestSize(Arc.StreamHead.UnpSize); |
102 | | Unpack.DoUnpack(Arc.StreamHead.UnpVer,false); |
103 | | |
104 | | if (Arc.StreamHead.StreamCRC!=DataIO.UnpHash.GetCRC32()) |
105 | | { |
106 | | uiMsg(UIERROR_STREAMBROKEN,Arc.FileName,StreamName); |
107 | | ErrHandler.SetErrorCode(RARX_CRC); |
108 | | } |
109 | | else |
110 | | CurFile.Close(); |
111 | | } |
112 | | |
113 | | // Restoring original file timestamps. |
114 | | File HostFile; |
115 | | if (HostFound && HostFile.Open(FileName,FMF_OPENSHARED|FMF_UPDATE)) |
116 | | SetFileTime(HostFile.GetHandle(),&FD.ftCreationTime,&FD.ftLastAccessTime, |
117 | | &FD.ftLastWriteTime); |
118 | | |
119 | | // Restoring original file attributes. |
120 | | // Important if file was read only or did not have "Archive" attribute. |
121 | | if ((FD.FileAttr & FILE_ATTRIBUTE_READONLY)!=0) |
122 | | SetFileAttr(FileName,FD.FileAttr); |
123 | | } |
124 | | #endif |
125 | | |
126 | | |
127 | | #ifdef _WIN_ALL |
128 | | void ExtractStreams(Archive &Arc,const std::wstring &FileName,bool TestMode) |
129 | | { |
130 | | std::wstring StreamName=GetStreamNameNTFS(Arc); |
131 | | if (StreamName[0]!=':') |
132 | | { |
133 | | uiMsg(UIERROR_STREAMBROKEN,Arc.FileName,FileName); |
134 | | ErrHandler.SetErrorCode(RARX_CRC); |
135 | | return; |
136 | | } |
137 | | |
138 | | if (TestMode) |
139 | | { |
140 | | File CurFile; |
141 | | Arc.ReadSubData(nullptr,&CurFile,true); |
142 | | return; |
143 | | } |
144 | | |
145 | | // Convert single character names like f:stream to .\f:stream to |
146 | | // resolve the ambiguity with drive letters. |
147 | | std::wstring FullName=FileName.size()==1 ? L".\\"+FileName:FileName; |
148 | | FullName+=StreamName; |
149 | | |
150 | | #ifdef PROPAGATE_MOTW |
151 | | // 2022.10.31: If we already propagated the archive Zone.Identifier stream, |
152 | | // also known as Mark of the Web, to extracted file, we overwrite it here |
153 | | // only if file zone is stricter. Received a user request for such behavior. |
154 | | |
155 | | std::string ParsedMotw; |
156 | | if (Arc.Motw.IsNameConflicting(StreamName)) |
157 | | { |
158 | | // Do not worry about excessive memory allocation, ReadSubData prevents it. |
159 | | std::vector<byte> FileMotw; |
160 | | if (!Arc.ReadSubData(&FileMotw,nullptr,false)) |
161 | | return; |
162 | | ParsedMotw.assign(FileMotw.begin(),FileMotw.end()); |
163 | | |
164 | | // We already set the archive stream. If file stream value isn't more |
165 | | // restricted, we do not want to write it over the existing archive stream. |
166 | | if (!Arc.Motw.IsFileStreamMoreSecure(ParsedMotw)) |
167 | | return; |
168 | | } |
169 | | |
170 | | // 2024.02.03: Prevent using :Zone.Identifier:$DATA to overwrite :Zone.Identifier |
171 | | // according to ZDI-CAN-23156 Trend Micro report. |
172 | | // 2024.03.14: Not needed after adding check for 2+ ':' in IsNtfsProhibitedStream((). |
173 | | // if (wcsnicomp(StreamName,L":Zone.Identifier:",17)==0) |
174 | | // return; |
175 | | #endif |
176 | | |
177 | | if (IsNtfsProhibitedStream(StreamName)) |
178 | | return; |
179 | | |
180 | | FindData FD; |
181 | | bool HostFound=FindFile::FastFind(FileName,&FD); |
182 | | |
183 | | if ((FD.FileAttr & FILE_ATTRIBUTE_READONLY)!=0) |
184 | | SetFileAttr(FileName,FD.FileAttr & ~FILE_ATTRIBUTE_READONLY); |
185 | | File CurFile; |
186 | | |
187 | | if (CurFile.WCreate(FullName)) |
188 | | { |
189 | | #ifdef PROPAGATE_MOTW |
190 | | if (!ParsedMotw.empty()) |
191 | | { |
192 | | // The archive propagated security zone is either missing |
193 | | // or less strict than file one. Write the file security zone here. |
194 | | CurFile.Write(ParsedMotw.data(),ParsedMotw.size()); |
195 | | CurFile.Close(); |
196 | | } |
197 | | else |
198 | | #endif |
199 | | if (Arc.ReadSubData(nullptr,&CurFile,false)) |
200 | | CurFile.Close(); |
201 | | } |
202 | | |
203 | | // Restoring original file timestamps. |
204 | | File HostFile; |
205 | | if (HostFound && HostFile.Open(FileName,FMF_OPENSHARED|FMF_UPDATE)) |
206 | | SetFileTime(HostFile.GetHandle(),&FD.ftCreationTime,&FD.ftLastAccessTime, |
207 | | &FD.ftLastWriteTime); |
208 | | |
209 | | // Restoring original file attributes. |
210 | | // Important if file was read only or did not have "Archive" attribute. |
211 | | if ((FD.FileAttr & FILE_ATTRIBUTE_READONLY)!=0) |
212 | | SetFileAttr(FileName,FD.FileAttr); |
213 | | } |
214 | | #endif |
215 | | |
216 | | |
217 | | std::wstring GetStreamNameNTFS(Archive &Arc) |
218 | 0 | { |
219 | 0 | std::wstring Dest; |
220 | 0 | if (Arc.Format==RARFMT15) |
221 | 0 | Dest=RawToWide(Arc.SubHead.SubData); |
222 | 0 | else |
223 | 0 | { |
224 | 0 | std::string Src(Arc.SubHead.SubData.begin(),Arc.SubHead.SubData.end()); |
225 | 0 | UtfToWide(Src.data(),Dest); |
226 | 0 | } |
227 | 0 | return Dest; |
228 | 0 | } |