Coverage Report

Created: 2025-04-11 06:56

/src/unrar/win32stm.cpp
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
}