Browse code

improve __zip_find_disk_trailer

git-svn: trunk@2000

Tomasz Kojm authored on 2006/05/30 22:00:04
Showing 3 changed files
... ...
@@ -1,3 +1,9 @@
1
+Tue May 30 14:49:39 CEST 2006 (tk)
2
+----------------------------------
3
+  * libclamav/unzip.c: improve __zip_find_disk_trailer, try to detect proper
4
+		       shift for offset of start of central directory in SFX
5
+		       files. Thanks to Sven for test files.
6
+
1 7
 Sun May 28 10:30:18 BST 2006 (njh)
2 8
 ----------------------------------
3 9
   * libclamav/mbox.c:	Extra patch similar to Tue May 16 21:15:25 BST 2006
... ...
@@ -44,7 +44,7 @@
44 44
 
45 45
 #define __sizeof(X) ((ssize_t)(sizeof(X)))
46 46
 
47
-#define ZIPBUFSIZ   512
47
+#define ZIPBUFSIZ   1024
48 48
 #define ZIP32K	    32768
49 49
 
50 50
 inline static void __fixup_rootseek(off_t offset_of_trailer, struct zip_disk_trailer *trailer)
... ...
@@ -55,10 +55,13 @@ inline static void __fixup_rootseek(off_t offset_of_trailer, struct zip_disk_tra
55 55
 	    trailer->z_rootseek = (uint32_t) (offset_of_trailer -  EC32(trailer->z_rootsize)); 
56 56
 }
57 57
 
58
-int __zip_find_disk_trailer(int fd, off_t filesize, struct zip_disk_trailer *trailer, off_t start)
58
+int __zip_find_disk_trailer(int fd, off_t filesize, struct zip_disk_trailer *trailer, off_t *start)
59 59
 {
60 60
 	char *buf, *end, *tail;
61
-	off_t offset = 0, bufsize = 0, pagesize = ZIPBUFSIZ;
61
+	off_t offset = 0, bufsize;
62
+	struct zip_root_dirent dirent;
63
+	uint32_t u_rootseek, shift = 0;
64
+	int i;
62 65
 
63 66
 
64 67
     if(!trailer) {
... ...
@@ -71,37 +74,33 @@ int __zip_find_disk_trailer(int fd, off_t filesize, struct zip_disk_trailer *tra
71 71
 	return CL_EFORMAT;
72 72
     }
73 73
 
74
-    if(!(buf = cli_malloc(2 * ZIPBUFSIZ)))
74
+    if(!(buf = cli_malloc(ZIPBUFSIZ)))
75 75
 	return CL_EMEM;
76 76
 
77 77
     offset = filesize;
78 78
     while(1) {
79 79
 
80
-	if(offset <= start) {
81
-	     cli_warnmsg("Unzip: __zip_find_disk_trailer: Central directory not found\n");
80
+	if(offset <= 0) {
81
+	     cli_dbgmsg("Unzip: __zip_find_disk_trailer: Central directory not found\n");
82 82
 	     free(buf);
83 83
 	     return CL_EFORMAT;
84 84
 	}
85 85
 
86
-	if(offset == filesize && filesize > pagesize)
87
-	    offset -= pagesize;
88
-
89
-	if(offset < pagesize) {
90
-	    bufsize = offset + pagesize;
91
-	    offset = 0;
86
+	if(offset >= ZIPBUFSIZ) {
87
+	    if(offset == filesize)
88
+		offset -= ZIPBUFSIZ;
89
+	    else
90
+		offset -= ZIPBUFSIZ - 4;
92 91
 
92
+	    bufsize = ZIPBUFSIZ;
93 93
 	} else {
94
-	    offset -= pagesize;
95
-	    bufsize = 2 * pagesize;
96
-	    if(offset & (pagesize - 1)) {
97
-		pagesize -= offset & (pagesize - 1);
98
-		offset += pagesize;
99
-		bufsize -= pagesize; 
100
-	    }    
101
-	}
94
+	    if(filesize < ZIPBUFSIZ)
95
+		bufsize = offset;
96
+	    else
97
+		bufsize = ZIPBUFSIZ;
102 98
 
103
-	if(offset + bufsize > filesize)
104
-	    bufsize = filesize - offset;
99
+	    offset = 0;
100
+	}
105 101
 
106 102
         if(lseek(fd, offset, SEEK_SET) < 0) {
107 103
 	    cli_errmsg("Unzip: __zip_find_disk_trailer: Can't lseek descriptor %d\n", fd);
... ...
@@ -110,7 +109,7 @@ int __zip_find_disk_trailer(int fd, off_t filesize, struct zip_disk_trailer *tra
110 110
 	}
111 111
 
112 112
         if(read(fd, buf, (size_t) bufsize) < (ssize_t) bufsize) {
113
-	    cli_errmsg("unzip: __zip_find_disk_trailer: Can't read %d bytes\n", bufsize);
113
+	    cli_errmsg("Unzip: __zip_find_disk_trailer: Can't read %d bytes\n", bufsize);
114 114
 	    free(buf);
115 115
 	    return CL_EIO;
116 116
 	}
... ...
@@ -125,8 +124,37 @@ int __zip_find_disk_trailer(int fd, off_t filesize, struct zip_disk_trailer *tra
125 125
 		    trailer->z_comment = 0; 
126 126
 		}
127 127
 		__fixup_rootseek(offset + tail - buf, trailer);
128
-		free(buf);
129
-		return CL_SUCCESS;
128
+
129
+		u_rootseek = EC32(trailer->z_rootseek);
130
+		if(u_rootseek > filesize) {
131
+		    cli_dbgmsg("Unzip: __zip_find_disk_trailer: u_rootseek > filesize, continue search\n");
132
+		    continue;
133
+		}
134
+
135
+		for(i = 0; i < 2; i++) {
136
+		    if(u_rootseek + shift + sizeof(dirent) < filesize) {
137
+			if(lseek(fd, u_rootseek + shift, SEEK_SET) < 0) {
138
+			    cli_errmsg("Unzip: __zip_find_disk_trailer: Can't lseek descriptor %d\n", fd);
139
+			    free(buf);
140
+			    return CL_EIO;
141
+			}
142
+
143
+			if(read(fd, &dirent, sizeof(dirent)) < __sizeof(dirent)) {
144
+			    cli_errmsg("Unzip: __zip_find_disk_trailer: Can't read %d bytes\n", bufsize);
145
+			    free(buf);
146
+			    return CL_EIO;
147
+			}
148
+
149
+			if(EC32(dirent.z_magic) == ZIP_ROOT_DIRENT_MAGIC) {
150
+			    cli_dbgmsg("Unzip: __zip_find_disk_trailer: found file header at %u, shift %u\n", u_rootseek + shift, shift);
151
+			    free(buf);
152
+			    *start = shift;
153
+			    return CL_SUCCESS;
154
+			}
155
+
156
+			shift = *start;
157
+		    }
158
+		}
130 159
 	    }
131 160
 	}
132 161
     }
... ...
@@ -271,7 +299,7 @@ static int __zip_dir_parse(zip_dir *dir, off_t start)
271 271
 	return CL_EIO;
272 272
     }
273 273
 
274
-    if((ret = __zip_find_disk_trailer(dir->fd, sb.st_size, &trailer, start)))
274
+    if((ret = __zip_find_disk_trailer(dir->fd, sb.st_size, &trailer, &start)))
275 275
 	return ret;
276 276
 
277 277
     if((ret = __zip_parse_root_directory(dir->fd, &trailer, &dir->hdr0, start)))
... ...
@@ -41,7 +41,7 @@
41 41
 struct zip_file_header
42 42
 {
43 43
 #   define ZIP_FILE_HEADER_MAGIC 0x04034b50
44
-    unsigned char   z_magic[4];	    /* local file header signature */
44
+    uint32_t	    z_magic;	    /* local file header signature */
45 45
     uint16_t	    z_version;	    /* version needed to extract */
46 46
     uint16_t	    z_flags;	    /* general purpose bit flag */
47 47
     uint16_t	    z_compr;	    /* compression method */
... ...
@@ -71,7 +71,7 @@ struct zip_file_trailer
71 71
 struct zip_root_dirent
72 72
 {
73 73
 #   define ZIP_ROOT_DIRENT_MAGIC 0x02014b50
74
-    unsigned char   z_magic[4];	    /* central file header signature */
74
+    uint32_t	    z_magic;	    /* central file header signature */
75 75
     uint16_t	    z_version1;	    /* version made by */
76 76
     uint16_t	    z_version2;	    /* version needed to extract */
77 77
     uint16_t	    z_flags;	    /* general purpose bit flag */
... ...
@@ -98,7 +98,7 @@ struct zip_root_dirent
98 98
 struct zip_disk_trailer
99 99
 {
100 100
 #   define	    ZIP_DISK_TRAILER_MAGIC 0x06054b50
101
-    unsigned char   z_magic[4];		/* end of central dir signature */
101
+    uint32_t	    z_magic;		/* end of central dir signature */
102 102
     uint16_t	    z_disk;		/* number of this disk */
103 103
     uint16_t	    z_finaldisk;	/* number of the disk with the start
104 104
 					 * of the central dir