Browse code

libclamav: add PNG validator based on pngcheck

Tomasz Kojm authored on 2011/04/08 01:19:19
Showing 8 changed files
... ...
@@ -1,3 +1,7 @@
1
+Thu Apr  7 18:17:56 CEST 2011 (tk)
2
+----------------------------------
3
+ * libclamav: add PNG validator based on pngcheck
4
+
1 5
 Tue Apr  5 16:33:15 CEST 2011 (tk)
2 6
 ----------------------------------
3 7
  * libclamav: add basic JPEG validator
... ...
@@ -366,7 +366,9 @@ libclamav_la_SOURCES = \
366 366
 	swf.c \
367 367
 	swf.h \
368 368
 	jpeg.c \
369
-	jpeg.h
369
+	jpeg.h \
370
+	png.c \
371
+	png.h
370 372
 
371 373
 if !LINK_TOMMATH
372 374
 libclamav_la_SOURCES += bignum.c \
... ...
@@ -158,7 +158,7 @@ am__libclamav_la_SOURCES_DIST = clamav.h matcher-ac.c matcher-ac.h \
158 158
 	bytecode_api_decl.c bytecode_api.h bytecode_api_impl.h \
159 159
 	bytecode_hooks.h cache.c cache.h bytecode_detect.c \
160 160
 	bytecode_detect.h builtin_bytecodes.h events.c events.h swf.c \
161
-	swf.h jpeg.c jpeg.h bignum.c bignum_class.h
161
+	swf.h jpeg.c jpeg.h png.c png.h bignum.c bignum_class.h
162 162
 @LINK_TOMMATH_FALSE@am__objects_1 = libclamav_la-bignum.lo
163 163
 am_libclamav_la_OBJECTS = libclamav_la-matcher-ac.lo \
164 164
 	libclamav_la-matcher-bm.lo libclamav_la-matcher-hash.lo \
... ...
@@ -210,7 +210,8 @@ am_libclamav_la_OBJECTS = libclamav_la-matcher-ac.lo \
210 210
 	libclamav_la-ishield.lo libclamav_la-bytecode_api.lo \
211 211
 	libclamav_la-bytecode_api_decl.lo libclamav_la-cache.lo \
212 212
 	libclamav_la-bytecode_detect.lo libclamav_la-events.lo \
213
-	libclamav_la-swf.lo libclamav_la-jpeg.lo $(am__objects_1)
213
+	libclamav_la-swf.lo libclamav_la-jpeg.lo libclamav_la-png.lo \
214
+	$(am__objects_1)
214 215
 libclamav_la_OBJECTS = $(am_libclamav_la_OBJECTS)
215 216
 AM_V_lt = $(am__v_lt_$(V))
216 217
 am__v_lt_ = $(am__v_lt_$(AM_DEFAULT_VERBOSITY))
... ...
@@ -664,7 +665,7 @@ libclamav_la_SOURCES = clamav.h matcher-ac.c matcher-ac.h matcher-bm.c \
664 664
 	bytecode_api_decl.c bytecode_api.h bytecode_api_impl.h \
665 665
 	bytecode_hooks.h cache.c cache.h bytecode_detect.c \
666 666
 	bytecode_detect.h builtin_bytecodes.h events.c events.h swf.c \
667
-	swf.h jpeg.c jpeg.h $(am__append_7)
667
+	swf.h jpeg.c jpeg.h png.c png.h $(am__append_7)
668 668
 noinst_LTLIBRARIES = libclamav_internal_utils.la libclamav_internal_utils_nothreads.la libclamav_nocxx.la
669 669
 COMMON_CLEANFILES = version.h version.h.tmp *.gcda *.gcno
670 670
 @MAINTAINER_MODE_TRUE@BUILT_SOURCES = jsparse/generated/operators.h jsparse/generated/keywords.h jsparse-keywords.gperf
... ...
@@ -859,6 +860,7 @@ distclean-compile:
859 859
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libclamav_la-phish_domaincheck_db.Plo@am__quote@
860 860
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libclamav_la-phish_whitelist.Plo@am__quote@
861 861
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libclamav_la-phishcheck.Plo@am__quote@
862
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libclamav_la-png.Plo@am__quote@
862 863
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libclamav_la-readdb.Plo@am__quote@
863 864
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libclamav_la-rebuildpe.Plo@am__quote@
864 865
 @AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libclamav_la-regex_list.Plo@am__quote@
... ...
@@ -1746,6 +1748,14 @@ libclamav_la-jpeg.lo: jpeg.c
1746 1746
 @AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
1747 1747
 @am__fastdepCC_FALSE@	$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libclamav_la_CFLAGS) $(CFLAGS) -c -o libclamav_la-jpeg.lo `test -f 'jpeg.c' || echo '$(srcdir)/'`jpeg.c
1748 1748
 
1749
+libclamav_la-png.lo: png.c
1750
+@am__fastdepCC_TRUE@	$(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libclamav_la_CFLAGS) $(CFLAGS) -MT libclamav_la-png.lo -MD -MP -MF $(DEPDIR)/libclamav_la-png.Tpo -c -o libclamav_la-png.lo `test -f 'png.c' || echo '$(srcdir)/'`png.c
1751
+@am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/libclamav_la-png.Tpo $(DEPDIR)/libclamav_la-png.Plo
1752
+@am__fastdepCC_FALSE@	$(AM_V_CC) @AM_BACKSLASH@
1753
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	source='png.c' object='libclamav_la-png.lo' libtool=yes @AMDEPBACKSLASH@
1754
+@AMDEP_TRUE@@am__fastdepCC_FALSE@	DEPDIR=$(DEPDIR) $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
1755
+@am__fastdepCC_FALSE@	$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libclamav_la_CFLAGS) $(CFLAGS) -c -o libclamav_la-png.lo `test -f 'png.c' || echo '$(srcdir)/'`png.c
1756
+
1749 1757
 libclamav_la-bignum.lo: bignum.c
1750 1758
 @am__fastdepCC_TRUE@	$(AM_V_CC)$(LIBTOOL) $(AM_V_lt) --tag=CC $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libclamav_la_CFLAGS) $(CFLAGS) -MT libclamav_la-bignum.lo -MD -MP -MF $(DEPDIR)/libclamav_la-bignum.Tpo -c -o libclamav_la-bignum.lo `test -f 'bignum.c' || echo '$(srcdir)/'`bignum.c
1751 1759
 @am__fastdepCC_TRUE@	$(AM_V_at)$(am__mv) $(DEPDIR)/libclamav_la-bignum.Tpo $(DEPDIR)/libclamav_la-bignum.Plo
1752 1760
new file mode 100644
... ...
@@ -0,0 +1,1432 @@
0
+/*
1
+ *   Copyright 1995-2007 by Alexander Lehmann <lehmann@usa.net>,
2
+ *                          Andreas Dilger <adilger@enel.ucalgary.ca>,
3
+ *                          Glenn Randers-Pehrson <randeg@alum.rpi.edu>,
4
+ *                          Greg Roelofs <newt@pobox.com>,
5
+ *                          John Bowler <jbowler@acm.org>,
6
+ *                          Tom Lane <tgl@sss.pgh.pa.us>
7
+ *   Copyright (C) 2011 Sourcefire, Inc.
8
+ *   Maintainer: Tomasz Kojm <tkojm@clamav.net>
9
+ *
10
+ *   Permission to use, copy, modify, and distribute this software and its
11
+ *   documentation for any purpose and without fee is hereby granted, provided
12
+ *   that the above copyright notice appear in all copies and that both that
13
+ *   copyright notice and this permission notice appear in supporting
14
+ *   documentation.  This software is provided "as is" without express or
15
+ *   implied warranty.
16
+ *
17
+ */
18
+
19
+#if HAVE_CONFIG_H
20
+#include "clamav-config.h"
21
+#endif
22
+
23
+#include <stdio.h>
24
+#include <stdlib.h>
25
+#include <ctype.h>
26
+#include <string.h>
27
+#include <fcntl.h>
28
+#include <sys/types.h>
29
+#include <sys/stat.h>
30
+#ifdef  HAVE_UNISTD_H
31
+#include <unistd.h>
32
+#endif
33
+#include <zlib.h>
34
+
35
+#include "clamav.h"
36
+#include "others.h"
37
+#include "png.h"
38
+
39
+typedef unsigned char  uch;
40
+typedef unsigned short ush;
41
+typedef unsigned long  ulg;
42
+
43
+#define BS 32000 /* size of read block for CRC calculation (and zlib) */
44
+
45
+/* Mark's macros to extract big-endian short and long ints: */
46
+#define SH(p) ((ush)(uch)((p)[1]) | ((ush)(uch)((p)[0]) << 8))
47
+#define LG(p) ((ulg)(SH((p)+2)) | ((ulg)(SH(p)) << 16))
48
+
49
+#define isASCIIalpha(x)     (ascii_alpha_table[x] & 0x1)
50
+
51
+#define ANCILLARY(chunkID)  ((chunkID)[0] & 0x20)
52
+#define PRIVATE(chunkID)    ((chunkID)[1] & 0x20)
53
+#define RESERVED(chunkID)   ((chunkID)[2] & 0x20)
54
+#define SAFECOPY(chunkID)   ((chunkID)[3] & 0x20)
55
+#define CRITICAL(chunkID)   (!ANCILLARY(chunkID))
56
+#define PUBLIC(chunkID)     (!PRIVATE(chunkID))
57
+
58
+/* what the PNG, MNG and JNG magic numbers should be */
59
+static const uch good_PNG_magic[8] = {137, 80, 78, 71, 13, 10, 26, 10};
60
+static const uch good_MNG_magic[8] = {138, 77, 78, 71, 13, 10, 26, 10};
61
+static const uch good_JNG_magic[8] = {139, 74, 78, 71, 13, 10, 26, 10};
62
+
63
+/* GRR FIXME:  could merge all three of these into single table (bit fields) */
64
+
65
+/* GRR 20061203:  for "isalpha()" that works even on EBCDIC machines */
66
+static const uch ascii_alpha_table[256] = {
67
+  0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
68
+  0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
69
+  0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,
70
+  0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,0,0,0,0,
71
+  0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
72
+  0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
73
+  0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
74
+  0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 
75
+};
76
+
77
+/* GRR 20070707:  list of forbidden characters in various keywords */
78
+static const uch latin1_keyword_forbidden[256] = {
79
+  1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
80
+  0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
81
+  0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
82
+  0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,
83
+  1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
84
+  1,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
85
+  0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
86
+  0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 
87
+};
88
+
89
+/* GRR 20070707:  list of discouraged (control) characters in tEXt/zTXt text */
90
+static const uch latin1_text_discouraged[256] = {
91
+  1,1,1,1,1,1,1,1,1,1,0,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
92
+  0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
93
+  0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
94
+  0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,1,
95
+  1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,
96
+  0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
97
+  0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
98
+  0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0 
99
+};
100
+
101
+/* PNG stuff */
102
+
103
+static const char *png_type[] = {		/* IHDR, tRNS, BASI, summary */
104
+  "grayscale",
105
+  "INVALID",
106
+  "RGB",
107
+  "palette",
108
+  "grayscale+alpha",
109
+  "INVALID",
110
+  "RGB+alpha"
111
+};
112
+
113
+#define CRCCOMPL(c) c
114
+#define CRCINIT (0)
115
+#define update_crc crc32
116
+
117
+static ulg getlong(fmap_t *map, unsigned int *offset, const char *where)
118
+{
119
+  ulg res = 0;
120
+  int j;
121
+
122
+  for (j = 0; j < 4; ++j) {
123
+    unsigned char c;
124
+    if(fmap_readn(map, &c, *offset, sizeof(c)) != sizeof(c)) {
125
+      cli_dbgmsg("PNG: EOF(?) while reading %s\n", where);
126
+      return 0;
127
+    }
128
+    (*offset)++;
129
+    res <<= 8;
130
+    res |= c & 0xff;
131
+  }
132
+
133
+  return res;
134
+}
135
+
136
+static int keywordlen(uch *buf, int maxsize)
137
+{
138
+  int j = 0;
139
+
140
+  while (j < maxsize && buf[j])
141
+    ++j;
142
+
143
+  return j;
144
+}
145
+
146
+static const char *getmonth(int m)
147
+{
148
+  static const char *month[] = {
149
+    "Jan", "Feb", "Mar", "Apr", "May", "Jun",
150
+    "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"
151
+  };
152
+
153
+  return (m < 1 || m > 12)? "INVALID" : month[m-1];
154
+}
155
+
156
+/* GRR 20061203:  now EBCDIC-safe */
157
+static int check_chunk_name(char *chunk_name)
158
+{
159
+  if (isASCIIalpha((int)chunk_name[0]) && isASCIIalpha((int)chunk_name[1]) &&
160
+      isASCIIalpha((int)chunk_name[2]) && isASCIIalpha((int)chunk_name[3]))
161
+    return 0;
162
+
163
+  cli_dbgmsg("PNG: invalid chunk name\n");
164
+  return CL_EPARSE;  /* usually means we've "jumped the tracks": bail! */
165
+}
166
+
167
+/* GRR 20050724 */
168
+/* caller must do return CL_EPARSE based on return value (0 == OK) */
169
+/* keyword_name is "keyword" for most chunks, but it can instead be "name" or
170
+ * "identifier" or whatever makes sense for the chunk in question */
171
+static int check_keyword(uch *buffer, int maxsize, int *pKeylen)
172
+{
173
+  int j, prev_space = 0;
174
+  int keylen = keywordlen(buffer, maxsize);
175
+
176
+  if (pKeylen)
177
+    *pKeylen = keylen;
178
+
179
+  if (keylen == 0) {
180
+    cli_dbgmsg("PNG: zero length keyword\n");
181
+    return 1;
182
+  }
183
+
184
+  if (keylen > 79) {
185
+    cli_dbgmsg("PNG: keyword is longer than 79 characters\n");
186
+    return 2;
187
+  }
188
+
189
+  if (buffer[0] == ' ') {
190
+    cli_dbgmsg("PNG: keyword has leading space(s)\n");
191
+    return 3;
192
+  }
193
+
194
+  if (buffer[keylen - 1] == ' ') {
195
+    cli_dbgmsg("PNG: keyword has trailing space(s)\n");
196
+    return 4;
197
+  }
198
+
199
+  for (j = 0; j < keylen; ++j) {
200
+    if (buffer[j] == ' ') {
201
+      if (prev_space) {
202
+        cli_dbgmsg("PNG: keyword has consecutive spaces\n");
203
+        return 5;
204
+      }
205
+      prev_space = 1;
206
+    } else {
207
+      prev_space = 0;
208
+    }
209
+  }
210
+
211
+  for (j = 0; j < keylen; ++j) {
212
+    if (latin1_keyword_forbidden[buffer[j]]) {   /* [0,31] || [127,160] */
213
+      cli_dbgmsg("PNG: keyword has control character(s)\n");
214
+      return 6;
215
+    }
216
+  }
217
+  return 0;
218
+}
219
+
220
+/* GRR 20070707 */
221
+/* caller must do return CL_EPARSE based on return value (0 == OK) */
222
+static int check_text(uch *buffer, int maxsize)
223
+{
224
+  int j;
225
+
226
+  for (j = 0; j < maxsize; ++j) {
227
+    if (buffer[j] == 0) {
228
+      cli_dbgmsg("PNG: text contains NULL character(s)\n");
229
+      return 1;
230
+    } else if (latin1_text_discouraged[buffer[j]]) {
231
+      cli_dbgmsg("PNG: text has control character(s)\n");
232
+      return 1;
233
+    }
234
+  }
235
+  return 0;
236
+}
237
+
238
+/* GRR 20061203 (used only for sCAL) */
239
+static int check_ascii_float(uch *buffer, int len)
240
+{
241
+  uch *qq = buffer, *bufEnd = buffer + len;
242
+  int have_sign = 0, have_integer = 0, have_dot = 0, have_fraction = 0;
243
+  int have_E = 0, have_Esign = 0, have_exponent = 0, in_digits = 0;
244
+  int have_nonzero = 0;
245
+  int rc = 0;
246
+
247
+  for (qq = buffer;  qq < bufEnd && !rc;  ++qq) {
248
+    switch (*qq) {
249
+      case '+':
250
+      case '-':
251
+        if (qq == buffer) {
252
+          have_sign = 1;
253
+          in_digits = 0;
254
+        } else if (have_E && !have_Esign) {
255
+          have_Esign = 1;
256
+          in_digits = 0;
257
+        } else {
258
+          cli_dbgmsg("PNG: invalid sign character\n");
259
+          rc = 1;
260
+        }
261
+        break;
262
+
263
+      case '.':
264
+        if (!have_dot && !have_E) {
265
+          have_dot = 1;
266
+          in_digits = 0;
267
+        } else {
268
+          cli_dbgmsg("PNG: invalid decimal point\n");
269
+          rc = 2;
270
+        }
271
+        break;
272
+
273
+      case 'e':
274
+      case 'E':
275
+        if (have_integer || have_fraction) {
276
+          have_E = 1;
277
+          in_digits = 0;
278
+        } else {
279
+          cli_dbgmsg("PNG: invalid exponent before mantissa\n");
280
+          rc = 3;
281
+        }
282
+        break;
283
+
284
+      default:
285
+        if (*qq < '0' || *qq > '9') {
286
+          cli_dbgmsg("PNG: invalid character\n");
287
+          rc = 4;
288
+        } else if (in_digits) {
289
+          /* still in digits:  do nothing except check for non-zero digits */
290
+          if (!have_exponent && *qq != '0')
291
+            have_nonzero = 1;
292
+        } else if (!have_integer && !have_dot) {
293
+          have_integer = 1;
294
+          in_digits = 1;
295
+          if (*qq != '0')
296
+            have_nonzero = 1;
297
+        } else if (have_dot && !have_fraction) {
298
+          have_fraction = 1;
299
+          in_digits = 1;
300
+          if (*qq != '0')
301
+            have_nonzero = 1;
302
+        } else if (have_E && !have_exponent) {
303
+          have_exponent = 1;
304
+          in_digits = 1;
305
+        } else {
306
+          /* is this case possible? */
307
+          cli_dbgmsg("PNG: invalid digits\n");
308
+          rc = 5;
309
+        }
310
+        break;
311
+    }
312
+  }
313
+
314
+  /* must have either integer part or fractional part; all else is optional */
315
+  if (rc == 0 && !have_integer && !have_fraction) {
316
+    cli_dbgmsg("PNG: missing mantissa\n");
317
+    rc = 6;
318
+  }
319
+
320
+  /* non-exponent part must be non-zero (=> must have seen a non-zero digit) */
321
+  if (rc == 0 && !have_nonzero) {
322
+    cli_dbgmsg("PNG: invalid zero value(s)\n");
323
+    rc = 7;
324
+  }
325
+
326
+  return rc;
327
+}
328
+
329
+int cli_parsepng(cli_ctx *ctx)
330
+{
331
+  long sz;
332
+  uch magic[8];
333
+  char chunkid[5] = {'\0', '\0', '\0', '\0', '\0'};
334
+  int toread;
335
+  int c;
336
+  int have_IHDR = 0, have_IEND = 0;
337
+  int have_PLTE = 0;
338
+  int have_IDAT = 0, have_JDAT = 0, last_is_IDAT = 0, last_is_JDAT = 0;
339
+  int have_bKGD = 0, have_cHRM = 0, have_gAMA = 0, have_hIST = 0, have_iCCP = 0;
340
+  int have_oFFs = 0, have_pCAL = 0, have_pHYs = 0, have_sBIT = 0, have_sCAL = 0;
341
+  int have_sRGB = 0, have_sTER = 0, have_tIME = 0, have_tRNS = 0;
342
+  ulg zhead = 1;   /* 0x10000 indicates both zlib header bytes read */
343
+  ulg crc, filecrc;
344
+  long num_chunks = 0L;
345
+  long w = 0L, h = 0L;
346
+  int bitdepth = 0, sampledepth = 0, lace = 0, nplte = 0;
347
+  unsigned int ityp = 1;
348
+  uch buffer[BS];
349
+  int first_idat = 1;           /* flag:  is this the first IDAT chunk? */
350
+  int zlib_error = 0;           /* reset in IHDR section; used for IDAT */
351
+  int check_zlib = 1;           /* validate zlib stream (just IDATs for now) */
352
+  unsigned zlib_windowbits = 15;
353
+  uch outbuf[BS];
354
+  z_stream zstrm;
355
+  unsigned int offset = 0;
356
+  fmap_t *map = *ctx->fmap;
357
+
358
+  cli_dbgmsg("in cli_parsepng()\n");
359
+
360
+  if(fmap_readn(map, magic, offset, 8) != 8)
361
+    return CL_SUCCESS; /* Ignore */
362
+
363
+  if(memcmp(magic, "\x89\x50\x4e\x47\x0d\x0a\x1a\x0a", 8))
364
+    return CL_SUCCESS; /* Not a PNG file */
365
+
366
+  offset += 8;
367
+
368
+  /*-------------------- BEGINNING OF IMMENSE WHILE-LOOP --------------------*/
369
+
370
+
371
+  while(fmap_readn(map, &c, offset, sizeof(c)) == sizeof(c)) {
372
+
373
+    if (have_IEND) {
374
+      cli_dbgmsg("PNG: additional data after END chunk\n");
375
+      return CL_EPARSE;
376
+    }
377
+
378
+    sz = getlong(map, &offset, "chunk length");
379
+    if (sz < 0 || sz > 0x7fffffff) {   /* FIXME:  convert to ulg, lose "< 0" */
380
+      cli_dbgmsg("PNG: invalid chunk length (too large)\n");
381
+      return CL_EPARSE;
382
+    }
383
+
384
+    if(fmap_readn(map, chunkid, offset, 4) != 4) {
385
+      cli_dbgmsg("PNG: EOF while reading chunk type\n");
386
+      return CL_EPARSE;
387
+    }
388
+    offset += 4;
389
+
390
+    /* GRR:  add 4-character EBCDIC conversion here (chunkid) */
391
+
392
+    chunkid[4] = '\0';
393
+    ++num_chunks;
394
+
395
+    if (check_chunk_name(chunkid) != 0)
396
+      return CL_EPARSE;
397
+
398
+    if (!have_IHDR && strcmp(chunkid,"IHDR")!=0)
399
+    {
400
+      cli_dbgmsg("PNG: first chunk must be IHDR\n");
401
+      return CL_EPARSE;
402
+    }
403
+
404
+    crc = update_crc(CRCINIT, (uch *)chunkid, 4);
405
+    toread = (sz > BS)? BS:sz;
406
+    if(toread && fmap_readn(map, buffer, offset, toread) != toread) {
407
+      cli_dbgmsg("PNG: EOF while reading data\n");
408
+      return CL_EPARSE;
409
+    }
410
+    offset += toread;
411
+
412
+    crc = update_crc(crc, (uch *)buffer, toread);
413
+
414
+    /*------*
415
+     | IHDR |
416
+     *------*/
417
+    if (strcmp(chunkid, "IHDR") == 0) {
418
+      if (have_IHDR) {
419
+        cli_dbgmsg("PNG: multiple IHDR not allowed\n");
420
+	return CL_EPARSE;
421
+      } else if (sz != 13) {
422
+        cli_dbgmsg("PNG: invalid IHDR length\n");
423
+	return CL_EPARSE;
424
+      } else {
425
+        int compr, filt;
426
+
427
+        w = LG(buffer);
428
+        h = LG(buffer+4);
429
+        if (w <= 0 || h <= 0 || w > 2147483647 || h > 2147483647) {
430
+          cli_dbgmsg("PNG: invalid image dimensions\n");
431
+	  return CL_EPARSE;
432
+        }
433
+        bitdepth = sampledepth = (uch)buffer[8];
434
+        ityp = (uch)buffer[9];
435
+        if (ityp == 1 || ityp == 5 || ityp > sizeof(png_type)/sizeof(char*)) {
436
+          cli_dbgmsg("PNG: invalid image type (%d)\n", ityp);
437
+	  return CL_EPARSE;
438
+        }
439
+        switch (sampledepth) {
440
+          case 1:
441
+          case 2:
442
+          case 4:
443
+            if (ityp == 2 || ityp == 4 || ityp == 6) { /* RGB or GA or RGBA */
444
+              cli_dbgmsg("PNG: invalid sample depth (%d)\n", sampledepth);
445
+	      return CL_EPARSE;
446
+            }
447
+            break;
448
+          case 8:
449
+            break;
450
+          case 16:
451
+            if (ityp == 3) { /* palette */
452
+              cli_dbgmsg("PNG: invalid sample depth (%d)\n", sampledepth);
453
+              return CL_EPARSE;
454
+            }
455
+            break;
456
+          default:
457
+              cli_dbgmsg("PNG: invalid sample depth (%d)\n", sampledepth);
458
+	      return CL_EPARSE;
459
+        }
460
+        compr = (uch)buffer[10];
461
+        if (compr > 127) {
462
+          cli_dbgmsg("PNG: private (invalid?) compression method (%d)\n", compr);
463
+          return CL_EPARSE;
464
+        } else if (compr > 0) {
465
+          cli_dbgmsg("PNG: invalid compression method (%d)\n", compr);
466
+          return CL_EPARSE;
467
+        }
468
+        filt = (uch)buffer[11];
469
+        if (filt > 127) {
470
+          cli_dbgmsg("PNG: private (invalid?) filter method (%d)\n", filt);
471
+          return CL_EPARSE;
472
+        } else if (filt > 0)
473
+        {
474
+          cli_dbgmsg("PNG: invalid filter method (%d)\n", filt);
475
+          return CL_EPARSE;
476
+        }
477
+        lace = (uch)buffer[12];
478
+        if (lace > 127) {
479
+          cli_dbgmsg("PNG: private (invalid?) interlace method (%d)\n", lace);
480
+          return CL_EPARSE;
481
+        } else if (lace > 1) {
482
+          cli_dbgmsg("PNG: invalid interlace method (%d)\n", lace);
483
+          return CL_EPARSE;
484
+        }
485
+        switch (ityp) {
486
+          case 2:
487
+            bitdepth = sampledepth * 3;   /* RGB */
488
+            break;
489
+          case 4:
490
+            bitdepth = sampledepth * 2;   /* gray+alpha */
491
+            break;
492
+          case 6:
493
+            bitdepth = sampledepth * 4;   /* RGBA */
494
+            break;
495
+        }
496
+      }
497
+      have_IHDR = 1;
498
+      last_is_IDAT = last_is_JDAT = 0;
499
+      first_idat = 1;  /* flag:  next IDAT will be the first in this subimage */
500
+      zlib_error = 0;  /* flag:  no zlib errors yet in this file */
501
+      /* GRR 20000304:  data dump not yet compatible with interlaced images: */
502
+    /*================================================*
503
+     * PNG chunks (with the exception of IHDR, above) *
504
+     *================================================*/
505
+
506
+    /*------*
507
+     | PLTE |
508
+     *------*/
509
+    } else if (strcmp(chunkid, "PLTE") == 0) {
510
+      if (have_PLTE) {
511
+        cli_dbgmsg("PNG: multiple PLTE not allowed\n");
512
+        return CL_EPARSE;
513
+      } else if (ityp != 3 && ityp != 2 && ityp != 6) {
514
+        cli_dbgmsg("PNG: PLTE not allowed in %s image\n", png_type[ityp]);
515
+        return CL_EPARSE;
516
+      } else if (have_IDAT) {
517
+        cli_dbgmsg("PNG: PLTE must precede IDAT\n");
518
+        return CL_EPARSE;
519
+      } else if (have_bKGD) {
520
+        cli_dbgmsg("PNG: PLTE must precede bKGD\n");
521
+        return CL_EPARSE;
522
+      } else if (sz > 768 || sz % 3 != 0) {
523
+        cli_dbgmsg("PNG: invalid number of PLTE entries (%g)\n", (double)sz / 3);
524
+        return CL_EPARSE;
525
+      } else {
526
+        nplte = sz / 3;
527
+        if (((bitdepth == 1 && nplte > 2) ||
528
+            (bitdepth == 2 && nplte > 4) || (bitdepth == 4 && nplte > 16)))
529
+        {
530
+          cli_dbgmsg("PNG: invalid number of PLTE entries (%d) for %d-bit image\n", nplte, bitdepth);
531
+          return CL_EPARSE;
532
+        }
533
+      }
534
+        if (ityp == 1)   /* for MNG and tRNS */
535
+          ityp = 3;
536
+      have_PLTE = 1;
537
+      last_is_IDAT = last_is_JDAT = 0;
538
+
539
+    } else if (strcmp(chunkid, "IDAT") == 0) {
540
+      /* GRR FIXME:  need to check for consecutive IDATs within MNG segments */
541
+      if (have_IDAT && !last_is_IDAT) {
542
+          cli_dbgmsg("PNG: IDAT chunks must be consecutive\n");
543
+          return CL_EPARSE;
544
+      } else if (ityp == 3 && !have_PLTE) {
545
+        cli_dbgmsg("PNG: IDAT must follow PLTE in %s image\n", png_type[ityp]);
546
+        return CL_EPARSE;
547
+      }
548
+
549
+      /* We just want to check that we have read at least the minimum (10)
550
+       * IDAT bytes possible, but avoid any overflow for short ints.  We
551
+       * must also take into account that 0-length IDAT chunks are legal.
552
+       */
553
+      if (have_IDAT <= 0)
554
+        have_IDAT = (sz > 0)? sz : -1;  /* -1 as marker for IDAT(s), no data */
555
+      else if (have_IDAT < 10)
556
+        have_IDAT += (sz > 10)? 10 : sz;  /* FIXME? could cap at 10 always */
557
+
558
+      /* Dump the zlib header from the first two bytes. */
559
+      if (zhead < 0x10000 && sz > 0) {
560
+        zhead = (zhead << 8) + buffer[0];
561
+        if (sz > 1 && zhead < 0x10000)
562
+          zhead = (zhead << 8) + buffer[1];
563
+        if (zhead >= 0x10000) {
564
+          /* formerly print_zlibheader(zhead & 0xffff); */
565
+          /* See the code in zlib deflate.c that writes out the header when
566
+             s->status is INIT_STATE.  In fact this code is based on the zlib
567
+             specification in RFC 1950 (ftp://ds.internic.net/rfc/rfc1950.txt),
568
+             with the implicit assumption that the zlib header *is* written (it
569
+             always should be inside a valid PNG file).  The variable names are
570
+             taken, verbatim, from the RFC. */
571
+          unsigned int CINFO = (zhead & 0xf000) >> 12;
572
+          unsigned int CM = (zhead & 0xf00) >> 8;
573
+	  zlib_windowbits = CINFO + 8;
574
+          if((zhead & 0xffff) % 31) {
575
+            cli_dbgmsg("PNG: compression header fails checksum\n");
576
+            return CL_EPARSE;
577
+          } else if (CM != 8) {
578
+            cli_dbgmsg("PNG: non-deflate compression method (%d)\n", CM);
579
+	    return CL_EPARSE;
580
+	  }
581
+        }
582
+      }
583
+
584
+      if (check_zlib && !zlib_error) {
585
+        static uch *p;   /* always points to next filter byte */
586
+        static int cur_y, cur_pass, cur_xoff, cur_yoff, cur_xskip, cur_yskip;
587
+        static long cur_width, cur_linebytes;
588
+        static long numfilt, numfilt_this_block, numfilt_total, numfilt_pass[7];
589
+        uch *eod;
590
+        int err=Z_OK;
591
+
592
+        zstrm.next_in = buffer;
593
+        zstrm.avail_in = toread;
594
+
595
+        /* initialize zlib and bit/byte/line variables if not already done */
596
+        if (first_idat) {
597
+          zstrm.next_out = p = outbuf;
598
+          zstrm.avail_out = BS;
599
+          zstrm.zalloc = (alloc_func)Z_NULL;
600
+          zstrm.zfree = (free_func)Z_NULL;
601
+          zstrm.opaque = (voidpf)Z_NULL;
602
+          if ((err = inflateInit2(&zstrm, zlib_windowbits)) != Z_OK) {
603
+            cli_dbgmsg("PNG: zlib: can't initialize (error = %d)\n", err);
604
+	    return CL_EUNPACK;
605
+          }
606
+          cur_y = 0;
607
+          cur_pass = 1;     /* interlace pass:  1 through 7 */
608
+          cur_xoff = cur_yoff = 0;
609
+          cur_xskip = cur_yskip = lace? 8 : 1;
610
+          cur_width = (w - cur_xoff + cur_xskip - 1) / cur_xskip; /* round up */
611
+          cur_linebytes = ((cur_width*bitdepth + 7) >> 3) + 1; /* round, fltr */
612
+          numfilt = 0L;
613
+          first_idat = 0;
614
+          if (lace) {   /* loop through passes to calculate total filters */
615
+            int passm1, yskip=0, yoff=0, xoff=0;
616
+
617
+            for (passm1 = 0;  passm1 < 7;  ++passm1) {
618
+              switch (passm1) {  /* (see table below for full summary) */
619
+                case 0:  yskip = 8; yoff = 0; xoff = 0; break;
620
+                case 1:  yskip = 8; yoff = 0; xoff = 4; break;
621
+                case 2:  yskip = 8; yoff = 4; xoff = 0; break;
622
+                case 3:  yskip = 4; yoff = 0; xoff = 2; break;
623
+                case 4:  yskip = 4; yoff = 2; xoff = 0; break;
624
+                case 5:  yskip = 2; yoff = 0; xoff = 1; break;
625
+                case 6:  yskip = 2; yoff = 1; xoff = 0; break;
626
+              }
627
+              /* effective height is reduced if odd pass:  subtract yoff (but
628
+               * if effective width of pass is 0 => no rows and no filters) */
629
+              numfilt_pass[passm1] =
630
+                (w <= xoff)? 0 : (h - yoff + yskip - 1) / yskip;
631
+              if (passm1 > 0)  /* now make it cumulative */
632
+                numfilt_pass[passm1] += numfilt_pass[passm1 - 1];
633
+            }
634
+          } else {
635
+            numfilt_pass[0] = h;   /* if non-interlaced */
636
+            numfilt_pass[1] = numfilt_pass[2] = numfilt_pass[3] = h;
637
+            numfilt_pass[4] = numfilt_pass[5] = numfilt_pass[6] = h;
638
+          }
639
+          numfilt_total = numfilt_pass[6];
640
+        }
641
+        numfilt_this_block = 0L;
642
+
643
+        while (err != Z_STREAM_END && zstrm.avail_in > 0) {
644
+          /* know zstrm.avail_out > 0:  get some image/filter data */
645
+          err = inflate(&zstrm, Z_SYNC_FLUSH);
646
+          if (err != Z_OK && err != Z_STREAM_END) {
647
+            cli_dbgmsg("PNG: zlib: inflate error\n");
648
+	    inflateEnd(&zstrm);
649
+	    return CL_EPARSE;
650
+          }
651
+
652
+          /* now have uncompressed, filtered image data in outbuf */
653
+          eod = outbuf + BS - zstrm.avail_out;
654
+          while (p < eod) {
655
+
656
+            if (cur_linebytes) {	/* GRP 20000727:  bugfix */
657
+              int filttype = p[0];
658
+              if (filttype > 127) {
659
+                if (lace > 1)
660
+                  break;  /* assume it's due to unknown interlace method */
661
+                if (numfilt_this_block == 0) {
662
+                  /* warn only on first one per block; don't break */
663
+                  cli_dbgmsg("PNG: private (invalid?) row-filter type (%d)\n", filttype);
664
+		  inflateEnd(&zstrm);
665
+                  return CL_EPARSE;
666
+                }
667
+              } else if (filttype > 4) {
668
+                if (lace <= 1) {
669
+                  cli_dbgmsg("PNG: invalid row-filter type (%d)\n", filttype);
670
+		  inflateEnd(&zstrm);
671
+                  return CL_EPARSE;
672
+                } /* else assume it's due to unknown interlace method */
673
+                break;
674
+              }
675
+              ++numfilt;
676
+              p += cur_linebytes;
677
+            }
678
+            cur_y += cur_yskip;
679
+
680
+            if (lace) {
681
+              while (cur_y >= h) {	/* may loop if very short image */
682
+                /*
683
+                    pass  xskip yskip  xoff yoff
684
+                      1     8     8      0    0
685
+                      2     8     8      4    0
686
+                      3     4     8      0    4
687
+                      4     4     4      2    0
688
+                      5     2     4      0    2
689
+                      6     2     2      1    0
690
+                      7     1     2      0    1
691
+                 */
692
+                ++cur_pass;
693
+                if (cur_pass & 1) {	/* beginning an odd pass */
694
+                  cur_yoff = cur_xoff;
695
+                  cur_xoff = 0;
696
+                  cur_xskip >>= 1;
697
+                } else {		/* beginning an even pass */
698
+                  if (cur_pass == 2)
699
+                    cur_xoff = 4;
700
+                  else {
701
+                    cur_xoff = cur_yoff >> 1;
702
+                    cur_yskip >>= 1;
703
+                  }
704
+                  cur_yoff = 0;
705
+                }
706
+                cur_y = cur_yoff;
707
+                /* effective width is reduced if even pass: subtract cur_xoff */
708
+                cur_width = (w - cur_xoff + cur_xskip - 1) / cur_xskip;
709
+                cur_linebytes = ((cur_width*bitdepth + 7) >> 3) + 1;
710
+                if (cur_linebytes == 1)	/* just the filter byte?  no can do */
711
+                    cur_linebytes = 0;	/* GRP 20000727:  added fix */
712
+              }
713
+            } else if (cur_y >= h) {
714
+                inflateEnd(&zstrm);
715
+		if(eod - p > 0) {
716
+		    cli_dbgmsg("PNG:  %li bytes remaining in buffer before inflateEnd()", eod-p);
717
+		    return CL_EPARSE;
718
+		}
719
+		err = Z_STREAM_END;
720
+		zlib_error = 1;
721
+            }
722
+          }
723
+          p -= (eod - outbuf);		/* wrap p back into outbuf region */
724
+          zstrm.next_out = outbuf;
725
+          zstrm.avail_out = BS;
726
+
727
+          /* get more input (waiting until buffer empties is not necessary best
728
+           * zlib strategy, but simpler than shifting leftover data around) */
729
+          if (zstrm.avail_in == 0 && sz > toread) {
730
+            int data_read;
731
+
732
+            sz -= toread;
733
+            toread = (sz > BS)? BS:sz;
734
+	    if((data_read = fmap_readn(map, buffer, offset, toread)) != toread) {
735
+              cli_dbgmsg("PNG: EOF while reading %s data\n", chunkid);
736
+              return CL_EPARSE;
737
+            }
738
+	    offset += toread;
739
+            crc = update_crc(crc, buffer, toread);
740
+            zstrm.next_in = buffer;
741
+            zstrm.avail_in = toread;
742
+          }
743
+        }
744
+      }
745
+      last_is_IDAT = 1;
746
+      last_is_JDAT = 0;
747
+
748
+    /*------*
749
+     | IEND |
750
+     *------*/
751
+    } else if (strcmp(chunkid, "IEND") == 0) {
752
+      if (have_IEND) {
753
+        cli_dbgmsg("PNG: multiple IEND not allowed\n");
754
+        return CL_EPARSE;
755
+      } else if (sz != 0) {
756
+        cli_dbgmsg("PNG: invalid IEND length\n");
757
+        return CL_EPARSE;
758
+      } else if (have_IDAT <= 0) {
759
+        cli_dbgmsg("PNG: no IDAT chunks\n");
760
+        return CL_EPARSE;
761
+      } else if (have_IDAT < 10) {
762
+        cli_dbgmsg("PNG: not enough IDAT data\n");
763
+        return CL_EPARSE;
764
+      }
765
+      have_IEND = 1;
766
+      last_is_IDAT = last_is_JDAT = 0;
767
+
768
+    /*------*
769
+     | bKGD |
770
+     *------*/
771
+    } else if (strcmp(chunkid, "bKGD") == 0) {
772
+      if (have_bKGD) {
773
+        cli_dbgmsg("PNG: multiple bKGD not allowed\n");
774
+        return CL_EPARSE;
775
+      } else if ((have_IDAT || have_JDAT)) {
776
+        cli_dbgmsg("PNG: bKGD must precede IDAT\n");
777
+        return CL_EPARSE;
778
+      }
779
+      switch (ityp) {
780
+        case 0:
781
+        case 4:
782
+          if (sz != 2) {
783
+            cli_dbgmsg("PNG: invalid bKGD length\n");
784
+            return CL_EPARSE;
785
+          }
786
+          break;
787
+        case 1: /* MNG top-level chunk (default values):  "as if 16-bit RGBA" */
788
+        case 2:
789
+        case 6:
790
+          if (sz != 6) {
791
+            cli_dbgmsg("PNG: invalid bKGD length\n");
792
+            return CL_EPARSE;
793
+          }
794
+          break;
795
+        case 3:
796
+          if (sz != 1) {
797
+            cli_dbgmsg("PNG: invalid bKGD length\n");
798
+            return CL_EPARSE;
799
+          } else if (buffer[0] >= nplte) {
800
+            cli_dbgmsg("PNG: bKGD index falls outside PLTE\n");
801
+            return CL_EPARSE;
802
+          }
803
+          break;
804
+      }
805
+      have_bKGD = 1;
806
+      last_is_IDAT = last_is_JDAT = 0;
807
+
808
+    /*------*
809
+     | cHRM |
810
+     *------*/
811
+    } else if (strcmp(chunkid, "cHRM") == 0) {
812
+      if (have_cHRM) {
813
+        cli_dbgmsg("PNG: multiple cHRM not allowed\n");
814
+        return CL_EPARSE;
815
+      } else if (have_PLTE) {
816
+        cli_dbgmsg("PNG: cHRM must precede PLTE\n");
817
+        return CL_EPARSE;
818
+      } else if ((have_IDAT || have_JDAT)) {
819
+        cli_dbgmsg("PNG: cHRM must precede IDAT\n");
820
+        return CL_EPARSE;
821
+      } else if (sz != 32) {
822
+        cli_dbgmsg("PNG: invalid cHRM length\n");
823
+        return CL_EPARSE;
824
+      } else {
825
+        double wx, wy, rx, ry, gx, gy, bx, by;
826
+
827
+        wx = (double)LG(buffer)/100000;
828
+        wy = (double)LG(buffer+4)/100000;
829
+        rx = (double)LG(buffer+8)/100000;
830
+        ry = (double)LG(buffer+12)/100000;
831
+        gx = (double)LG(buffer+16)/100000;
832
+        gy = (double)LG(buffer+20)/100000;
833
+        bx = (double)LG(buffer+24)/100000;
834
+        by = (double)LG(buffer+28)/100000;
835
+
836
+        if (wx < 0 || wx > 0.8 || wy < 0 || wy > 0.8 || wx + wy > 1.0) {
837
+          cli_dbgmsg("PNG: invalid cHRM white point\n");
838
+          return CL_EPARSE;
839
+        } else if (rx < 0 || rx > 0.8 || ry < 0 || ry > 0.8 || rx + ry > 1.0) {
840
+          cli_dbgmsg("PNG: invalid cHRM red point\n");
841
+          return CL_EPARSE;
842
+        } else if (gx < 0 || gx > 0.8 || gy < 0 || gy > 0.8 || gx + gy > 1.0) {
843
+          cli_dbgmsg("PNG: invalid cHRM green point\n");
844
+          return CL_EPARSE;
845
+        } else if (bx < 0 || bx > 0.8 || by < 0 || by > 0.8 || bx + by > 1.0) {
846
+          cli_dbgmsg("PNG: invalid cHRM blue point\n");
847
+          return CL_EPARSE;
848
+        }
849
+      }
850
+      have_cHRM = 1;
851
+      last_is_IDAT = last_is_JDAT = 0;
852
+
853
+    /*------*
854
+     | fRAc |
855
+     *------*/
856
+    } else if (strcmp(chunkid, "fRAc") == 0) {
857
+      last_is_IDAT = last_is_JDAT = 0;
858
+
859
+    /*------*
860
+     | gAMA |
861
+     *------*/
862
+    } else if (strcmp(chunkid, "gAMA") == 0) {
863
+      if (have_gAMA) {
864
+        cli_dbgmsg("PNG: multiple gAMA not allowed\n");
865
+        return CL_EPARSE;
866
+      } else if (have_IDAT || have_JDAT) {
867
+        cli_dbgmsg("PNG: gAMA must precede IDAT\n");
868
+        return CL_EPARSE;
869
+      } else if (have_PLTE) {
870
+        cli_dbgmsg("PNG: gAMA must precede PLTE\n");
871
+        return CL_EPARSE;
872
+      } else if (sz != 4) {
873
+        cli_dbgmsg("PNG: invalid gAMA length\n");
874
+        return CL_EPARSE;
875
+      } else if (LG(buffer) == 0) {
876
+        cli_dbgmsg("PNG: invalid gAMA value (0.0000)\n");
877
+        return CL_EPARSE;
878
+      }
879
+      have_gAMA = 1;
880
+      last_is_IDAT = last_is_JDAT = 0;
881
+
882
+    /*------*
883
+     | gIFg |
884
+     *------*/
885
+    } else if (strcmp(chunkid, "gIFg") == 0) {
886
+      if (sz != 4) {
887
+        cli_dbgmsg("PNG: invalid gIFg length\n");
888
+        return CL_EPARSE;
889
+      }
890
+      last_is_IDAT = last_is_JDAT = 0;
891
+
892
+    /*------*
893
+     | gIFt |
894
+     *------*/
895
+    } else if (strcmp(chunkid, "gIFt") == 0) {
896
+      if (sz < 24) {
897
+        cli_dbgmsg("PNG: invalid gIFt length\n");
898
+        return CL_EPARSE;
899
+      }
900
+      last_is_IDAT = last_is_JDAT = 0;
901
+
902
+    /*------*
903
+     | gIFx |
904
+     *------*/
905
+    } else if (strcmp(chunkid, "gIFx") == 0) {
906
+      if (sz < 11) {
907
+        cli_dbgmsg("PNG: invalid gIFx length\n");
908
+        return CL_EPARSE;
909
+      }
910
+      last_is_IDAT = last_is_JDAT = 0;
911
+
912
+    /*------*
913
+     | hIST |
914
+     *------*/
915
+    } else if (strcmp(chunkid, "hIST") == 0) {
916
+      if (have_hIST) {
917
+        cli_dbgmsg("PNG: multiple hIST not allowed\n");
918
+        return CL_EPARSE;
919
+      } else if (!have_PLTE) {
920
+        cli_dbgmsg("PNG: hIST must follow PLTE\n");
921
+        return CL_EPARSE;
922
+      } else if (have_IDAT) {
923
+        cli_dbgmsg("PNG: hIST must precede IDAT\n");
924
+        return CL_EPARSE;
925
+      } else if (sz != nplte * 2) {
926
+        cli_dbgmsg("PNG: invalid number of hIST entries (%g)\n", (double)sz / 2);
927
+        return CL_EPARSE;
928
+      }
929
+      have_hIST = 1;
930
+      last_is_IDAT = last_is_JDAT = 0;
931
+
932
+    /*------*
933
+     | iCCP |
934
+     *------*/
935
+    } else if (strcmp(chunkid, "iCCP") == 0) {
936
+      int name_len;
937
+
938
+      if (have_iCCP) {
939
+        cli_dbgmsg("PNG: multiple iCCP not allowed\n");
940
+        return CL_EPARSE;
941
+      } else if (have_sRGB) {
942
+        cli_dbgmsg("PNG: iCCP not allowed with sRGB\n");
943
+        return CL_EPARSE;
944
+      } else if (have_PLTE) {
945
+        cli_dbgmsg("PNG: iCCP must precede PLTE\n");
946
+        return CL_EPARSE;
947
+      } else if (have_IDAT || have_JDAT) {
948
+        cli_dbgmsg("PNG: iCCP must precede IDAT\n");
949
+        return CL_EPARSE;
950
+      } else if (check_keyword(buffer, toread, &name_len)) {
951
+        return CL_EPARSE;
952
+      } else {
953
+        int remainder = toread - name_len - 3;
954
+        uch compr = buffer[name_len+1];
955
+
956
+        if (remainder < 0) {
957
+          cli_dbgmsg("PNG: invalid iCCP length\n");
958
+          return CL_EPARSE;
959
+        } else if (buffer[name_len] != 0) {
960
+          cli_dbgmsg("PNG: missing NULL after iCCP profile name\n");
961
+          return CL_EPARSE;
962
+        } else if (compr > 0 && compr < 128) {
963
+          cli_dbgmsg("PNG: invalid iCCP compression method (%d)\n", compr);
964
+          return CL_EPARSE;
965
+        } else if (compr >= 128) {
966
+          return CL_EPARSE;
967
+        }
968
+      }
969
+      have_iCCP = 1;
970
+      last_is_IDAT = last_is_JDAT = 0;
971
+
972
+    /*------*
973
+     | iTXt |
974
+     *------*/
975
+    } else if (strcmp(chunkid, "iTXt") == 0) {
976
+      int keylen;
977
+
978
+      if (check_keyword(buffer, toread, &keylen))
979
+        return CL_EPARSE;
980
+      else {
981
+        int compressed = 0, compr = 0;
982
+
983
+	if(keylen + 1 >= BS)
984
+	    return CL_EPARSE;
985
+        compressed = buffer[keylen+1];
986
+        if (compressed < 0 || compressed > 1) {
987
+          cli_dbgmsg("PNG: invalid iTXt compression flag (%d)\n", compressed);
988
+          return CL_EPARSE;
989
+        } else if ((compr = (uch)buffer[keylen+2]) > 127) {
990
+          cli_dbgmsg("PNG: private (invalid?) iTXt compression method (%d)\n", compr);
991
+          return CL_EPARSE;
992
+        } else if (compr > 0) {
993
+          cli_dbgmsg("PNG: invalid iTXt compression method (%d)\n", compr);
994
+          return CL_EPARSE;
995
+        }
996
+      }
997
+      last_is_IDAT = last_is_JDAT = 0;
998
+
999
+    /*------*
1000
+     | oFFs |
1001
+     *------*/
1002
+    } else if (strcmp(chunkid, "oFFs") == 0) {
1003
+      if (have_oFFs) {
1004
+        cli_dbgmsg("PNG: multiple oFFs not allowed\n");
1005
+        return CL_EPARSE;
1006
+      } else if (have_IDAT || have_JDAT) {
1007
+        cli_dbgmsg("PNG: oFFs must precede IDAT\n");
1008
+        return CL_EPARSE;
1009
+      } else if (sz != 9) {
1010
+        cli_dbgmsg("PNG: invalid oFFs length\n");
1011
+        return CL_EPARSE;
1012
+      } else if (buffer[8] > 1) {
1013
+        cli_dbgmsg("PNG: invalid oFFs unit specifier (%u)\n", buffer[8]);
1014
+        return CL_EPARSE;
1015
+      }
1016
+      have_oFFs = 1;
1017
+      last_is_IDAT = last_is_JDAT = 0;
1018
+
1019
+    /*------*
1020
+     | pCAL |
1021
+     *------*/
1022
+    } else if (strcmp(chunkid, "pCAL") == 0) {
1023
+      if (have_pCAL) {
1024
+        cli_dbgmsg("PNG: multiple pCAL not allowed\n");
1025
+        return CL_EPARSE;
1026
+      } else if (have_IDAT) {
1027
+        cli_dbgmsg("PNG: pCAL must precede IDAT\n");
1028
+        return CL_EPARSE;
1029
+      }
1030
+      have_pCAL = 1;
1031
+      last_is_IDAT = last_is_JDAT = 0;
1032
+
1033
+    /*------*
1034
+     | pHYs |
1035
+     *------*/
1036
+    } else if (strcmp(chunkid, "pHYs") == 0) {
1037
+      if (have_pHYs) {
1038
+        cli_dbgmsg("PNG: multiple pHYs not allowed\n");
1039
+        return CL_EPARSE;
1040
+      } else if (have_IDAT || have_JDAT) {
1041
+        cli_dbgmsg("PNG: pHYS must precede DAT\n");
1042
+        return CL_EPARSE;
1043
+      } else if (sz != 9) {
1044
+        cli_dbgmsg("PNG: invalid pHYS length\n");
1045
+        return CL_EPARSE;
1046
+      } else if (buffer[8] > 1) {
1047
+        cli_dbgmsg("PNG: invalid pHYs unit specifier (%u)\n", buffer[8]);
1048
+        return CL_EPARSE;
1049
+      }
1050
+      have_pHYs = 1;
1051
+      last_is_IDAT = last_is_JDAT = 0;
1052
+
1053
+    /*------*
1054
+     | sBIT |
1055
+     *------*/
1056
+    } else if (strcmp(chunkid, "sBIT") == 0) {
1057
+      int maxbits = (ityp == 3)? 8 : sampledepth;
1058
+
1059
+      if (have_sBIT) {
1060
+        cli_dbgmsg("PNG: multiple sBIT not allowed\n");
1061
+        return CL_EPARSE;
1062
+      } else if (have_PLTE) {
1063
+        cli_dbgmsg("PNG: sBIT must precede PLTE\n");
1064
+        return CL_EPARSE;
1065
+      } else if (have_IDAT) {
1066
+        cli_dbgmsg("PNG: sBIT must precede IDAT\n");
1067
+        return CL_EPARSE;
1068
+      }
1069
+      switch (ityp) {
1070
+        case 0:
1071
+          if (sz != 1) {
1072
+            cli_dbgmsg("PNG: invalid sBIT length\n");
1073
+            return CL_EPARSE;
1074
+          } else if (buffer[0] == 0 || buffer[0] > maxbits) {
1075
+            cli_dbgmsg("PNG: sBIT grey bits invalid for sample image\n");
1076
+            return CL_EPARSE;
1077
+          } 
1078
+          break;
1079
+        case 2:
1080
+        case 3:
1081
+          if (sz != 3) {
1082
+            cli_dbgmsg("PNG: invalid sBIT length\n");
1083
+            return CL_EPARSE;
1084
+          } else if (buffer[0] == 0 || buffer[0] > maxbits) {
1085
+            cli_dbgmsg("PNG: sBIT red bits invalid for sample image\n");
1086
+            return CL_EPARSE;
1087
+          } else if (buffer[1] == 0 || buffer[1] > maxbits) {
1088
+            cli_dbgmsg("PNG: sBIT green bits invalid for sample image\n");
1089
+            return CL_EPARSE;
1090
+          } else if (buffer[2] == 0 || buffer[2] > maxbits) {
1091
+            cli_dbgmsg("PNG: sBIT blue bits invalid for sample image\n");
1092
+            return CL_EPARSE;
1093
+          }
1094
+          break;
1095
+        case 4:
1096
+          if (sz != 2) {
1097
+            cli_dbgmsg("PNG: invalid length\n");
1098
+            return CL_EPARSE;
1099
+          } else if (buffer[0] == 0 || buffer[0] > maxbits) {
1100
+            cli_dbgmsg("PNG: grey bits invalid for sample image\n");
1101
+            return CL_EPARSE;
1102
+          } else if (buffer[1] == 0 || buffer[1] > maxbits) {
1103
+            cli_dbgmsg("PNG: alpha bits invalid for sample image\n");
1104
+            return CL_EPARSE;
1105
+          }
1106
+          break;
1107
+        case 6:
1108
+          if (sz != 4) {
1109
+            cli_dbgmsg("PNG: invalid sBIT length\n");
1110
+            return CL_EPARSE;
1111
+          } else if (buffer[0] == 0 || buffer[0] > maxbits) {
1112
+            cli_dbgmsg("PNG: red bits invalid for sample image\n");
1113
+            return CL_EPARSE;
1114
+          } else if (buffer[1] == 0 || buffer[1] > maxbits) {
1115
+            cli_dbgmsg("PNG: green bits invalid for sample image\n");
1116
+            return CL_EPARSE;
1117
+          } else if (buffer[2] == 0 || buffer[2] > maxbits) {
1118
+            cli_dbgmsg("PNG: blue bits invalid for sample image\n");
1119
+            return CL_EPARSE;
1120
+          } else if (buffer[3] == 0 || buffer[3] > maxbits) {
1121
+            cli_dbgmsg("PNG: alpha bits invalid for sample image\n");
1122
+            return CL_EPARSE;
1123
+          }
1124
+          break;
1125
+      }
1126
+      have_sBIT = 1;
1127
+      last_is_IDAT = last_is_JDAT = 0;
1128
+
1129
+    /*------*
1130
+     | sCAL |
1131
+     *------*/
1132
+    } else if (strcmp(chunkid, "sCAL") == 0) {
1133
+      int unittype = buffer[0];
1134
+      uch *pPixwidth = buffer+1, *pPixheight=NULL;
1135
+
1136
+      if (have_sCAL) {
1137
+        cli_dbgmsg("PNG: multiple sCAL not allowed\n");
1138
+        return CL_EPARSE;
1139
+      } else if (have_IDAT || have_JDAT) {
1140
+        cli_dbgmsg("PNG: sCAL must precede IDAT\n");
1141
+        return CL_EPARSE;
1142
+      } else if (sz < 4) {
1143
+        cli_dbgmsg("PNG: invalid sCAL length\n");
1144
+        return CL_EPARSE;
1145
+      } else if (unittype < 1 || unittype > 2) {
1146
+        cli_dbgmsg("PNG: invalid sCAL unit specifier (%d)\n", unittype);
1147
+        return CL_EPARSE;
1148
+      } else {
1149
+        uch *qq;
1150
+        for (qq = pPixwidth;  qq < buffer+sz;  ++qq) {
1151
+          if (*qq == 0)
1152
+            break;
1153
+        }
1154
+        if (qq == buffer+sz) {
1155
+          cli_dbgmsg("PNG: missing sCAL null separator\n");
1156
+          return CL_EPARSE;
1157
+        } else {
1158
+          pPixheight = qq + 1;
1159
+          if (pPixheight == buffer+sz || *pPixheight == 0) {
1160
+            cli_dbgmsg("PNG: missing sCAL pixel height\n");
1161
+            return CL_EPARSE;
1162
+          }
1163
+        }
1164
+          for (qq = pPixheight;  qq < buffer+sz;  ++qq) {
1165
+            if (*qq == 0)
1166
+              break;
1167
+          }
1168
+          if (qq != buffer+sz) {
1169
+            cli_dbgmsg("PNG: extra sCAL null separator\n");
1170
+            return CL_EPARSE;
1171
+          }
1172
+          if (*pPixwidth == '-' || *pPixheight == '-') {
1173
+            cli_dbgmsg("PNG: invalid negative sCAL value(s)\n");
1174
+            return CL_EPARSE;
1175
+          } else if (check_ascii_float(pPixwidth, pPixheight-pPixwidth-1) ||
1176
+                     check_ascii_float(pPixheight, buffer+sz-pPixheight))
1177
+          {
1178
+            return CL_EPARSE;
1179
+          }
1180
+      }
1181
+      have_sCAL = 1;
1182
+      last_is_IDAT = last_is_JDAT = 0;
1183
+
1184
+    /*------*
1185
+     | sPLT |
1186
+     *------*/
1187
+    } else if (strcmp(chunkid, "sPLT") == 0) {
1188
+      int name_len;
1189
+
1190
+      if (have_IDAT) {
1191
+        cli_dbgmsg("PNG: sPLT must precede IDAT\n");
1192
+        return CL_EPARSE;
1193
+      } else if (check_keyword(buffer, toread, &name_len)) {
1194
+        return CL_EPARSE;
1195
+      } else {
1196
+        uch bps = buffer[name_len+1];
1197
+        int remainder = toread - name_len - 2;
1198
+        int bytes = (bps >> 3);
1199
+        int entry_sz = 4*bytes + 2;
1200
+
1201
+        if (remainder < 0) {
1202
+          cli_dbgmsg("PNG: invalid sPLT length\n");
1203
+          return CL_EPARSE;
1204
+        } else if (buffer[name_len] != 0) {
1205
+          cli_dbgmsg("PNG: missing NULL after sPLT palette name\n");
1206
+          return CL_EPARSE;
1207
+        } else if (bps != 8 && bps != 16) {
1208
+          cli_dbgmsg("PNG: invalid sPLT sample depth\n");
1209
+          return CL_EPARSE;
1210
+        } else if (remainder % entry_sz != 0) {
1211
+          cli_dbgmsg("PNG: invalid number of sPLT entries\n");
1212
+          return CL_EPARSE;
1213
+        }
1214
+      }
1215
+      last_is_IDAT = last_is_JDAT = 0;
1216
+
1217
+    /*------*
1218
+     | sRGB |
1219
+     *------*/
1220
+    } else if (strcmp(chunkid, "sRGB") == 0) {
1221
+      if (have_sRGB) {
1222
+        cli_dbgmsg("PNG: multiple sRGB not allowed\n");
1223
+        return CL_EPARSE;
1224
+      } else if (have_iCCP) {
1225
+        cli_dbgmsg("PNG: sRGB not allowed with iCCP\n");
1226
+        return CL_EPARSE;
1227
+      } else if (have_PLTE) {
1228
+        cli_dbgmsg("PNG: sRGB must precede PLTE\n");
1229
+        return CL_EPARSE;
1230
+      } else if (have_IDAT || have_JDAT) {
1231
+        cli_dbgmsg("PNG: sRGB must precede IDAT\n");
1232
+        return CL_EPARSE;
1233
+      } else if (sz != 1) {
1234
+        cli_dbgmsg("PNG: invalid sRGB length\n");
1235
+        return CL_EPARSE;
1236
+      } else if (buffer[0] > 3) {
1237
+        cli_dbgmsg("PNG: sRGB invalid rendering intent\n");
1238
+        return CL_EPARSE;
1239
+      }
1240
+      have_sRGB = 1;
1241
+      last_is_IDAT = last_is_JDAT = 0;
1242
+
1243
+    /*------*
1244
+     | sTER |
1245
+     *------*/
1246
+    } else if (strcmp(chunkid, "sTER") == 0) {
1247
+      if (have_sTER) {
1248
+        cli_dbgmsg("PNG: multiple sTER not allowed\n");
1249
+        return CL_EPARSE;
1250
+      } else if (have_IDAT || have_JDAT) {
1251
+        cli_dbgmsg("PNG: sTER must precede IDAT\n");
1252
+        return CL_EPARSE;
1253
+      } else if (sz != 1) {
1254
+        cli_dbgmsg("PNG: invalid sTER length\n");
1255
+        return CL_EPARSE;
1256
+      } else if (buffer[0] > 1) {
1257
+        cli_dbgmsg("PNG: invalid sTER layout mode\n");
1258
+        return CL_EPARSE;
1259
+      }
1260
+      have_sTER = 1;
1261
+      last_is_IDAT = last_is_JDAT = 0;
1262
+
1263
+    /*------*  *------*
1264
+     | tEXt |  | zTXt |
1265
+     *------*  *------*/
1266
+    } else if (strcmp(chunkid, "tEXt") == 0 || strcmp(chunkid, "zTXt") == 0) {
1267
+      int ztxt = (chunkid[0] == 'z');
1268
+      int keylen;
1269
+
1270
+      if (check_keyword(buffer, toread, &keylen))
1271
+        return CL_EPARSE;
1272
+      else if (ztxt) {
1273
+        int compr = (uch)buffer[keylen+1];
1274
+        if (compr > 127) {
1275
+          cli_dbgmsg("PNG: private (possibly invalid) compression method\n");
1276
+          return CL_EPARSE;
1277
+        } else if (compr > 0) {
1278
+          cli_dbgmsg("PNG: invalid compression method\n");
1279
+          return CL_EPARSE;
1280
+        }
1281
+      }
1282
+      else if (check_text(buffer + keylen + 1, toread - keylen - 1)) {
1283
+        return CL_EPARSE;
1284
+      }
1285
+      last_is_IDAT = last_is_JDAT = 0;
1286
+
1287
+    /*------*
1288
+     | tIME |
1289
+     *------*/
1290
+    } else if (strcmp(chunkid, "tIME") == 0) {
1291
+      if (have_tIME) {
1292
+        cli_dbgmsg("PNG: multiple tIME not allowed\n");
1293
+        return CL_EPARSE;
1294
+      } else if (sz != 7) {
1295
+        cli_dbgmsg("PNG: invalid tIME length\n");
1296
+        return CL_EPARSE;
1297
+      } else {
1298
+        int yr = SH(buffer);
1299
+        int mo = buffer[2];
1300
+        int dy = buffer[3];
1301
+        int hh = buffer[4];
1302
+        int mm = buffer[5];
1303
+        int ss = buffer[6];
1304
+
1305
+        if (yr < 1995) {
1306
+          /* conversion to PNG format counts as modification... */
1307
+          /* FIXME:  also test for future dates? (may allow current year + 1) */
1308
+          cli_dbgmsg("PNG: invalid year\n");
1309
+          return CL_EPARSE;
1310
+        } else if (mo < 1 || mo > 12) {
1311
+          cli_dbgmsg("PNG: invalid month\n");
1312
+          return CL_EPARSE;
1313
+        } else if (dy < 1 || dy > 31) {
1314
+          /* FIXME:  also validate day given specified month? */
1315
+          cli_dbgmsg("PNG: invalid day\n");
1316
+          return CL_EPARSE;
1317
+        } else if (hh < 0 || hh > 23) {
1318
+          cli_dbgmsg("PNG: invalid hour\n");
1319
+          return CL_EPARSE;
1320
+        } else if (mm < 0 || mm > 59) {
1321
+          cli_dbgmsg("PNG: invalid minute\n");
1322
+          return CL_EPARSE;
1323
+        } else if (ss < 0 || ss > 60) {
1324
+          cli_dbgmsg("PNG: invalid second\n");
1325
+          return CL_EPARSE;
1326
+        }
1327
+        cli_dbgmsg("PNG: Time: %2d %s %4d %02d:%02d:%02d UTC\n", dy, getmonth(mo), yr, hh, mm, ss);
1328
+      }
1329
+      have_tIME = 1;
1330
+      last_is_IDAT = last_is_JDAT = 0;
1331
+
1332
+    /*------*
1333
+     | tRNS |
1334
+     *------*/
1335
+    } else if (strcmp(chunkid, "tRNS") == 0) {
1336
+      if (have_tRNS) {
1337
+        cli_dbgmsg("PNG: multiple tRNS not allowed\n");
1338
+        return CL_EPARSE;
1339
+      } else if (ityp == 3 && !have_PLTE) {
1340
+        cli_dbgmsg("PNG: tRNS must follow PLTE\n");
1341
+        return CL_EPARSE;
1342
+      } else if (have_IDAT) {
1343
+        cli_dbgmsg("PNG: tRNS must precede IDAT\n");
1344
+        return CL_EPARSE;
1345
+      } else {
1346
+        switch (ityp) {
1347
+          case 0:
1348
+            if (sz != 2) {
1349
+              cli_dbgmsg("PNG: invalid tRNS length for %s image\n", png_type[ityp]);
1350
+              return CL_EPARSE;
1351
+            }
1352
+            break;
1353
+          case 2:
1354
+            if (sz != 6) {
1355
+              cli_dbgmsg("PNG: invalid tRNS length for %s image\n", png_type[ityp]);
1356
+              return CL_EPARSE;
1357
+            }
1358
+            break;
1359
+          case 3:
1360
+            if (sz > nplte) {
1361
+              cli_dbgmsg("PNG: invalid tRNS length for %s image\n", png_type[ityp]);
1362
+              return CL_EPARSE;
1363
+            }
1364
+            break;
1365
+          default:
1366
+            cli_dbgmsg("PNG: tRNS not allowed in %s image\n", png_type[ityp]);
1367
+            return CL_EPARSE;
1368
+            break;
1369
+        }
1370
+      }
1371
+      have_tRNS = 1;
1372
+      last_is_IDAT = last_is_JDAT = 0;
1373
+
1374
+    /*===============*
1375
+     * unknown chunk *
1376
+     *===============*/
1377
+
1378
+    } else {
1379
+      if (CRITICAL(chunkid) && SAFECOPY(chunkid)) {
1380
+        /* a critical, safe-to-copy chunk is an error */
1381
+        cli_dbgmsg("PNG: illegal critical, safe-to-copy chunk\n");
1382
+        return CL_EPARSE;
1383
+      } else if (RESERVED(chunkid)) {
1384
+        /* a chunk with the reserved bit set is an error (or spec updated) */
1385
+        cli_dbgmsg("PNG: illegal reserved-bit-set chunk\n");
1386
+        return CL_EPARSE;
1387
+      } else if (PUBLIC(chunkid)) {
1388
+        /* GRR 20050725:  all registered (public) PNG/MNG/JNG chunks are now
1389
+         *  known to pngcheck, so any unknown public ones are invalid (or have
1390
+         *  been proposed and approved since the last release of pngcheck) */
1391
+        cli_dbgmsg("PNG: illegal (unless recently approved) unknown, public\n");
1392
+        return CL_EPARSE;
1393
+      } else if (/* !PUBLIC(chunkid) && */ CRITICAL(chunkid)) {
1394
+        cli_dbgmsg("PNG: private, critical chunk (warning)\n");
1395
+        return CL_EPARSE;  /* not an error if used only internally */
1396
+      }
1397
+      last_is_IDAT = last_is_JDAT = 0;
1398
+    }
1399
+
1400
+      while (sz > toread) {
1401
+        int data_read;
1402
+        sz -= toread;
1403
+        toread = (sz > BS)? BS:sz;
1404
+
1405
+	data_read = fmap_readn(map, buffer, offset, toread);
1406
+        if (data_read != toread) {
1407
+          cli_dbgmsg("PNG: EOF while reading final data\n");
1408
+          return CL_EPARSE;
1409
+        }
1410
+	offset += toread;
1411
+        crc = update_crc(crc, (uch *)buffer, toread);
1412
+      }
1413
+
1414
+      filecrc = getlong(map, &offset, "CRC value");
1415
+
1416
+      if (filecrc != CRCCOMPL(crc)) {
1417
+        cli_dbgmsg("PNG: CRC error in chunk %s (computed %08lx, expected %08lx)\n",
1418
+               chunkid, CRCCOMPL(crc), filecrc);
1419
+        return CL_EPARSE;
1420
+      }
1421
+  }
1422
+
1423
+  /*----------------------- END OF IMMENSE WHILE-LOOP -----------------------*/
1424
+
1425
+  if (!have_IEND) {
1426
+    cli_dbgmsg("PNG: file doesn't end with a IEND chunk\n");
1427
+    return CL_EPARSE;
1428
+  }
1429
+
1430
+  return CL_SUCCESS;
1431
+}
0 1432
new file mode 100644
... ...
@@ -0,0 +1,27 @@
0
+/*
1
+ *   Copyright 1995-2007 by Alexander Lehmann <lehmann@usa.net>,
2
+ *                          Andreas Dilger <adilger@enel.ucalgary.ca>,
3
+ *                          Glenn Randers-Pehrson <randeg@alum.rpi.edu>,
4
+ *                          Greg Roelofs <newt@pobox.com>,
5
+ *                          John Bowler <jbowler@acm.org>,
6
+ *                          Tom Lane <tgl@sss.pgh.pa.us>
7
+ *   Copyright (C) 2011 Sourcefire, Inc.
8
+ *   Maintainer: Tomasz Kojm <tkojm@clamav.net>
9
+ *
10
+ *   Permission to use, copy, modify, and distribute this software and its
11
+ *   documentation for any purpose and without fee is hereby granted, provided
12
+ *   that the above copyright notice appear in all copies and that both that
13
+ *   copyright notice and this permission notice appear in supporting
14
+ *   documentation.  This software is provided "as is" without express or
15
+ *   implied warranty.
16
+ *
17
+ */
18
+
19
+#ifndef __PNG_H
20
+#define __PNG_H
21
+
22
+#include "others.h"
23
+
24
+int cli_parsepng(cli_ctx *ctx);
25
+
26
+#endif
... ...
@@ -92,6 +92,7 @@
92 92
 #include "events.h"
93 93
 #include "swf.h"
94 94
 #include "jpeg.h"
95
+#include "png.h"
95 96
 
96 97
 #ifdef HAVE_BZLIB_H
97 98
 #include <bzlib.h>
... ...
@@ -2441,6 +2442,9 @@ static int magic_scandesc(int desc, cli_ctx *ctx, cli_file_t type)
2441 2441
 
2442 2442
 	    if(ctx->img_validate && SCAN_ALGO && ret != CL_VIRUS)
2443 2443
 		ret = cli_parsejpeg(ctx);
2444
+
2445
+	    if(ctx->img_validate && SCAN_ALGO && ret != CL_VIRUS && ret != CL_EPARSE)
2446
+		ret = cli_parsepng(ctx);
2444 2447
 	    break;
2445 2448
 
2446 2449
         case CL_TYPE_PDF: /* FIXMELIMITS: pdf should be an archive! */
... ...
@@ -505,7 +505,7 @@
505 505
 /* #undef USE_SYSLOG */
506 506
 
507 507
 /* Version number of package */
508
-#define VERSION "devel-clamav-0.97-45-g8df46b3"
508
+#define VERSION "devel-clamav-0.97-55-g03adf2a"
509 509
 
510 510
 /* Version suffix for package */
511 511
 #define VERSION_SUFFIX ""
... ...
@@ -166,6 +166,7 @@
166 166
     <ResourceCompile Include="res\libclamav.rc"/>
167 167
   </ItemGroup>
168 168
   <ItemGroup>
169
+    <ClCompile Include="..\libclamav\png.c"/>
169 170
     <ClCompile Include="..\libclamav\jpeg.c"/>
170 171
     <ClCompile Include="..\libclamav\swf.c"/>
171 172
     <ClCompile Include="..\libclamav\matcher-hash.c"/>