1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* This Source Code Form is subject to the terms of the Mozilla Public
3  * License, v. 2.0. If a copy of the MPL was not distributed with this
4  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5 
6 /*
7  * This module implements a simple archive extractor for the PKZIP format.
8  *
9  * The underlying nsZipArchive is NOT thread-safe. Do not pass references
10  * or pointers to it across thread boundaries.
11  */
12 
13 // This must be the first include in the file in order for the
14 // PL_ARENA_CONST_ALIGN_MASK macro to be effective.
15 #define PL_ARENA_CONST_ALIGN_MASK  (sizeof(void*)-1)
16 #include "plarena.h"
17 
18 #define READTYPE  int32_t
19 #include "zlib.h"
20 #include "nsISupportsUtils.h"
21 #include "prio.h"
22 #include "plstr.h"
23 #include "mozilla/Logging.h"
24 #include "mozilla/UniquePtrExtensions.h"
25 #include "stdlib.h"
26 #include "nsWildCard.h"
27 #include "nsZipArchive.h"
28 #include "nsString.h"
29 #include "prenv.h"
30 #if defined(XP_WIN)
31 #include <windows.h>
32 #endif
33 
34 // For placement new used for arena allocations of zip file list
35 #include <new>
36 #define ZIP_ARENABLOCKSIZE (1*1024)
37 
38 #ifdef XP_UNIX
39     #include <sys/mman.h>
40     #include <sys/types.h>
41     #include <sys/stat.h>
42     #include <limits.h>
43     #include <unistd.h>
44 #elif defined(XP_WIN)
45     #include <io.h>
46 #endif
47 
48 #ifdef __SYMBIAN32__
49     #include <sys/syslimits.h>
50 #endif /*__SYMBIAN32__*/
51 
52 
53 #ifndef XP_UNIX /* we need some constants defined in limits.h and unistd.h */
54 #  ifndef S_IFMT
55 #    define S_IFMT 0170000
56 #  endif
57 #  ifndef S_IFLNK
58 #    define S_IFLNK  0120000
59 #  endif
60 #  ifndef PATH_MAX
61 #    define PATH_MAX 1024
62 #  endif
63 #endif  /* XP_UNIX */
64 
65 #ifdef XP_WIN
66 #include "private/pprio.h"  // To get PR_ImportFile
67 #endif
68 
69 using namespace mozilla;
70 
71 static const uint32_t kMaxNameLength = PATH_MAX; /* Maximum name length */
72 // For synthetic zip entries. Date/time corresponds to 1980-01-01 00:00.
73 static const uint16_t kSyntheticTime = 0;
74 static const uint16_t kSyntheticDate = (1 + (1 << 5) + (0 << 9));
75 
76 static uint16_t xtoint(const uint8_t *ii);
77 static uint32_t xtolong(const uint8_t *ll);
78 static uint32_t HashName(const char* aName, uint16_t nameLen);
79 #ifdef XP_UNIX
80 static nsresult ResolveSymlink(const char *path);
81 #endif
82 
83 class ZipArchiveLogger {
84 public:
Write(const nsACString & zip,const char * entry) const85   void Write(const nsACString &zip, const char *entry) const {
86     if (!fd) {
87       char *env = PR_GetEnv("MOZ_JAR_LOG_FILE");
88       if (!env)
89         return;
90 
91       nsCOMPtr<nsIFile> logFile;
92       nsresult rv = NS_NewLocalFile(NS_ConvertUTF8toUTF16(env), false, getter_AddRefs(logFile));
93       if (NS_FAILED(rv))
94         return;
95 
96       // Create the log file and its parent directory (in case it doesn't exist)
97       rv = logFile->Create(nsIFile::NORMAL_FILE_TYPE, 0644);
98       if (NS_FAILED(rv))
99         return;
100 
101       PRFileDesc* file;
102 #ifdef XP_WIN
103       // PR_APPEND is racy on Windows, so open a handle ourselves with flags that
104       // will work, and use PR_ImportFile to make it a PRFileDesc.
105       // This can go away when bug 840435 is fixed.
106       nsAutoString path;
107       logFile->GetPath(path);
108       if (path.IsEmpty())
109         return;
110       HANDLE handle = CreateFileW(path.get(), FILE_APPEND_DATA, FILE_SHARE_WRITE,
111                                   nullptr, OPEN_ALWAYS, 0, nullptr);
112       if (handle == INVALID_HANDLE_VALUE)
113         return;
114       file = PR_ImportFile((PROsfd)handle);
115       if (!file)
116         return;
117 #else
118       rv = logFile->OpenNSPRFileDesc(PR_WRONLY|PR_CREATE_FILE|PR_APPEND, 0644, &file);
119       if (NS_FAILED(rv))
120         return;
121 #endif
122       fd = file;
123     }
124     nsCString buf(zip);
125     buf.Append(' ');
126     buf.Append(entry);
127     buf.Append('\n');
128     PR_Write(fd, buf.get(), buf.Length());
129   }
130 
AddRef()131   void AddRef() {
132     MOZ_ASSERT(refCnt >= 0);
133     ++refCnt;
134   }
135 
Release()136   void Release() {
137     MOZ_ASSERT(refCnt > 0);
138     if ((0 == --refCnt) && fd) {
139       PR_Close(fd);
140       fd = nullptr;
141     }
142   }
143 private:
144   int refCnt;
145   mutable PRFileDesc *fd;
146 };
147 
148 static ZipArchiveLogger zipLog;
149 
150 //***********************************************************
151 // For every inflation the following allocations are done:
152 // malloc(1 * 9520)
153 // malloc(32768 * 1)
154 //***********************************************************
155 
gZlibInit(z_stream * zs)156 nsresult gZlibInit(z_stream *zs)
157 {
158   memset(zs, 0, sizeof(z_stream));
159   int zerr = inflateInit2(zs, -MAX_WBITS);
160   if (zerr != Z_OK) return NS_ERROR_OUT_OF_MEMORY;
161 
162   return NS_OK;
163 }
164 
nsZipHandle()165 nsZipHandle::nsZipHandle()
166   : mFileData(nullptr)
167   , mLen(0)
168   , mMap(nullptr)
169   , mRefCnt(0)
170   , mFileStart(nullptr)
171   , mTotalLen(0)
172 {
173   MOZ_COUNT_CTOR(nsZipHandle);
174 }
175 
176 NS_IMPL_ADDREF(nsZipHandle)
NS_IMPL_RELEASE(nsZipHandle)177 NS_IMPL_RELEASE(nsZipHandle)
178 
179 nsresult nsZipHandle::Init(nsIFile *file, nsZipHandle **ret,
180                            PRFileDesc **aFd)
181 {
182   mozilla::AutoFDClose fd;
183   int32_t flags = PR_RDONLY;
184 #if defined(XP_WIN)
185   flags |= nsIFile::OS_READAHEAD;
186 #endif
187   nsresult rv = file->OpenNSPRFileDesc(flags, 0000, &fd.rwget());
188   if (NS_FAILED(rv))
189     return rv;
190 
191   int64_t size = PR_Available64(fd);
192   if (size >= INT32_MAX)
193     return NS_ERROR_FILE_TOO_BIG;
194 
195   PRFileMap *map = PR_CreateFileMap(fd, size, PR_PROT_READONLY);
196   if (!map)
197     return NS_ERROR_FAILURE;
198 
199   uint8_t *buf = (uint8_t*) PR_MemMap(map, 0, (uint32_t) size);
200   // Bug 525755: PR_MemMap fails when fd points at something other than a normal file.
201   if (!buf) {
202     PR_CloseFileMap(map);
203     return NS_ERROR_FAILURE;
204   }
205 
206   RefPtr<nsZipHandle> handle = new nsZipHandle();
207   if (!handle) {
208     PR_MemUnmap(buf, (uint32_t) size);
209     PR_CloseFileMap(map);
210     return NS_ERROR_OUT_OF_MEMORY;
211   }
212 
213 #if defined(XP_WIN)
214   if (aFd) {
215     *aFd = fd.forget();
216   }
217 #else
218   handle->mNSPRFileDesc = fd.forget();
219 #endif
220   handle->mMap = map;
221   handle->mFile.Init(file);
222   handle->mTotalLen = (uint32_t) size;
223   handle->mFileStart = buf;
224   rv = handle->findDataStart();
225   if (NS_FAILED(rv)) {
226     PR_MemUnmap(buf, (uint32_t) size);
227     PR_CloseFileMap(map);
228     return rv;
229   }
230   handle.forget(ret);
231   return NS_OK;
232 }
233 
Init(nsZipArchive * zip,const char * entry,nsZipHandle ** ret)234 nsresult nsZipHandle::Init(nsZipArchive *zip, const char *entry,
235                            nsZipHandle **ret)
236 {
237   RefPtr<nsZipHandle> handle = new nsZipHandle();
238   if (!handle)
239     return NS_ERROR_OUT_OF_MEMORY;
240 
241   handle->mBuf = new nsZipItemPtr<uint8_t>(zip, entry);
242   if (!handle->mBuf)
243     return NS_ERROR_OUT_OF_MEMORY;
244 
245   if (!handle->mBuf->Buffer())
246     return NS_ERROR_UNEXPECTED;
247 
248   handle->mMap = nullptr;
249   handle->mFile.Init(zip, entry);
250   handle->mTotalLen = handle->mBuf->Length();
251   handle->mFileStart = handle->mBuf->Buffer();
252   nsresult rv = handle->findDataStart();
253   if (NS_FAILED(rv)) {
254     return rv;
255   }
256   handle.forget(ret);
257   return NS_OK;
258 }
259 
Init(const uint8_t * aData,uint32_t aLen,nsZipHandle ** aRet)260 nsresult nsZipHandle::Init(const uint8_t* aData, uint32_t aLen,
261                            nsZipHandle **aRet)
262 {
263   RefPtr<nsZipHandle> handle = new nsZipHandle();
264 
265   handle->mFileStart = aData;
266   handle->mTotalLen = aLen;
267   nsresult rv = handle->findDataStart();
268   if (NS_FAILED(rv)) {
269     return rv;
270   }
271   handle.forget(aRet);
272   return NS_OK;
273 }
274 
275 // This function finds the start of the ZIP data. If the file is a regular ZIP,
276 // this is just the start of the file. If the file is a CRX file, the start of
277 // the data is after the CRX header.
278 // CRX header reference: (CRX version 2)
279 //    Header requires little-endian byte ordering with 4-byte alignment.
280 //    32 bits       : magicNumber   - Defined as a |char m[] = "Cr24"|.
281 //                                    Equivilant to |uint32_t m = 0x34327243|.
282 //    32 bits       : version       - Unsigned integer representing the CRX file
283 //                                    format version. Currently equal to 2.
284 //    32 bits       : pubKeyLength  - Unsigned integer representing the length
285 //                                    of the public key in bytes.
286 //    32 bits       : sigLength     - Unsigned integer representing the length
287 //                                    of the signature in bytes.
288 //    pubKeyLength  : publicKey     - Contents of the author's public key.
289 //    sigLength     : signature     - Signature of the ZIP content.
290 //                                    Signature is created using the RSA
291 //                                    algorighm with the SHA-1 hash function.
findDataStart()292 nsresult nsZipHandle::findDataStart()
293 {
294   // In the CRX header, integers are 32 bits. Our pointer to the file is of
295   // type |uint8_t|, which is guaranteed to be 8 bits.
296   const uint32_t CRXIntSize = 4;
297 
298 MOZ_WIN_MEM_TRY_BEGIN
299   if (mTotalLen > CRXIntSize * 4 && xtolong(mFileStart) == kCRXMagic) {
300     const uint8_t* headerData = mFileStart;
301     headerData += CRXIntSize * 2; // Skip magic number and version number
302     uint32_t pubKeyLength = xtolong(headerData);
303     headerData += CRXIntSize;
304     uint32_t sigLength = xtolong(headerData);
305     uint32_t headerSize = CRXIntSize * 4 + pubKeyLength + sigLength;
306     if (mTotalLen > headerSize) {
307       mLen = mTotalLen - headerSize;
308       mFileData = mFileStart + headerSize;
309       return NS_OK;
310     }
311   }
312   mLen = mTotalLen;
313   mFileData = mFileStart;
314 MOZ_WIN_MEM_TRY_CATCH(return NS_ERROR_FAILURE)
315   return NS_OK;
316 }
317 
SizeOfMapping()318 int64_t nsZipHandle::SizeOfMapping()
319 {
320   return mTotalLen;
321 }
322 
GetNSPRFileDesc(PRFileDesc ** aNSPRFileDesc)323 nsresult nsZipHandle::GetNSPRFileDesc(PRFileDesc** aNSPRFileDesc)
324 {
325   if (!aNSPRFileDesc) {
326     return NS_ERROR_ILLEGAL_VALUE;
327   }
328 
329   *aNSPRFileDesc = mNSPRFileDesc;
330   if (!mNSPRFileDesc) {
331     return NS_ERROR_NOT_AVAILABLE;
332   }
333 
334   return NS_OK;
335 }
336 
~nsZipHandle()337 nsZipHandle::~nsZipHandle()
338 {
339   if (mMap) {
340     PR_MemUnmap((void *)mFileStart, mTotalLen);
341     PR_CloseFileMap(mMap);
342   }
343   mFileStart = nullptr;
344   mFileData = nullptr;
345   mMap = nullptr;
346   mBuf = nullptr;
347   MOZ_COUNT_DTOR(nsZipHandle);
348 }
349 
350 //***********************************************************
351 //      nsZipArchive  --  public methods
352 //***********************************************************
353 
354 //---------------------------------------------
355 //  nsZipArchive::OpenArchive
356 //---------------------------------------------
OpenArchive(nsZipHandle * aZipHandle,PRFileDesc * aFd)357 nsresult nsZipArchive::OpenArchive(nsZipHandle *aZipHandle, PRFileDesc *aFd)
358 {
359   mFd = aZipHandle;
360 
361   // Initialize our arena
362   PL_INIT_ARENA_POOL(&mArena, "ZipArena", ZIP_ARENABLOCKSIZE);
363 
364   //-- get table of contents for archive
365   nsresult rv = BuildFileList(aFd);
366   if (NS_SUCCEEDED(rv)) {
367     if (aZipHandle->mFile)
368       aZipHandle->mFile.GetURIString(mURI);
369   }
370   return rv;
371 }
372 
OpenArchive(nsIFile * aFile)373 nsresult nsZipArchive::OpenArchive(nsIFile *aFile)
374 {
375   RefPtr<nsZipHandle> handle;
376 #if defined(XP_WIN)
377   mozilla::AutoFDClose fd;
378   nsresult rv = nsZipHandle::Init(aFile, getter_AddRefs(handle),
379                                   &fd.rwget());
380 #else
381   nsresult rv = nsZipHandle::Init(aFile, getter_AddRefs(handle));
382 #endif
383   if (NS_FAILED(rv))
384     return rv;
385 
386 #if defined(XP_WIN)
387   return OpenArchive(handle, fd.get());
388 #else
389   return OpenArchive(handle);
390 #endif
391 }
392 
393 //---------------------------------------------
394 //  nsZipArchive::Test
395 //---------------------------------------------
Test(const char * aEntryName)396 nsresult nsZipArchive::Test(const char *aEntryName)
397 {
398   nsZipItem* currItem;
399 
400   if (aEntryName) // only test specified item
401   {
402     currItem = GetItem(aEntryName);
403     if (!currItem)
404       return NS_ERROR_FILE_TARGET_DOES_NOT_EXIST;
405     //-- don't test (synthetic) directory items
406     if (currItem->IsDirectory())
407       return NS_OK;
408     return ExtractFile(currItem, 0, 0);
409   }
410 
411   // test all items in archive
412   for (int i = 0; i < ZIP_TABSIZE; i++) {
413     for (currItem = mFiles[i]; currItem; currItem = currItem->next) {
414       //-- don't test (synthetic) directory items
415       if (currItem->IsDirectory())
416         continue;
417       nsresult rv = ExtractFile(currItem, 0, 0);
418       if (rv != NS_OK)
419         return rv;
420     }
421   }
422 
423   return NS_OK;
424 }
425 
426 //---------------------------------------------
427 //  nsZipArchive::CloseArchive
428 //---------------------------------------------
CloseArchive()429 nsresult nsZipArchive::CloseArchive()
430 {
431   if (mFd) {
432     PL_FinishArenaPool(&mArena);
433     mFd = nullptr;
434   }
435 
436   // CAUTION:
437   // We don't need to delete each of the nsZipItem as the memory for
438   // the zip item and the filename it holds are both allocated from the Arena.
439   // Hence, destroying the Arena is like destroying all the memory
440   // for all the nsZipItem in one shot. But if the ~nsZipItem is doing
441   // anything more than cleaning up memory, we should start calling it.
442   // Let us also cleanup the mFiles table for re-use on the next 'open' call
443   memset(mFiles, 0, sizeof(mFiles));
444   mBuiltSynthetics = false;
445   return NS_OK;
446 }
447 
448 //---------------------------------------------
449 // nsZipArchive::GetItem
450 //---------------------------------------------
GetItem(const char * aEntryName)451 nsZipItem*  nsZipArchive::GetItem(const char * aEntryName)
452 {
453   if (aEntryName) {
454     uint32_t len = strlen(aEntryName);
455     //-- If the request is for a directory, make sure that synthetic entries
456     //-- are created for the directories without their own entry.
457     if (!mBuiltSynthetics) {
458         if ((len > 0) && (aEntryName[len-1] == '/')) {
459             if (BuildSynthetics() != NS_OK)
460                 return 0;
461         }
462     }
463 MOZ_WIN_MEM_TRY_BEGIN
464     nsZipItem* item = mFiles[ HashName(aEntryName, len) ];
465     while (item) {
466       if ((len == item->nameLength) &&
467           (!memcmp(aEntryName, item->Name(), len))) {
468 
469         // Successful GetItem() is a good indicator that the file is about to be read
470         zipLog.Write(mURI, aEntryName);
471         return item; //-- found it
472       }
473       item = item->next;
474     }
475 MOZ_WIN_MEM_TRY_CATCH(return nullptr)
476   }
477   return nullptr;
478 }
479 
480 //---------------------------------------------
481 // nsZipArchive::ExtractFile
482 // This extracts the item to the filehandle provided.
483 // If 'aFd' is null, it only tests the extraction.
484 // On extraction error(s) it removes the file.
485 // When needed, it also resolves the symlink.
486 //---------------------------------------------
ExtractFile(nsZipItem * item,const char * outname,PRFileDesc * aFd)487 nsresult nsZipArchive::ExtractFile(nsZipItem *item, const char *outname,
488                                    PRFileDesc* aFd)
489 {
490   if (!item)
491     return NS_ERROR_ILLEGAL_VALUE;
492   if (!mFd)
493     return NS_ERROR_FAILURE;
494 
495   // Directory extraction is handled in nsJAR::Extract,
496   // so the item to be extracted should never be a directory
497   MOZ_ASSERT(!item->IsDirectory());
498 
499   Bytef outbuf[ZIP_BUFLEN];
500 
501   nsZipCursor cursor(item, this, outbuf, ZIP_BUFLEN, true);
502 
503   nsresult rv = NS_OK;
504 
505   while (true) {
506     uint32_t count = 0;
507     uint8_t* buf = cursor.Read(&count);
508     if (!buf) {
509       nsZipArchive::sFileCorruptedReason = "nsZipArchive: Read() failed to return a buffer";
510       rv = NS_ERROR_FILE_CORRUPTED;
511       break;
512     } else if (count == 0) {
513       break;
514     }
515 
516     if (aFd && PR_Write(aFd, buf, count) < (READTYPE)count) {
517       rv = NS_ERROR_FILE_DISK_FULL;
518       break;
519     }
520   }
521 
522   //-- delete the file on errors, or resolve symlink if needed
523   if (aFd) {
524     PR_Close(aFd);
525     if (rv != NS_OK)
526       PR_Delete(outname);
527 #ifdef XP_UNIX
528     else if (item->IsSymlink())
529       rv = ResolveSymlink(outname);
530 #endif
531   }
532 
533   return rv;
534 }
535 
536 //---------------------------------------------
537 // nsZipArchive::FindInit
538 //---------------------------------------------
539 nsresult
FindInit(const char * aPattern,nsZipFind ** aFind)540 nsZipArchive::FindInit(const char * aPattern, nsZipFind **aFind)
541 {
542   if (!aFind)
543     return NS_ERROR_ILLEGAL_VALUE;
544 
545   // null out param in case an error happens
546   *aFind = nullptr;
547 
548   bool    regExp = false;
549   char*   pattern = 0;
550 
551   // Create synthetic directory entries on demand
552   nsresult rv = BuildSynthetics();
553   if (rv != NS_OK)
554     return rv;
555 
556   // validate the pattern
557   if (aPattern)
558   {
559     switch (NS_WildCardValid((char*)aPattern))
560     {
561       case INVALID_SXP:
562         return NS_ERROR_ILLEGAL_VALUE;
563 
564       case NON_SXP:
565         regExp = false;
566         break;
567 
568       case VALID_SXP:
569         regExp = true;
570         break;
571 
572       default:
573         // undocumented return value from RegExpValid!
574         PR_ASSERT(false);
575         return NS_ERROR_ILLEGAL_VALUE;
576     }
577 
578     pattern = PL_strdup(aPattern);
579     if (!pattern)
580       return NS_ERROR_OUT_OF_MEMORY;
581   }
582 
583   *aFind = new nsZipFind(this, pattern, regExp);
584   if (!*aFind) {
585     PL_strfree(pattern);
586     return NS_ERROR_OUT_OF_MEMORY;
587   }
588 
589   return NS_OK;
590 }
591 
592 
593 
594 //---------------------------------------------
595 // nsZipFind::FindNext
596 //---------------------------------------------
FindNext(const char ** aResult,uint16_t * aNameLen)597 nsresult nsZipFind::FindNext(const char ** aResult, uint16_t *aNameLen)
598 {
599   if (!mArchive || !aResult || !aNameLen)
600     return NS_ERROR_ILLEGAL_VALUE;
601 
602   *aResult = 0;
603   *aNameLen = 0;
604 MOZ_WIN_MEM_TRY_BEGIN
605   // we start from last match, look for next
606   while (mSlot < ZIP_TABSIZE)
607   {
608     // move to next in current chain, or move to new slot
609     mItem = mItem ? mItem->next : mArchive->mFiles[mSlot];
610 
611     bool found = false;
612     if (!mItem)
613       ++mSlot;                          // no more in this chain, move to next slot
614     else if (!mPattern)
615       found = true;            // always match
616     else if (mRegExp)
617     {
618       char buf[kMaxNameLength+1];
619       memcpy(buf, mItem->Name(), mItem->nameLength);
620       buf[mItem->nameLength]='\0';
621       found = (NS_WildCardMatch(buf, mPattern, false) == MATCH);
622     }
623     else
624       found = ((mItem->nameLength == strlen(mPattern)) &&
625                (memcmp(mItem->Name(), mPattern, mItem->nameLength) == 0));
626     if (found) {
627       // Need also to return the name length, as it is NOT zero-terminatdd...
628       *aResult = mItem->Name();
629       *aNameLen = mItem->nameLength;
630       return NS_OK;
631     }
632   }
633 MOZ_WIN_MEM_TRY_CATCH(return NS_ERROR_FAILURE)
634   return NS_ERROR_FILE_TARGET_DOES_NOT_EXIST;
635 }
636 
637 #ifdef XP_UNIX
638 //---------------------------------------------
639 // ResolveSymlink
640 //---------------------------------------------
ResolveSymlink(const char * path)641 static nsresult ResolveSymlink(const char *path)
642 {
643   PRFileDesc * fIn = PR_Open(path, PR_RDONLY, 0000);
644   if (!fIn)
645     return NS_ERROR_FILE_DISK_FULL;
646 
647   char buf[PATH_MAX+1];
648   int32_t length = PR_Read(fIn, (void*)buf, PATH_MAX);
649   PR_Close(fIn);
650 
651   if ( (length <= 0)
652     || ((buf[length] = 0, PR_Delete(path)) != 0)
653     || (symlink(buf, path) != 0))
654   {
655      return NS_ERROR_FILE_DISK_FULL;
656   }
657   return NS_OK;
658 }
659 #endif
660 
661 //***********************************************************
662 //      nsZipArchive  --  private implementation
663 //***********************************************************
664 
665 //---------------------------------------------
666 //  nsZipArchive::CreateZipItem
667 //---------------------------------------------
CreateZipItem()668 nsZipItem* nsZipArchive::CreateZipItem()
669 {
670   // Arena allocate the nsZipItem
671   void *mem;
672   PL_ARENA_ALLOCATE(mem, &mArena, sizeof(nsZipItem));
673   return (nsZipItem*)mem;
674 }
675 
676 //---------------------------------------------
677 //  nsZipArchive::BuildFileList
678 //---------------------------------------------
BuildFileList(PRFileDesc * aFd)679 nsresult nsZipArchive::BuildFileList(PRFileDesc *aFd)
680 {
681   // Get archive size using end pos
682   const uint8_t* buf;
683   const uint8_t* startp = mFd->mFileData;
684   const uint8_t* endp = startp + mFd->mLen;
685 MOZ_WIN_MEM_TRY_BEGIN
686   uint32_t centralOffset = 4;
687   if (mFd->mLen > ZIPCENTRAL_SIZE && xtolong(startp + centralOffset) == CENTRALSIG) {
688     // Success means optimized jar layout from bug 559961 is in effect
689     uint32_t readaheadLength = xtolong(startp);
690     if (readaheadLength) {
691 #if defined(XP_UNIX)
692       madvise(const_cast<uint8_t*>(startp), readaheadLength, MADV_WILLNEED);
693 #elif defined(XP_WIN)
694       if (aFd) {
695         HANDLE hFile = (HANDLE) PR_FileDesc2NativeHandle(aFd);
696         mozilla::ReadAhead(hFile, 0, readaheadLength);
697       }
698 #endif
699     }
700   } else {
701     for (buf = endp - ZIPEND_SIZE; buf > startp; buf--)
702       {
703         if (xtolong(buf) == ENDSIG) {
704           centralOffset = xtolong(((ZipEnd *)buf)->offset_central_dir);
705           break;
706         }
707       }
708   }
709 
710   if (!centralOffset) {
711     nsZipArchive::sFileCorruptedReason = "nsZipArchive: no central offset";
712     return NS_ERROR_FILE_CORRUPTED;
713   }
714 
715   buf = startp + centralOffset;
716 
717   // avoid overflow of startp + centralOffset.
718   if (buf < startp) {
719     nsZipArchive::sFileCorruptedReason = "nsZipArchive: overflow looking for central directory";
720     return NS_ERROR_FILE_CORRUPTED;
721   }
722 
723   //-- Read the central directory headers
724   uint32_t sig = 0;
725   while ((buf + int32_t(sizeof(uint32_t)) > buf) &&
726          (buf + int32_t(sizeof(uint32_t)) <= endp) &&
727          ((sig = xtolong(buf)) == CENTRALSIG)) {
728     // Make sure there is enough data available.
729     if ((buf > endp) || (endp - buf < ZIPCENTRAL_SIZE)) {
730       nsZipArchive::sFileCorruptedReason = "nsZipArchive: central directory too small";
731       return NS_ERROR_FILE_CORRUPTED;
732     }
733 
734     // Read the fixed-size data.
735     ZipCentral* central = (ZipCentral*)buf;
736 
737     uint16_t namelen = xtoint(central->filename_len);
738     uint16_t extralen = xtoint(central->extrafield_len);
739     uint16_t commentlen = xtoint(central->commentfield_len);
740     uint32_t diff = ZIPCENTRAL_SIZE + namelen + extralen + commentlen;
741 
742     // Sanity check variable sizes and refuse to deal with
743     // anything too big: it's likely a corrupt archive.
744     if (namelen < 1 ||
745         namelen > kMaxNameLength) {
746       nsZipArchive::sFileCorruptedReason = "nsZipArchive: namelen out of range";
747       return NS_ERROR_FILE_CORRUPTED;
748     }
749     if (buf >= buf + diff || // No overflow
750         buf >= endp - diff) {
751       nsZipArchive::sFileCorruptedReason = "nsZipArchive: overflow looking for next item";
752       return NS_ERROR_FILE_CORRUPTED;
753     }
754 
755     // Point to the next item at the top of loop
756     buf += diff;
757 
758     nsZipItem* item = CreateZipItem();
759     if (!item)
760       return NS_ERROR_OUT_OF_MEMORY;
761 
762     item->central = central;
763     item->nameLength = namelen;
764     item->isSynthetic = false;
765 
766     // Add item to file table
767     uint32_t hash = HashName(item->Name(), namelen);
768     item->next = mFiles[hash];
769     mFiles[hash] = item;
770 
771     sig = 0;
772   } /* while reading central directory records */
773 
774   if (sig != ENDSIG) {
775     nsZipArchive::sFileCorruptedReason = "nsZipArchive: unexpected sig";
776     return NS_ERROR_FILE_CORRUPTED;
777   }
778 
779   // Make the comment available for consumers.
780   if ((endp >= buf) && (endp - buf >= ZIPEND_SIZE)) {
781     ZipEnd *zipend = (ZipEnd *)buf;
782 
783     buf += ZIPEND_SIZE;
784     uint16_t commentlen = xtoint(zipend->commentfield_len);
785     if (endp - buf >= commentlen) {
786       mCommentPtr = (const char *)buf;
787       mCommentLen = commentlen;
788     }
789   }
790 
791 MOZ_WIN_MEM_TRY_CATCH(return NS_ERROR_FAILURE)
792   return NS_OK;
793 }
794 
795 //---------------------------------------------
796 //  nsZipArchive::BuildSynthetics
797 //---------------------------------------------
BuildSynthetics()798 nsresult nsZipArchive::BuildSynthetics()
799 {
800   if (mBuiltSynthetics)
801     return NS_OK;
802   mBuiltSynthetics = true;
803 
804 MOZ_WIN_MEM_TRY_BEGIN
805   // Create synthetic entries for any missing directories.
806   // Do this when all ziptable has scanned to prevent double entries.
807   for (int i = 0; i < ZIP_TABSIZE; ++i)
808   {
809     for (nsZipItem* item = mFiles[i]; item != nullptr; item = item->next)
810     {
811       if (item->isSynthetic)
812         continue;
813 
814       //-- add entries for directories in the current item's path
815       //-- go from end to beginning, because then we can stop trying
816       //-- to create diritems if we find that the diritem we want to
817       //-- create already exists
818       //-- start just before the last char so as to not add the item
819       //-- twice if it's a directory
820       uint16_t namelen = item->nameLength;
821       MOZ_ASSERT(namelen > 0, "Attempt to build synthetic for zero-length entry name!");
822       const char *name = item->Name();
823       for (uint16_t dirlen = namelen - 1; dirlen > 0; dirlen--)
824       {
825         if (name[dirlen-1] != '/')
826           continue;
827 
828         // The character before this is '/', so if this is also '/' then we
829         // have an empty path component. Skip it.
830         if (name[dirlen] == '/')
831           continue;
832 
833         // Is the directory already in the file table?
834         uint32_t hash = HashName(item->Name(), dirlen);
835         bool found = false;
836         for (nsZipItem* zi = mFiles[hash]; zi != nullptr; zi = zi->next)
837         {
838           if ((dirlen == zi->nameLength) &&
839               (0 == memcmp(item->Name(), zi->Name(), dirlen)))
840           {
841             // we've already added this dir and all its parents
842             found = true;
843             break;
844           }
845         }
846         // if the directory was found, break out of the directory
847         // creation loop now that we know all implicit directories
848         // are there -- otherwise, start creating the zip item
849         if (found)
850           break;
851 
852         nsZipItem* diritem = CreateZipItem();
853         if (!diritem)
854           return NS_ERROR_OUT_OF_MEMORY;
855 
856         // Point to the central record of the original item for the name part.
857         diritem->central =  item->central;
858         diritem->nameLength = dirlen;
859         diritem->isSynthetic = true;
860 
861         // add diritem to the file table
862         diritem->next = mFiles[hash];
863         mFiles[hash] = diritem;
864       } /* end processing of dirs in item's name */
865     }
866   }
867 MOZ_WIN_MEM_TRY_CATCH(return NS_ERROR_FAILURE)
868   return NS_OK;
869 }
870 
GetFD()871 nsZipHandle* nsZipArchive::GetFD()
872 {
873   if (!mFd)
874     return nullptr;
875   return mFd.get();
876 }
877 
878 //---------------------------------------------
879 // nsZipArchive::GetDataOffset
880 //---------------------------------------------
GetDataOffset(nsZipItem * aItem)881 uint32_t nsZipArchive::GetDataOffset(nsZipItem* aItem)
882 {
883   MOZ_ASSERT(aItem);
884 MOZ_WIN_MEM_TRY_BEGIN
885   //-- read local header to get variable length values and calculate
886   //-- the real data offset
887   uint32_t len = mFd->mLen;
888   const uint8_t* data = mFd->mFileData;
889   uint32_t offset = aItem->LocalOffset();
890   if (len < ZIPLOCAL_SIZE || offset > len - ZIPLOCAL_SIZE)
891     return 0;
892 
893   // -- check signature before using the structure, in case the zip file is corrupt
894   ZipLocal* Local = (ZipLocal*)(data + offset);
895   if ((xtolong(Local->signature) != LOCALSIG))
896     return 0;
897 
898   //-- NOTE: extralen is different in central header and local header
899   //--       for archives created using the Unix "zip" utility. To set
900   //--       the offset accurately we need the _local_ extralen.
901   offset += ZIPLOCAL_SIZE +
902             xtoint(Local->filename_len) +
903             xtoint(Local->extrafield_len);
904 
905   return offset;
906 MOZ_WIN_MEM_TRY_CATCH(return 0)
907 }
908 
909 //---------------------------------------------
910 // nsZipArchive::GetData
911 //---------------------------------------------
GetData(nsZipItem * aItem)912 const uint8_t* nsZipArchive::GetData(nsZipItem* aItem)
913 {
914   MOZ_ASSERT(aItem);
915 MOZ_WIN_MEM_TRY_BEGIN
916   uint32_t offset = GetDataOffset(aItem);
917 
918   // -- check if there is enough source data in the file
919   if (!offset ||
920       mFd->mLen < aItem->Size() ||
921       offset > mFd->mLen - aItem->Size() ||
922       (aItem->Compression() == STORED && aItem->Size() != aItem->RealSize())) {
923     return nullptr;
924   }
925 
926   return mFd->mFileData + offset;
927 MOZ_WIN_MEM_TRY_CATCH(return nullptr)
928 }
929 
930 // nsZipArchive::GetComment
GetComment(nsACString & aComment)931 bool nsZipArchive::GetComment(nsACString &aComment)
932 {
933 MOZ_WIN_MEM_TRY_BEGIN
934   aComment.Assign(mCommentPtr, mCommentLen);
935 MOZ_WIN_MEM_TRY_CATCH(return false)
936   return true;
937 }
938 
939 //---------------------------------------------
940 // nsZipArchive::SizeOfMapping
941 //---------------------------------------------
SizeOfMapping()942 int64_t nsZipArchive::SizeOfMapping()
943 {
944     return mFd ? mFd->SizeOfMapping() : 0;
945 }
946 
947 //------------------------------------------
948 // nsZipArchive constructor and destructor
949 //------------------------------------------
950 
nsZipArchive()951 nsZipArchive::nsZipArchive()
952   : mRefCnt(0)
953   , mCommentPtr(nullptr)
954   , mCommentLen(0)
955   , mBuiltSynthetics(false)
956 {
957   zipLog.AddRef();
958 
959   MOZ_COUNT_CTOR(nsZipArchive);
960 
961   // initialize the table to nullptr
962   memset(mFiles, 0, sizeof(mFiles));
963 }
964 
965 NS_IMPL_ADDREF(nsZipArchive)
NS_IMPL_RELEASE(nsZipArchive)966 NS_IMPL_RELEASE(nsZipArchive)
967 
968 nsZipArchive::~nsZipArchive()
969 {
970   CloseArchive();
971 
972   MOZ_COUNT_DTOR(nsZipArchive);
973 
974   zipLog.Release();
975 }
976 
977 
978 //------------------------------------------
979 // nsZipFind constructor and destructor
980 //------------------------------------------
981 
nsZipFind(nsZipArchive * aZip,char * aPattern,bool aRegExp)982 nsZipFind::nsZipFind(nsZipArchive* aZip, char* aPattern, bool aRegExp)
983   : mArchive(aZip)
984   , mPattern(aPattern)
985   , mItem(nullptr)
986   , mSlot(0)
987   , mRegExp(aRegExp)
988 {
989   MOZ_COUNT_CTOR(nsZipFind);
990 }
991 
~nsZipFind()992 nsZipFind::~nsZipFind()
993 {
994   PL_strfree(mPattern);
995 
996   MOZ_COUNT_DTOR(nsZipFind);
997 }
998 
999 //------------------------------------------
1000 // helper functions
1001 //------------------------------------------
1002 
1003 /*
1004  * HashName
1005  *
1006  * returns a hash key for the entry name
1007  */
HashName(const char * aName,uint16_t len)1008 static uint32_t HashName(const char* aName, uint16_t len)
1009 {
1010   MOZ_ASSERT(aName != 0);
1011 
1012   const uint8_t* p = (const uint8_t*)aName;
1013   const uint8_t* endp = p + len;
1014   uint32_t val = 0;
1015   while (p != endp) {
1016     val = val*37 + *p++;
1017   }
1018 
1019   return (val % ZIP_TABSIZE);
1020 }
1021 
1022 /*
1023  *  x t o i n t
1024  *
1025  *  Converts a two byte ugly endianed integer
1026  *  to our platform's integer.
1027  */
xtoint(const uint8_t * ii)1028 static uint16_t xtoint (const uint8_t *ii)
1029 {
1030   return (uint16_t) ((ii [0]) | (ii [1] << 8));
1031 }
1032 
1033 /*
1034  *  x t o l o n g
1035  *
1036  *  Converts a four byte ugly endianed integer
1037  *  to our platform's integer.
1038  */
xtolong(const uint8_t * ll)1039 static uint32_t xtolong (const uint8_t *ll)
1040 {
1041   return (uint32_t)( (ll [0] <<  0) |
1042                      (ll [1] <<  8) |
1043                      (ll [2] << 16) |
1044                      (ll [3] << 24) );
1045 }
1046 
1047 /*
1048  * GetModTime
1049  *
1050  * returns last modification time in microseconds
1051  */
GetModTime(uint16_t aDate,uint16_t aTime)1052 static PRTime GetModTime(uint16_t aDate, uint16_t aTime)
1053 {
1054   // Note that on DST shift we can't handle correctly the hour that is valid
1055   // in both DST zones
1056   PRExplodedTime time;
1057 
1058   time.tm_usec = 0;
1059 
1060   time.tm_hour = (aTime >> 11) & 0x1F;
1061   time.tm_min = (aTime >> 5) & 0x3F;
1062   time.tm_sec = (aTime & 0x1F) * 2;
1063 
1064   time.tm_year = (aDate >> 9) + 1980;
1065   time.tm_month = ((aDate >> 5) & 0x0F) - 1;
1066   time.tm_mday = aDate & 0x1F;
1067 
1068   time.tm_params.tp_gmt_offset = 0;
1069   time.tm_params.tp_dst_offset = 0;
1070 
1071   PR_NormalizeTime(&time, PR_GMTParameters);
1072   time.tm_params.tp_gmt_offset = PR_LocalTimeParameters(&time).tp_gmt_offset;
1073   PR_NormalizeTime(&time, PR_GMTParameters);
1074   time.tm_params.tp_dst_offset = PR_LocalTimeParameters(&time).tp_dst_offset;
1075 
1076   return PR_ImplodeTime(&time);
1077 }
1078 
nsZipItem()1079 nsZipItem::nsZipItem()
1080   : next(nullptr)
1081   , central(nullptr)
1082   , nameLength(0)
1083   , isSynthetic(false)
1084 {}
1085 
LocalOffset()1086 uint32_t nsZipItem::LocalOffset()
1087 {
1088   return xtolong(central->localhdr_offset);
1089 }
1090 
Size()1091 uint32_t nsZipItem::Size()
1092 {
1093   return isSynthetic ? 0 : xtolong(central->size);
1094 }
1095 
RealSize()1096 uint32_t nsZipItem::RealSize()
1097 {
1098   return isSynthetic ? 0 : xtolong(central->orglen);
1099 }
1100 
CRC32()1101 uint32_t nsZipItem::CRC32()
1102 {
1103   return isSynthetic ? 0 : xtolong(central->crc32);
1104 }
1105 
Date()1106 uint16_t nsZipItem::Date()
1107 {
1108   return isSynthetic ? kSyntheticDate : xtoint(central->date);
1109 }
1110 
Time()1111 uint16_t nsZipItem::Time()
1112 {
1113   return isSynthetic ? kSyntheticTime : xtoint(central->time);
1114 }
1115 
Compression()1116 uint16_t nsZipItem::Compression()
1117 {
1118   return isSynthetic ? STORED : xtoint(central->method);
1119 }
1120 
IsDirectory()1121 bool nsZipItem::IsDirectory()
1122 {
1123   return isSynthetic || ((nameLength > 0) && ('/' == Name()[nameLength - 1]));
1124 }
1125 
Mode()1126 uint16_t nsZipItem::Mode()
1127 {
1128   if (isSynthetic) return 0755;
1129   return ((uint16_t)(central->external_attributes[2]) | 0x100);
1130 }
1131 
GetExtraField(uint16_t aTag,uint16_t * aBlockSize)1132 const uint8_t * nsZipItem::GetExtraField(uint16_t aTag, uint16_t *aBlockSize)
1133 {
1134   if (isSynthetic) return nullptr;
1135 MOZ_WIN_MEM_TRY_BEGIN
1136   const unsigned char *buf = ((const unsigned char*)central) + ZIPCENTRAL_SIZE +
1137                              nameLength;
1138   uint32_t buflen = (uint32_t)xtoint(central->extrafield_len);
1139   uint32_t pos = 0;
1140   uint16_t tag, blocksize;
1141 
1142   while (buf && (pos + 4) <= buflen) {
1143     tag = xtoint(buf + pos);
1144     blocksize = xtoint(buf + pos + 2);
1145 
1146     if (aTag == tag && (pos + 4 + blocksize) <= buflen) {
1147       *aBlockSize = blocksize;
1148       return buf + pos;
1149     }
1150 
1151     pos += blocksize + 4;
1152   }
1153 
1154 MOZ_WIN_MEM_TRY_CATCH(return nullptr)
1155   return nullptr;
1156 }
1157 
1158 
LastModTime()1159 PRTime nsZipItem::LastModTime()
1160 {
1161   if (isSynthetic) return GetModTime(kSyntheticDate, kSyntheticTime);
1162 
1163   // Try to read timestamp from extra field
1164   uint16_t blocksize;
1165   const uint8_t *tsField = GetExtraField(EXTENDED_TIMESTAMP_FIELD, &blocksize);
1166   if (tsField && blocksize >= 5 && tsField[4] & EXTENDED_TIMESTAMP_MODTIME) {
1167     return (PRTime)(xtolong(tsField + 5)) * PR_USEC_PER_SEC;
1168   }
1169 
1170   return GetModTime(Date(), Time());
1171 }
1172 
1173 #ifdef XP_UNIX
IsSymlink()1174 bool nsZipItem::IsSymlink()
1175 {
1176   if (isSynthetic) return false;
1177   return (xtoint(central->external_attributes+2) & S_IFMT) == S_IFLNK;
1178 }
1179 #endif
1180 
nsZipCursor(nsZipItem * item,nsZipArchive * aZip,uint8_t * aBuf,uint32_t aBufSize,bool doCRC)1181 nsZipCursor::nsZipCursor(nsZipItem *item, nsZipArchive *aZip, uint8_t* aBuf,
1182                          uint32_t aBufSize, bool doCRC)
1183   : mItem(item)
1184   , mBuf(aBuf)
1185   , mBufSize(aBufSize)
1186   , mCRC(0)
1187   , mDoCRC(doCRC)
1188 {
1189   if (mItem->Compression() == DEFLATED) {
1190 #ifdef DEBUG
1191     nsresult status =
1192 #endif
1193       gZlibInit(&mZs);
1194     NS_ASSERTION(status == NS_OK, "Zlib failed to initialize");
1195     NS_ASSERTION(aBuf, "Must pass in a buffer for DEFLATED nsZipItem");
1196   }
1197 
1198   mZs.avail_in = item->Size();
1199   mZs.next_in = (Bytef*)aZip->GetData(item);
1200 
1201   if (doCRC)
1202     mCRC = crc32(0L, Z_NULL, 0);
1203 }
1204 
~nsZipCursor()1205 nsZipCursor::~nsZipCursor()
1206 {
1207   if (mItem->Compression() == DEFLATED) {
1208     inflateEnd(&mZs);
1209   }
1210 }
1211 
ReadOrCopy(uint32_t * aBytesRead,bool aCopy)1212 uint8_t* nsZipCursor::ReadOrCopy(uint32_t *aBytesRead, bool aCopy) {
1213   int zerr;
1214   uint8_t *buf = nullptr;
1215   bool verifyCRC = true;
1216 
1217   if (!mZs.next_in)
1218     return nullptr;
1219 MOZ_WIN_MEM_TRY_BEGIN
1220   switch (mItem->Compression()) {
1221   case STORED:
1222     if (!aCopy) {
1223       *aBytesRead = mZs.avail_in;
1224       buf = mZs.next_in;
1225       mZs.next_in += mZs.avail_in;
1226       mZs.avail_in = 0;
1227     } else {
1228       *aBytesRead = mZs.avail_in > mBufSize ? mBufSize : mZs.avail_in;
1229       memcpy(mBuf, mZs.next_in, *aBytesRead);
1230       mZs.avail_in -= *aBytesRead;
1231       mZs.next_in += *aBytesRead;
1232     }
1233     break;
1234   case DEFLATED:
1235     buf = mBuf;
1236     mZs.next_out = buf;
1237     mZs.avail_out = mBufSize;
1238 
1239     zerr = inflate(&mZs, Z_PARTIAL_FLUSH);
1240     if (zerr != Z_OK && zerr != Z_STREAM_END)
1241       return nullptr;
1242 
1243     *aBytesRead = mZs.next_out - buf;
1244     verifyCRC = (zerr == Z_STREAM_END);
1245     break;
1246   default:
1247     return nullptr;
1248   }
1249 
1250   if (mDoCRC) {
1251     mCRC = crc32(mCRC, (const unsigned char*)buf, *aBytesRead);
1252     if (verifyCRC && mCRC != mItem->CRC32())
1253       return nullptr;
1254   }
1255 MOZ_WIN_MEM_TRY_CATCH(return nullptr)
1256   return buf;
1257 }
1258 
nsZipItemPtr_base(nsZipArchive * aZip,const char * aEntryName,bool doCRC)1259 nsZipItemPtr_base::nsZipItemPtr_base(nsZipArchive *aZip,
1260                                      const char * aEntryName, bool doCRC)
1261   : mReturnBuf(nullptr)
1262   , mReadlen(0)
1263 {
1264   // make sure the ziparchive hangs around
1265   mZipHandle = aZip->GetFD();
1266 
1267   nsZipItem* item = aZip->GetItem(aEntryName);
1268   if (!item)
1269     return;
1270 
1271   uint32_t size = 0;
1272   if (item->Compression() == DEFLATED) {
1273     size = item->RealSize();
1274     mAutoBuf = MakeUniqueFallible<uint8_t[]>(size);
1275     if (!mAutoBuf) {
1276       return;
1277     }
1278   }
1279 
1280   nsZipCursor cursor(item, aZip, mAutoBuf.get(), size, doCRC);
1281   mReturnBuf = cursor.Read(&mReadlen);
1282   if (!mReturnBuf) {
1283     return;
1284   }
1285 
1286   if (mReadlen != item->RealSize()) {
1287     NS_ASSERTION(mReadlen == item->RealSize(), "nsZipCursor underflow");
1288     mReturnBuf = nullptr;
1289     return;
1290   }
1291 }
1292 
1293 /* static */ const char*
1294 nsZipArchive::sFileCorruptedReason = nullptr;
1295