Browse code

libclamav/ole2_extract.c: handle files with unusual parameters (bb#954)

git-svn: trunk@4206

Tomasz Kojm authored on 2008/09/24 22:06:57
Showing 2 changed files
... ...
@@ -1,3 +1,7 @@
1
+Wed Sep 24 15:17:12 CEST 2008 (tk)
2
+----------------------------------
3
+  * libclamav/ole2_extract.c: handle files with unusual parameters (bb#954)
4
+
1 5
 Mon Sep 22 23:38:16 CEST 2008 (tk)
2 6
 ----------------------------------
3 7
   * configure: use AF_UNSPEC when testing getaddrinfo()/IPv6 support (bb#1196)
... ...
@@ -81,7 +81,7 @@ typedef struct ole2_header_tag
81 81
 	int16_t byte_order __attribute__ ((packed));			/* -2=intel */
82 82
 
83 83
 	uint16_t log2_big_block_size __attribute__ ((packed));		/* usually 9 (2^9 = 512) */
84
-	uint32_t log2_small_block_size __attribute__ ((packed));	/* usually 6 (2^6 = 128) */
84
+	uint32_t log2_small_block_size __attribute__ ((packed));	/* usually 6 (2^6 = 64) */
85 85
 
86 86
 	int32_t reserved[2] __attribute__ ((packed));
87 87
 	int32_t bat_count __attribute__ ((packed));
... ...
@@ -288,7 +288,7 @@ static void print_ole2_header(ole2_header_t *hdr)
288 288
 	return;
289 289
 }
290 290
 
291
-static int ole2_read_block(int fd, ole2_header_t *hdr, void *buff, int32_t blockno)
291
+static int ole2_read_block(int fd, ole2_header_t *hdr, void *buff, unsigned int size, int32_t blockno)
292 292
 {
293 293
 	off_t offset, offend;
294 294
 
... ...
@@ -297,21 +297,21 @@ static int ole2_read_block(int fd, ole2_header_t *hdr, void *buff, int32_t block
297 297
 	}
298 298
 	
299 299
 	/* other methods: (blockno+1) * 512 or (blockno * block_size) + 512; */
300
-	offset = (blockno << hdr->log2_big_block_size) + 512;	/* 512 is header size */
300
+	offset = (blockno << hdr->log2_big_block_size) + MAX(512, 1 << hdr->log2_big_block_size); /* 512 is header size */
301 301
 	
302 302
 	if (hdr->m_area == NULL) {
303 303
 		if (lseek(fd, offset, SEEK_SET) != offset) {
304 304
 			return FALSE;
305 305
 		}
306
-		if (cli_readn(fd, buff, (1 << hdr->log2_big_block_size)) != (1 << hdr->log2_big_block_size)) {
306
+		if (cli_readn(fd, buff, size) != size) {
307 307
 			return FALSE;
308 308
 		}
309 309
 	} else {
310
-		offend = offset + (1 << hdr->log2_big_block_size);
310
+		offend = offset + size;
311 311
 		if ((offend <= 0) || (offend > hdr->m_length)) {
312 312
 			return FALSE;
313 313
 		}
314
-		memcpy(buff, hdr->m_area+offset, (1 << hdr->log2_big_block_size));
314
+		memcpy(buff, hdr->m_area+offset, size);
315 315
 	}
316 316
 	return TRUE;
317 317
 }
... ...
@@ -330,7 +330,7 @@ static int32_t ole2_get_next_bat_block(int fd, ole2_header_t *hdr, int32_t curre
330 330
 		cli_dbgmsg("bat_array index error\n");
331 331
 		return -10;
332 332
 	}
333
-	if (!ole2_read_block(fd, hdr, &bat,
333
+	if (!ole2_read_block(fd, hdr, &bat, 512,
334 334
 			ole2_endian_convert_32(hdr->bat_array[bat_array_index]))) {
335 335
 		return -1;
336 336
 	}
... ...
@@ -356,20 +356,20 @@ static int32_t ole2_get_next_xbat_block(int fd, ole2_header_t *hdr, int32_t curr
356 356
 
357 357
 	bat_index = current_block % 128;
358 358
 
359
-	if (!ole2_read_block(fd, hdr, &xbat, hdr->xbat_start)) {
359
+	if (!ole2_read_block(fd, hdr, &xbat, 512, hdr->xbat_start)) {
360 360
 		return -1;
361 361
 	}
362 362
 
363 363
 	/* Follow the chain of XBAT blocks */
364 364
 	while (xbat_block_index > 0) {
365
-		if (!ole2_read_block(fd, hdr, &xbat,
365
+		if (!ole2_read_block(fd, hdr, &xbat, 512,
366 366
 				ole2_endian_convert_32(xbat[127]))) {
367 367
 			return -1;
368 368
 		}
369 369
 		xbat_block_index--;
370 370
 	}
371 371
 
372
-	if (!ole2_read_block(fd, hdr, &bat, ole2_endian_convert_32(xbat[bat_blockno]))) {
372
+	if (!ole2_read_block(fd, hdr, &bat, 512, ole2_endian_convert_32(xbat[bat_blockno]))) {
373 373
 		return -1;
374 374
 	}
375 375
 
... ...
@@ -404,7 +404,7 @@ static int32_t ole2_get_next_sbat_block(int fd, ole2_header_t *hdr, int32_t curr
404 404
 		current_bat_block = ole2_get_next_block_number(fd, hdr, current_bat_block);
405 405
 		iter--;
406 406
 	}
407
-	if (!ole2_read_block(fd, hdr, &sbat, current_bat_block)) {
407
+	if (!ole2_read_block(fd, hdr, &sbat, 512, current_bat_block)) {
408 408
 		return -1;
409 409
 	}
410 410
 	return ole2_endian_convert_32(sbat[current_block % 128]);
... ...
@@ -424,7 +424,7 @@ static int32_t ole2_get_sbat_data_block(int fd, ole2_header_t *hdr, void *buff,
424 424
 		return FALSE;
425 425
 	}
426 426
 
427
-	block_count = sbat_index / 8;			/* 8 small blocks per big block */
427
+	block_count = sbat_index / (1 << (hdr->log2_big_block_size - hdr->log2_small_block_size));
428 428
 	current_block = hdr->sbat_root_start;
429 429
 	while (block_count > 0) {
430 430
 		current_block = ole2_get_next_block_number(fd, hdr, current_block);
... ...
@@ -433,62 +433,9 @@ static int32_t ole2_get_sbat_data_block(int fd, ole2_header_t *hdr, void *buff,
433 433
 	/* current_block now contains the block number of the sbat array
434 434
 	   containing the entry for the required small block */
435 435
 
436
-	return(ole2_read_block(fd, hdr, buff, current_block));
436
+	return(ole2_read_block(fd, hdr, buff, 1 << hdr->log2_big_block_size, current_block));
437 437
 }
438 438
 
439
-/* Read the property tree.
440
-   It is read as just an array rather than a tree */
441
-/*
442
-static void ole2_read_property_tree(int fd, ole2_header_t *hdr, const char *dir,
443
-				int (*handler)(int fd, ole2_header_t *hdr, property_t *prop, const char *dir))
444
-{
445
-	property_t prop_block[4];
446
-	int32_t index, current_block, count=0;
447
-	
448
-	current_block = hdr->prop_start;
449
-
450
-	while(current_block >= 0) {
451
-		if (!ole2_read_block(fd, hdr, prop_block,
452
-					current_block)) {
453
-			return;
454
-		}
455
-		for (index=0 ; index < 4 ; index++) {
456
-			if (prop_block[index].type > 0) {
457
-				prop_block[index].name_size = ole2_endian_convert_16(prop_block[index].name_size);
458
-				prop_block[index].prev = ole2_endian_convert_32(prop_block[index].prev);
459
-				prop_block[index].next = ole2_endian_convert_32(prop_block[index].next);
460
-				prop_block[index].child = ole2_endian_convert_32(prop_block[index].child);
461
-				prop_block[index].user_flags = ole2_endian_convert_32(prop_block[index].user_flags);
462
-				prop_block[index].create_lowdate = ole2_endian_convert_32(prop_block[index].create_lowdate);
463
-				prop_block[index].create_highdate = ole2_endian_convert_32(prop_block[index].create_highdate);
464
-				prop_block[index].mod_lowdate = ole2_endian_convert_32(prop_block[index].mod_lowdate);
465
-				prop_block[index].mod_highdate = ole2_endian_convert_32(prop_block[index].mod_highdate);
466
-				prop_block[index].start_block = ole2_endian_convert_32(prop_block[index].start_block);
467
-				prop_block[index].size = ole2_endian_convert_32(prop_block[index].size);
468
-				if (prop_block[index].type > 5) {
469
-					cli_dbgmsg("ERROR: invalid property type: %d\n", prop_block[index].type);
470
-					return;
471
-				}
472
-				if (prop_block[index].type == 5) {
473
-					hdr->sbat_root_start = prop_block[index].start_block;
474
-				}
475
-				print_ole2_property(&prop_block[index]);
476
-				if (!handler(fd, hdr, &prop_block[index], dir)) {
477
-					cli_dbgmsg("ERROR: handler failed\n");
478
-					return;
479
-				}
480
-			}
481
-		}
482
-		current_block = ole2_get_next_block_number(fd, hdr, current_block);
483
-		if (++count > 100000) {
484
-			cli_dbgmsg("ERROR: loop detected\n");
485
-			return;
486
-		}
487
-	}
488
-	return;
489
-}
490
-*/
491
-
492 439
 static int ole2_walk_property_tree(int fd, ole2_header_t *hdr, const char *dir, int32_t prop_index,
493 440
 				   int (*handler)(int fd, ole2_header_t *hdr, property_t *prop, const char *dir, cli_ctx *ctx),
494 441
 				   unsigned int rec_level, unsigned int *file_count, cli_ctx *ctx, unsigned long *scansize)
... ...
@@ -522,7 +469,7 @@ static int ole2_walk_property_tree(int fd, ole2_header_t *hdr, const char *dir,
522 522
 		}
523 523
 	}
524 524
 	idx = prop_index % 4;
525
-	if (!ole2_read_block(fd, hdr, prop_block,
525
+	if (!ole2_read_block(fd, hdr, prop_block, 512,
526 526
 			current_block)) {
527 527
 		return CL_SUCCESS;
528 528
 	}	
... ...
@@ -664,6 +611,7 @@ static int handler_writefile(int fd, ole2_header_t *hdr, property_t *prop, const
664 664
 	if (!blk_bitset) {
665 665
 		cli_errmsg("OLE2 [handler_writefile]: init bitset failed\n");
666 666
 		close(ofd);
667
+		free(buff);
667 668
 		return CL_BREAK;
668 669
 	}
669 670
 	while((current_block >= 0) && (len > 0)) {
... ...
@@ -698,20 +646,21 @@ static int handler_writefile(int fd, ole2_header_t *hdr, property_t *prop, const
698 698
 				cli_bitset_free(blk_bitset);
699 699
 				return CL_SUCCESS;
700 700
 			}
701
-			/* buff now contains the block with 8 small blocks in it */
702
-			offset = 64 * (current_block % 8);
703
-			if (cli_writen(ofd, &buff[offset], MIN(len,64)) != MIN(len,64)) {
701
+			/* buff now contains the block with N small blocks in it */
702
+			offset = (1 << hdr->log2_small_block_size) * (current_block % (1 << (hdr->log2_big_block_size - hdr->log2_small_block_size)));
703
+
704
+			if (cli_writen(ofd, &buff[offset], MIN(len,1 << hdr->log2_small_block_size)) != MIN(len,1 << hdr->log2_small_block_size)) {
704 705
 				close(ofd);
705 706
 				free(buff);
706 707
 				cli_bitset_free(blk_bitset);
707 708
 				return CL_BREAK;
708 709
 			}
709 710
 
710
-			len -= MIN(len,64);
711
+			len -= MIN(len,1 << hdr->log2_small_block_size);
711 712
 			current_block = ole2_get_next_sbat_block(fd, hdr, current_block);
712 713
 		} else {
713 714
 			/* Big block file */
714
-			if (!ole2_read_block(fd, hdr, buff, current_block)) {
715
+			if (!ole2_read_block(fd, hdr, buff, 1 << hdr->log2_big_block_size, current_block)) {
715 716
 				close(ofd);
716 717
 				free(buff);
717 718
 				cli_bitset_free(blk_bitset);
... ...
@@ -821,9 +770,9 @@ static int handler_otf(int fd, ole2_header_t *hdr, property_t *prop, const char
821 821
 	cli_dbgmsg("ole2_get_sbat_data_block failed\n");
822 822
 	break;
823 823
       }
824
-      /* buff now contains the block with 8 small blocks in it */
825
-      offset = 64 * (current_block % 8);
826
-      if (cli_writen(ofd, &buff[offset], MIN(len,64)) != MIN(len,64)) {
824
+      /* buff now contains the block with N small blocks in it */
825
+      offset = (1 << hdr->log2_small_block_size) * (current_block % (1 << (hdr->log2_big_block_size - hdr->log2_small_block_size)));
826
+      if (cli_writen(ofd, &buff[offset], MIN(len,1 << hdr->log2_small_block_size)) != MIN(len,1 << hdr->log2_small_block_size)) {
827 827
 	close(ofd);
828 828
 	free(buff);
829 829
 	cli_bitset_free(blk_bitset);
... ...
@@ -835,11 +784,11 @@ static int handler_otf(int fd, ole2_header_t *hdr, property_t *prop, const char
835 835
 	return CL_BREAK;
836 836
       }
837 837
 
838
-      len -= MIN(len,64);
838
+      len -= MIN(len,1 << hdr->log2_small_block_size);
839 839
       current_block = ole2_get_next_sbat_block(fd, hdr, current_block);
840 840
     } else {
841 841
       /* Big block file */
842
-      if (!ole2_read_block(fd, hdr, buff, current_block)) {
842
+      if (!ole2_read_block(fd, hdr, buff, 1 << hdr->log2_big_block_size, current_block)) {
843 843
 	break;
844 844
       }
845 845
       if (cli_writen(ofd, buff, MIN(len,(1 << hdr->log2_big_block_size))) !=
... ...
@@ -1020,22 +969,22 @@ int cli_ole2_extract(int fd, const char *dirname, cli_ctx *ctx, struct uniq **vb
1020 1020
 		goto abort;
1021 1021
 	}
1022 1022
 
1023
-	if (hdr.log2_big_block_size != 9) {
1024
-		cli_errmsg("WARNING: not scanned; untested big block size - please report\n");
1023
+	if (hdr.log2_big_block_size < 6 || hdr.log2_big_block_size > 30) {
1024
+		cli_dbgmsg("CAN'T PARSE: Invalid big block size (2^%u)\n", hdr.log2_big_block_size);
1025 1025
 		goto abort;
1026 1026
 	}
1027
-	if (hdr.log2_small_block_size != 6) {
1028
-		cli_errmsg("WARNING: not scanned; untested small block size - please report\n");
1027
+	if (!hdr.log2_small_block_size || hdr.log2_small_block_size > hdr.log2_big_block_size) {
1028
+		cli_dbgmsg("CAN'T PARSE: Invalid small block size (2^%u)\n", hdr.log2_small_block_size);
1029 1029
 		goto abort;
1030 1030
 	}
1031
+
1031 1032
 	if (hdr.sbat_cutoff != 4096) {
1032
-		cli_errmsg("WARNING: not scanned; untested sbat cutoff - please report\n");
1033
-		goto abort;
1033
+		cli_dbgmsg("WARNING: Untested sbat cutoff (%u); data may not extract correctly\n", hdr.sbat_cutoff);
1034 1034
 	}
1035 1035
 
1036 1036
 	/* 8 SBAT blocks per file block */
1037
-	hdr.max_block_no = ((statbuf.st_size / hdr.log2_big_block_size) + 1) * 8;
1038
-	
1037
+	hdr.max_block_no = (statbuf.st_size - MAX(512, 1 << hdr.log2_big_block_size)) / (1 << hdr.log2_small_block_size);
1038
+
1039 1039
 	print_ole2_header(&hdr);
1040 1040
 	cli_dbgmsg("Max block number: %lu\n", (unsigned long int) hdr.max_block_no);
1041 1041