1 /*
2  * Leak values functions
3  *
4  * Copyright (C) 2009-2021, Joachim Metz <joachim.metz@gmail.com>
5  *
6  * Refer to AUTHORS for acknowledgements.
7  *
8  * This program is free software: you can redistribute it and/or modify
9  * it under the terms of the GNU Lesser General Public License as published by
10  * the Free Software Foundation, either version 3 of the License, or
11  * (at your option) any later version.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  * GNU General Public License for more details.
17  *
18  * You should have received a copy of the GNU Lesser General Public License
19  * along with this program.  If not, see <https://www.gnu.org/licenses/>.
20  */
21 
22 #include <common.h>
23 #include <byte_stream.h>
24 #include <memory.h>
25 #include <system_string.h>
26 #include <types.h>
27 
28 #include "libmsiecf_debug.h"
29 #include "libmsiecf_definitions.h"
30 #include "libmsiecf_libbfio.h"
31 #include "libmsiecf_libcerror.h"
32 #include "libmsiecf_libcnotify.h"
33 #include "libmsiecf_libfdatetime.h"
34 #include "libmsiecf_libfvalue.h"
35 #include "libmsiecf_leak_values.h"
36 
37 #include "msiecf_leak_record.h"
38 
39 /* Creates leak values
40  * Make sure the value leak_values is referencing, is set to NULL
41  * Returns 1 if successful or -1 on error
42  */
libmsiecf_leak_values_initialize(libmsiecf_leak_values_t ** leak_values,libcerror_error_t ** error)43 int libmsiecf_leak_values_initialize(
44      libmsiecf_leak_values_t **leak_values,
45      libcerror_error_t **error )
46 {
47 	static char *function = "libmsiecf_leak_values_initialize";
48 
49 	if( leak_values == NULL )
50 	{
51 		libcerror_error_set(
52 		 error,
53 		 LIBCERROR_ERROR_DOMAIN_ARGUMENTS,
54 		 LIBCERROR_ARGUMENT_ERROR_INVALID_VALUE,
55 		 "%s: invalid leak values.",
56 		 function );
57 
58 		return( -1 );
59 	}
60 	if( *leak_values != NULL )
61 	{
62 		libcerror_error_set(
63 		 error,
64 		 LIBCERROR_ERROR_DOMAIN_RUNTIME,
65 		 LIBCERROR_RUNTIME_ERROR_VALUE_ALREADY_SET,
66 		 "%s: invalid leak values value already set.",
67 		 function );
68 
69 		return( -1 );
70 	}
71 	*leak_values = memory_allocate_structure(
72 	                libmsiecf_leak_values_t );
73 
74 	if( *leak_values == NULL )
75 	{
76 		libcerror_error_set(
77 		 error,
78 		 LIBCERROR_ERROR_DOMAIN_MEMORY,
79 		 LIBCERROR_MEMORY_ERROR_INSUFFICIENT,
80 		 "%s: unable to create leak values.",
81 		 function );
82 
83 		goto on_error;
84 	}
85 	if( memory_set(
86 	     *leak_values,
87 	     0,
88 	     sizeof( libmsiecf_leak_values_t ) ) == NULL )
89 	{
90 		libcerror_error_set(
91 		 error,
92 		 LIBCERROR_ERROR_DOMAIN_MEMORY,
93 		 LIBCERROR_MEMORY_ERROR_SET_FAILED,
94 		 "%s: unable to clear leak values.",
95 		 function );
96 
97 		goto on_error;
98 	}
99 	return( 1 );
100 
101 on_error:
102 	if( *leak_values != NULL )
103 	{
104 		memory_free(
105 		 *leak_values );
106 
107 		*leak_values = NULL;
108 	}
109 	return( -1 );
110 }
111 
112 /* Frees leak values
113  * Returns 1 if successful or -1 on error
114  */
libmsiecf_leak_values_free(libmsiecf_leak_values_t ** leak_values,libcerror_error_t ** error)115 int libmsiecf_leak_values_free(
116      libmsiecf_leak_values_t **leak_values,
117      libcerror_error_t **error )
118 {
119 	static char *function = "libmsiecf_leak_values_free";
120 	int result            = 1;
121 
122 	if( leak_values == NULL )
123 	{
124 		libcerror_error_set(
125 		 error,
126 		 LIBCERROR_ERROR_DOMAIN_ARGUMENTS,
127 		 LIBCERROR_ARGUMENT_ERROR_INVALID_VALUE,
128 		 "%s: invalid leak values.",
129 		 function );
130 
131 		return( -1 );
132 	}
133 	if( *leak_values != NULL )
134 	{
135 		if( ( *leak_values )->filename != NULL )
136 		{
137 			if( libfvalue_value_free(
138 			     &( ( *leak_values )->filename ),
139 			     error ) != 1 )
140 			{
141 				libcerror_error_set(
142 				 error,
143 				 LIBCERROR_ERROR_DOMAIN_RUNTIME,
144 				 LIBCERROR_RUNTIME_ERROR_FINALIZE_FAILED,
145 				 "%s: unable to free filename value.",
146 				 function );
147 
148 				result = -1;
149 			}
150 		}
151 		memory_free(
152 		 *leak_values );
153 
154 		*leak_values = NULL;
155 	}
156 	return( result );
157 }
158 
159 /* Reads the leak values from a LEAK record
160  * Returns 1 if successful or -1 on error
161  */
libmsiecf_leak_values_read_data(libmsiecf_leak_values_t * leak_values,const uint8_t * data,size_t data_size,int ascii_codepage,uint8_t item_flags,libcerror_error_t ** error)162 int libmsiecf_leak_values_read_data(
163      libmsiecf_leak_values_t *leak_values,
164      const uint8_t *data,
165      size_t data_size,
166      int ascii_codepage,
167      uint8_t item_flags,
168      libcerror_error_t **error )
169 {
170 	static char *function    = "libmsiecf_leak_values_read_data";
171 	ssize_t value_size       = 0;
172 	uint32_t filename_offset = 0;
173 
174 #if defined( HAVE_DEBUG_OUTPUT )
175 	uint32_t value_32bit     = 0;
176 #endif
177 
178 	if( leak_values == NULL )
179 	{
180 		libcerror_error_set(
181 		 error,
182 		 LIBCERROR_ERROR_DOMAIN_ARGUMENTS,
183 		 LIBCERROR_ARGUMENT_ERROR_INVALID_VALUE,
184 		 "%s: invalid leak values.",
185 		 function );
186 
187 		return( -1 );
188 	}
189 	if( data == NULL )
190 	{
191 		libcerror_error_set(
192 		 error,
193 		 LIBCERROR_ERROR_DOMAIN_ARGUMENTS,
194 		 LIBCERROR_ARGUMENT_ERROR_INVALID_VALUE,
195 		 "%s: invalid data.",
196 		 function );
197 
198 		return( -1 );
199 	}
200 	if( data_size < sizeof( msiecf_leak_record_header_t ) )
201 	{
202 		libcerror_error_set(
203 		 error,
204 		 LIBCERROR_ERROR_DOMAIN_ARGUMENTS,
205 		 LIBCERROR_ARGUMENT_ERROR_VALUE_TOO_SMALL,
206 		 "%s: invalid data size value too small.",
207 		 function );
208 
209 		return( -1 );
210 	}
211 	if( data_size > (size_t) SSIZE_MAX )
212 	{
213 		libcerror_error_set(
214 		 error,
215 		 LIBCERROR_ERROR_DOMAIN_RUNTIME,
216 		 LIBCERROR_RUNTIME_ERROR_VALUE_EXCEEDS_MAXIMUM,
217 		 "%s: invalid data size value exceeds maximum.",
218 		 function );
219 
220 		return( -1 );
221 	}
222 #if defined( HAVE_DEBUG_OUTPUT )
223 	if( libcnotify_verbose != 0 )
224 	{
225 		libcnotify_printf(
226 		 "%s: LEAK record data:\n",
227 		 function );
228 		libcnotify_print_data(
229 		 data,
230 		 data_size,
231 		 0 );
232 	}
233 #endif
234 	if( memory_compare(
235 	     ( (msiecf_leak_record_header_t *) data )->signature,
236 	     "LEAK",
237 	     4 ) != 0 )
238 	{
239 		libcerror_error_set(
240 		 error,
241 		 LIBCERROR_ERROR_DOMAIN_RUNTIME,
242 		 LIBCERROR_RUNTIME_ERROR_UNSUPPORTED_VALUE,
243 		 "%s: unsupported signature.",
244 		 function );
245 
246 		goto on_error;
247 	}
248 	byte_stream_copy_to_uint32_little_endian(
249 	 ( (msiecf_leak_record_header_t *) data )->cached_file_size,
250 	 leak_values->cached_file_size );
251 
252 	leak_values->cache_directory_index = ( (msiecf_leak_record_header_t *) data )->cache_directory_index;
253 
254 	byte_stream_copy_to_uint32_little_endian(
255 	 ( (msiecf_leak_record_header_t *) data )->filename_offset,
256 	 filename_offset );
257 
258 #if defined( HAVE_DEBUG_OUTPUT )
259 	if( libcnotify_verbose != 0 )
260 	{
261 		libcnotify_printf(
262 		 "%s: signature\t\t\t\t: %c%c%c%c\n",
263 		 function,
264 		 ( (msiecf_leak_record_header_t *) data )->signature[ 0 ],
265 		 ( (msiecf_leak_record_header_t *) data )->signature[ 1 ],
266 		 ( (msiecf_leak_record_header_t *) data )->signature[ 2 ],
267 		 ( (msiecf_leak_record_header_t *) data )->signature[ 3 ] );
268 
269 		byte_stream_copy_to_uint32_little_endian(
270 		 ( (msiecf_leak_record_header_t *) data )->number_of_blocks,
271 		 value_32bit );
272 		libcnotify_printf(
273 		 "%s: number of blocks\t\t\t: %" PRIu32 "\n",
274 		 function,
275 		 value_32bit );
276 
277 		libcnotify_printf(
278 		 "%s: unknown1:\n",
279 		 function );
280 		libcnotify_print_data(
281 		 ( (msiecf_leak_record_header_t *) data )->unknown1,
282 		 24,
283 		 0 );
284 
285 		libcnotify_printf(
286 		 "%s: cached file size\t\t\t: %" PRIu32 " bytes\n",
287 		 function,
288 		 leak_values->cached_file_size );
289 
290 		libcnotify_printf(
291 		 "%s: unknown3:\n",
292 		 function );
293 		libcnotify_print_data(
294 		 ( (msiecf_leak_record_header_t *) data )->unknown3,
295 		 8,
296 		 0 );
297 
298 		byte_stream_copy_to_uint32_little_endian(
299 		 ( (msiecf_leak_record_header_t *) data )->unknown4,
300 		 value_32bit );
301 		libcnotify_printf(
302 		 "%s: unknown4\t\t\t\t: %" PRIu32 "\n",
303 		 function,
304 		 value_32bit );
305 
306 		libcnotify_printf(
307 		 "%s: unknown5:\n",
308 		 function );
309 		libcnotify_print_data(
310 		 ( (msiecf_leak_record_header_t *) data )->unknown5,
311 		 8,
312 		 0 );
313 
314 		libcnotify_printf(
315 		 "%s: cache directory index\t\t\t: %" PRIu8 "\n",
316 		 function,
317 		 leak_values->cache_directory_index );
318 
319 		libcnotify_printf(
320 		 "%s: unknown7:\n",
321 		 function );
322 		libcnotify_print_data(
323 		 ( (msiecf_leak_record_header_t *) data )->unknown7,
324 		 3,
325 		 0 );
326 
327 		libcnotify_printf(
328 		 "%s: filename offset\t\t\t: %" PRIu32 "\n",
329 		 function,
330 		 filename_offset );
331 
332 		libcnotify_printf(
333 		 "%s: unknown9:\n",
334 		 function );
335 		libcnotify_print_data(
336 		 ( (msiecf_leak_record_header_t *) data )->unknown9,
337 		 24,
338 		 0 );
339 
340 		byte_stream_copy_to_uint32_little_endian(
341 		 ( (msiecf_leak_record_header_t *) data )->unknown10,
342 		 value_32bit );
343 		libcnotify_printf(
344 		 "%s: unknown10\t\t\t\t: %" PRIu32 "\n",
345 		 function,
346 		 value_32bit );
347 
348 		if( libmsiecf_debug_print_fat_date_time_value(
349 		     function,
350 		     "unknown time\t\t\t\t",
351 		     ( (msiecf_leak_record_header_t *) data )->unknown_time,
352 		     4,
353 		     LIBFDATETIME_ENDIAN_LITTLE,
354 		     LIBFDATETIME_STRING_FORMAT_TYPE_CTIME | LIBFDATETIME_STRING_FORMAT_FLAG_DATE_TIME,
355 		     error ) != 1 )
356 		{
357 			libcerror_error_set(
358 			 error,
359 			 LIBCERROR_ERROR_DOMAIN_RUNTIME,
360 			 LIBCERROR_RUNTIME_ERROR_PRINT_FAILED,
361 			 "%s: unable to print FAT date time value.",
362 			 function );
363 
364 			goto on_error;
365 		}
366 	}
367 #endif /* defined( HAVE_DEBUG_OUTPUT ) */
368 
369 	if( filename_offset > 0 )
370 	{
371 		if( filename_offset > data_size )
372 		{
373 			if( ( item_flags & LIBMSIECF_ITEM_FLAG_PARTIAL ) == 0 )
374 			{
375 				libcerror_error_set(
376 				 error,
377 				 LIBCERROR_ERROR_DOMAIN_RUNTIME,
378 				 LIBCERROR_RUNTIME_ERROR_VALUE_OUT_OF_BOUNDS,
379 				 "%s: filename offset exceeds size of URL record data.",
380 				 function );
381 
382 				goto on_error;
383 			}
384 		}
385 		else
386 		{
387 /* TODO remove need for libfvalue */
388 			if( libfvalue_value_type_initialize(
389 			     &( leak_values->filename ),
390 			     LIBFVALUE_VALUE_TYPE_STRING_BYTE_STREAM,
391 			     error ) != 1 )
392 			{
393 				libcerror_error_set(
394 				 error,
395 				 LIBCERROR_ERROR_DOMAIN_RUNTIME,
396 				 LIBCERROR_RUNTIME_ERROR_INITIALIZE_FAILED,
397 				 "%s: unable to create filename value.",
398 				 function );
399 
400 				goto on_error;
401 			}
402 			value_size = libfvalue_value_type_set_data_string(
403 			              leak_values->filename,
404 			              &( data[ filename_offset ] ),
405 			              data_size - filename_offset,
406 			              ascii_codepage,
407 			              LIBFVALUE_VALUE_DATA_FLAG_MANAGED,
408 			              error );
409 
410 			if( value_size == -1 )
411 			{
412 				libcerror_error_set(
413 				 error,
414 				 LIBCERROR_ERROR_DOMAIN_RUNTIME,
415 				 LIBCERROR_RUNTIME_ERROR_SET_FAILED,
416 				 "%s: unable to set data of filename value.",
417 				 function );
418 
419 				goto on_error;
420 			}
421 #if defined( HAVE_DEBUG_OUTPUT )
422 			if( libcnotify_verbose != 0 )
423 			{
424 				libcnotify_printf(
425 				 "%s: filename\t\t\t\t: ",
426 				 function );
427 
428 				if( libfvalue_value_print(
429 				     leak_values->filename,
430 				     0,
431 				     0,
432 				     error ) != 1 )
433 				{
434 					libcerror_error_set(
435 					 error,
436 					 LIBCERROR_ERROR_DOMAIN_RUNTIME,
437 					 LIBCERROR_RUNTIME_ERROR_PRINT_FAILED,
438 					 "%s: unable to print filename value.",
439 					 function );
440 
441 					goto on_error;
442 				}
443 				libcnotify_printf(
444 				 "\n" );
445 			}
446 #endif /* defined( HAVE_DEBUG_OUTPUT ) */
447 
448 			if( ( data[ filename_offset + value_size - 1 ] != 0 )
449 			 && ( ( item_flags & LIBMSIECF_ITEM_FLAG_PARTIAL ) == 0 ) )
450 			{
451 				libcerror_error_set(
452 				 error,
453 				 LIBCERROR_ERROR_DOMAIN_RUNTIME,
454 				 LIBCERROR_RUNTIME_ERROR_UNSUPPORTED_VALUE,
455 				 "%s: unsupported unterminated filename string.",
456 				 function );
457 
458 				goto on_error;
459 			}
460 		}
461 	}
462 #if defined( HAVE_DEBUG_OUTPUT )
463 	if( libcnotify_verbose != 0 )
464 	{
465 		libcnotify_printf(
466 		 "\n" );
467 	}
468 #endif
469 	return( 1 );
470 
471 on_error:
472 	if( leak_values->filename != NULL )
473 	{
474 		libfvalue_value_free(
475 		 &( leak_values->filename ),
476 		 NULL );
477 	}
478 	return( 1 );
479 }
480 
481 /* Reads the leak values from a LEAK record
482  * Returns 1 if successful or -1 on error
483  */
libmsiecf_leak_values_read_file_io_handle(libmsiecf_leak_values_t * leak_values,libbfio_handle_t * file_io_handle,off64_t leak_record_offset,size32_t record_size,int ascii_codepage,uint8_t item_flags,libcerror_error_t ** error)484 int libmsiecf_leak_values_read_file_io_handle(
485      libmsiecf_leak_values_t *leak_values,
486      libbfio_handle_t *file_io_handle,
487      off64_t leak_record_offset,
488      size32_t record_size,
489      int ascii_codepage,
490      uint8_t item_flags,
491      libcerror_error_t **error )
492 {
493 	uint8_t *record_data  = NULL;
494 	static char *function = "libmsiecf_leak_values_read_file_io_handle";
495 	ssize_t read_count    = 0;
496 
497 	if( leak_values == NULL )
498 	{
499 		libcerror_error_set(
500 		 error,
501 		 LIBCERROR_ERROR_DOMAIN_ARGUMENTS,
502 		 LIBCERROR_ARGUMENT_ERROR_INVALID_VALUE,
503 		 "%s: invalid leak values.",
504 		 function );
505 
506 		return( -1 );
507 	}
508 	if( record_size > (size32_t) MEMORY_MAXIMUM_ALLOCATION_SIZE )
509 	{
510 		libcerror_error_set(
511 		 error,
512 		 LIBCERROR_ERROR_DOMAIN_RUNTIME,
513 		 LIBCERROR_RUNTIME_ERROR_VALUE_EXCEEDS_MAXIMUM,
514 		 "%s: invalid record size value exceeds maximum allocation size.",
515 		 function );
516 
517 		return( -1 );
518 	}
519 	if( ( record_size == 0 )
520 	 || ( ( record_size % 8 ) != 0 ) )
521 	{
522 		libcerror_error_set(
523 		 error,
524 		 LIBCERROR_ERROR_DOMAIN_RUNTIME,
525 		 LIBCERROR_RUNTIME_ERROR_UNSUPPORTED_VALUE,
526 		 "%s: unsupported record size.",
527 		 function );
528 
529 		return( -1 );
530 	}
531 	record_data = (uint8_t *) memory_allocate(
532 	                           sizeof( uint8_t ) * record_size );
533 
534 	if( record_data == NULL )
535 	{
536 		libcerror_error_set(
537 		 error,
538 		 LIBCERROR_ERROR_DOMAIN_MEMORY,
539 		 LIBCERROR_MEMORY_ERROR_INSUFFICIENT,
540 		 "%s: unable to create LEAK record data.",
541 		 function );
542 
543 		goto on_error;
544 	}
545 #if defined( HAVE_DEBUG_OUTPUT )
546 	if( libcnotify_verbose != 0 )
547 	{
548 		libcnotify_printf(
549 		 "%s: reading LEAK record at offset: %" PRIi64 " (0x%08" PRIx64 ")\n",
550 		 function,
551 		 leak_record_offset,
552 		 leak_record_offset );
553 	}
554 #endif
555 	read_count = libbfio_handle_read_buffer_at_offset(
556 	              file_io_handle,
557 	              record_data,
558 	              (size_t) record_size,
559 	              leak_record_offset,
560 	              error );
561 
562 	if( read_count != (ssize_t) record_size )
563 	{
564 		libcerror_error_set(
565 		 error,
566 		 LIBCERROR_ERROR_DOMAIN_IO,
567 		 LIBCERROR_IO_ERROR_READ_FAILED,
568 		 "%s: unable to read LEAK record data at offset: %" PRIi64 " (0x%08" PRIx64 ").",
569 		 function,
570 		 leak_record_offset,
571 		 leak_record_offset );
572 
573 		goto on_error;
574 	}
575 	if( libmsiecf_leak_values_read_data(
576 	     leak_values,
577 	     record_data,
578 	     (size_t) record_size,
579 	     ascii_codepage,
580 	     item_flags,
581 	     error ) != 1 )
582 	{
583 		libcerror_error_set(
584 		 error,
585 		 LIBCERROR_ERROR_DOMAIN_IO,
586 		 LIBCERROR_IO_ERROR_READ_FAILED,
587 		 "%s: unable to read LEAK record.",
588 		 function );
589 
590 		goto on_error;
591 	}
592 	memory_free(
593 	 record_data );
594 
595 	return( 1 );
596 
597 on_error:
598 	if( record_data != NULL )
599 	{
600 		memory_free(
601 		 record_data );
602 	}
603 	return( -1 );
604 }
605 
606