libextractor

GNU libextractor
Log | Files | Refs | Submodules | README | LICENSE

deb_extractor.c (13041B)


      1 /*
      2      This file is part of libextractor.
      3      Copyright (C) 2002, 2003, 2004, 2012 Vidyut Samanta and Christian Grothoff
      4 
      5      libextractor is free software; you can redistribute it and/or modify
      6      it under the terms of the GNU General Public License as published
      7      by the Free Software Foundation; either version 3, or (at your
      8      option) any later version.
      9 
     10      libextractor is distributed in the hope that it will be useful, but
     11      WITHOUT ANY WARRANTY; without even the implied warranty of
     12      MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
     13      General Public License for more details.
     14 
     15      You should have received a copy of the GNU General Public License
     16      along with libextractor; see the file COPYING.  If not, write to the
     17      Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
     18      Boston, MA 02110-1301, USA.
     19  */
     20 /**
     21  * @file plugins/deb_extractor.c
     22  * @brief plugin to support Debian archives
     23  * @author Christian Grothoff
     24  *
     25  * The .deb is an ar-chive file.  It contains a tar.gz file
     26  * named "control.tar.gz" which then contains a file 'control'
     27  * that has the meta-data.  And which variant of the various
     28  * ar file formats is used is also not quite certain. Yuck.
     29  *
     30  * References:
     31  * http://www.mkssoftware.com/docs/man4/tar.4.asp
     32  * http://lists.debian.org/debian-policy/2003/12/msg00000.html
     33  * http://www.opengroup.org/onlinepubs/009695399/utilities/ar.html
     34  */
     35 #include "platform.h"
     36 #include "extractor.h"
     37 #include <zlib.h>
     38 
     39 
     40 /**
     41  * Maximum file size we allow for control.tar.gz files.
     42  * This is a sanity check to avoid allocating huge amounts
     43  * of memory.
     44  */
     45 #define MAX_CONTROL_SIZE (1024 * 1024)
     46 
     47 
     48 /**
     49  * Re-implementation of 'strndup'.
     50  *
     51  * @param str string to duplicate
     52  * @param n maximum number of bytes to copy
     53  * @return NULL on error, otherwise 0-terminated copy of 'str'
     54  *         with at most n characters
     55  */
     56 static char *
     57 stndup (const char *str, size_t n)
     58 {
     59   char *tmp;
     60 
     61   if (NULL == (tmp = malloc (n + 1)))
     62     return NULL;
     63   tmp[n] = '\0';
     64   memcpy (tmp, str, n);
     65   return tmp;
     66 }
     67 
     68 
     69 /**
     70  * Entry in the mapping from control data to LE types.
     71  */
     72 struct Matches
     73 {
     74   /**
     75    * Key in the Debian control file.
     76    */
     77   const char *text;
     78 
     79   /**
     80    * Corresponding type in LE.
     81    */
     82   enum EXTRACTOR_MetaType type;
     83 };
     84 
     85 
     86 /**
     87  * Map from deb-control entries to LE types.
     88  *
     89  * see also: "man 5 deb-control"
     90  */
     91 static struct Matches tmap[] = {
     92   {"Package: ",       EXTRACTOR_METATYPE_PACKAGE_NAME},
     93   {"Version: ",       EXTRACTOR_METATYPE_PACKAGE_VERSION},
     94   {"Section: ",       EXTRACTOR_METATYPE_SECTION},
     95   {"Priority: ",      EXTRACTOR_METATYPE_UPLOAD_PRIORITY},
     96   {"Architecture: ",  EXTRACTOR_METATYPE_TARGET_ARCHITECTURE},
     97   {"Depends: ",       EXTRACTOR_METATYPE_PACKAGE_DEPENDENCY},
     98   {"Recommends: ",    EXTRACTOR_METATYPE_PACKAGE_RECOMMENDS},
     99   {"Suggests: ",      EXTRACTOR_METATYPE_PACKAGE_SUGGESTS},
    100   {"Installed-Size: ",EXTRACTOR_METATYPE_PACKAGE_INSTALLED_SIZE},
    101   {"Maintainer: ",    EXTRACTOR_METATYPE_PACKAGE_MAINTAINER},
    102   {"Description: ",   EXTRACTOR_METATYPE_DESCRIPTION},
    103   {"Source: ",        EXTRACTOR_METATYPE_PACKAGE_SOURCE},
    104   {"Pre-Depends: ",   EXTRACTOR_METATYPE_PACKAGE_PRE_DEPENDENCY},
    105   {"Conflicts: ",     EXTRACTOR_METATYPE_PACKAGE_CONFLICTS},
    106   {"Replaces: ",      EXTRACTOR_METATYPE_PACKAGE_REPLACES},
    107   {"Provides: ",      EXTRACTOR_METATYPE_PACKAGE_PROVIDES},
    108   {"Essential: ",     EXTRACTOR_METATYPE_PACKAGE_ESSENTIAL},
    109   {NULL, 0}
    110 };
    111 
    112 
    113 /**
    114  * Process the "control" file from the control.tar.gz
    115  *
    116  * @param data decompressed control data
    117  * @param size number of bytes in data
    118  * @param proc function to call with meta data
    119  * @param proc_cls closure for 'proc'
    120  * @return 0 to continue extracting, 1 if we are done
    121  */
    122 static int
    123 processControl (const char *data,
    124                 const size_t size,
    125                 EXTRACTOR_MetaDataProcessor proc,
    126                 void *proc_cls)
    127 {
    128   size_t pos;
    129   char *key;
    130   char *val;
    131   size_t colon;
    132   size_t eol;
    133   unsigned int i;
    134 
    135   pos = 0;
    136   while (pos < size)
    137   {
    138     for (colon = pos; colon < size; colon++)
    139       if ((':' == data[colon]) || ('\n' == data[colon]))
    140         break;
    141     if ((colon >= size) || (':' != data[colon]))
    142       return 0;
    143     colon++;
    144     while ((colon < size) && (isspace ((unsigned char) data[colon])))
    145       colon++;
    146     eol = colon;
    147     while ((eol < size) &&
    148            (('\n' != data[eol]) ||
    149             ((eol + 1 < size) && (' '  == data[eol + 1]))))
    150       eol++;
    151     if ((eol == colon) || (eol > size))
    152       return 0;
    153     if (NULL == (key = stndup (&data[pos], colon - pos)))
    154       return 0;
    155     for (i = 0; NULL != tmap[i].text; i++)
    156     {
    157       if (0 != strcmp (key, tmap[i].text))
    158         continue;
    159       if (NULL == (val = stndup (&data[colon], eol - colon)))
    160       {
    161         free (key);
    162         return 0;
    163       }
    164       if (0 != proc (proc_cls,
    165                      "deb",
    166                      tmap[i].type,
    167                      EXTRACTOR_METAFORMAT_UTF8,
    168                      "text/plain",
    169                      val,
    170                      strlen (val) + 1))
    171       {
    172         free (val);
    173         free (key);
    174         return 1;
    175       }
    176       free (val);
    177       break;
    178     }
    179     free (key);
    180     pos = eol + 1;
    181   }
    182   return 0;
    183 }
    184 
    185 
    186 /**
    187  * Header of an entry in a TAR file.
    188  */
    189 struct TarHeader
    190 {
    191   /**
    192    * Filename.
    193    */
    194   char name[100];
    195 
    196   /**
    197    * File access modes.
    198    */
    199   char mode[8];
    200 
    201   /**
    202    * Owner of the file.
    203    */
    204   char userId[8];
    205 
    206   /**
    207    * Group of the file.
    208    */
    209   char groupId[8];
    210 
    211   /**
    212    * Size of the file, in octal.
    213    */
    214   char filesize[12];
    215 
    216   /**
    217    * Last modification time.
    218    */
    219   char lastModTime[12];
    220 
    221   /**
    222    * Checksum of the file.
    223    */
    224   char chksum[8];
    225 
    226   /**
    227    * Is the file a link?
    228    */
    229   char link;
    230 
    231   /**
    232    * Destination of the link.
    233    */
    234   char linkName[100];
    235 };
    236 
    237 
    238 /**
    239  * Extended TAR header for USTar format.
    240  */
    241 struct USTarHeader
    242 {
    243   /**
    244    * Original TAR header.
    245    */
    246   struct TarHeader tar;
    247 
    248   /**
    249    * Additinal magic for USTar.
    250    */
    251   char magic[6];
    252 
    253   /**
    254    * Format version.
    255    */
    256   char version[2];
    257 
    258   /**
    259    * User name.
    260    */
    261   char uname[32];
    262 
    263   /**
    264    * Group name.
    265    */
    266   char gname[32];
    267 
    268   /**
    269    * Device major number.
    270    */
    271   char devmajor[8];
    272 
    273   /**
    274    * Device minor number.
    275    */
    276   char devminor[8];
    277 
    278   /**
    279    * Unknown (padding?).
    280    */
    281   char prefix[155];
    282 };
    283 
    284 
    285 /**
    286  * Process the control.tar file.
    287  *
    288  * @param data the deflated control.tar file data
    289  * @param size number of bytes in data
    290  * @param proc function to call with meta data
    291  * @param proc_cls closure for 'proc'
    292  * @return 0 to continue extracting, 1 if we are done
    293  */
    294 static int
    295 processControlTar (const char *data,
    296                    size_t size,
    297                    EXTRACTOR_MetaDataProcessor proc,
    298                    void *proc_cls)
    299 {
    300   struct TarHeader *tar;
    301   struct USTarHeader *ustar;
    302   size_t pos;
    303 
    304   pos = 0;
    305   while (pos + sizeof (struct TarHeader) < size)
    306   {
    307     unsigned long long fsize;
    308     char buf[13];
    309 
    310     tar = (struct TarHeader *) &data[pos];
    311     if (pos + sizeof (struct USTarHeader) < size)
    312     {
    313       ustar = (struct USTarHeader *) &data[pos];
    314       if (0 == strncmp ("ustar", &ustar->magic[0], strlen ("ustar")))
    315         pos += 512;             /* sizeof (struct USTarHeader); */
    316       else
    317         pos += 257;             /* sizeof (struct TarHeader); minus gcc alignment... */
    318     }
    319     else
    320     {
    321       pos += 257;               /* sizeof (struct TarHeader); minus gcc alignment... */
    322     }
    323 
    324     memcpy (buf, &tar->filesize[0], 12);
    325     buf[12] = '\0';
    326     if (1 != sscanf (buf, "%12llo", &fsize))    /* octal! Yuck yuck! */
    327       return 0;
    328     if ((pos + fsize > size) || (fsize > size) || (pos + fsize < pos))
    329       return 0;
    330 
    331     if (0 == strncmp (&tar->name[0], "./control", strlen ("./control")))
    332     {
    333       /* found the 'control' file we were looking for */
    334       return processControl (&data[pos], fsize, proc, proc_cls);
    335     }
    336     if (0 != (fsize & 511))
    337       fsize = (fsize | 511) + 1;        /* round up! */
    338     if (pos + fsize < pos)
    339       return 0;
    340     pos += fsize;
    341   }
    342   return 0;
    343 }
    344 
    345 
    346 /**
    347  * Process the control.tar.gz file.
    348  *
    349  * @param ec extractor context with control.tar.gz at current read position
    350  * @param size number of bytes in the control file
    351  * @return 0 to continue extracting, 1 if we are done
    352  */
    353 static int
    354 processControlTGZ (struct EXTRACTOR_ExtractContext *ec,
    355                    unsigned long long size)
    356 {
    357   uint32_t bufSize;
    358   char *buf;
    359   void *data;
    360   unsigned char *cdata;
    361   z_stream strm;
    362   int ret;
    363   ssize_t sret;
    364   unsigned long long off;
    365 
    366   if (size > MAX_CONTROL_SIZE)
    367     return 0;
    368   if (0 == size)
    369     return 0;
    370   if (size < 4)
    371     return 0;
    372   if (NULL == (cdata = malloc (size)))
    373     return 0;
    374   off = 0;
    375   while (off < size)
    376   {
    377     if (0 >= (sret = ec->read (ec->cls,
    378                                &data,
    379                                size - off)))
    380     {
    381       free (cdata);
    382       return 0;
    383     }
    384     memcpy (&cdata[off],
    385             data,
    386             sret);
    387     off += sret;
    388   }
    389   /* The top byte must be widened before it is shifted: `unsigned char'
    390      promotes to `int', and 0x80 << 24 overflows a 32 bit int. */
    391   bufSize = ((uint32_t) cdata[size - 4])
    392             + (((uint32_t) cdata[size - 3]) << 8)
    393             + (((uint32_t) cdata[size - 2]) << 16)
    394             + (((uint32_t) cdata[size - 1]) << 24);
    395   if (bufSize > MAX_CONTROL_SIZE)
    396   {
    397     free (cdata);
    398     return 0;
    399   }
    400   if (NULL == (buf = malloc (bufSize)))
    401   {
    402     free (cdata);
    403     return 0;
    404   }
    405   ret = 0;
    406   memset (&strm,
    407           0,
    408           sizeof (z_stream));
    409   strm.next_in = (Bytef *) cdata;
    410   strm.avail_in = size;
    411   if (Z_OK ==
    412       inflateInit2 (&strm,
    413                     15 + 32))
    414   {
    415     strm.next_out = (Bytef *) buf;
    416     strm.avail_out = bufSize;
    417     inflate (&strm,
    418              Z_FINISH);
    419     if (strm.total_out > 0)
    420       ret = processControlTar (buf,
    421                                strm.total_out,
    422                                ec->proc,
    423                                ec->cls);
    424     inflateEnd (&strm);
    425   }
    426   free (buf);
    427   free (cdata);
    428   return ret;
    429 }
    430 
    431 
    432 /**
    433  * Header of an object in an "AR"chive file.
    434  */
    435 struct ObjectHeader
    436 {
    437   /**
    438    * Name of the file.
    439    */
    440   char name[16];
    441 
    442   /**
    443    * Last modification time for the file.
    444    */
    445   char lastModTime[12];
    446 
    447   /**
    448    * User ID of the owner.
    449    */
    450   char userId[6];
    451 
    452   /**
    453    * Group ID of the owner.
    454    */
    455   char groupId[6];
    456 
    457   /**
    458    * File access modes.
    459    */
    460   char modeInOctal[8];
    461 
    462   /**
    463    * Size of the file (as decimal string)
    464    */
    465   char filesize[10];
    466 
    467   /**
    468    * Tailer of the object header ("`\n")
    469    */
    470   char trailer[2];
    471 };
    472 
    473 
    474 /**
    475  * Main entry method for the DEB extraction plugin.
    476  *
    477  * @param ec extraction context provided to the plugin
    478  */
    479 void
    480 EXTRACTOR_deb_extract_method (struct EXTRACTOR_ExtractContext *ec);
    481 
    482 void
    483 EXTRACTOR_deb_extract_method (struct EXTRACTOR_ExtractContext *ec)
    484 {
    485   uint64_t pos;
    486   int done = 0;
    487   struct ObjectHeader hdrbuf;
    488   const struct ObjectHeader *hdr;
    489   uint64_t fsize;
    490   unsigned long long csize;
    491   char buf[11];
    492   void *data;
    493 
    494   fsize = ec->get_size (ec->cls);
    495   if (fsize < 128)
    496     return;
    497   if (8 !=
    498       ec->read (ec->cls, &data, 8))
    499     return;
    500   if (0 != strncmp ("!<arch>\n", data, 8))
    501     return;
    502   pos = 8;
    503   while (pos + sizeof (struct ObjectHeader) < fsize)
    504   {
    505     if (pos !=
    506         ec->seek (ec->cls, pos, SEEK_SET))
    507       return;
    508     if (sizeof (struct ObjectHeader) !=
    509         ec->read (ec->cls, &data, sizeof (struct ObjectHeader)))
    510       return;
    511     /* The pointer read() returns addresses the shared memory window and
    512        is only valid until the next read() or seek() slides it.
    513        processControlTGZ() below does both, so take a copy of the object
    514        header now instead of dereferencing it again afterwards. */
    515     memcpy (&hdrbuf, data, sizeof (hdrbuf));
    516     hdr = &hdrbuf;
    517     if (0 != strncmp (&hdr->trailer[0], "`\n", 2))
    518       return;
    519     memcpy (buf, &hdr->filesize[0], 10);
    520     buf[10] = '\0';
    521     if (1 != sscanf (buf, "%10llu", &csize))
    522       return;
    523     pos += sizeof (struct ObjectHeader);
    524     if ((pos + csize > fsize) || (csize > fsize) || (pos + csize < pos))
    525       return;
    526     if (0 == strncmp (&hdr->name[0],
    527                       "control.tar.gz",
    528                       strlen ("control.tar.gz")))
    529     {
    530       if (0 != processControlTGZ (ec,
    531                                   csize))
    532         return;
    533       done++;
    534     }
    535     if (0 == strncmp (&hdr->name[0],
    536                       "debian-binary", strlen ("debian-binary")))
    537     {
    538       if (0 != ec->proc (ec->cls,
    539                          "deb",
    540                          EXTRACTOR_METATYPE_MIMETYPE,
    541                          EXTRACTOR_METAFORMAT_UTF8,
    542                          "text/plain",
    543                          "application/x-debian-package",
    544                          strlen ("application/x-debian-package") + 1))
    545         return;
    546       done++;
    547     }
    548     pos += csize;
    549     if (2 == done)
    550       break;                    /* no need to process the rest of the archive */
    551   }
    552 }
    553 
    554 
    555 /* end of deb_extractor.c */