Coverage Report

Created: 2024-04-23 06:19

/src/unrar/arcread.cpp
Line
Count
Source (jump to first uncovered line)
1
#include "rar.hpp"
2
3
size_t Archive::ReadHeader()
4
169k
{
5
  // Once we failed to decrypt an encrypted block, there is no reason to
6
  // attempt to do it further. We'll never be successful and only generate
7
  // endless errors.
8
169k
  if (FailedHeaderDecryption)
9
0
    return 0;
10
11
169k
  CurBlockPos=Tell();
12
13
  // Other developers asked us to initialize it to suppress "may be used
14
  // uninitialized" warning in code below in some compilers.
15
169k
  size_t ReadSize=0;
16
17
169k
  switch(Format)
18
169k
  {
19
0
#ifndef SFX_MODULE
20
1.80k
    case RARFMT14:
21
1.80k
      ReadSize=ReadHeader14();
22
1.80k
      break;
23
0
#endif
24
17.6k
    case RARFMT15:
25
17.6k
      ReadSize=ReadHeader15();
26
17.6k
      break;
27
149k
    case RARFMT50:
28
149k
      ReadSize=ReadHeader50();
29
149k
      break;
30
169k
  }
31
32
  // It is important to check ReadSize>0 here, because it is normal
33
  // for RAR2 and RAR3 archives without end of archive block to have
34
  // NextBlockPos==CurBlockPos after the end of archive has reached.
35
169k
  if (ReadSize>0 && NextBlockPos<=CurBlockPos)
36
38
  {
37
38
    BrokenHeaderMsg();
38
38
    ReadSize=0;
39
38
  }
40
41
169k
  if (ReadSize==0)
42
7.33k
    CurHeaderType=HEAD_UNKNOWN;
43
44
169k
  return ReadSize;
45
169k
}
46
47
48
size_t Archive::SearchBlock(HEADER_TYPE HeaderType)
49
0
{
50
0
  size_t Size,Count=0;
51
0
  while ((Size=ReadHeader())!=0 &&
52
0
         (HeaderType==HEAD_ENDARC || GetHeaderType()!=HEAD_ENDARC))
53
0
  {
54
0
    if ((++Count & 127)==0)
55
0
      Wait();
56
0
    if (GetHeaderType()==HeaderType)
57
0
      return Size;
58
0
    SeekToNext();
59
0
  }
60
0
  return 0;
61
0
}
62
63
64
size_t Archive::SearchSubBlock(const wchar *Type)
65
62
{
66
62
  size_t Size,Count=0;
67
5.37k
  while ((Size=ReadHeader())!=0 && GetHeaderType()!=HEAD_ENDARC)
68
5.31k
  {
69
5.31k
    if ((++Count & 127)==0)
70
35
      Wait();
71
5.31k
    if (GetHeaderType()==HEAD_SERVICE && SubHead.CmpName(Type))
72
0
      return Size;
73
5.31k
    SeekToNext();
74
5.31k
  }
75
62
  return 0;
76
62
}
77
78
79
size_t Archive::SearchRR()
80
0
{
81
  // If locator extra field is available for recovery record, let's utilize it.
82
0
  if (MainHead.Locator && MainHead.RROffset!=0)
83
0
  {
84
0
    uint64 CurPos=Tell();
85
0
    Seek(MainHead.RROffset,SEEK_SET);
86
0
    size_t Size=ReadHeader();
87
0
    if (Size!=0 && !BrokenHeader && GetHeaderType()==HEAD_SERVICE && SubHead.CmpName(SUBHEAD_TYPE_RR))
88
0
      return Size;
89
0
    Seek(CurPos,SEEK_SET);
90
0
  }
91
  // Otherwise scan the entire archive to find the recovery record.
92
0
  return SearchSubBlock(SUBHEAD_TYPE_RR);
93
0
}
94
95
96
void Archive::UnexpEndArcMsg()
97
3.65k
{
98
3.65k
  int64 ArcSize=FileLength();
99
100
  // If block positions are equal to file size, this is not an error.
101
  // It can happen when we reached the end of older RAR 1.5 archive,
102
  // which did not have the end of archive block.
103
3.65k
  if (CurBlockPos!=ArcSize || NextBlockPos!=ArcSize)
104
3.64k
  {
105
3.64k
    uiMsg(UIERROR_UNEXPEOF,FileName);
106
3.64k
    ErrHandler.SetErrorCode(RARX_WARNING);
107
3.64k
  }
108
3.65k
}
109
110
111
void Archive::BrokenHeaderMsg()
112
149k
{
113
149k
  uiMsg(UIERROR_HEADERBROKEN,FileName);
114
149k
  BrokenHeader=true;
115
149k
  ErrHandler.SetErrorCode(RARX_CRC);
116
149k
}
117
118
119
void Archive::UnkEncVerMsg(const wchar *Name,const wchar *Info)
120
302
{
121
302
  uiMsg(UIERROR_UNKNOWNENCMETHOD,FileName,Name,Info);
122
302
  ErrHandler.SetErrorCode(RARX_WARNING);
123
302
}
124
125
126
// Return f in case of signed integer overflow or negative parameters
127
// or v1+v2 otherwise. We use it for file offsets, which are signed
128
// for compatibility with off_t in POSIX file functions and third party code.
129
// Signed integer overflow is the undefined behavior according to
130
// C++ standard and it causes fuzzers to complain.
131
inline int64 SafeAdd(int64 v1,int64 v2,int64 f)
132
149k
{
133
149k
  return v1>=0 && v2>=0 && v1<=MAX_INT64-v2 ? v1+v2 : f;
134
149k
}
135
136
137
size_t Archive::ReadHeader15()
138
17.6k
{
139
17.6k
  RawRead Raw(this);
140
141
17.6k
  bool Decrypt=Encrypted && CurBlockPos>(int64)SFXSize+SIZEOF_MARKHEAD3;
142
143
17.6k
  if (Decrypt)
144
17
  {
145
#ifdef RAR_NOCRYPT // For rarext.dll and unrar_nocrypt.dll.
146
    return 0;
147
#else
148
17
    RequestArcPassword();
149
150
17
    byte Salt[SIZE_SALT30];
151
17
    if (Read(Salt,SIZE_SALT30)!=SIZE_SALT30)
152
0
    {
153
0
      UnexpEndArcMsg();
154
0
      return 0;
155
0
    }
156
17
    HeadersCrypt.SetCryptKeys(false,CRYPT_RAR30,&Cmd->Password,Salt,NULL,0,NULL,NULL);
157
17
    Raw.SetCrypt(&HeadersCrypt);
158
17
#endif
159
17
  }
160
161
17.6k
  Raw.Read(SIZEOF_SHORTBLOCKHEAD);
162
17.6k
  if (Raw.Size()==0)
163
2.30k
  {
164
2.30k
    UnexpEndArcMsg();
165
2.30k
    return 0;
166
2.30k
  }
167
168
15.2k
  ShortBlock.HeadCRC=Raw.Get2();
169
170
15.2k
  ShortBlock.Reset();
171
172
15.2k
  uint HeaderType=Raw.Get1();
173
15.2k
  ShortBlock.Flags=Raw.Get2();
174
15.2k
  ShortBlock.SkipIfUnknown=(ShortBlock.Flags & SKIP_IF_UNKNOWN)!=0;
175
15.2k
  ShortBlock.HeadSize=Raw.Get2();
176
177
15.2k
  ShortBlock.HeaderType=(HEADER_TYPE)HeaderType;
178
15.2k
  if (ShortBlock.HeadSize<SIZEOF_SHORTBLOCKHEAD)
179
1.40k
  {
180
1.40k
    BrokenHeaderMsg();
181
1.40k
    return 0;
182
1.40k
  }
183
184
  // For simpler further processing we map header types common
185
  // for RAR 1.5 and 5.0 formats to RAR 5.0 values. It does not include
186
  // header types specific for RAR 1.5 - 4.x only.
187
13.8k
  switch(ShortBlock.HeaderType)
188
13.8k
  {
189
14
    case HEAD3_MAIN:    ShortBlock.HeaderType=HEAD_MAIN;     break;
190
0
    case HEAD3_FILE:    ShortBlock.HeaderType=HEAD_FILE;     break;
191
37
    case HEAD3_SERVICE: ShortBlock.HeaderType=HEAD_SERVICE;  break;
192
10
    case HEAD3_ENDARC:  ShortBlock.HeaderType=HEAD_ENDARC;   break;
193
13.8k
  }
194
13.8k
  CurHeaderType=ShortBlock.HeaderType;
195
196
13.8k
  if (ShortBlock.HeaderType==HEAD3_CMT)
197
8
  {
198
    // Old style (up to RAR 2.9) comment header embedded into main
199
    // or file header. We must not read the entire ShortBlock.HeadSize here
200
    // to not break the comment processing logic later.
201
8
    Raw.Read(SIZEOF_COMMHEAD-SIZEOF_SHORTBLOCKHEAD);
202
8
  }
203
13.8k
  else
204
13.8k
    if (ShortBlock.HeaderType==HEAD_MAIN && (ShortBlock.Flags & MHD_COMMENT)!=0)
205
731
    {
206
      // Old style (up to RAR 2.9) main archive comment embedded into
207
      // the main archive header found. While we can read the entire
208
      // ShortBlock.HeadSize here and remove this part of "if", it would be
209
      // waste of memory, because we'll read and process this comment data
210
      // in other function anyway and we do not need them here now.
211
731
      Raw.Read(SIZEOF_MAINHEAD3-SIZEOF_SHORTBLOCKHEAD);
212
731
    }
213
13.1k
    else
214
13.1k
      Raw.Read(ShortBlock.HeadSize-SIZEOF_SHORTBLOCKHEAD);
215
216
13.8k
  NextBlockPos=CurBlockPos+FullHeaderSize(ShortBlock.HeadSize);
217
218
13.8k
  switch(ShortBlock.HeaderType)
219
13.8k
  {
220
7.90k
    case HEAD_MAIN:
221
7.90k
      MainHead.Reset();
222
7.90k
      *(BaseBlock *)&MainHead=ShortBlock;
223
7.90k
      MainHead.HighPosAV=Raw.Get2();
224
7.90k
      MainHead.PosAV=Raw.Get4();
225
226
7.90k
      Volume=(MainHead.Flags & MHD_VOLUME)!=0;
227
7.90k
      Solid=(MainHead.Flags & MHD_SOLID)!=0;
228
7.90k
      Locked=(MainHead.Flags & MHD_LOCK)!=0;
229
7.90k
      Protected=(MainHead.Flags & MHD_PROTECT)!=0;
230
7.90k
      Encrypted=(MainHead.Flags & MHD_PASSWORD)!=0;
231
7.90k
      Signed=MainHead.PosAV!=0 || MainHead.HighPosAV!=0;
232
7.90k
      MainHead.CommentInHeader=(MainHead.Flags & MHD_COMMENT)!=0;
233
234
      // Only for encrypted 3.0+ archives. 2.x archives did not have this
235
      // flag, so for non-encrypted archives, we'll set it later based on
236
      // file attributes.
237
7.90k
      FirstVolume=(MainHead.Flags & MHD_FIRSTVOLUME)!=0;
238
239
7.90k
      NewNumbering=(MainHead.Flags & MHD_NEWNUMBERING)!=0;
240
7.90k
      break;
241
2.67k
    case HEAD_FILE:
242
2.77k
    case HEAD_SERVICE:
243
2.77k
      {
244
2.77k
        bool FileBlock=ShortBlock.HeaderType==HEAD_FILE;
245
2.77k
        FileHeader *hd=FileBlock ? &FileHead:&SubHead;
246
2.77k
        hd->Reset();
247
248
2.77k
        *(BaseBlock *)hd=ShortBlock;
249
250
2.77k
        hd->SplitBefore=(hd->Flags & LHD_SPLIT_BEFORE)!=0;
251
2.77k
        hd->SplitAfter=(hd->Flags & LHD_SPLIT_AFTER)!=0;
252
2.77k
        hd->Encrypted=(hd->Flags & LHD_PASSWORD)!=0;
253
2.77k
        hd->SaltSet=(hd->Flags & LHD_SALT)!=0;
254
2.77k
        hd->Solid=FileBlock && (hd->Flags & LHD_SOLID)!=0;
255
2.77k
        hd->SubBlock=!FileBlock && (hd->Flags & LHD_SOLID)!=0;
256
2.77k
        hd->Dir=(hd->Flags & LHD_WINDOWMASK)==LHD_DIRECTORY;
257
2.77k
        hd->WinSize=hd->Dir ? 0:0x10000<<((hd->Flags & LHD_WINDOWMASK)>>5);
258
2.77k
        hd->CommentInHeader=(hd->Flags & LHD_COMMENT)!=0;
259
2.77k
        hd->Version=(hd->Flags & LHD_VERSION)!=0;
260
261
2.77k
        hd->DataSize=Raw.Get4();
262
2.77k
        uint LowUnpSize=Raw.Get4();
263
2.77k
        hd->HostOS=Raw.Get1();
264
265
2.77k
        hd->FileHash.Type=HASH_CRC32;
266
2.77k
        hd->FileHash.CRC32=Raw.Get4();
267
268
2.77k
        uint FileTime=Raw.Get4();
269
2.77k
        hd->UnpVer=Raw.Get1();
270
271
2.77k
        hd->Method=Raw.Get1()-0x30;
272
2.77k
        size_t NameSize=Raw.Get2();
273
2.77k
        hd->FileAttr=Raw.Get4();
274
275
        // RAR15 did not use the special dictionary size to mark dirs.
276
2.77k
        if (hd->UnpVer<20 && (hd->FileAttr & 0x10)!=0)
277
35
          hd->Dir=true;
278
279
2.77k
        hd->CryptMethod=CRYPT_NONE;
280
2.77k
        if (hd->Encrypted)
281
72
          switch(hd->UnpVer)
282
72
          {
283
0
            case 13: hd->CryptMethod=CRYPT_RAR13; break;
284
0
            case 15: hd->CryptMethod=CRYPT_RAR15; break;
285
2
            case 20:
286
4
            case 26: hd->CryptMethod=CRYPT_RAR20; break;
287
68
            default: hd->CryptMethod=CRYPT_RAR30; break;
288
72
          }
289
290
2.77k
        hd->HSType=HSYS_UNKNOWN;
291
2.77k
        if (hd->HostOS==HOST_UNIX || hd->HostOS==HOST_BEOS)
292
28
          hd->HSType=HSYS_UNIX;
293
2.74k
        else
294
2.74k
          if (hd->HostOS<HOST_MAX)
295
277
            hd->HSType=HSYS_WINDOWS;
296
297
2.77k
        hd->RedirType=FSREDIR_NONE;
298
299
        // RAR 4.x Unix symlink.
300
2.77k
        if (hd->HostOS==HOST_UNIX && (hd->FileAttr & 0xF000)==0xA000)
301
17
        {
302
17
          hd->RedirType=FSREDIR_UNIXSYMLINK;
303
17
          *hd->RedirName=0;
304
17
        }
305
306
2.77k
        hd->Inherited=!FileBlock && (hd->SubFlags & SUBHEAD_FLAGS_INHERITED)!=0;
307
308
2.77k
        hd->LargeFile=(hd->Flags & LHD_LARGE)!=0;
309
310
2.77k
        uint HighPackSize,HighUnpSize;
311
2.77k
        if (hd->LargeFile)
312
143
        {
313
143
          HighPackSize=Raw.Get4();
314
143
          HighUnpSize=Raw.Get4();
315
143
          hd->UnknownUnpSize=(LowUnpSize==0xffffffff && HighUnpSize==0xffffffff);
316
143
        }
317
2.63k
        else
318
2.63k
        {
319
2.63k
          HighPackSize=HighUnpSize=0;
320
          // UnpSize equal to 0xffffffff without LHD_LARGE flag indicates
321
          // that we do not know the unpacked file size and must unpack it
322
          // until we find the end of file marker in compressed data.
323
2.63k
          hd->UnknownUnpSize=(LowUnpSize==0xffffffff);
324
2.63k
        }
325
2.77k
        hd->PackSize=INT32TO64(HighPackSize,hd->DataSize);
326
2.77k
        hd->UnpSize=INT32TO64(HighUnpSize,LowUnpSize);
327
2.77k
        if (hd->UnknownUnpSize)
328
7
          hd->UnpSize=INT64NDF;
329
330
2.77k
        char FileName[NM*4];
331
2.77k
        size_t ReadNameSize=Min(NameSize,ASIZE(FileName)-1);
332
2.77k
        Raw.GetB((byte *)FileName,ReadNameSize);
333
2.77k
        FileName[ReadNameSize]=0;
334
335
2.77k
        if (FileBlock)
336
2.67k
        {
337
2.67k
          *hd->FileName=0;
338
2.67k
          if ((hd->Flags & LHD_UNICODE)!=0)
339
2.62k
          {
340
2.62k
            EncodeFileName NameCoder;
341
2.62k
            size_t Length=strlen(FileName);
342
2.62k
            Length++;
343
2.62k
            if (ReadNameSize>Length)
344
2.60k
              NameCoder.Decode(FileName,ReadNameSize,(byte *)FileName+Length,
345
2.60k
                               ReadNameSize-Length,hd->FileName,
346
2.60k
                               ASIZE(hd->FileName));
347
2.62k
          }
348
349
2.67k
          if (*hd->FileName==0)
350
446
            ArcCharToWide(FileName,hd->FileName,ASIZE(hd->FileName),ACTW_OEM);
351
352
2.67k
#ifndef SFX_MODULE
353
2.67k
          ConvertNameCase(hd->FileName);
354
2.67k
#endif
355
2.67k
          ConvertFileHeader(hd);
356
2.67k
        }
357
97
        else
358
97
        {
359
97
          CharToWide(FileName,hd->FileName,ASIZE(hd->FileName));
360
361
          // Calculate the size of optional data.
362
97
          int DataSize=int(hd->HeadSize-NameSize-SIZEOF_FILEHEAD3);
363
97
          if ((hd->Flags & LHD_SALT)!=0)
364
16
            DataSize-=SIZE_SALT30;
365
366
97
          if (DataSize>0)
367
51
          {
368
            // Here we read optional additional fields for subheaders.
369
            // They are stored after the file name and before salt.
370
51
            hd->SubData.Alloc(DataSize);
371
51
            Raw.GetB(&hd->SubData[0],DataSize);
372
373
51
          }
374
375
97
          if (hd->CmpName(SUBHEAD_TYPE_CMT))
376
0
            MainComment=true;
377
97
        }
378
2.77k
        if ((hd->Flags & LHD_SALT)!=0)
379
86
          Raw.GetB(hd->Salt,SIZE_SALT30);
380
2.77k
        hd->mtime.SetDos(FileTime);
381
2.77k
        if ((hd->Flags & LHD_EXTTIME)!=0)
382
123
        {
383
123
          ushort Flags=Raw.Get2();
384
123
          RarTime *tbl[4];
385
123
          tbl[0]=&FileHead.mtime;
386
123
          tbl[1]=&FileHead.ctime;
387
123
          tbl[2]=&FileHead.atime;
388
123
          tbl[3]=NULL; // Archive time is not used now.
389
615
          for (int I=0;I<4;I++)
390
492
          {
391
492
            RarTime *CurTime=tbl[I];
392
492
            uint rmode=Flags>>(3-I)*4;
393
492
            if ((rmode & 8)==0 || CurTime==NULL)
394
409
              continue;
395
83
            if (I!=0)
396
57
            {
397
57
              uint DosTime=Raw.Get4();
398
57
              CurTime->SetDos(DosTime);
399
57
            }
400
83
            RarLocalTime rlt;
401
83
            CurTime->GetLocal(&rlt);
402
83
            if (rmode & 4)
403
61
              rlt.Second++;
404
83
            rlt.Reminder=0;
405
83
            uint count=rmode&3;
406
258
            for (uint J=0;J<count;J++)
407
175
            {
408
175
              byte CurByte=Raw.Get1();
409
175
              rlt.Reminder|=(((uint)CurByte)<<((J+3-count)*8));
410
175
            }
411
            // Convert from 100ns RAR precision to REMINDER_PRECISION.
412
83
            rlt.Reminder*=RarTime::REMINDER_PRECISION/10000000;
413
83
            CurTime->SetLocal(&rlt);
414
83
          }
415
123
        }
416
        // Set to 0 in case of overflow, so end of ReadHeader cares about it.
417
2.77k
        NextBlockPos=SafeAdd(NextBlockPos,hd->PackSize,0);
418
419
2.77k
        bool CRCProcessedOnly=hd->CommentInHeader;
420
2.77k
        ushort HeaderCRC=Raw.GetCRC15(CRCProcessedOnly);
421
2.77k
        if (hd->HeadCRC!=HeaderCRC)
422
2.77k
        {
423
2.77k
          BrokenHeader=true;
424
2.77k
          ErrHandler.SetErrorCode(RARX_WARNING);
425
426
          // If we have a broken encrypted header, we do not need to display
427
          // the error message here, because it will be displayed for such
428
          // headers later in this function. Also such headers are unlikely
429
          // to have anything sensible in file name field, so it is useless
430
          // to display the file name.
431
2.77k
          if (!Decrypt)
432
2.77k
            uiMsg(UIERROR_FHEADERBROKEN,Archive::FileName,hd->FileName);
433
2.77k
        }
434
2.77k
      }
435
0
      break;
436
94
    case HEAD_ENDARC:
437
94
      *(BaseBlock *)&EndArcHead=ShortBlock;
438
94
      EndArcHead.NextVolume=(EndArcHead.Flags & EARC_NEXT_VOLUME)!=0;
439
94
      EndArcHead.DataCRC=(EndArcHead.Flags & EARC_DATACRC)!=0;
440
94
      EndArcHead.RevSpace=(EndArcHead.Flags & EARC_REVSPACE)!=0;
441
94
      EndArcHead.StoreVolNumber=(EndArcHead.Flags & EARC_VOLNUMBER)!=0;
442
94
      if (EndArcHead.DataCRC)
443
9
        EndArcHead.ArcDataCRC=Raw.Get4();
444
94
      if (EndArcHead.StoreVolNumber)
445
10
        VolNumber=EndArcHead.VolNumber=Raw.Get2();
446
94
      break;
447
0
#ifndef SFX_MODULE
448
8
    case HEAD3_CMT:
449
8
      *(BaseBlock *)&CommHead=ShortBlock;
450
8
      CommHead.UnpSize=Raw.Get2();
451
8
      CommHead.UnpVer=Raw.Get1();
452
8
      CommHead.Method=Raw.Get1();
453
8
      CommHead.CommCRC=Raw.Get2();
454
8
      break;
455
3
    case HEAD3_PROTECT:
456
3
      *(BaseBlock *)&ProtectHead=ShortBlock;
457
3
      ProtectHead.DataSize=Raw.Get4();
458
3
      ProtectHead.Version=Raw.Get1();
459
3
      ProtectHead.RecSectors=Raw.Get2();
460
3
      ProtectHead.TotalBlocks=Raw.Get4();
461
3
      Raw.GetB(ProtectHead.Mark,8);
462
3
      NextBlockPos+=ProtectHead.DataSize;
463
3
      break;
464
9
    case HEAD3_OLDSERVICE: // RAR 2.9 and earlier.
465
9
      *(BaseBlock *)&SubBlockHead=ShortBlock;
466
9
      SubBlockHead.DataSize=Raw.Get4();
467
9
      NextBlockPos+=SubBlockHead.DataSize;
468
9
      SubBlockHead.SubType=Raw.Get2();
469
9
      SubBlockHead.Level=Raw.Get1();
470
9
      switch(SubBlockHead.SubType)
471
9
      {
472
0
        case UO_HEAD:
473
0
          *(SubBlockHeader *)&UOHead=SubBlockHead;
474
0
          UOHead.OwnerNameSize=Raw.Get2();
475
0
          UOHead.GroupNameSize=Raw.Get2();
476
0
          if (UOHead.OwnerNameSize>=ASIZE(UOHead.OwnerName))
477
0
            UOHead.OwnerNameSize=ASIZE(UOHead.OwnerName)-1;
478
0
          if (UOHead.GroupNameSize>=ASIZE(UOHead.GroupName))
479
0
            UOHead.GroupNameSize=ASIZE(UOHead.GroupName)-1;
480
0
          Raw.GetB(UOHead.OwnerName,UOHead.OwnerNameSize);
481
0
          Raw.GetB(UOHead.GroupName,UOHead.GroupNameSize);
482
0
          UOHead.OwnerName[UOHead.OwnerNameSize]=0;
483
0
          UOHead.GroupName[UOHead.GroupNameSize]=0;
484
0
          break;
485
1
        case NTACL_HEAD:
486
1
          *(SubBlockHeader *)&EAHead=SubBlockHead;
487
1
          EAHead.UnpSize=Raw.Get4();
488
1
          EAHead.UnpVer=Raw.Get1();
489
1
          EAHead.Method=Raw.Get1();
490
1
          EAHead.EACRC=Raw.Get4();
491
1
          break;
492
0
        case STREAM_HEAD:
493
0
          *(SubBlockHeader *)&StreamHead=SubBlockHead;
494
0
          StreamHead.UnpSize=Raw.Get4();
495
0
          StreamHead.UnpVer=Raw.Get1();
496
0
          StreamHead.Method=Raw.Get1();
497
0
          StreamHead.StreamCRC=Raw.Get4();
498
0
          StreamHead.StreamNameSize=Raw.Get2();
499
0
          if (StreamHead.StreamNameSize>=ASIZE(StreamHead.StreamName))
500
0
            StreamHead.StreamNameSize=ASIZE(StreamHead.StreamName)-1;
501
0
          Raw.GetB(StreamHead.StreamName,StreamHead.StreamNameSize);
502
0
          StreamHead.StreamName[StreamHead.StreamNameSize]=0;
503
0
          break;
504
9
      }
505
9
      break;
506
9
#endif
507
3.08k
    default:
508
3.08k
      if (ShortBlock.Flags & LONG_BLOCK)
509
91
        NextBlockPos+=Raw.Get4();
510
3.08k
      break;
511
13.8k
  }
512
513
13.8k
  ushort HeaderCRC=Raw.GetCRC15(false);
514
515
  // Old AV header does not have header CRC properly set.
516
13.8k
  if (ShortBlock.HeadCRC!=HeaderCRC && ShortBlock.HeaderType!=HEAD3_SIGN &&
517
13.8k
      ShortBlock.HeaderType!=HEAD3_AV)
518
13.7k
  {
519
13.7k
    bool Recovered=false;
520
13.7k
    if (ShortBlock.HeaderType==HEAD_ENDARC && EndArcHead.RevSpace)
521
87
    {
522
      // Last 7 bytes of recovered volume can contain zeroes, because
523
      // REV files store its own information (volume number, etc.) here.
524
87
      int64 Length=Tell();
525
87
      Seek(Length-7,SEEK_SET);
526
87
      Recovered=true;
527
696
      for (int J=0;J<7;J++)
528
609
        if (GetByte()!=0)
529
588
          Recovered=false;
530
87
    }
531
13.7k
    if (!Recovered)
532
13.7k
    {
533
13.7k
      BrokenHeader=true;
534
13.7k
      ErrHandler.SetErrorCode(RARX_CRC);
535
536
13.7k
      if (Decrypt)
537
0
      {
538
0
        uiMsg(UIERROR_CHECKSUMENC,FileName,FileName);
539
0
        FailedHeaderDecryption=true;
540
0
        return 0;
541
0
      }
542
13.7k
    }
543
13.7k
  }
544
545
13.8k
  return Raw.Size();
546
13.8k
}
547
548
549
size_t Archive::ReadHeader50()
550
149k
{
551
149k
  RawRead Raw(this);
552
553
149k
  bool Decrypt=Encrypted && CurBlockPos>(int64)SFXSize+SIZEOF_MARKHEAD5;
554
555
149k
  if (Decrypt)
556
9
  {
557
#if defined(RAR_NOCRYPT)
558
    return 0;
559
#else
560
561
9
    if (Cmd->SkipEncrypted)
562
0
    {
563
0
      uiMsg(UIMSG_SKIPENCARC,FileName);
564
0
      FailedHeaderDecryption=true; // Suppress error messages and quit quietly.
565
0
      return 0;
566
0
    }
567
568
9
    byte HeadersInitV[SIZE_INITV];
569
9
    if (Read(HeadersInitV,SIZE_INITV)!=SIZE_INITV)
570
2
    {
571
2
      UnexpEndArcMsg();
572
2
      return 0;
573
2
    }
574
575
    // We repeat the password request only for manually entered passwords
576
    // and not for -p<pwd>. Wrong password can be intentionally provided
577
    // in -p<pwd> to not stop batch processing for encrypted archives.
578
7
    bool GlobalPassword=Cmd->Password.IsSet() || uiIsGlobalPasswordSet();
579
580
7
    while (true) // Repeat the password prompt for wrong passwords.
581
7
    {
582
7
      RequestArcPassword();
583
584
7
      byte PswCheck[SIZE_PSWCHECK];
585
7
      HeadersCrypt.SetCryptKeys(false,CRYPT_RAR50,&Cmd->Password,CryptHead.Salt,HeadersInitV,CryptHead.Lg2Count,NULL,PswCheck);
586
      // Verify password validity.
587
7
      if (CryptHead.UsePswCheck && memcmp(PswCheck,CryptHead.PswCheck,SIZE_PSWCHECK)!=0)
588
0
      {
589
0
        if (GlobalPassword) // For -p<pwd> or Ctrl+P.
590
0
        {
591
          // This message is used by Android GUI to reset cached passwords.
592
          // Update appropriate code if changed.
593
0
          uiMsg(UIERROR_BADPSW,FileName,FileName);
594
0
          FailedHeaderDecryption=true;
595
0
          ErrHandler.SetErrorCode(RARX_BADPWD);
596
0
          return 0;
597
0
        }
598
0
        else // For passwords entered manually.
599
0
        {
600
          // This message is used by Android GUI and Windows GUI and SFX to
601
          // reset cached passwords. Update appropriate code if changed.
602
0
          uiMsg(UIWAIT_BADPSW,FileName,FileName);
603
0
          Cmd->Password.Clean();
604
0
        }
605
606
0
#ifdef RARDLL
607
        // Avoid new requests for unrar.dll to prevent the infinite loop
608
        // if app always returns the same password.
609
0
        ErrHandler.SetErrorCode(RARX_BADPWD);
610
0
        Cmd->DllError=ERAR_BAD_PASSWORD;
611
0
        ErrHandler.Exit(RARX_BADPWD);
612
#else
613
        continue; // Request a password again.
614
#endif
615
0
      }
616
7
      break;
617
7
    }
618
619
7
    Raw.SetCrypt(&HeadersCrypt);
620
7
#endif
621
7
  }
622
623
  // Header size must not occupy more than 3 variable length integer bytes
624
  // resulting in 2 MB maximum header size (MAX_HEADER_SIZE_RAR5),
625
  // so here we read 4 byte CRC32 followed by 3 bytes or less of header size.
626
149k
  const size_t FirstReadSize=7; // Smallest possible block size.
627
149k
  if (Raw.Read(FirstReadSize)<FirstReadSize)
628
1.20k
  {
629
1.20k
    UnexpEndArcMsg();
630
1.20k
    return 0;
631
1.20k
  }
632
633
148k
  ShortBlock.Reset();
634
148k
  ShortBlock.HeadCRC=Raw.Get4();
635
148k
  uint SizeBytes=Raw.GetVSize(4);
636
148k
  uint64 BlockSize=Raw.GetV();
637
638
148k
  if (BlockSize==0 || SizeBytes==0)
639
1.29k
  {
640
1.29k
    BrokenHeaderMsg();
641
1.29k
    return 0;
642
1.29k
  }
643
644
147k
  int SizeToRead=int(BlockSize);
645
147k
  SizeToRead-=FirstReadSize-SizeBytes-4; // Adjust overread size bytes if any.
646
147k
  uint HeaderSize=4+SizeBytes+(uint)BlockSize;
647
648
147k
  if (SizeToRead<0 || HeaderSize<SIZEOF_SHORTBLOCKHEAD5)
649
158
  {
650
158
    BrokenHeaderMsg();
651
158
    return 0;
652
158
  }
653
654
147k
  Raw.Read(SizeToRead);
655
656
147k
  if (Raw.Size()<HeaderSize)
657
137
  {
658
137
    UnexpEndArcMsg();
659
137
    return 0;
660
137
  }
661
662
146k
  uint HeaderCRC=Raw.GetCRC50();
663
664
146k
  ShortBlock.HeaderType=(HEADER_TYPE)Raw.GetV();
665
146k
  ShortBlock.Flags=(uint)Raw.GetV();
666
146k
  ShortBlock.SkipIfUnknown=(ShortBlock.Flags & HFL_SKIPIFUNKNOWN)!=0;
667
146k
  ShortBlock.HeadSize=HeaderSize;
668
669
146k
  CurHeaderType=ShortBlock.HeaderType;
670
671
146k
  bool BadCRC=(ShortBlock.HeadCRC!=HeaderCRC);
672
146k
  if (BadCRC)
673
146k
  {
674
146k
    BrokenHeaderMsg(); // Report, but attempt to process.
675
676
146k
    BrokenHeader=true;
677
146k
    ErrHandler.SetErrorCode(RARX_CRC);
678
679
146k
    if (Decrypt)
680
0
    {
681
0
      uiMsg(UIERROR_CHECKSUMENC,FileName,FileName);
682
0
      FailedHeaderDecryption=true;
683
0
      return 0;
684
0
    }
685
146k
  }
686
687
146k
  uint64 ExtraSize=0;
688
146k
  if ((ShortBlock.Flags & HFL_EXTRA)!=0)
689
4.53k
  {
690
4.53k
    ExtraSize=Raw.GetV();
691
4.53k
    if (ExtraSize>=ShortBlock.HeadSize)
692
109
    {
693
109
      BrokenHeaderMsg();
694
109
      return 0;
695
109
    }
696
4.53k
  }
697
698
146k
  uint64 DataSize=0;
699
146k
  if ((ShortBlock.Flags & HFL_DATA)!=0)
700
143k
    DataSize=Raw.GetV();
701
702
146k
  NextBlockPos=CurBlockPos+FullHeaderSize(ShortBlock.HeadSize);
703
  // Set to 0 in case of overflow, so end of ReadHeader cares about it.
704
146k
  NextBlockPos=SafeAdd(NextBlockPos,DataSize,0);
705
706
146k
  switch(ShortBlock.HeaderType)
707
146k
  {
708
12
    case HEAD_CRYPT:
709
12
      {
710
12
        *(BaseBlock *)&CryptHead=ShortBlock;
711
12
        uint CryptVersion=(uint)Raw.GetV();
712
12
        if (CryptVersion>CRYPT_VERSION)
713
3
        {
714
3
          wchar Info[20];
715
3
          swprintf(Info,ASIZE(Info),L"h%u",CryptVersion);
716
3
          UnkEncVerMsg(FileName,Info);
717
3
          return 0;
718
3
        }
719
9
        uint EncFlags=(uint)Raw.GetV();
720
9
        CryptHead.UsePswCheck=(EncFlags & CHFL_CRYPT_PSWCHECK)!=0;
721
9
        CryptHead.Lg2Count=Raw.Get1();
722
9
        if (CryptHead.Lg2Count>CRYPT5_KDF_LG2_COUNT_MAX)
723
0
        {
724
0
          wchar Info[20];
725
0
          swprintf(Info,ASIZE(Info),L"hc%u",CryptHead.Lg2Count);
726
0
          UnkEncVerMsg(FileName,Info);
727
0
          return 0;
728
0
        }
729
730
9
        Raw.GetB(CryptHead.Salt,SIZE_SALT50);
731
9
        if (CryptHead.UsePswCheck)
732
1
        {
733
1
          Raw.GetB(CryptHead.PswCheck,SIZE_PSWCHECK);
734
735
1
          byte csum[SIZE_PSWCHECK_CSUM];
736
1
          Raw.GetB(csum,SIZE_PSWCHECK_CSUM);
737
738
1
          sha256_context ctx;
739
1
          sha256_init(&ctx);
740
1
          sha256_process(&ctx, CryptHead.PswCheck, SIZE_PSWCHECK);
741
742
1
          byte Digest[SHA256_DIGEST_SIZE];
743
1
          sha256_done(&ctx, Digest);
744
745
1
          CryptHead.UsePswCheck=memcmp(csum,Digest,SIZE_PSWCHECK_CSUM)==0;
746
1
        }
747
9
        Encrypted=true;
748
9
      }
749
0
      break;
750
445
    case HEAD_MAIN:
751
445
      {
752
445
        MainHead.Reset();
753
445
        *(BaseBlock *)&MainHead=ShortBlock;
754
445
        uint ArcFlags=(uint)Raw.GetV();
755
756
445
        Volume=(ArcFlags & MHFL_VOLUME)!=0;
757
445
        Solid=(ArcFlags & MHFL_SOLID)!=0;
758
445
        Locked=(ArcFlags & MHFL_LOCK)!=0;
759
445
        Protected=(ArcFlags & MHFL_PROTECT)!=0;
760
445
        Signed=false;
761
445
        NewNumbering=true;
762
763
445
        if ((ArcFlags & MHFL_VOLNUMBER)!=0)
764
194
          VolNumber=(uint)Raw.GetV();
765
251
        else
766
251
          VolNumber=0;
767
445
        FirstVolume=Volume && VolNumber==0;
768
769
445
        if (ExtraSize!=0)
770
384
          ProcessExtra50(&Raw,(size_t)ExtraSize,&MainHead);
771
772
445
#ifdef USE_QOPEN
773
445
        if (!ProhibitQOpen && MainHead.Locator && MainHead.QOpenOffset>0 && Cmd->QOpenMode!=QOPEN_NONE)
774
279
        {
775
          // We seek to QO block in the end of archive when processing
776
          // QOpen.Load, so we need to preserve current block positions
777
          // to not break normal archive processing by calling function.
778
279
          int64 SaveCurBlockPos=CurBlockPos,SaveNextBlockPos=NextBlockPos;
779
279
          HEADER_TYPE SaveCurHeaderType=CurHeaderType;
780
781
279
          QOpen.Init(this,false);
782
279
          QOpen.Load(MainHead.QOpenOffset);
783
784
279
          CurBlockPos=SaveCurBlockPos;
785
279
          NextBlockPos=SaveNextBlockPos;
786
279
          CurHeaderType=SaveCurHeaderType;
787
279
        }
788
445
#endif
789
445
      }
790
445
      break;
791
141k
    case HEAD_FILE:
792
142k
    case HEAD_SERVICE:
793
142k
      {
794
142k
        FileHeader *hd=ShortBlock.HeaderType==HEAD_FILE ? &FileHead:&SubHead;
795
142k
        hd->Reset(); // Clear hash, time fields and other stuff like flags.
796
142k
        *(BaseBlock *)hd=ShortBlock;
797
798
142k
        bool FileBlock=ShortBlock.HeaderType==HEAD_FILE;
799
800
142k
        hd->LargeFile=true;
801
802
142k
        hd->PackSize=DataSize;
803
142k
        hd->FileFlags=(uint)Raw.GetV();
804
142k
        hd->UnpSize=Raw.GetV();
805
806
142k
        hd->UnknownUnpSize=(hd->FileFlags & FHFL_UNPUNKNOWN)!=0;
807
142k
        if (hd->UnknownUnpSize)
808
147
          hd->UnpSize=INT64NDF;
809
810
142k
        hd->MaxSize=Max(hd->PackSize,hd->UnpSize);
811
142k
        hd->FileAttr=(uint)Raw.GetV();
812
142k
        if ((hd->FileFlags & FHFL_UTIME)!=0)
813
1.01k
          hd->mtime.SetUnix((time_t)Raw.Get4());
814
815
142k
        hd->FileHash.Type=HASH_NONE;
816
142k
        if ((hd->FileFlags & FHFL_CRC32)!=0)
817
77
        {
818
77
          hd->FileHash.Type=HASH_CRC32;
819
77
          hd->FileHash.CRC32=Raw.Get4();
820
77
        }
821
822
142k
        hd->RedirType=FSREDIR_NONE;
823
824
142k
        uint CompInfo=(uint)Raw.GetV();
825
142k
        hd->Method=(CompInfo>>7) & 7;
826
827
        // "+ 50" to not mix with old RAR format algorithms. For example,
828
        // we may need to use the compression algorithm 15 in the future,
829
        // but it was already used in RAR 1.5 and Unpack needs to distinguish
830
        // them.
831
142k
        hd->UnpVer=(CompInfo & 0x3f) + 50;
832
142k
        if (hd->UnpVer!=50) // Only 5.0 compression is known now.
833
1.06k
          hd->UnpVer=VER_UNKNOWN;
834
835
142k
        hd->HostOS=(byte)Raw.GetV();
836
142k
        size_t NameSize=(size_t)Raw.GetV();
837
142k
        hd->Inherited=(ShortBlock.Flags & HFL_INHERITED)!=0;
838
839
142k
        hd->HSType=HSYS_UNKNOWN;
840
142k
        if (hd->HostOS==HOST5_UNIX)
841
14
          hd->HSType=HSYS_UNIX;
842
142k
        else
843
142k
          if (hd->HostOS==HOST5_WINDOWS)
844
140k
            hd->HSType=HSYS_WINDOWS;
845
846
142k
        hd->SplitBefore=(hd->Flags & HFL_SPLITBEFORE)!=0;
847
142k
        hd->SplitAfter=(hd->Flags & HFL_SPLITAFTER)!=0;
848
142k
        hd->SubBlock=(hd->Flags & HFL_CHILD)!=0;
849
142k
        hd->Solid=FileBlock && (CompInfo & FCI_SOLID)!=0;
850
142k
        hd->Dir=(hd->FileFlags & FHFL_DIRECTORY)!=0;
851
142k
        hd->WinSize=hd->Dir ? 0:size_t(0x20000)<<((CompInfo>>10)&0xf);
852
853
142k
        hd->CryptMethod=hd->Encrypted ? CRYPT_RAR50:CRYPT_NONE;
854
855
142k
        char FileName[NM*4];
856
142k
        size_t ReadNameSize=Min(NameSize,ASIZE(FileName)-1);
857
142k
        Raw.GetB((byte *)FileName,ReadNameSize);
858
142k
        FileName[ReadNameSize]=0;
859
860
142k
        UtfToWide(FileName,hd->FileName,ASIZE(hd->FileName));
861
862
        // Should do it before converting names, because extra fields can
863
        // affect name processing, like in case of NTFS streams.
864
142k
        if (ExtraSize!=0)
865
2.47k
          ProcessExtra50(&Raw,(size_t)ExtraSize,hd);
866
867
142k
        if (FileBlock)
868
141k
        {
869
141k
#ifndef SFX_MODULE
870
141k
          ConvertNameCase(hd->FileName);
871
141k
#endif
872
141k
          ConvertFileHeader(hd);
873
141k
        }
874
875
142k
        if (!FileBlock && hd->CmpName(SUBHEAD_TYPE_CMT))
876
0
          MainComment=true;
877
878
#if 0
879
        // For RAR5 format we read the user specified recovery percent here.
880
        // It would be useful to do it for shell extension too, so we display
881
        // the correct recovery record size in archive properties. But then
882
        // we would need to include the entire recovery record processing
883
        // code to shell extension, which is not done now.
884
        if (!FileBlock && hd->CmpName(SUBHEAD_TYPE_RR) && hd->SubData.Size()>0)
885
        {
886
          // It is stored as a single byte up to RAR 6.02 and as vint since
887
          // 6.10, where we extended the maximum RR size from 99% to 1000%.
888
          RawRead RawPercent;
889
          RawPercent.Read(&hd->SubData[0],hd->SubData.Size());
890
          RecoveryPercent=(int)RawPercent.GetV();
891
892
          RSBlockHeader Header;
893
          GetRRInfo(this,&Header);
894
          RecoverySize=Header.RecSectionSize*Header.RecCount;
895
        }
896
#endif
897
898
142k
        if (BadCRC) // Add the file name to broken header message displayed above.
899
142k
          uiMsg(UIERROR_FHEADERBROKEN,Archive::FileName,hd->FileName);
900
142k
      }
901
142k
      break;
902
245
    case HEAD_ENDARC:
903
245
      {
904
245
        *(BaseBlock *)&EndArcHead=ShortBlock;
905
245
        uint ArcFlags=(uint)Raw.GetV();
906
245
        EndArcHead.NextVolume=(ArcFlags & EHFL_NEXTVOLUME)!=0;
907
245
        EndArcHead.StoreVolNumber=false;
908
245
        EndArcHead.DataCRC=false;
909
245
        EndArcHead.RevSpace=false;
910
245
      }
911
245
      break;
912
146k
  }
913
914
146k
  return Raw.Size();
915
146k
}
916
917
918
#if !defined(RAR_NOCRYPT)
919
void Archive::RequestArcPassword()
920
24
{
921
24
  if (!Cmd->Password.IsSet())
922
24
  {
923
24
#ifdef RARDLL
924
24
    if (Cmd->Callback!=NULL)
925
0
    {
926
0
      wchar PasswordW[MAXPASSWORD];
927
0
      *PasswordW=0;
928
0
      if (Cmd->Callback(UCM_NEEDPASSWORDW,Cmd->UserData,(LPARAM)PasswordW,ASIZE(PasswordW))==-1)
929
0
        *PasswordW=0;
930
0
      if (*PasswordW==0)
931
0
      {
932
0
        char PasswordA[MAXPASSWORD];
933
0
        *PasswordA=0;
934
0
        if (Cmd->Callback(UCM_NEEDPASSWORD,Cmd->UserData,(LPARAM)PasswordA,ASIZE(PasswordA))==-1)
935
0
          *PasswordA=0;
936
0
        GetWideName(PasswordA,NULL,PasswordW,ASIZE(PasswordW));
937
0
        cleandata(PasswordA,sizeof(PasswordA));
938
0
      }
939
0
      Cmd->Password.Set(PasswordW);
940
0
      cleandata(PasswordW,sizeof(PasswordW));
941
0
    }
942
24
    if (!Cmd->Password.IsSet())
943
24
    {
944
24
      Close();
945
24
      Cmd->DllError=ERAR_MISSING_PASSWORD;
946
24
      ErrHandler.Exit(RARX_USERBREAK);
947
24
    }
948
#else
949
    if (!uiGetPassword(UIPASSWORD_ARCHIVE,FileName,&Cmd->Password))
950
    {
951
      Close();
952
      uiMsg(UIERROR_INCERRCOUNT); // Prevent archive deleting if delete after extraction is on.
953
      ErrHandler.Exit(RARX_USERBREAK);
954
    }
955
#endif
956
24
    Cmd->ManualPassword=true;
957
24
  }
958
24
}
959
#endif
960
961
962
void Archive::ProcessExtra50(RawRead *Raw,size_t ExtraSize,BaseBlock *bb)
963
2.85k
{
964
  // Read extra data from the end of block skipping any fields before it.
965
2.85k
  size_t ExtraStart=Raw->Size()-ExtraSize;
966
2.85k
  if (ExtraStart<Raw->GetPos())
967
2.13k
    return;
968
717
  Raw->SetPos(ExtraStart);
969
4.47k
  while (Raw->DataLeft()>=2)
970
4.39k
  {
971
4.39k
    int64 FieldSize=Raw->GetV(); // Needs to be signed for check below and can be negative.
972
4.39k
    if (FieldSize<=0 || Raw->DataLeft()==0 || FieldSize>(int64)Raw->DataLeft())
973
514
      break;
974
3.87k
    size_t NextPos=size_t(Raw->GetPos()+FieldSize);
975
3.87k
    uint64 FieldType=Raw->GetV();
976
977
3.87k
    FieldSize=int64(NextPos-Raw->GetPos()); // Field size without size and type fields.
978
979
3.87k
    if (FieldSize<0) // FieldType is longer than expected extra field size.
980
117
      break;
981
982
3.76k
    if (bb->HeaderType==HEAD_MAIN)
983
1.65k
    {
984
1.65k
      MainHeader *hd=(MainHeader *)bb;
985
1.65k
      if (FieldType==MHEXTRA_LOCATOR)
986
1.29k
      {
987
1.29k
        hd->Locator=true;
988
1.29k
        uint Flags=(uint)Raw->GetV();
989
1.29k
        if ((Flags & MHEXTRA_LOCATOR_QLIST)!=0)
990
1.16k
        {
991
1.16k
          uint64 Offset=Raw->GetV();
992
1.16k
          if (Offset!=0) // 0 means that reserved space was not enough to write the offset.
993
1.15k
            hd->QOpenOffset=Offset+CurBlockPos;
994
1.16k
        }
995
1.29k
        if ((Flags & MHEXTRA_LOCATOR_RR)!=0)
996
180
        {
997
180
          uint64 Offset=Raw->GetV();
998
180
          if (Offset!=0) // 0 means that reserved space was not enough to write the offset.
999
139
            hd->RROffset=Offset+CurBlockPos;
1000
180
        }
1001
1.29k
      }
1002
1.65k
    }
1003
1004
3.76k
    if (bb->HeaderType==HEAD_FILE || bb->HeaderType==HEAD_SERVICE)
1005
2.11k
    {
1006
2.11k
      FileHeader *hd=(FileHeader *)bb;
1007
2.11k
      switch(FieldType)
1008
2.11k
      {
1009
299
        case FHEXTRA_CRYPT:
1010
299
          {
1011
299
            FileHeader *hd=(FileHeader *)bb;
1012
299
            uint EncVersion=(uint)Raw->GetV();
1013
299
            if (EncVersion>CRYPT_VERSION)
1014
297
            {
1015
297
              wchar Info[20];
1016
297
              swprintf(Info,ASIZE(Info),L"x%u",EncVersion);
1017
297
              UnkEncVerMsg(hd->FileName,Info);
1018
297
            }
1019
2
            else
1020
2
            {
1021
2
              uint Flags=(uint)Raw->GetV();
1022
2
              hd->UsePswCheck=(Flags & FHEXTRA_CRYPT_PSWCHECK)!=0;
1023
2
              hd->UseHashKey=(Flags & FHEXTRA_CRYPT_HASHMAC)!=0;
1024
2
              hd->Lg2Count=Raw->Get1();
1025
2
              if (hd->Lg2Count>CRYPT5_KDF_LG2_COUNT_MAX)
1026
2
              {
1027
2
                wchar Info[20];
1028
2
                swprintf(Info,ASIZE(Info),L"xc%u",hd->Lg2Count);
1029
2
                UnkEncVerMsg(hd->FileName,Info);
1030
2
              }
1031
2
              Raw->GetB(hd->Salt,SIZE_SALT50);
1032
2
              Raw->GetB(hd->InitV,SIZE_INITV);
1033
2
              if (hd->UsePswCheck)
1034
1
              {
1035
1
                Raw->GetB(hd->PswCheck,SIZE_PSWCHECK);
1036
1037
                // It is important to know if password check data is valid.
1038
                // If it is damaged and header CRC32 fails to detect it,
1039
                // archiver would refuse to decompress a possibly valid file.
1040
                // Since we want to be sure distinguishing a wrong password
1041
                // or corrupt file data, we use 64-bit password check data
1042
                // and to control its validity we use 32 bits of password
1043
                // check data SHA-256 additionally to 32-bit header CRC32.
1044
1
                byte csum[SIZE_PSWCHECK_CSUM];
1045
1
                Raw->GetB(csum,SIZE_PSWCHECK_CSUM);
1046
1047
1
                sha256_context ctx;
1048
1
                sha256_init(&ctx);
1049
1
                sha256_process(&ctx, hd->PswCheck, SIZE_PSWCHECK);
1050
1051
1
                byte Digest[SHA256_DIGEST_SIZE];
1052
1
                sha256_done(&ctx, Digest);
1053
1054
1
                hd->UsePswCheck=memcmp(csum,Digest,SIZE_PSWCHECK_CSUM)==0;
1055
1056
                // RAR 5.21 and earlier set PswCheck field in service records to 0
1057
                // even if UsePswCheck was present.
1058
1
                if (bb->HeaderType==HEAD_SERVICE && memcmp(hd->PswCheck,"\0\0\0\0\0\0\0\0",SIZE_PSWCHECK)==0)
1059
0
                  hd->UsePswCheck=0;
1060
1
              }
1061
2
              hd->SaltSet=true;
1062
2
              hd->CryptMethod=CRYPT_RAR50;
1063
2
              hd->Encrypted=true;
1064
2
            }
1065
299
          }
1066
299
          break;
1067
813
        case FHEXTRA_HASH:
1068
813
          {
1069
813
            FileHeader *hd=(FileHeader *)bb;
1070
813
            uint Type=(uint)Raw->GetV();
1071
813
            if (Type==FHEXTRA_HASH_BLAKE2)
1072
163
            {
1073
163
              hd->FileHash.Type=HASH_BLAKE2;
1074
163
              Raw->GetB(hd->FileHash.Digest,BLAKE2_DIGEST_SIZE);
1075
163
            }
1076
813
          }
1077
813
          break;
1078
58
        case FHEXTRA_HTIME:
1079
58
          if (FieldSize>=5)
1080
2
          {
1081
2
            byte Flags=(byte)Raw->GetV();
1082
2
            bool UnixTime=(Flags & FHEXTRA_HTIME_UNIXTIME)!=0;
1083
2
            if ((Flags & FHEXTRA_HTIME_MTIME)!=0)
1084
1
              if (UnixTime)
1085
1
                hd->mtime.SetUnix(Raw->Get4());
1086
0
              else
1087
0
                hd->mtime.SetWin(Raw->Get8());
1088
2
            if ((Flags & FHEXTRA_HTIME_CTIME)!=0)
1089
1
              if (UnixTime)
1090
1
                hd->ctime.SetUnix(Raw->Get4());
1091
0
              else
1092
0
                hd->ctime.SetWin(Raw->Get8());
1093
2
            if ((Flags & FHEXTRA_HTIME_ATIME)!=0)
1094
2
              if (UnixTime)
1095
2
                hd->atime.SetUnix((time_t)Raw->Get4());
1096
0
              else
1097
0
                hd->atime.SetWin(Raw->Get8());
1098
2
            if (UnixTime && (Flags & FHEXTRA_HTIME_UNIX_NS)!=0) // Add nanoseconds.
1099
2
            {
1100
2
              uint ns;
1101
2
              if ((Flags & FHEXTRA_HTIME_MTIME)!=0 && (ns=(Raw->Get4() & 0x3fffffff))<1000000000)
1102
1
                hd->mtime.Adjust(ns);
1103
2
              if ((Flags & FHEXTRA_HTIME_CTIME)!=0 && (ns=(Raw->Get4() & 0x3fffffff))<1000000000)
1104
1
                hd->ctime.Adjust(ns);
1105
2
              if ((Flags & FHEXTRA_HTIME_ATIME)!=0 && (ns=(Raw->Get4() & 0x3fffffff))<1000000000)
1106
1
                hd->atime.Adjust(ns);
1107
2
            }
1108
2
          }
1109
58
          break;
1110
6
        case FHEXTRA_VERSION:
1111
6
          if (FieldSize>=1)
1112
3
          {
1113
3
            Raw->GetV(); // Skip flags field.
1114
3
            uint Version=(uint)Raw->GetV();
1115
3
            if (Version!=0)
1116
3
            {
1117
3
              hd->Version=true;
1118
1119
3
              wchar VerText[20];
1120
3
              swprintf(VerText,ASIZE(VerText),L";%u",Version);
1121
3
              wcsncatz(hd->FileName,VerText,ASIZE(hd->FileName));
1122
3
            }
1123
3
          }
1124
6
          break;
1125
785
        case FHEXTRA_REDIR:
1126
785
          {
1127
785
            hd->RedirType=(FILE_SYSTEM_REDIRECT)Raw->GetV();
1128
785
            uint Flags=(uint)Raw->GetV();
1129
785
            hd->DirTarget=(Flags & FHEXTRA_REDIR_DIR)!=0;
1130
785
            size_t NameSize=(size_t)Raw->GetV();
1131
1132
785
            char UtfName[NM*4];
1133
785
            *UtfName=0;
1134
785
            if (NameSize<ASIZE(UtfName)-1)
1135
781
            {
1136
781
              Raw->GetB(UtfName,NameSize);
1137
781
              UtfName[NameSize]=0;
1138
781
            }
1139
#ifdef _WIN_ALL
1140
            UnixSlashToDos(UtfName,UtfName,ASIZE(UtfName));
1141
#endif
1142
785
            UtfToWide(UtfName,hd->RedirName,ASIZE(hd->RedirName));
1143
785
          }
1144
785
          break;
1145
2
        case FHEXTRA_UOWNER:
1146
2
          {
1147
2
            uint Flags=(uint)Raw->GetV();
1148
2
            hd->UnixOwnerNumeric=(Flags & FHEXTRA_UOWNER_NUMUID)!=0;
1149
2
            hd->UnixGroupNumeric=(Flags & FHEXTRA_UOWNER_NUMGID)!=0;
1150
2
            *hd->UnixOwnerName=*hd->UnixGroupName=0;
1151
2
            if ((Flags & FHEXTRA_UOWNER_UNAME)!=0)
1152
0
            {
1153
0
              size_t Length=(size_t)Raw->GetV();
1154
0
              Length=Min(Length,ASIZE(hd->UnixOwnerName)-1);
1155
0
              Raw->GetB(hd->UnixOwnerName,Length);
1156
0
              hd->UnixOwnerName[Length]=0;
1157
0
            }
1158
2
            if ((Flags & FHEXTRA_UOWNER_GNAME)!=0)
1159
1
            {
1160
1
              size_t Length=(size_t)Raw->GetV();
1161
1
              Length=Min(Length,ASIZE(hd->UnixGroupName)-1);
1162
1
              Raw->GetB(hd->UnixGroupName,Length);
1163
1
              hd->UnixGroupName[Length]=0;
1164
1
            }
1165
2
#ifdef _UNIX
1166
2
            if (hd->UnixOwnerNumeric)
1167
0
              hd->UnixOwnerID=(uid_t)Raw->GetV();
1168
2
            if (hd->UnixGroupNumeric)
1169
0
              hd->UnixGroupID=(gid_t)Raw->GetV();
1170
#else
1171
            // Need these fields in Windows too for 'list' command,
1172
            // but uid_t and gid_t are not defined.
1173
            if (hd->UnixOwnerNumeric)
1174
              hd->UnixOwnerID=(uint)Raw->GetV();
1175
            if (hd->UnixGroupNumeric)
1176
              hd->UnixGroupID=(uint)Raw->GetV();
1177
#endif
1178
2
            hd->UnixOwnerSet=true;
1179
2
          }
1180
2
          break;
1181
3
        case FHEXTRA_SUBDATA:
1182
3
          {
1183
            // RAR 5.21 and earlier set FHEXTRA_SUBDATA size to 1 less than
1184
            // required. It did not hurt extraction, because UnRAR 5.21
1185
            // and earlier ignored this field and set FieldSize as data left
1186
            // in entire extra area. But now we set the correct field size
1187
            // and set FieldSize based on the actual extra record size,
1188
            // so we need to adjust it for those older archives here.
1189
            // FHEXTRA_SUBDATA in those archives always belongs to HEAD_SERVICE
1190
            // and always is last in extra area. So since its size is by 1
1191
            // less than needed, we always have 1 byte left in extra area,
1192
            // which fact we use here to detect such archives.
1193
3
            if (bb->HeaderType==HEAD_SERVICE && Raw->Size()-NextPos==1)
1194
2
              FieldSize++;
1195
1196
            // We cannot allocate too much memory here, because above
1197
            // we check FieldSize againt Raw size and we control that Raw size
1198
            // is sensible when reading headers.
1199
3
            hd->SubData.Alloc((size_t)FieldSize);
1200
3
            Raw->GetB(hd->SubData.Addr(0),(size_t)FieldSize);
1201
3
          }
1202
3
          break;
1203
2.11k
      }
1204
2.11k
    }
1205
1206
3.76k
    Raw->SetPos(NextPos);
1207
3.76k
  }
1208
717
}
1209
1210
1211
#ifndef SFX_MODULE
1212
size_t Archive::ReadHeader14()
1213
1.80k
{
1214
1.80k
  RawRead Raw(this);
1215
1.80k
  if (CurBlockPos<=(int64)SFXSize)
1216
444
  {
1217
444
    Raw.Read(SIZEOF_MAINHEAD14);
1218
444
    MainHead.Reset();
1219
444
    byte Mark[4];
1220
444
    Raw.GetB(Mark,4);
1221
444
    uint HeadSize=Raw.Get2();
1222
444
    if (HeadSize<7)
1223
186
      return false;
1224
258
    byte Flags=Raw.Get1();
1225
258
    NextBlockPos=CurBlockPos+HeadSize;
1226
258
    CurHeaderType=HEAD_MAIN;
1227
1228
258
    Volume=(Flags & MHD_VOLUME)!=0;
1229
258
    Solid=(Flags & MHD_SOLID)!=0;
1230
258
    Locked=(Flags & MHD_LOCK)!=0;
1231
258
    MainHead.CommentInHeader=(Flags & MHD_COMMENT)!=0;
1232
258
    MainHead.PackComment=(Flags & MHD_PACK_COMMENT)!=0;
1233
258
  }
1234
1.36k
  else
1235
1.36k
  {
1236
1.36k
    Raw.Read(SIZEOF_FILEHEAD14);
1237
1.36k
    FileHead.Reset();
1238
1239
1.36k
    FileHead.HeaderType=HEAD_FILE;
1240
1.36k
    FileHead.DataSize=Raw.Get4();
1241
1.36k
    FileHead.UnpSize=Raw.Get4();
1242
1.36k
    FileHead.FileHash.Type=HASH_RAR14;
1243
1.36k
    FileHead.FileHash.CRC32=Raw.Get2();
1244
1.36k
    FileHead.HeadSize=Raw.Get2();
1245
1.36k
    if (FileHead.HeadSize<21)
1246
487
      return false;
1247
875
    uint FileTime=Raw.Get4();
1248
875
    FileHead.FileAttr=Raw.Get1();
1249
875
    FileHead.Flags=Raw.Get1()|LONG_BLOCK;
1250
875
    FileHead.UnpVer=(Raw.Get1()==2) ? 13 : 10;
1251
875
    size_t NameSize=Raw.Get1();
1252
875
    FileHead.Method=Raw.Get1();
1253
1254
875
    FileHead.SplitBefore=(FileHead.Flags & LHD_SPLIT_BEFORE)!=0;
1255
875
    FileHead.SplitAfter=(FileHead.Flags & LHD_SPLIT_AFTER)!=0;
1256
875
    FileHead.Encrypted=(FileHead.Flags & LHD_PASSWORD)!=0;
1257
875
    FileHead.CryptMethod=FileHead.Encrypted ? CRYPT_RAR13:CRYPT_NONE;
1258
1259
875
    FileHead.PackSize=FileHead.DataSize;
1260
875
    FileHead.WinSize=0x10000;
1261
875
    FileHead.Dir=(FileHead.FileAttr & 0x10)!=0;
1262
1263
875
    FileHead.HostOS=HOST_MSDOS;
1264
875
    FileHead.HSType=HSYS_WINDOWS;
1265
1266
875
    FileHead.mtime.SetDos(FileTime);
1267
1268
875
    Raw.Read(NameSize);
1269
1270
875
    char FileName[NM];
1271
875
    size_t ReadNameSize=Min(NameSize,ASIZE(FileName)-1);
1272
875
    Raw.GetB((byte *)FileName,ReadNameSize);
1273
875
    FileName[ReadNameSize]=0;
1274
875
    IntToExt(FileName,FileName,ASIZE(FileName));
1275
875
    CharToWide(FileName,FileHead.FileName,ASIZE(FileHead.FileName));
1276
875
    ConvertNameCase(FileHead.FileName);
1277
875
    ConvertFileHeader(&FileHead);
1278
1279
875
    if (Raw.Size()!=0)
1280
875
      NextBlockPos=CurBlockPos+FileHead.HeadSize+FileHead.PackSize;
1281
875
    CurHeaderType=HEAD_FILE;
1282
875
  }
1283
1.13k
  return NextBlockPos>CurBlockPos ? Raw.Size() : 0;
1284
1.80k
}
1285
#endif
1286
1287
1288
#ifndef SFX_MODULE
1289
void Archive::ConvertNameCase(wchar *Name)
1290
144k
{
1291
144k
  if (Cmd->ConvertNames==NAMES_UPPERCASE)
1292
0
    wcsupper(Name);
1293
144k
  if (Cmd->ConvertNames==NAMES_LOWERCASE)
1294
0
    wcslower(Name);
1295
144k
}
1296
#endif
1297
1298
1299
bool Archive::IsArcDir()
1300
286k
{
1301
286k
  return FileHead.Dir;
1302
286k
}
1303
1304
1305
void Archive::ConvertAttributes()
1306
143k
{
1307
#if defined(_WIN_ALL) || defined(_EMX)
1308
  if (FileHead.HSType!=HSYS_WINDOWS)
1309
    FileHead.FileAttr=FileHead.Dir ? 0x10 : 0x20;
1310
#endif
1311
143k
#ifdef _UNIX
1312
  // umask defines which permission bits must not be set by default
1313
  // when creating a file or directory. The typical default value
1314
  // for the process umask is S_IWGRP | S_IWOTH (octal 022),
1315
  // resulting in 0644 mode for new files.
1316
  // Normally umask is applied automatically when creating a file,
1317
  // but we set attributes with chmod later, so we need to calculate
1318
  // resulting attributes here. We do it only for non-Unix archives.
1319
  // We restore native Unix attributes as is, because it can be backup.
1320
143k
  static mode_t mask = (mode_t) -1;
1321
1322
143k
  if (mask == (mode_t) -1)
1323
1
  {
1324
    // umask call returns the current umask value. Argument (022) is not
1325
    // really important here.
1326
1
    mask = umask(022);
1327
1328
    // Restore the original umask value, which was changed to 022 above.
1329
1
    umask(mask);
1330
1
  }
1331
1332
143k
  switch(FileHead.HSType)
1333
143k
  {
1334
139k
    case HSYS_WINDOWS:
1335
139k
      {
1336
        // Mapping MSDOS, OS/2 and Windows file attributes to Unix.
1337
1338
139k
        if (FileHead.FileAttr & 0x10) // FILE_ATTRIBUTE_DIRECTORY
1339
359
        {
1340
          // For directories we use 0777 mask.
1341
359
          FileHead.FileAttr=0777 & ~mask;
1342
359
        }
1343
139k
        else
1344
139k
          if (FileHead.FileAttr & 1)  // FILE_ATTRIBUTE_READONLY
1345
84
          {
1346
            // For read only files we use 0444 mask with 'w' bits turned off.
1347
84
            FileHead.FileAttr=0444 & ~mask;
1348
84
          }
1349
139k
          else
1350
139k
          {
1351
            // umask does not set +x for regular files, so we use 0666
1352
            // instead of 0777 as for directories.
1353
139k
            FileHead.FileAttr=0666 & ~mask;
1354
139k
          }
1355
139k
      }
1356
139k
      break;
1357
21
    case HSYS_UNIX:
1358
21
      break;
1359
3.64k
    default:
1360
3.64k
      if (FileHead.Dir)
1361
113
        FileHead.FileAttr=0x41ff & ~mask;
1362
3.53k
      else
1363
3.53k
        FileHead.FileAttr=0x81b6 & ~mask;
1364
3.64k
      break;
1365
143k
  }
1366
143k
#endif
1367
143k
}
1368
1369
1370
void Archive::ConvertFileHeader(FileHeader *hd)
1371
144k
{
1372
144k
  if (hd->HSType==HSYS_UNKNOWN)
1373
4.26k
    if (hd->Dir)
1374
133
      hd->FileAttr=0x10;
1375
4.12k
    else
1376
4.12k
      hd->FileAttr=0x20;
1377
1378
#ifdef _WIN_ALL
1379
  if (hd->HSType==HSYS_UNIX) // Convert Unix, OS X and Android decomposed chracters to Windows precomposed.
1380
    ConvertToPrecomposed(hd->FileName,ASIZE(hd->FileName));
1381
#endif
1382
1383
394k
  for (wchar *s=hd->FileName;*s!=0;s++)
1384
249k
  {
1385
249k
#ifdef _UNIX
1386
    // Backslash is the invalid character for Windows file headers,
1387
    // but it can present in Unix file names extracted in Unix.
1388
249k
    if (*s=='\\' && Format==RARFMT50 && hd->HSType==HSYS_WINDOWS)
1389
0
      *s='_';
1390
249k
#endif
1391
1392
#if defined(_WIN_ALL) || defined(_EMX)
1393
    // RAR 5.0 archives do not use '\' as path separator, so if we see it,
1394
    // it means that it is a part of Unix file name, which we cannot
1395
    // extract in Windows.
1396
    if (*s=='\\' && Format==RARFMT50)
1397
      *s='_';
1398
1399
    // ':' in file names is allowed in Unix, but not in Windows.
1400
    // Even worse, file data will be written to NTFS stream on NTFS,
1401
    // so automatic name correction on file create error in extraction
1402
    // routine does not work. In Windows and DOS versions we better
1403
    // replace ':' now.
1404
    if (*s==':')
1405
      *s='_';
1406
#endif
1407
1408
    // This code must be performed only after other path separator checks,
1409
    // because it produces backslashes illegal for some of checks above.
1410
    // Backslash is allowed in file names in Unix, but not in Windows.
1411
    // Still, RAR 4.x uses backslashes as path separator even in Unix.
1412
    // Forward slash is not allowed in both systems. In RAR 5.0 we use
1413
    // the forward slash as universal path separator.
1414
249k
    if (*s=='/' || *s=='\\' && Format!=RARFMT50)
1415
3.68k
      *s=CPATHDIVIDER;
1416
249k
  }
1417
144k
}
1418
1419
1420
int64 Archive::GetStartPos()
1421
62
{
1422
62
  int64 StartPos=SFXSize+MarkHead.HeadSize;
1423
62
  if (Format==RARFMT15)
1424
62
    StartPos+=MainHead.HeadSize;
1425
0
  else // RAR 5.0.
1426
0
    StartPos+=CryptHead.HeadSize+FullHeaderSize(MainHead.HeadSize);
1427
62
  return StartPos;
1428
62
}
1429
1430
1431
bool Archive::ReadSubData(Array<byte> *UnpData,File *DestFile,bool TestMode)
1432
0
{
1433
0
  if (BrokenHeader)
1434
0
  {
1435
0
    uiMsg(UIERROR_SUBHEADERBROKEN,FileName);
1436
0
    ErrHandler.SetErrorCode(RARX_CRC);
1437
0
    return false;
1438
0
  }
1439
0
  if (SubHead.Method>5 || SubHead.UnpVer>(Format==RARFMT50 ? VER_UNPACK5:VER_UNPACK))
1440
0
  {
1441
0
    uiMsg(UIERROR_SUBHEADERUNKNOWN,FileName);
1442
0
    return false;
1443
0
  }
1444
1445
0
  if (SubHead.PackSize==0 && !SubHead.SplitAfter)
1446
0
    return true;
1447
1448
0
  SubDataIO.Init();
1449
0
  Unpack Unpack(&SubDataIO);
1450
0
  Unpack.Init(SubHead.WinSize,false);
1451
1452
0
  if (DestFile==NULL)
1453
0
  {
1454
0
    if (SubHead.UnpSize>0x1000000)
1455
0
    {
1456
      // So huge allocation must never happen in valid archives.
1457
0
      uiMsg(UIERROR_SUBHEADERUNKNOWN,FileName);
1458
0
      return false;
1459
0
    }
1460
0
    if (UnpData==NULL)
1461
0
      SubDataIO.SetTestMode(true);
1462
0
    else
1463
0
    {
1464
0
      UnpData->Alloc((size_t)SubHead.UnpSize);
1465
0
      SubDataIO.SetUnpackToMemory(&(*UnpData)[0],(uint)SubHead.UnpSize);
1466
0
    }
1467
0
  }
1468
0
  if (SubHead.Encrypted)
1469
0
    if (Cmd->Password.IsSet())
1470
0
      SubDataIO.SetEncryption(false,SubHead.CryptMethod,&Cmd->Password,
1471
0
                SubHead.SaltSet ? SubHead.Salt:NULL,SubHead.InitV,
1472
0
                SubHead.Lg2Count,SubHead.HashKey,SubHead.PswCheck);
1473
0
    else
1474
0
      return false;
1475
0
  SubDataIO.UnpHash.Init(SubHead.FileHash.Type,1);
1476
0
  SubDataIO.SetPackedSizeToRead(SubHead.PackSize);
1477
0
  SubDataIO.EnableShowProgress(false);
1478
0
  SubDataIO.SetFiles(this,DestFile);
1479
0
  SubDataIO.SetTestMode(TestMode);
1480
0
  SubDataIO.UnpVolume=SubHead.SplitAfter;
1481
0
  SubDataIO.SetSubHeader(&SubHead,NULL);
1482
0
  Unpack.SetDestSize(SubHead.UnpSize);
1483
0
  if (SubHead.Method==0)
1484
0
    CmdExtract::UnstoreFile(SubDataIO,SubHead.UnpSize);
1485
0
  else
1486
0
    Unpack.DoUnpack(SubHead.UnpVer,false);
1487
1488
0
  if (!SubDataIO.UnpHash.Cmp(&SubHead.FileHash,SubHead.UseHashKey ? SubHead.HashKey:NULL))
1489
0
  {
1490
0
    uiMsg(UIERROR_SUBHEADERDATABROKEN,FileName,SubHead.FileName);
1491
0
    ErrHandler.SetErrorCode(RARX_CRC);
1492
0
    if (UnpData!=NULL)
1493
0
      UnpData->Reset();
1494
0
    return false;
1495
0
  }
1496
0
  return true;
1497
0
}