Set version to 3.0.9.
[privoxy.git] / deanimate.c
index 637743b..4e4aaac 100644 (file)
@@ -1,16 +1,19 @@
-const char deanimate_rcs[] = "$Id: deanimate.c,v 1.3 2001/07/15 13:57:50 jongfoster Exp $";
+const char deanimate_rcs[] = "$Id: deanimate.c,v 1.16 2007/07/14 08:01:58 fabiankeil Exp $";
 /*********************************************************************
  *
  * File        :  $Source: /cvsroot/ijbswa/current/deanimate.c,v $
  *
- * Purpose     :  Declares functions to deanimate GIF images on the fly.
+ * Purpose     :  Declares functions to manipulate binary images on the
+ *                fly.  High-level functions include:
+ *                  - Deanimation of GIF images
+ *                  - Fixup of malformed comment block in JPEG headers
  *                
  *                Functions declared include: gif_deanimate, buf_free,
- *                buf_copy,  buf_getbyte, gif_skip_data_block, and
- *                gif_extract_image
+ *                buf_copy,  buf_getbyte, gif_skip_data_block,
+ *                gif_extract_image and jpeg_inspect
  *
- * Copyright   :  Written by and Copyright (C) 2001 by the the SourceForge
- *                IJBSWA team.  http://ijbswa.sourceforge.net
+ * Copyright   :  Written by and Copyright (C) 2001 - 2004, 2006 by the
+ *                SourceForge Privoxy team. http://www.privoxy.org/
  *
  *                Based on the GIF file format specification (see
  *                http://tronche.com/computer-graphics/gif/gif89a.html)
@@ -37,6 +40,57 @@ const char deanimate_rcs[] = "$Id: deanimate.c,v 1.3 2001/07/15 13:57:50 jongfos
  *
  * Revisions   :
  *    $Log: deanimate.c,v $
+ *    Revision 1.16  2007/07/14 08:01:58  fabiankeil
+ *    s@failiure@failure@
+ *
+ *    Revision 1.15  2007/01/03 14:39:19  fabiankeil
+ *    Fix a gcc43 warning and mark the binbuffer
+ *    as immutable for buf_getbyte().
+ *
+ *    Revision 1.14  2006/07/18 14:48:45  david__schmidt
+ *    Reorganizing the repository: swapping out what was HEAD (the old 3.1 branch)
+ *    with what was really the latest development (the v_3_0_branch branch)
+ *
+ *    Revision 1.12.2.1  2004/10/03 12:53:32  david__schmidt
+ *    Add the ability to check jpeg images for invalid
+ *    lengths of comment blocks.  Defensive strategy
+ *    against the exploit:
+ *       Microsoft Security Bulletin MS04-028
+ *       Buffer Overrun in JPEG Processing (GDI+) Could
+ *       Allow Code Execution (833987)
+ *    Enabled with +inspect-jpegs in actions files.
+ *
+ *    Revision 1.12  2002/05/12 21:36:29  jongfoster
+ *    Correcting function comments
+ *
+ *    Revision 1.11  2002/03/26 22:29:54  swa
+ *    we have a new homepage!
+ *
+ *    Revision 1.10  2002/03/24 13:25:43  swa
+ *    name change related issues
+ *
+ *    Revision 1.9  2002/03/13 00:27:04  jongfoster
+ *    Killing warnings
+ *
+ *    Revision 1.8  2002/03/09 19:42:47  jongfoster
+ *    Fixing more warnings
+ *
+ *    Revision 1.7  2002/03/08 17:46:04  jongfoster
+ *    Fixing int/size_t warnings
+ *
+ *    Revision 1.6  2002/03/07 03:46:17  oes
+ *    Fixed compiler warnings
+ *
+ *    Revision 1.5  2001/09/10 10:16:06  oes
+ *    Silenced compiler warnings
+ *
+ *    Revision 1.4  2001/07/18 12:28:49  oes
+ *    - Added feature for extracting the first frame
+ *      to gif_deanimate
+ *    - Separated image buffer extension into buf_extend
+ *    - Extended gif deanimation to GIF87a (untested!)
+ *    - Cosmetics
+ *
  *    Revision 1.3  2001/07/15 13:57:50  jongfoster
  *    Adding #includes string.h and miscutil.h
  *
@@ -52,6 +106,7 @@ const char deanimate_rcs[] = "$Id: deanimate.c,v 1.3 2001/07/15 13:57:50 jongfos
 #include <string.h>
 #include <fcntl.h>
 
+#include "errlog.h"
 #include "project.h"
 #include "deanimate.h"
 #include "miscutil.h"
@@ -90,7 +145,7 @@ void buf_free(struct binbuffer *buf)
  *
  * Description :  Ensure that a given binbuffer can hold a given amount
  *                of bytes, by reallocating its buffer if necessary.
- *                Allocate new mem in chunks of 1000 bytes, so we don't
+ *                Allocate new mem in chunks of 1024 bytes, so we don't
  *                have to realloc() too often.
  *
  * Parameters  :
@@ -98,16 +153,16 @@ void buf_free(struct binbuffer *buf)
  *          2  :  length = Desired minimum size
  *                
  *
- * Returns     :  0 on success, 1 on failiure.
+ * Returns     :  0 on success, 1 on failure.
  *
  *********************************************************************/
-int buf_extend(struct binbuffer *buf, int length)
+static int buf_extend(struct binbuffer *buf, size_t length)
 {
    char *newbuf;
 
    if (buf->offset + length > buf->size)
    {
-      buf->size = buf->size + length + 1000 - (buf->size + length) % 1000;
+      buf->size = ((buf->size + length + (size_t)1023) & ~(size_t)1023);
       newbuf = (char *)realloc(buf->buffer, buf->size);
 
       if (newbuf == NULL)
@@ -139,10 +194,10 @@ int buf_extend(struct binbuffer *buf, int length)
  *          2  :  dst = Pointer to the destination binbuffer
  *          3  :  length = Number of bytes to be copied
  *
- * Returns     :  0 on success, 1 on failiure.
+ * Returns     :  0 on success, 1 on failure.
  *
  *********************************************************************/
-int buf_copy(struct binbuffer *src, struct binbuffer *dst, int length)
+static int buf_copy(struct binbuffer *src, struct binbuffer *dst, size_t length)
 {
 
    /*
@@ -182,13 +237,13 @@ int buf_copy(struct binbuffer *src, struct binbuffer *dst, int length)
  *                given offset
  *
  * Parameters  :
- *          1  :  buf = Pointer to the source binbuffer
+ *          1  :  src = Pointer to the source binbuffer
  *          2  :  offset = Offset to the desired byte
  *
- * Returns     :  The byte on success, or 0 on failiure
+ * Returns     :  The byte on success, or 0 on failure
  *
  *********************************************************************/
-unsigned char buf_getbyte(struct binbuffer *src, int offset)
+static unsigned char buf_getbyte(const struct binbuffer *src, size_t offset)
 {
    if (src->offset + offset < src->size)
    {
@@ -214,10 +269,10 @@ unsigned char buf_getbyte(struct binbuffer *src, int offset)
  * Parameters  :
  *          1  :  buf = Pointer to the binbuffer
  *
- * Returns     :  0 on success, or 1 on failiure
+ * Returns     :  0 on success, or 1 on failure
  *
  *********************************************************************/
-int gif_skip_data_block(struct binbuffer *buf)
+static int gif_skip_data_block(struct binbuffer *buf)
 {
    unsigned char c;
 
@@ -226,9 +281,10 @@ int gif_skip_data_block(struct binbuffer *buf)
     * by a one-byte length field, with the last chunk having
     * zero length.
     */
-   while(c = buf_getbyte(buf, 0))
+   while((c = buf_getbyte(buf, 0)) != '\0')
    {
-      if ((buf->offset += c + 1) >= buf->size - 1)
+      buf->offset += (size_t)c + 1;
+      if (buf->offset >= buf->size - 1)
       {
          return 1;
       }
@@ -253,13 +309,13 @@ int gif_skip_data_block(struct binbuffer *buf)
  *          1  :  src = Pointer to the source binbuffer
  *          2  :  dst = Pointer to the destination binbuffer
  *
- * Returns     :  0 on success, or 1 on failiure
+ * Returns     :  0 on success, or 1 on failure
  *
  *********************************************************************/
-int gif_extract_image(struct binbuffer *src, struct binbuffer *dst)
+static int gif_extract_image(struct binbuffer *src, struct binbuffer *dst)
 {
    unsigned char c;
-   
+
    /*
     * Remember the colormap flag and copy the image head
     */
@@ -274,7 +330,7 @@ int gif_extract_image(struct binbuffer *src, struct binbuffer *dst)
     */
    if (c & 0x80)
    {
-      if (buf_copy(src, dst, 3 * (1 << ((c & 0x07) + 1))))
+      if (buf_copy(src, dst, (size_t) 3 * (1 << ((c & 0x07) + 1))))
       {
          return 1;
       }           
@@ -284,9 +340,9 @@ int gif_extract_image(struct binbuffer *src, struct binbuffer *dst)
    /*
     * Copy the image chunk by chunk.
     */
-   while(c = buf_getbyte(src, 0))
+   while((c = buf_getbyte(src, 0)) != '\0')
    {
-      if (buf_copy(src, dst, c + 1)) return 1;
+      if (buf_copy(src, dst, 1 + (size_t) c)) return 1;
    }
    if (buf_copy(src, dst, 1)) return 1;
 
@@ -318,7 +374,7 @@ int gif_extract_image(struct binbuffer *src, struct binbuffer *dst)
  *          3  :  get_first_image = Flag: If set, get the first image
  *                                        If unset (default), get the last
  *
- * Returns     :  0 on success, or 1 on failiure
+ * Returns     :  0 on success, or 1 on failure
  *
  *********************************************************************/
 int gif_deanimate(struct binbuffer *src, struct binbuffer *dst, int get_first_image)
@@ -353,7 +409,7 @@ int gif_deanimate(struct binbuffer *src, struct binbuffer *dst, int get_first_im
     */
    if(c & 0x80)
    {
-      if (buf_copy(src, dst, 3 * (1 << ((c & 0x07) + 1))))
+      if (buf_copy(src, dst, (size_t) 3 * (1 << ((c & 0x07) + 1))))
       {
          return 1;
       }
@@ -469,6 +525,113 @@ write:
 }
 
 
+/*********************************************************************
+ * 
+ * Function    :  jpeg_inspect
+ *
+ * Description :  Checks a jpeg image for an invalid length in a 
+ *                comment block (0xFFFE0000 or 0xFFFE0001) and
+ *                changes it to 0xFFFE0002.  Defensive strategy 
+ *                against the exploit:
+ *                  Microsoft Security Bulletin MS04-028
+ *                  Buffer Overrun in JPEG Processing (GDI+) Could
+ *                  Allow Code Execution (833987)
+ *
+ * Parameters  :
+ *          1  :  src = Pointer to the image binbuffer
+ *
+ * Returns     :  0 on success, or 1 on failure
+ *
+ *********************************************************************/
+int jpeg_inspect(struct binbuffer *src, struct binbuffer *dst)
+{
+   long i;
+   /*
+    * We process the image using a simple finite state machine, 
+    * searching for byte patterns.
+    */
+   enum { J_INIT, /* The initial state */
+          J_FF,   /* Found byte 0xFF */
+          J_FE,   /* Found bytes 0xFF 0xFE */
+          J_00,   /* Found bytes 0xFF 0xFE 0x00 */
+          J_DA    /*
+                   * Found bytes 0xFF 0xDA; short-circuit to done-ness
+                   * since this signals the beginning end of headers.
+                   */
+        };
+   short state = J_INIT;
+   unsigned char c;
+
+   if (NULL == src || NULL == dst)
+   {
+      return 1;
+   }
+
+   if (buf_copy(src, dst, src->size))
+   {
+      return 1;
+   }
+
+   /* Need to search the jpg for patterns:
+    * 0xFF 0xFE 0x00 0x00
+    * or 
+    * 0xFF 0xFE 0x00 0x01
+    * from beginning until:
+    * 0xFF 0xDA
+    * (or the end of the buffer)
+    * If found, change the pattern to 0xFF 0xFE 0x00 0x02
+    */
+
+   for (i = 0; i < dst->size; i++)
+   {
+      c = dst->buffer[i];
+      switch (state)
+      {
+         case J_INIT:
+            if (c == 0xFF)
+               state = J_FF;
+            break;
+         case J_FF:
+            if (c == 0xDA)
+               state = J_DA; /* End of headers - we're done with this image. */
+            else if (c == 0xFE)
+               state = J_FE;
+            else
+               state = J_INIT;
+            break;
+         case J_FE:
+            if (c == 0x00)
+               state = J_00;
+            else
+               state = J_INIT;
+            break;
+         case J_00:
+            if ((c == 0x00) || (c == 0x01))
+            {
+               dst->buffer[i] = 2; /* Reset comment block size to 2. */
+               log_error(LOG_LEVEL_INFO, "JPEG comment exploit removed.");
+               /* TODO:
+                * I'm unsure if we can have more than one comment block.  Just in case,
+                * we'll scan the rest of the header for more by going back to J_INIT
+                * state.  If there is no possibility of >1 comment block, we could 
+                * short-circuit to done-ness here.
+                */
+               state = J_INIT;
+            }
+            else
+               state = J_INIT;
+            break;
+         default:
+            break;
+      }
+      if (state == J_DA)
+         break;
+   }
+
+   return 0;
+}
+
+
 /*
   Local Variables:
   tab-width: 3