/* * Windows Security database export functions * * Copyright (C) 2009-2023, Joachim Metz * * Refer to AUTHORS for acknowledgements. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see . */ #include #include #include #include #include #include #include #include #include "esedbtools_libcerror.h" #include "esedbtools_libesedb.h" #include "esedbtools_libfdatetime.h" #include "esedbtools_libuna.h" #include "export.h" #include "export_handle.h" #include "windows_security.h" enum WINDOWS_SECURITY_KNOWN_COLUMN_TYPES { WINDOWS_SECURITY_KNOWN_COLUMN_TYPE_UNDEFINED, WINDOWS_SECURITY_KNOWN_COLUMN_TYPE_FILETIME, WINDOWS_SECURITY_KNOWN_COLUMN_TYPE_STRING_UTF16_LITTLE_ENDIAN, }; /* Exports a filetime value in a binary data table record value * Returns 1 if successful or -1 on error */ int windows_security_export_record_value_filetime( libesedb_record_t *record, int record_value_entry, uint8_t byte_order, FILE *record_file_stream, libcerror_error_t **error ) { system_character_t filetime_string[ 32 ]; libfdatetime_filetime_t *filetime = NULL; uint8_t *value_data = NULL; static char *function = "windows_security_export_record_value_filetime"; size_t value_data_size = 0; uint32_t column_type = 0; uint8_t value_data_flags = 0; int result = 0; if( record == NULL ) { libcerror_error_set( error, LIBCERROR_ERROR_DOMAIN_ARGUMENTS, LIBCERROR_ARGUMENT_ERROR_INVALID_VALUE, "%s: invalid record.", function ); return( -1 ); } if( record_file_stream == NULL ) { libcerror_error_set( error, LIBCERROR_ERROR_DOMAIN_ARGUMENTS, LIBCERROR_ARGUMENT_ERROR_INVALID_VALUE, "%s: invalid record file stream.", function ); return( -1 ); } if( libesedb_record_get_column_type( record, record_value_entry, &column_type, error ) != 1 ) { libcerror_error_set( error, LIBCERROR_ERROR_DOMAIN_RUNTIME, LIBCERROR_RUNTIME_ERROR_GET_FAILED, "%s: unable to retrieve column type of value: %d.", function, record_value_entry ); goto on_error; } if( column_type != LIBESEDB_COLUMN_TYPE_BINARY_DATA ) { libcerror_error_set( error, LIBCERROR_ERROR_DOMAIN_ARGUMENTS, LIBCERROR_ARGUMENT_ERROR_UNSUPPORTED_VALUE, "%s: unsupported column type: %" PRIu32 "", function, column_type ); goto on_error; } if( libesedb_record_get_value_data_size( record, record_value_entry, &value_data_size, error ) != 1 ) { libcerror_error_set( error, LIBCERROR_ERROR_DOMAIN_RUNTIME, LIBCERROR_RUNTIME_ERROR_GET_FAILED, "%s: unable to retrieve value: %d data size.", function, record_value_entry ); goto on_error; } if( value_data_size > 0 ) { value_data = (uint8_t *) memory_allocate( sizeof( uint8_t ) * value_data_size ); if( value_data == NULL ) { libcerror_error_set( error, LIBCERROR_ERROR_DOMAIN_MEMORY, LIBCERROR_MEMORY_ERROR_INSUFFICIENT, "%s: unable to create value data.", function ); goto on_error; } if( libesedb_record_get_value_data( record, record_value_entry, value_data, value_data_size, error ) != 1 ) { libcerror_error_set( error, LIBCERROR_ERROR_DOMAIN_RUNTIME, LIBCERROR_RUNTIME_ERROR_GET_FAILED, "%s: unable to retrieve value: %d data.", function, record_value_entry ); goto on_error; } } if( libesedb_record_get_value_data_flags( record, record_value_entry, &value_data_flags, error ) != 1 ) { libcerror_error_set( error, LIBCERROR_ERROR_DOMAIN_RUNTIME, LIBCERROR_RUNTIME_ERROR_GET_FAILED, "%s: unable to retrieve value: %d data flags.", function, record_value_entry ); goto on_error; } if( ( value_data_flags & ~( LIBESEDB_VALUE_FLAG_VARIABLE_SIZE ) ) == 0 ) { if( value_data != NULL ) { if( value_data_size != 8 ) { libcerror_error_set( error, LIBCERROR_ERROR_DOMAIN_ARGUMENTS, LIBCERROR_ARGUMENT_ERROR_UNSUPPORTED_VALUE, "%s: unsupported value data size: %" PRIzd "", function, value_data_size ); goto on_error; } if( libfdatetime_filetime_initialize( &filetime, error ) != 1 ) { libcerror_error_set( error, LIBCERROR_ERROR_DOMAIN_RUNTIME, LIBCERROR_RUNTIME_ERROR_INITIALIZE_FAILED, "%s: unable to create filetime.", function ); goto on_error; } if( libfdatetime_filetime_copy_from_byte_stream( filetime, value_data, value_data_size, byte_order, error ) != 1 ) { libcerror_error_set( error, LIBCERROR_ERROR_DOMAIN_RUNTIME, LIBCERROR_RUNTIME_ERROR_COPY_FAILED, "%s: unable to copy byte stream to filetime.", function ); goto on_error; } #if defined( HAVE_WIDE_SYSTEM_CHARACTER ) result = libfdatetime_filetime_copy_to_utf16_string( filetime, (uint16_t *) filetime_string, 32, LIBFDATETIME_STRING_FORMAT_TYPE_CTIME | LIBFDATETIME_STRING_FORMAT_FLAG_DATE_TIME_NANO_SECONDS, error ); #else result = libfdatetime_filetime_copy_to_utf8_string( filetime, (uint8_t *) filetime_string, 32, LIBFDATETIME_STRING_FORMAT_TYPE_CTIME | LIBFDATETIME_STRING_FORMAT_FLAG_DATE_TIME_NANO_SECONDS, error ); #endif if( result != 1 ) { libcerror_error_set( error, LIBCERROR_ERROR_DOMAIN_RUNTIME, LIBCERROR_RUNTIME_ERROR_COPY_FAILED, "%s: unable to copy filetime to string.", function ); goto on_error; } if( libfdatetime_filetime_free( &filetime, error ) != 1 ) { libcerror_error_set( error, LIBCERROR_ERROR_DOMAIN_RUNTIME, LIBCERROR_RUNTIME_ERROR_FINALIZE_FAILED, "%s: unable to free filetime.", function ); goto on_error; } fprintf( record_file_stream, "%" PRIs_SYSTEM "", filetime_string ); } } else { export_binary_data( value_data, value_data_size, record_file_stream ); } memory_free( value_data ); return( 1 ); on_error: if( filetime != NULL ) { libfdatetime_filetime_free( &filetime, NULL ); } if( value_data != NULL ) { memory_free( value_data ); } return( -1 ); } /* Exports an UTF-16 string in a binary data table record value * Returns 1 if successful or -1 on error */ int windows_security_export_record_value_utf16_string( libesedb_record_t *record, int record_value_entry, uint8_t byte_order, FILE *record_file_stream, libcerror_error_t **error ) { system_character_t *value_string = NULL; uint8_t *value_data = NULL; static char *function = "windows_security_export_record_value_utf16_string"; size_t value_data_size = 0; size_t value_string_size = 0; uint32_t column_type = 0; uint8_t value_data_flags = 0; int result = 0; if( record == NULL ) { libcerror_error_set( error, LIBCERROR_ERROR_DOMAIN_ARGUMENTS, LIBCERROR_ARGUMENT_ERROR_INVALID_VALUE, "%s: invalid record.", function ); return( -1 ); } if( record_file_stream == NULL ) { libcerror_error_set( error, LIBCERROR_ERROR_DOMAIN_ARGUMENTS, LIBCERROR_ARGUMENT_ERROR_INVALID_VALUE, "%s: invalid record file stream.", function ); return( -1 ); } if( libesedb_record_get_column_type( record, record_value_entry, &column_type, error ) != 1 ) { libcerror_error_set( error, LIBCERROR_ERROR_DOMAIN_RUNTIME, LIBCERROR_RUNTIME_ERROR_GET_FAILED, "%s: unable to retrieve column type of value: %d.", function, record_value_entry ); goto on_error; } if( ( column_type != LIBESEDB_COLUMN_TYPE_BINARY_DATA ) && ( column_type != LIBESEDB_COLUMN_TYPE_LARGE_BINARY_DATA ) ) { libcerror_error_set( error, LIBCERROR_ERROR_DOMAIN_ARGUMENTS, LIBCERROR_ARGUMENT_ERROR_UNSUPPORTED_VALUE, "%s: unsupported column type: %" PRIu32 "", function, column_type ); goto on_error; } if( libesedb_record_get_value_data_size( record, record_value_entry, &value_data_size, error ) != 1 ) { libcerror_error_set( error, LIBCERROR_ERROR_DOMAIN_RUNTIME, LIBCERROR_RUNTIME_ERROR_GET_FAILED, "%s: unable to retrieve value: %d data size.", function, record_value_entry ); goto on_error; } if( value_data_size > 0 ) { value_data = (uint8_t *) memory_allocate( sizeof( uint8_t ) * value_data_size ); if( value_data == NULL ) { libcerror_error_set( error, LIBCERROR_ERROR_DOMAIN_MEMORY, LIBCERROR_MEMORY_ERROR_INSUFFICIENT, "%s: unable to create value data.", function ); goto on_error; } if( libesedb_record_get_value_data( record, record_value_entry, value_data, value_data_size, error ) != 1 ) { libcerror_error_set( error, LIBCERROR_ERROR_DOMAIN_RUNTIME, LIBCERROR_RUNTIME_ERROR_GET_FAILED, "%s: unable to retrieve value: %d data.", function, record_value_entry ); goto on_error; } } if( libesedb_record_get_value_data_flags( record, record_value_entry, &value_data_flags, error ) != 1 ) { libcerror_error_set( error, LIBCERROR_ERROR_DOMAIN_RUNTIME, LIBCERROR_RUNTIME_ERROR_GET_FAILED, "%s: unable to retrieve value: %d data flags.", function, record_value_entry ); goto on_error; } if( ( value_data_flags & ~( LIBESEDB_VALUE_FLAG_VARIABLE_SIZE ) ) == 0 ) { if( value_data != NULL ) { #if defined( HAVE_WIDE_SYSTEM_CHARACTER ) result = libuna_utf16_string_size_from_utf16_stream( value_data, value_data_size, byte_order, &value_string_size, error ); #else result = libuna_utf8_string_size_from_utf16_stream( value_data, value_data_size, byte_order, &value_string_size, error ); #endif if( result != 1 ) { libcerror_error_set( error, LIBCERROR_ERROR_DOMAIN_RUNTIME, LIBCERROR_RUNTIME_ERROR_GET_FAILED, "%s: unable to determine size of value string: %d.", function, record_value_entry ); goto on_error; } value_string = system_string_allocate( value_string_size ); if( value_string == NULL ) { libcerror_error_set( error, LIBCERROR_ERROR_DOMAIN_MEMORY, LIBCERROR_MEMORY_ERROR_INSUFFICIENT, "%s: unable to create value string.", function ); return( -1 ); } #if defined( HAVE_WIDE_SYSTEM_CHARACTER ) result = libuna_utf16_string_copy_from_utf16_stream( (uint16_t *) value_string, value_string_size, value_data, value_data_size, byte_order, error ); #else result = libuna_utf8_string_copy_from_utf16_stream( (uint8_t *) value_string, value_string_size, value_data, value_data_size, byte_order, error ); #endif if( result != 1 ) { libcerror_error_set( error, LIBCERROR_ERROR_DOMAIN_RUNTIME, LIBCERROR_RUNTIME_ERROR_GET_FAILED, "%s: unable to retrieve value string: %d.", function, record_value_entry ); memory_free( value_string ); goto on_error; } export_text( value_string, value_string_size, record_file_stream ); memory_free( value_string ); } } else { export_binary_data( value_data, value_data_size, record_file_stream ); } memory_free( value_data ); return( 1 ); on_error: if( value_data != NULL ) { memory_free( value_data ); } return( -1 ); } /* Exports the values in a SmTblVersion table record * Returns 1 if successful or -1 on error */ int windows_security_export_record_smtblversion( libesedb_record_t *record, FILE *record_file_stream, log_handle_t *log_handle, libcerror_error_t **error ) { system_character_t column_name[ 256 ]; static char *function = "windows_security_export_record_smtblversion"; size_t column_name_size = 0; uint32_t column_type = 0; int known_column_type = 0; int number_of_values = 0; int result = 0; int value_iterator = 0; uint8_t byte_order = _BYTE_STREAM_ENDIAN_LITTLE; if( record == NULL ) { libcerror_error_set( error, LIBCERROR_ERROR_DOMAIN_ARGUMENTS, LIBCERROR_ARGUMENT_ERROR_INVALID_VALUE, "%s: invalid record.", function ); return( -1 ); } if( record_file_stream == NULL ) { libcerror_error_set( error, LIBCERROR_ERROR_DOMAIN_ARGUMENTS, LIBCERROR_ARGUMENT_ERROR_INVALID_VALUE, "%s: invalid record file stream.", function ); return( -1 ); } if( libesedb_record_get_number_of_values( record, &number_of_values, error ) != 1 ) { libcerror_error_set( error, LIBCERROR_ERROR_DOMAIN_RUNTIME, LIBCERROR_RUNTIME_ERROR_GET_FAILED, "%s: unable to retrieve number of values.", function ); return( -1 ); } for( value_iterator = 0; value_iterator < number_of_values; value_iterator++ ) { #if defined( HAVE_WIDE_SYSTEM_CHARACTER ) result = libesedb_record_get_utf16_column_name_size( record, value_iterator, &column_name_size, error ); #else result = libesedb_record_get_utf8_column_name_size( record, value_iterator, &column_name_size, error ); #endif if( result != 1 ) { libcerror_error_set( error, LIBCERROR_ERROR_DOMAIN_RUNTIME, LIBCERROR_RUNTIME_ERROR_GET_FAILED, "%s: unable to retrieve column name size of value: %d.", function, value_iterator ); return( -1 ); } /* It is assumed that the column name cannot be larger than 255 characters * otherwise using dynamic allocation is more appropriate */ if( column_name_size > 256 ) { libcerror_error_set( error, LIBCERROR_ERROR_DOMAIN_RUNTIME, LIBCERROR_RUNTIME_ERROR_VALUE_OUT_OF_BOUNDS, "%s: column name size value exceeds maximum.", function ); return( -1 ); } #if defined( HAVE_WIDE_SYSTEM_CHARACTER ) result = libesedb_record_get_utf16_column_name( record, value_iterator, (uint16_t *) column_name, column_name_size, error ); #else result = libesedb_record_get_utf8_column_name( record, value_iterator, (uint8_t *) column_name, column_name_size, error ); #endif if( result != 1 ) { libcerror_error_set( error, LIBCERROR_ERROR_DOMAIN_RUNTIME, LIBCERROR_RUNTIME_ERROR_GET_FAILED, "%s: unable to retrieve column name of value: %d.", function, value_iterator ); return( -1 ); } if( libesedb_record_get_column_type( record, value_iterator, &column_type, error ) != 1 ) { libcerror_error_set( error, LIBCERROR_ERROR_DOMAIN_RUNTIME, LIBCERROR_RUNTIME_ERROR_GET_FAILED, "%s: unable to retrieve column type of value: %d.", function, value_iterator ); return( -1 ); } known_column_type = WINDOWS_SECURITY_KNOWN_COLUMN_TYPE_UNDEFINED; /* Only check for known columns of the binary data type * some columns get their type reassigned over time */ if( ( column_type == LIBESEDB_COLUMN_TYPE_BINARY_DATA ) || ( column_type == LIBESEDB_COLUMN_TYPE_LARGE_BINARY_DATA ) ) { if( column_name_size == 16 ) { if( system_string_compare( column_name, _SYSTEM_STRING( "ConfigTimeStamp" ), 15 ) == 0 ) { known_column_type = WINDOWS_SECURITY_KNOWN_COLUMN_TYPE_FILETIME; } } else if( column_name_size == 17 ) { if( system_string_compare( column_name, _SYSTEM_STRING( "AnalyzeTimeStamp" ), 16 ) == 0 ) { known_column_type = WINDOWS_SECURITY_KNOWN_COLUMN_TYPE_FILETIME; } } else if( column_name_size == 19 ) { if( system_string_compare( column_name, _SYSTEM_STRING( "ProfileDescription" ), 18 ) == 0 ) { known_column_type = WINDOWS_SECURITY_KNOWN_COLUMN_TYPE_STRING_UTF16_LITTLE_ENDIAN; } } } if( known_column_type == WINDOWS_SECURITY_KNOWN_COLUMN_TYPE_FILETIME ) { result = windows_security_export_record_value_filetime( record, value_iterator, byte_order, record_file_stream, error ); } else if( known_column_type == WINDOWS_SECURITY_KNOWN_COLUMN_TYPE_STRING_UTF16_LITTLE_ENDIAN ) { result = windows_security_export_record_value_utf16_string( record, value_iterator, LIBUNA_ENDIAN_LITTLE, record_file_stream, error ); } else if( known_column_type == WINDOWS_SECURITY_KNOWN_COLUMN_TYPE_UNDEFINED ) { result = export_handle_export_record_value( record, value_iterator, record_file_stream, log_handle, error ); } if( result != 1 ) { libcerror_error_set( error, LIBCERROR_ERROR_DOMAIN_RUNTIME, LIBCERROR_RUNTIME_ERROR_GENERIC, "%s: unable to export record value: %d.", function, value_iterator ); return( -1 ); } if( value_iterator == ( number_of_values - 1 ) ) { fprintf( record_file_stream, "\n" ); } else { fprintf( record_file_stream, "\t" ); } } return( 1 ); } /* Exports the values in a SmTblSection table record * Returns 1 if successful or -1 on error */ int windows_security_export_record_smtblsection( libesedb_record_t *record, FILE *record_file_stream, log_handle_t *log_handle, libcerror_error_t **error ) { system_character_t column_name[ 256 ]; static char *function = "windows_security_export_record_smtblsection"; size_t column_name_size = 0; uint32_t column_type = 0; int known_column_type = 0; int number_of_values = 0; int result = 0; int value_iterator = 0; uint8_t byte_order = _BYTE_STREAM_ENDIAN_LITTLE; if( record == NULL ) { libcerror_error_set( error, LIBCERROR_ERROR_DOMAIN_ARGUMENTS, LIBCERROR_ARGUMENT_ERROR_INVALID_VALUE, "%s: invalid record.", function ); return( -1 ); } if( record_file_stream == NULL ) { libcerror_error_set( error, LIBCERROR_ERROR_DOMAIN_ARGUMENTS, LIBCERROR_ARGUMENT_ERROR_INVALID_VALUE, "%s: invalid record file stream.", function ); return( -1 ); } if( libesedb_record_get_number_of_values( record, &number_of_values, error ) != 1 ) { libcerror_error_set( error, LIBCERROR_ERROR_DOMAIN_RUNTIME, LIBCERROR_RUNTIME_ERROR_GET_FAILED, "%s: unable to retrieve number of values.", function ); return( -1 ); } for( value_iterator = 0; value_iterator < number_of_values; value_iterator++ ) { #if defined( HAVE_WIDE_SYSTEM_CHARACTER ) result = libesedb_record_get_utf16_column_name_size( record, value_iterator, &column_name_size, error ); #else result = libesedb_record_get_utf8_column_name_size( record, value_iterator, &column_name_size, error ); #endif if( result != 1 ) { libcerror_error_set( error, LIBCERROR_ERROR_DOMAIN_RUNTIME, LIBCERROR_RUNTIME_ERROR_GET_FAILED, "%s: unable to retrieve column name size of value: %d.", function, value_iterator ); return( -1 ); } /* It is assumed that the column name cannot be larger than 255 characters * otherwise using dynamic allocation is more appropriate */ if( column_name_size > 256 ) { libcerror_error_set( error, LIBCERROR_ERROR_DOMAIN_RUNTIME, LIBCERROR_RUNTIME_ERROR_VALUE_OUT_OF_BOUNDS, "%s: column name size value exceeds maximum.", function ); return( -1 ); } #if defined( HAVE_WIDE_SYSTEM_CHARACTER ) result = libesedb_record_get_utf16_column_name( record, value_iterator, (uint16_t *) column_name, column_name_size, error ); #else result = libesedb_record_get_utf8_column_name( record, value_iterator, (uint8_t *) column_name, column_name_size, error ); #endif if( result != 1 ) { libcerror_error_set( error, LIBCERROR_ERROR_DOMAIN_RUNTIME, LIBCERROR_RUNTIME_ERROR_GET_FAILED, "%s: unable to retrieve column name of value: %d.", function, value_iterator ); return( -1 ); } if( libesedb_record_get_column_type( record, value_iterator, &column_type, error ) != 1 ) { libcerror_error_set( error, LIBCERROR_ERROR_DOMAIN_RUNTIME, LIBCERROR_RUNTIME_ERROR_GET_FAILED, "%s: unable to retrieve column type of value: %d.", function, value_iterator ); return( -1 ); } known_column_type = WINDOWS_SECURITY_KNOWN_COLUMN_TYPE_UNDEFINED; /* Only check for known columns of the binary data type * some columns get their type reassigned over time */ if( ( column_type == LIBESEDB_COLUMN_TYPE_BINARY_DATA ) || ( column_type == LIBESEDB_COLUMN_TYPE_LARGE_BINARY_DATA ) ) { if( column_name_size == 5 ) { if( system_string_compare( column_name, _SYSTEM_STRING( "Name" ), 4 ) == 0 ) { known_column_type = WINDOWS_SECURITY_KNOWN_COLUMN_TYPE_STRING_UTF16_LITTLE_ENDIAN; } } } if( known_column_type == WINDOWS_SECURITY_KNOWN_COLUMN_TYPE_FILETIME ) { result = windows_security_export_record_value_filetime( record, value_iterator, byte_order, record_file_stream, error ); } else if( known_column_type == WINDOWS_SECURITY_KNOWN_COLUMN_TYPE_STRING_UTF16_LITTLE_ENDIAN ) { result = windows_security_export_record_value_utf16_string( record, value_iterator, LIBUNA_ENDIAN_LITTLE, record_file_stream, error ); } else if( known_column_type == WINDOWS_SECURITY_KNOWN_COLUMN_TYPE_UNDEFINED ) { result = export_handle_export_record_value( record, value_iterator, record_file_stream, log_handle, error ); } if( result != 1 ) { libcerror_error_set( error, LIBCERROR_ERROR_DOMAIN_RUNTIME, LIBCERROR_RUNTIME_ERROR_GENERIC, "%s: unable to export record value: %d.", function, value_iterator ); return( -1 ); } if( value_iterator == ( number_of_values - 1 ) ) { fprintf( record_file_stream, "\n" ); } else { fprintf( record_file_stream, "\t" ); } } return( 1 ); }