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