Files
simplewall/src/db.c
T
2023-07-23 15:35:00 +06:00

2031 lines
38 KiB
C

// simplewall
// Copyright (c) 2019-2023 Henry++
#include "global.h"
_Success_ (NT_SUCCESS (return))
NTSTATUS _app_db_initialize (
_Out_ PDB_INFORMATION db_info,
_In_ BOOLEAN is_reader
)
{
NTSTATUS status;
RtlZeroMemory (db_info, sizeof (DB_INFORMATION));
status = _r_xml_initializelibrary (&db_info->xml_library, is_reader);
return status;
}
VOID _app_db_destroy (
_Inout_ PDB_INFORMATION db_info
)
{
if (db_info->bytes)
_r_obj_clearreference (&db_info->bytes);
_r_xml_destroylibrary (&db_info->xml_library);
}
_Success_ (NT_SUCCESS (return))
NTSTATUS _app_db_encrypt (
_In_ PR_BYTEREF bytes,
_Out_ PR_BYTE_PTR out_buffer
)
{
R_CRYPT_CONTEXT crypt_context;
R_BYTEREF key;
NTSTATUS status;
*out_buffer = NULL;
status = _r_crypt_createcryptcontext (&crypt_context, BCRYPT_AES_ALGORITHM);
if (!NT_SUCCESS (status))
return status;
_r_obj_initializebyteref (&key, PROFILE2_KEY);
status = _r_crypt_generatekey (&crypt_context, &key);
if (!NT_SUCCESS (status))
goto CleanupExit;
status = _r_crypt_encryptbuffer (&crypt_context, bytes->buffer, (ULONG)bytes->length, out_buffer);
CleanupExit:
_r_crypt_destroycryptcontext (&crypt_context);
return status;
}
_Success_ (NT_SUCCESS (return))
NTSTATUS _app_db_decrypt (
_In_ PR_BYTEREF bytes,
_Out_ PR_BYTE_PTR out_buffer
)
{
R_CRYPT_CONTEXT crypt_context;
R_BYTEREF key;
NTSTATUS status;
*out_buffer = NULL;
status = _r_crypt_createcryptcontext (&crypt_context, BCRYPT_AES_ALGORITHM);
if (!NT_SUCCESS (status))
return status;
_r_obj_initializebyteref (&key, PROFILE2_KEY);
status = _r_crypt_generatekey (&crypt_context, &key);
if (!NT_SUCCESS (status))
goto CleanupExit;
status = _r_crypt_decryptbuffer (&crypt_context, bytes->buffer, (ULONG)bytes->length, out_buffer);
CleanupExit:
_r_crypt_destroycryptcontext (&crypt_context);
return status;
}
_Success_ (NT_SUCCESS (return))
NTSTATUS _app_db_gethash (
_In_ PR_BYTEREF bytes,
_Out_ PR_BYTE_PTR out_buffer
)
{
R_CRYPT_CONTEXT hash_context;
NTSTATUS status;
*out_buffer = NULL;
status = _r_crypt_createhashcontext (&hash_context, BCRYPT_SHA256_ALGORITHM);
if (!NT_SUCCESS (status))
return status;
status = _r_crypt_hashbuffer (&hash_context, bytes->buffer, (ULONG)bytes->length);
if (NT_SUCCESS (status))
status = _r_crypt_finalhashcontext (&hash_context, NULL, out_buffer);
_r_crypt_destroycryptcontext (&hash_context);
return status;
}
BYTE _app_getprofiletype ()
{
LONG profile_type;
profile_type = _r_config_getlong (L"ProfileType", 0);
switch (profile_type)
{
case 1:
{
return PROFILE2_ID_COMPRESSED;
}
case 2:
{
return PROFILE2_ID_ENCRYPTED;
}
}
return PROFILE2_ID_PLAIN;
}
_Success_ (return == STATUS_SUCCESS)
NTSTATUS _app_db_ishashvalid (
_In_ PR_BYTEREF buffer,
_In_ PR_BYTEREF hash_bytes
)
{
PR_BYTE new_hash_bytes = NULL;
NTSTATUS status;
status = _app_db_gethash (buffer, &new_hash_bytes);
if (!NT_SUCCESS (status))
return status;
if (RtlEqualMemory (hash_bytes->buffer, new_hash_bytes->buffer, new_hash_bytes->length))
{
status = STATUS_SUCCESS;
}
else
{
status = STATUS_HASH_NOT_PRESENT;
}
_r_obj_dereference (new_hash_bytes);
return status;
}
_Success_ (return == STATUS_SUCCESS)
NTSTATUS _app_db_istypevalid (
_In_ PDB_INFORMATION db_info,
_In_ ENUM_TYPE_XML type,
_In_ ENUM_VERSION_XML min_version
)
{
if (db_info->type != type)
return STATUS_NDIS_INVALID_DATA;
if (db_info->version >= min_version)
return STATUS_SUCCESS;
return STATUS_FILE_NOT_SUPPORTED;
}
_Success_ (return == STATUS_SUCCESS)
NTSTATUS _app_db_openfrombuffer (
_Inout_ PDB_INFORMATION db_info,
_In_ PR_BYTEREF buffer,
_In_ ENUM_VERSION_XML min_version,
_In_ ENUM_TYPE_XML type
)
{
NTSTATUS status;
_r_obj_movereference (&db_info->bytes, _r_obj_createbyte3 (buffer));
status = _app_db_decodebuffer (db_info);
if (status != STATUS_SUCCESS)
return status;
status = _app_db_istypevalid (db_info, type, min_version);
return status;
}
_Success_ (return == STATUS_SUCCESS)
NTSTATUS _app_db_openfromfile (
_Inout_ PDB_INFORMATION db_info,
_In_ PR_STRING path,
_In_ ENUM_VERSION_XML min_version,
_In_ ENUM_TYPE_XML type
)
{
NTSTATUS status;
if (db_info->bytes)
_r_obj_dereference (db_info->bytes);
status = _r_fs_mapfile (path->buffer, NULL, &db_info->bytes);
if (status != ERROR_SUCCESS)
return status;
status = _app_db_decodebuffer (db_info);
if (status != STATUS_SUCCESS)
return status;
status = _app_db_istypevalid (db_info, type, min_version);
return status;
}
VOID _app_db_parse_app (
_Inout_ PDB_INFORMATION db_info
)
{
PITEM_APP ptr_app;
ULONG_PTR app_hash;
PR_STRING string;
PR_STRING hash;
PR_STRING dos_path;
LONG64 timestamp;
LONG64 timer;
BOOLEAN is_enabled;
BOOLEAN is_silent;
string = _r_xml_getattribute_string (&db_info->xml_library, L"path");
if (!string)
return;
// workaround for native paths
// https://github.com/henrypp/simplewall/issues/817
if (_r_str_isstartswith2 (&string->sr, L"\\device\\", TRUE))
{
dos_path = _r_path_dospathfromnt (string);
if (dos_path)
_r_obj_movereference (&string, dos_path);
}
if (!_r_obj_isstringempty2 (string))
{
app_hash = _app_addapplication (NULL, DATA_UNKNOWN, string, NULL, NULL);
if (app_hash)
{
ptr_app = _app_getappitem (app_hash);
if (ptr_app)
{
is_enabled = _r_xml_getattribute_boolean (&db_info->xml_library, L"is_enabled");
is_silent = _r_xml_getattribute_boolean (&db_info->xml_library, L"is_silent");
timestamp = _r_xml_getattribute_long64 (&db_info->xml_library, L"timestamp");
timer = _r_xml_getattribute_long64 (&db_info->xml_library, L"timer");
hash = _r_xml_getattribute_string (&db_info->xml_library, L"hash");
if (hash)
_app_setappinfo (ptr_app, INFO_HASH, hash);
if (is_silent)
_app_setappinfo (ptr_app, INFO_IS_SILENT, IntToPtr (is_silent));
if (is_enabled)
_app_setappinfo (ptr_app, INFO_IS_ENABLED, IntToPtr (is_enabled));
if (timestamp)
_app_setappinfo (ptr_app, INFO_TIMESTAMP, &timestamp);
if (timer)
_app_setappinfo (ptr_app, INFO_TIMER, &timer);
_r_obj_dereference (ptr_app);
}
}
}
_r_obj_dereference (string);
}
VOID _app_db_parse_rule (
_Inout_ PDB_INFORMATION db_info,
_In_ ENUM_TYPE_DATA type
)
{
R_STRINGBUILDER sb;
R_STRINGREF sr;
R_STRINGREF first_part;
ULONG_PTR app_hash;
PR_STRING rule_name;
PR_STRING rule_remote;
PR_STRING rule_local;
PR_STRING path_string;
PR_STRING string;
PITEM_RULE_CONFIG ptr_config;
LONG blocklist_spy_state;
LONG blocklist_update_state;
LONG blocklist_extra_state;
FWP_DIRECTION direction;
UINT8 protocol;
ADDRESS_FAMILY af;
PITEM_RULE ptr_rule;
ULONG_PTR rule_hash;
BOOLEAN is_internal;
NTSTATUS status;
// check support version
if (_r_xml_getattribute (&db_info->xml_library, L"os_version", &sr))
{
if (!_app_isrulesupportedbyos (&sr))
return;
}
rule_name = _r_xml_getattribute_string (&db_info->xml_library, L"name");
if (!rule_name)
return;
rule_remote = _r_xml_getattribute_string (&db_info->xml_library, L"rule");
rule_local = _r_xml_getattribute_string (&db_info->xml_library, L"rule_local");
direction = (FWP_DIRECTION)_r_xml_getattribute_long (&db_info->xml_library, L"dir");
protocol = (UINT8)_r_xml_getattribute_long (&db_info->xml_library, L"protocol");
af = (ADDRESS_FAMILY)_r_xml_getattribute_long (&db_info->xml_library, L"version");
ptr_rule = _app_addrule (rule_name, rule_remote, rule_local, direction, protocol, af);
_r_obj_dereference (rule_name);
if (rule_remote)
_r_obj_dereference (rule_remote);
if (rule_local)
_r_obj_dereference (rule_local);
rule_hash = _r_str_gethash2 (ptr_rule->name, TRUE);
ptr_rule->type = (type == DATA_RULE_SYSTEM_USER) ? DATA_RULE_USER : type;
ptr_rule->action = _r_xml_getattribute_boolean (&db_info->xml_library, L"is_block") ? FWP_ACTION_BLOCK : FWP_ACTION_PERMIT;
ptr_rule->is_forservices = _r_xml_getattribute_boolean (&db_info->xml_library, L"is_services");
ptr_rule->is_readonly = (type != DATA_RULE_USER);
ptr_rule->is_enabled = _r_xml_getattribute_boolean (&db_info->xml_library, L"is_enabled");
// calculate rule weight
if (type == DATA_RULE_BLOCKLIST)
{
ptr_rule->weight = FW_WEIGHT_RULE_BLOCKLIST;
}
else if (type == DATA_RULE_SYSTEM || type == DATA_RULE_SYSTEM_USER)
{
ptr_rule->weight = FW_WEIGHT_RULE_SYSTEM;
}
else if (type == DATA_RULE_USER)
{
ptr_rule->weight = (ptr_rule->action == FWP_ACTION_BLOCK) ? FW_WEIGHT_RULE_USER_BLOCK : FW_WEIGHT_RULE_USER;
}
if (type == DATA_RULE_BLOCKLIST)
{
blocklist_spy_state = _r_calc_clamp (_r_config_getlong (L"BlocklistSpyState", 2), 0, 2);
blocklist_update_state = _r_calc_clamp (_r_config_getlong (L"BlocklistUpdateState", 0), 0, 2);
blocklist_extra_state = _r_calc_clamp (_r_config_getlong (L"BlocklistExtraState", 0), 0, 2);
_app_ruleblocklistsetstate (ptr_rule, blocklist_spy_state, blocklist_update_state, blocklist_extra_state);
}
else
{
ptr_rule->is_enabled_default = ptr_rule->is_enabled; // set default value for rule
}
// load rules config
ptr_config = NULL;
is_internal = (type == DATA_RULE_BLOCKLIST || type == DATA_RULE_SYSTEM || type == DATA_RULE_SYSTEM_USER);
if (is_internal)
{
// internal rules
ptr_config = _app_getruleconfigitem (rule_hash);
if (ptr_config)
ptr_rule->is_enabled = ptr_config->is_enabled;
}
// load apps
_r_obj_initializestringbuilder (&sb);
string = _r_xml_getattribute_string (&db_info->xml_library, L"apps");
if (!_r_obj_isstringempty (string))
{
_r_obj_appendstringbuilder2 (&sb, string);
_r_obj_dereference (string);
}
if (is_internal && ptr_config && !_r_obj_isstringempty (ptr_config->apps))
{
if (!_r_obj_isstringempty2 (sb.string))
_r_obj_appendstringbuilder (&sb, DIVIDER_APP);
_r_obj_appendstringbuilder2 (&sb, ptr_config->apps);
}
string = _r_obj_finalstringbuilder (&sb);
if (!_r_obj_isstringempty2 (string))
{
if (db_info->version < XML_VERSION_3)
_r_str_replacechar (&string->sr, DIVIDER_RULE[0], DIVIDER_APP[0]);
_r_obj_initializestringref2 (&sr, string);
while (sr.length != 0)
{
_r_str_splitatchar (&sr, DIVIDER_APP[0], &first_part, &sr);
status = _r_str_environmentexpandstring (&first_part, &path_string);
if (!NT_SUCCESS (status))
path_string = _r_obj_createstring3 (&first_part);
app_hash = _r_str_gethash2 (path_string, TRUE);
if (app_hash)
{
if (ptr_rule->is_forservices && _app_issystemhash (app_hash))
{
_r_obj_dereference (path_string);
continue;
}
if (!_app_isappfound (app_hash))
app_hash = _app_addapplication (NULL, DATA_UNKNOWN, path_string, NULL, NULL);
if (app_hash)
{
_r_obj_addhashtableitem (ptr_rule->apps, app_hash, NULL);
if (ptr_rule->type == DATA_RULE_SYSTEM)
_app_setappinfobyhash (app_hash, INFO_IS_UNDELETABLE, IntToPtr (TRUE));
}
}
_r_obj_dereference (path_string);
}
// Check if no app is added into rule, then disable it!
if (ptr_rule->is_enabled)
{
if (_r_obj_ishashtableempty (ptr_rule->apps))
ptr_rule->is_enabled = FALSE;
}
}
_r_queuedlock_acquireexclusive (&lock_rules);
_r_obj_addlistitem (rules_list, ptr_rule);
_r_queuedlock_releaseexclusive (&lock_rules);
_r_obj_deletestringbuilder (&sb);
}
VOID _app_db_parse_ruleconfig (
_Inout_ PDB_INFORMATION db_info
)
{
PR_STRING rule_name;
PITEM_RULE_CONFIG ptr_config;
ULONG_PTR rule_hash;
rule_name = _r_xml_getattribute_string (&db_info->xml_library, L"name");
if (!rule_name)
return;
rule_hash = _r_str_gethash2 (rule_name, TRUE);
if (!rule_hash)
{
_r_obj_dereference (rule_name);
return;
}
ptr_config = _app_getruleconfigitem (rule_hash);
if (!ptr_config)
{
_r_queuedlock_acquireexclusive (&lock_rules_config);
ptr_config = _app_addruleconfigtable (rules_config, rule_hash, rule_name, _r_xml_getattribute_boolean (&db_info->xml_library, L"is_enabled"));
_r_queuedlock_releaseexclusive (&lock_rules_config);
if (ptr_config)
{
ptr_config->apps = _r_xml_getattribute_string (&db_info->xml_library, L"apps");
if (ptr_config->apps && db_info->version < XML_VERSION_3)
_r_str_replacechar (&ptr_config->apps->sr, DIVIDER_RULE[0], DIVIDER_APP[0]);
}
}
_r_obj_dereference (rule_name);
}
_Success_ (return == STATUS_SUCCESS)
NTSTATUS _app_db_decodebody (
_Inout_ PDB_INFORMATION db_info
)
{
static USHORT format[] = {COMPRESSION_FORMAT_LZNT1, COMPRESSION_FORMAT_XPRESS};
PR_BYTE new_bytes;
USHORT architecture;
BYTE profile_type;
NTSTATUS status;
if (db_info->bytes->length < PROFILE2_HEADER_LENGTH)
return STATUS_SUCCESS;
if (!RtlEqualMemory (db_info->bytes->buffer, profile2_fourcc, sizeof (profile2_fourcc)))
return STATUS_SUCCESS;
profile_type = db_info->bytes->buffer[sizeof (profile2_fourcc)];
// skip fourcc
_r_obj_skipbytelength (&db_info->bytes->sr, PROFILE2_FOURCC_LENGTH);
// read the hash
_r_obj_movereference (&db_info->hash, _r_obj_createbyte_ex (db_info->bytes->buffer, PROFILE2_SHA256_LENGTH));
// skip hash
_r_obj_skipbytelength (&db_info->bytes->sr, PROFILE2_SHA256_LENGTH);
switch (profile_type)
{
case PROFILE2_ID_COMPRESSED:
{
// decompress bytes
for (SIZE_T i = 0; i < RTL_NUMBER_OF (format); i++)
{
status = _r_sys_decompressbuffer (format[i], &db_info->bytes->sr, &new_bytes);
if (NT_SUCCESS (status))
{
_r_obj_movereference (&db_info->bytes, new_bytes);
break;
}
}
break;
}
case PROFILE2_ID_ENCRYPTED:
{
// decrypt bytes
status = _app_db_decrypt (&db_info->bytes->sr, &new_bytes);
if (!NT_SUCCESS (status))
return status;
_r_obj_movereference (&db_info->bytes, new_bytes);
break;
}
default:
{
return STATUS_FILE_NOT_SUPPORTED;
}
}
if (!NT_SUCCESS (status))
return status;
if (RtlEqualMemory (db_info->bytes->buffer, profile2_fourcc, sizeof (profile2_fourcc)))
return STATUS_MORE_PROCESSING_REQUIRED;
// fix arm64 crash that was introduced by Micro$oft (issue #1228)
if (NT_SUCCESS (_r_sys_getprocessorinformation (&architecture, NULL, NULL)))
{
if (architecture == PROCESSOR_ARCHITECTURE_ARM || architecture == PROCESSOR_ARCHITECTURE_ARM64)
return STATUS_SUCCESS;
}
// validate hash
if (db_info->hash)
status = _app_db_ishashvalid (&db_info->bytes->sr, &db_info->hash->sr);
return status;
}
_Success_ (NT_SUCCESS (return))
NTSTATUS _app_db_encodebody (
_Inout_ PDB_INFORMATION db_info,
_In_ BYTE profile_type,
_Out_ PR_BYTE_PTR out_buffer
)
{
PR_BYTE bytes;
PR_BYTE new_bytes;
PR_BYTE body_bytes;
PR_BYTE hash_value;
NTSTATUS status;
*out_buffer = NULL;
status = _r_xml_readstream (&db_info->xml_library, &bytes);
if (FAILED (status))
return status;
// generate body hash
status = _app_db_gethash (&bytes->sr, &hash_value);
if (!NT_SUCCESS (status))
{
_r_obj_dereference (hash_value);
_r_obj_dereference (bytes);
return status;
}
switch (profile_type)
{
case PROFILE2_ID_PLAIN:
{
new_bytes = _r_obj_reference (bytes);
break;
}
case PROFILE2_ID_COMPRESSED:
{
// compress body
status = _r_sys_compressbuffer (COMPRESSION_FORMAT_XPRESS | COMPRESSION_ENGINE_MAXIMUM, &bytes->sr, &new_bytes);
if (!NT_SUCCESS (status))
{
_r_obj_dereference (hash_value);
_r_obj_dereference (bytes);
return status;
}
break;
}
case PROFILE2_ID_ENCRYPTED:
{
status = _app_db_encrypt (&bytes->sr, &new_bytes);
if (!NT_SUCCESS (status))
{
_r_obj_dereference (hash_value);
_r_obj_dereference (bytes);
return status;
}
break;
}
default:
{
return STATUS_FILE_NOT_SUPPORTED;
}
}
_r_obj_movereference (&bytes, new_bytes);
status = _app_db_generatebody (profile_type, hash_value, bytes, &body_bytes);
*out_buffer = body_bytes;
_r_obj_dereference (hash_value);
_r_obj_dereference (bytes);
return status;
}
_Success_ (NT_SUCCESS (return))
NTSTATUS _app_db_generatebody (
_In_ BYTE profile_type,
_In_ PR_BYTE hash_value,
_In_ PR_BYTE buffer,
_Outptr_ PR_BYTE_PTR out_buffer
)
{
PR_BYTE bytes;
PVOID ptr;
switch (profile_type)
{
case PROFILE2_ID_PLAIN:
{
bytes = _r_obj_reference (buffer);
break;
}
case PROFILE2_ID_COMPRESSED:
case PROFILE2_ID_ENCRYPTED:
{
bytes = _r_obj_createbyte_ex (NULL, PROFILE2_HEADER_LENGTH + buffer->length);
RtlCopyMemory (bytes->buffer, profile2_fourcc, sizeof (profile2_fourcc));
ptr = PTR_ADD_OFFSET (bytes->buffer, sizeof (profile2_fourcc));
RtlCopyMemory (ptr, &profile_type, sizeof (BYTE));
ptr = PTR_ADD_OFFSET (bytes->buffer, PROFILE2_FOURCC_LENGTH);
RtlCopyMemory (ptr, hash_value->buffer, hash_value->length);
ptr = PTR_ADD_OFFSET (bytes->buffer, PROFILE2_HEADER_LENGTH);
RtlCopyMemory (ptr, buffer->buffer, buffer->length);
break;
}
default:
{
*out_buffer = NULL;
return STATUS_FILE_NOT_SUPPORTED;
}
}
*out_buffer = bytes;
return STATUS_SUCCESS;
}
_Success_ (return == STATUS_SUCCESS)
NTSTATUS _app_db_decodebuffer (
_Inout_ PDB_INFORMATION db_info
)
{
ULONG attempts = 6;
NTSTATUS status;
if (!db_info->bytes)
return STATUS_BUFFER_ALL_ZEROS;
do
{
status = _app_db_decodebody (db_info);
if (status != STATUS_MORE_PROCESSING_REQUIRED)
break;
}
while (--attempts);
if (status != STATUS_SUCCESS)
return status;
status = _r_xml_parsestring (&db_info->xml_library, db_info->bytes->buffer, (ULONG)db_info->bytes->length);
if (FAILED (status))
return status;
if (!_r_xml_findchildbytagname (&db_info->xml_library, L"root"))
return STATUS_DATA_ERROR;
db_info->timestamp = _r_xml_getattribute_long64 (&db_info->xml_library, L"timestamp");
db_info->type = _r_xml_getattribute_long (&db_info->xml_library, L"type");
db_info->version = _r_xml_getattribute_long (&db_info->xml_library, L"version");
return STATUS_SUCCESS;
}
BOOLEAN _app_db_parse (
_Inout_ PDB_INFORMATION db_info,
_In_ ENUM_TYPE_XML type
)
{
if (!_r_xml_findchildbytagname (&db_info->xml_library, L"root"))
return FALSE;
switch (type)
{
case XML_TYPE_PROFILE:
{
// load apps
if (_r_xml_findchildbytagname (&db_info->xml_library, L"apps"))
{
while (_r_xml_enumchilditemsbytagname (&db_info->xml_library, L"item"))
{
_app_db_parse_app (db_info);
}
}
// load rules config
if (_r_xml_findchildbytagname (&db_info->xml_library, L"rules_config"))
{
while (_r_xml_enumchilditemsbytagname (&db_info->xml_library, L"item"))
{
_app_db_parse_ruleconfig (db_info);
}
}
// load user rules
if (_r_xml_findchildbytagname (&db_info->xml_library, L"rules_custom"))
{
while (_r_xml_enumchilditemsbytagname (&db_info->xml_library, L"item"))
{
_app_db_parse_rule (db_info, DATA_RULE_USER);
}
}
break;
}
case XML_TYPE_PROFILE_INTERNAL:
{
// load system rules
if (_r_xml_findchildbytagname (&db_info->xml_library, L"rules_system"))
{
while (_r_xml_enumchilditemsbytagname (&db_info->xml_library, L"item"))
{
_app_db_parse_rule (db_info, DATA_RULE_SYSTEM);
}
}
// load internal custom rules
if (_r_xml_findchildbytagname (&db_info->xml_library, L"rules_custom"))
{
while (_r_xml_enumchilditemsbytagname (&db_info->xml_library, L"item"))
{
_app_db_parse_rule (db_info, DATA_RULE_SYSTEM_USER);
}
}
// load blocklist rules
if (_r_xml_findchildbytagname (&db_info->xml_library, L"rules_blocklist"))
{
while (_r_xml_enumchilditemsbytagname (&db_info->xml_library, L"item"))
{
_app_db_parse_rule (db_info, DATA_RULE_BLOCKLIST);
}
}
break;
}
}
return TRUE;
}
_Success_ (NT_SUCCESS (return))
NTSTATUS _app_db_savetofile (
_Inout_ PDB_INFORMATION db_info,
_In_ PR_STRING path,
_In_ ENUM_VERSION_XML version,
_In_ ENUM_TYPE_XML type,
_In_ LONG64 timestamp
)
{
NTSTATUS status;
status = _r_xml_createstream (&db_info->xml_library, NULL, 1024);
if (FAILED (status))
return status;
_r_xml_writestartdocument (&db_info->xml_library);
_r_xml_writestartelement (&db_info->xml_library, L"root");
_r_xml_setattribute_long (&db_info->xml_library, L"version", version);
_r_xml_setattribute_long (&db_info->xml_library, L"type", type);
_r_xml_setattribute_long64 (&db_info->xml_library, L"timestamp", timestamp);
_app_db_save_app (db_info);
_app_db_save_rule (db_info);
_app_db_save_ruleconfig (db_info);
_r_xml_writewhitespace (&db_info->xml_library, L"\n");
_r_xml_writeendelement (&db_info->xml_library);
_r_xml_writewhitespace (&db_info->xml_library, L"\n");
_r_xml_writeenddocument (&db_info->xml_library);
status = _app_db_save_streamtofile (db_info, path);
return status;
}
_Success_ (NT_SUCCESS (return))
NTSTATUS _app_db_save_streamtofile (
_Inout_ PDB_INFORMATION db_info,
_In_ PR_STRING path
)
{
IO_STATUS_BLOCK isb;
PR_BYTE new_bytes;
HANDLE hfile;
BYTE profile_type;
NTSTATUS status;
hfile = CreateFile (path->buffer, GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_DELETE, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
if (!_r_fs_isvalidhandle (hfile))
return RtlGetLastNtStatus ();
profile_type = _app_getprofiletype ();
status = _app_db_encodebody (db_info, profile_type, &new_bytes);
if (NT_SUCCESS (status))
{
NtWriteFile (hfile, NULL, NULL, NULL, &isb, new_bytes->buffer, (ULONG)new_bytes->length, NULL, NULL);
_r_obj_dereference (new_bytes);
}
NtClose (hfile);
return status;
}
FORCEINLINE VOID _app_db_writeelementstart (
_Inout_ PDB_INFORMATION db_info,
_In_ LPCWSTR name
)
{
_r_xml_writewhitespace (&db_info->xml_library, L"\n\t");
_r_xml_writestartelement (&db_info->xml_library, name);
}
FORCEINLINE VOID _app_db_writeelementend (
_Inout_ PDB_INFORMATION db_info
)
{
_r_xml_writewhitespace (&db_info->xml_library, L"\n\t");
_r_xml_writeendelement (&db_info->xml_library);
}
VOID _app_db_save_app (
_Inout_ PDB_INFORMATION db_info
)
{
PITEM_APP ptr_app = NULL;
SIZE_T enum_key = 0;
BOOLEAN is_keepunusedapps;
BOOLEAN is_usedapp;
is_keepunusedapps = _r_config_getboolean (L"IsKeepUnusedApps", TRUE);
_app_db_writeelementstart (db_info, L"apps");
_r_queuedlock_acquireshared (&lock_apps);
while (_r_obj_enumhashtablepointer (apps_table, &ptr_app, NULL, &enum_key))
{
if (_r_obj_isstringempty (ptr_app->original_path))
continue;
is_usedapp = _app_isappused (ptr_app, TRUE);
// do not save unused apps/uwp apps...
if (!is_usedapp && !is_keepunusedapps)
continue;
_r_xml_writewhitespace (&db_info->xml_library, L"\n\t\t");
_r_xml_writestartelement (&db_info->xml_library, L"item");
_r_xml_setattribute (&db_info->xml_library, L"path", ptr_app->original_path->buffer);
if (ptr_app->hash)
_r_xml_setattribute (&db_info->xml_library, L"hash", ptr_app->hash->buffer);
if (ptr_app->timestamp)
_r_xml_setattribute_long64 (&db_info->xml_library, L"timestamp", ptr_app->timestamp);
// set timer (if presented)
if (ptr_app->timer && _app_istimerset (ptr_app))
_r_xml_setattribute_long64 (&db_info->xml_library, L"timer", ptr_app->timer);
// ffu!
if (ptr_app->profile)
_r_xml_setattribute_long (&db_info->xml_library, L"profile", ptr_app->profile);
if (ptr_app->is_silent)
_r_xml_setattribute_boolean (&db_info->xml_library, L"is_silent", !!ptr_app->is_silent);
if (ptr_app->is_enabled)
_r_xml_setattribute_boolean (&db_info->xml_library, L"is_enabled", !!ptr_app->is_enabled);
_r_xml_writeendelement (&db_info->xml_library);
}
_r_queuedlock_releaseshared (&lock_apps);
_app_db_writeelementend (db_info);
}
VOID _app_db_save_rule (
_Inout_ PDB_INFORMATION db_info
)
{
PITEM_RULE ptr_rule;
PR_STRING apps_string;
_app_db_writeelementstart (db_info, L"rules_custom");
_r_queuedlock_acquireshared (&lock_rules);
for (SIZE_T i = 0; i < _r_obj_getlistsize (rules_list); i++)
{
ptr_rule = _r_obj_getlistitem (rules_list, i);
if (!ptr_rule || ptr_rule->is_readonly || _r_obj_isstringempty (ptr_rule->name))
continue;
_r_xml_writewhitespace (&db_info->xml_library, L"\n\t\t");
_r_xml_writestartelement (&db_info->xml_library, L"item");
_r_xml_setattribute (&db_info->xml_library, L"name", ptr_rule->name->buffer);
if (!_r_obj_isstringempty (ptr_rule->rule_remote))
_r_xml_setattribute (&db_info->xml_library, L"rule", ptr_rule->rule_remote->buffer);
if (!_r_obj_isstringempty (ptr_rule->rule_local))
_r_xml_setattribute (&db_info->xml_library, L"rule_local", ptr_rule->rule_local->buffer);
// ffu!
if (ptr_rule->profile)
_r_xml_setattribute_long (&db_info->xml_library, L"profile", ptr_rule->profile);
if (ptr_rule->direction != FWP_DIRECTION_OUTBOUND)
_r_xml_setattribute_long (&db_info->xml_library, L"dir", ptr_rule->direction);
if (ptr_rule->protocol != 0)
_r_xml_setattribute_long (&db_info->xml_library, L"protocol", ptr_rule->protocol);
if (ptr_rule->af != AF_UNSPEC)
_r_xml_setattribute_long (&db_info->xml_library, L"version", ptr_rule->af);
// add apps attribute
if (!_r_obj_ishashtableempty (ptr_rule->apps))
{
apps_string = _app_rulesexpandapps (ptr_rule, FALSE, DIVIDER_APP);
if (apps_string)
{
_r_xml_setattribute (&db_info->xml_library, L"apps", apps_string->buffer);
_r_obj_dereference (apps_string);
}
}
if (ptr_rule->action == FWP_ACTION_BLOCK)
_r_xml_setattribute_boolean (&db_info->xml_library, L"is_block", TRUE);
if (ptr_rule->is_enabled)
_r_xml_setattribute_boolean (&db_info->xml_library, L"is_enabled", !!ptr_rule->is_enabled);
_r_xml_writeendelement (&db_info->xml_library);
}
_r_queuedlock_releaseshared (&lock_rules);
_app_db_writeelementend (db_info);
}
VOID _app_db_save_ruleconfig (
_Inout_ PDB_INFORMATION db_info
)
{
PITEM_RULE_CONFIG ptr_config = NULL;
PITEM_RULE ptr_rule;
PR_STRING apps_string;
ULONG_PTR rule_hash;
SIZE_T enum_key = 0;
BOOLEAN is_enabled_default;
_app_db_writeelementstart (db_info, L"rules_config");
_r_queuedlock_acquireshared (&lock_rules_config);
while (_r_obj_enumhashtable (rules_config, &ptr_config, NULL, &enum_key))
{
if (_r_obj_isstringempty (ptr_config->name))
continue;
is_enabled_default = ptr_config->is_enabled;
rule_hash = _r_str_gethash2 (ptr_config->name, TRUE);
ptr_rule = _app_getrulebyhash (rule_hash);
apps_string = NULL;
if (ptr_rule)
{
is_enabled_default = !!ptr_rule->is_enabled_default;
if (ptr_rule->type == DATA_RULE_USER && !_r_obj_ishashtableempty (ptr_rule->apps))
apps_string = _app_rulesexpandapps (ptr_rule, FALSE, DIVIDER_APP);
_r_obj_dereference (ptr_rule);
}
// skip saving untouched configuration
if (ptr_config->is_enabled == is_enabled_default && !apps_string)
continue;
_r_xml_writewhitespace (&db_info->xml_library, L"\n\t\t");
_r_xml_writestartelement (&db_info->xml_library, L"item");
_r_xml_setattribute (&db_info->xml_library, L"name", ptr_config->name->buffer);
if (apps_string)
{
_r_xml_setattribute (&db_info->xml_library, L"apps", apps_string->buffer);
_r_obj_clearreference (&apps_string);
}
_r_xml_setattribute_boolean (&db_info->xml_library, L"is_enabled", ptr_config->is_enabled);
_r_xml_writeendelement (&db_info->xml_library);
}
_r_queuedlock_releaseshared (&lock_rules_config);
_app_db_writeelementend (db_info);
}
_Ret_maybenull_
LPCWSTR _app_db_getconnectionstatename (
_In_ ULONG state
)
{
switch (state)
{
case MIB_TCP_STATE_CLOSED:
return L"Closed";
case MIB_TCP_STATE_LISTEN:
return L"Listen";
case MIB_TCP_STATE_SYN_SENT:
return L"SYN sent";
case MIB_TCP_STATE_SYN_RCVD:
return L"SYN received";
case MIB_TCP_STATE_ESTAB:
return L"Established";
case MIB_TCP_STATE_FIN_WAIT1:
return L"FIN wait 1";
case MIB_TCP_STATE_FIN_WAIT2:
return L"FIN wait 2";
case MIB_TCP_STATE_CLOSE_WAIT:
return L"Close wait";
case MIB_TCP_STATE_CLOSING:
return L"Closing";
case MIB_TCP_STATE_LAST_ACK:
return L"Last ACK";
case MIB_TCP_STATE_TIME_WAIT:
return L"Time wait";
case MIB_TCP_STATE_DELETE_TCB:
return L"Delete TCB";
}
return NULL;
}
_Ret_maybenull_
PR_STRING _app_db_getdirectionname (
_In_ FWP_DIRECTION direction,
_In_ BOOLEAN is_loopback,
_In_ BOOLEAN is_localized
)
{
LPCWSTR text = NULL;
if (is_localized)
{
if (direction == FWP_DIRECTION_OUTBOUND)
{
text = _r_locale_getstring (IDS_DIRECTION_1);
}
else if (direction == FWP_DIRECTION_INBOUND)
{
text = _r_locale_getstring (IDS_DIRECTION_2);
}
else if (direction == FWP_DIRECTION_MAX)
{
text = _r_locale_getstring (IDS_ANY);
}
}
else
{
if (direction == FWP_DIRECTION_OUTBOUND)
{
text = SZ_DIRECTION_OUT;
}
else if (direction == FWP_DIRECTION_INBOUND)
{
text = SZ_DIRECTION_IN;
}
else if (direction == FWP_DIRECTION_MAX)
{
text = SZ_DIRECTION_ANY;
}
}
if (!text)
return NULL;
if (is_loopback)
return _r_obj_concatstrings (2, text, L" (" SZ_DIRECTION_LOOPBACK L")");
return _r_obj_createstring (text);
}
_Ret_maybenull_
PR_STRING _app_db_getprotoname (
_In_ ULONG proto,
_In_ ADDRESS_FAMILY af,
_In_ BOOLEAN is_notnull
)
{
switch (proto)
{
// NOTE: this is used for "any" protocol
case IPPROTO_HOPOPTS:
break;
case IPPROTO_ICMP:
return _r_obj_createstring (L"icmp");
case IPPROTO_IGMP:
return _r_obj_createstring (L"igmp");
case IPPROTO_GGP:
return _r_obj_createstring (L"ggp");
case IPPROTO_IPV4:
return _r_obj_createstring (L"ipv4");
case IPPROTO_ST:
return _r_obj_createstring (L"st");
case IPPROTO_TCP:
return _r_obj_createstring (((af == AF_INET6) ? L"tcp6" : L"tcp"));
case IPPROTO_CBT:
return _r_obj_createstring (L"cbt");
case IPPROTO_EGP:
return _r_obj_createstring (L"egp");
case IPPROTO_IGP:
return _r_obj_createstring (L"igp");
case IPPROTO_PUP:
return _r_obj_createstring (L"pup");
case IPPROTO_UDP:
return _r_obj_createstring (((af == AF_INET6) ? L"udp6" : L"udp"));
case IPPROTO_IDP:
return _r_obj_createstring (L"xns-idp");
case IPPROTO_RDP:
return _r_obj_createstring (L"rdp");
case IPPROTO_IPV6:
return _r_obj_createstring (L"ipv6");
case IPPROTO_ROUTING:
return _r_obj_createstring (L"ipv6-route");
case IPPROTO_FRAGMENT:
return _r_obj_createstring (L"ipv6-frag");
case IPPROTO_ESP:
return _r_obj_createstring (L"esp");
case IPPROTO_AH:
return _r_obj_createstring (L"ah");
case IPPROTO_ICMPV6:
return _r_obj_createstring (L"ipv6-icmp");
case IPPROTO_DSTOPTS:
return _r_obj_createstring (L"ipv6-opts");
case IPPROTO_L2TP:
return _r_obj_createstring (L"l2tp");
case IPPROTO_SCTP:
return _r_obj_createstring (L"sctp");
}
if (is_notnull)
return _r_format_string (L"Protocol #%" TEXT (PR_ULONG), proto);
return NULL;
}
_Ret_maybenull_
LPCWSTR _app_db_getservicename (
_In_ UINT16 port,
_In_ UINT8 proto,
_In_opt_ LPCWSTR default_value
)
{
switch (port)
{
case 1:
return L"tcpmux";
case 7:
return L"echo";
case 9:
return L"discard";
case 11:
return L"systat";
case 13:
return L"daytime";
case 20:
return L"ftp-data";
case 21:
return L"ftp";
case 22:
return L"ssh";
case 23:
return L"telnet";
case 25:
return L"smtp";
case 26:
return L"rsftp";
case 37:
return L"time";
case 39:
return L"rlp";
case 42:
return L"nameserver";
case 43:
return L"nicname";
case 48:
return L"auditd";
case 53:
return L"domain";
case 63:
return L"whois++";
case 67:
case 68:
return L"dhcp";
case 69:
return L"tftp";
case 78:
return L"vettcp";
case 79:
case 2003:
return L"finger";
case 80:
return L"http";
case 81:
return L"hosts2-ns";
case 84:
return L"ctf";
case 88:
return L"kerberos-sec";
case 90:
return L"dnsix";
case 92:
return L"npp";
case 93:
return L"dcp";
case 94:
return L"objcall";
case 95:
return L"supdup";
case 101:
return L"hostname";
case 105:
return L"cso";
case 106:
return L"pop3pw";
case 107:
return L"rtelnet";
case 109:
return L"pop2";
case 110:
return L"pop3";
case 111:
return L"rpcbind";
case 112:
return L"mcidas";
case 113:
return L"auth";
case 115:
return L"sftp";
case 118:
return L"sqlserv";
case 119:
return L"nntp";
case 123:
return L"ntp";
case 126:
return L"nxedit";
case 129:
return L"pwdgen";
case 135:
return L"msrpc";
case 136:
return L"profile";
case 137:
return L"netbios-ns";
case 138:
return L"netbios-dgm";
case 139:
return L"netbios-ssn";
case 143:
return L"imap";
case 144:
return L"news";
case 145:
return L"uaac";
case 150:
return L"sql-net";
case 152:
return L"bftp";
case 156:
return L"sqlsrv";
case 159:
return L"nss-routing";
case 160:
return L"sgmp-traps";
case 161:
return L"snmp";
case 162:
return L"snmptrap";
case 169:
return L"send";
case 174:
return L"mailq";
case 175:
return L"vmnet";
case 179:
return L"bgp";
case 182:
return L"audit";
case 185:
return L"remote-kis";
case 186:
return L"kis";
case 194:
case 529:
return L"irc";
case 195:
return L"dn6-nlm-aud";
case 196:
return L"dn6-smm-red";
case 197:
return L"dls";
case 199:
return L"smux";
case 209:
return L"qmtp";
case 245:
return L"link";
case 280:
return L"http-mgmt";
case 322:
return L"rtsps";
case 349:
return L"mftp";
case 389:
return L"ldap";
case 427:
return L"svrloc";
case 443:
{
if (proto == IPPROTO_UDP)
return L"quic";
return L"https";
}
case 444:
return L"snpp";
case 445:
return L"microsoft-ds";
case 464:
return L"kerberos";
case 465:
return L"smtps";
case 500:
return L"isakmp";
case 513:
return L"login";
case 514:
return L"shell";
case 515:
return L"printer";
case 524:
return L"ncp";
case 530:
return L"rpc";
case 543:
return L"klogin";
case 544:
return L"kshell";
case 546:
return L"dhcpv6-client";
case 547:
return L"dhcpv6-server";
case 548:
return L"afp";
case 554:
return L"rtsp";
case 565:
return L"whoami";
case 558:
return L"sdnskmp";
case 585:
return L"imap4-ssl";
case 587:
return L"submission";
case 631:
return L"ipp";
case 636:
return L"ldaps";
case 646:
return L"ldp";
case 647:
return L"dhcp-failover";
case 666:
return L"doom"; // khe-khe-khe!
case 847:
return L"dhcp-failover2";
case 861:
return L"owamp-control";
case 862:
return L"twamp-control";
case 873:
return L"rsync";
case 853:
return L"domain-s";
case 989:
return L"ftps-data";
case 990:
return L"ftps";
case 992:
return L"telnets";
case 993:
return L"imaps";
case 994:
return L"ircs";
case 995:
return L"pop3s";
case 1025:
return L"NFS-or-IIS";
case 1027:
return L"IIS";
case 1029:
return L"ms-lsa";
case 1110:
return L"nfsd";
case 1111:
return L"lmsocialserver";
case 1112:
case 1114:
case 4333:
return L"mini-sql";
case 1119:
return L"bnetgame";
case 1120:
return L"bnetfile";
case 1123:
return L"murray";
case 1194:
return L"openvpn";
case 1337:
return L"menandmice-dns";
case 1433:
return L"ms-sql-s";
case 1688:
return L"nsjtp-data";
case 1701:
return L"l2tp";
case 1720:
return L"h323q931";
case 1723:
return L"pptp";
case 1863:
return L"msnp";
case 1900:
case 5000:
return L"upnp";
case 2000:
return L"cisco-sccp";
case 2054:
return L"weblogin";
case 2086:
return L"gnunet";
case 2001:
return L"dc";
case 2121:
return L"ccproxy-ftp";
case 2164:
return L"ddns-v3";
case 2167:
return L"raw-serial";
case 2171:
return L"msfw-storage";
case 2172:
return L"msfw-s-storage";
case 2173:
return L"msfw-replica";
case 2174:
return L"msfw-array";
case 2371:
return L"worldwire";
case 2717:
return L"pn-requester";
case 2869:
return L"icslap";
case 3000:
return L"ppp";
case 3074:
return L"xbox";
case 3128:
return L"squid-http";
case 3306:
return L"mysql";
case 3389:
return L"ms-wbt-server";
case 3407:
return L"ldap-admin";
case 3540:
return L"pnrp-port";
case 3558:
return L"mcp-port";
case 3587:
return L"p2pgroup";
case 3702:
return L"ws-discovery";
case 3713:
return L"tftps";
case 3724:
return L"blizwow";
case 4500:
return L"ipsec-nat-t";
case 4554:
return L"msfrs";
case 4687:
return L"nst";
case 4876:
return L"tritium-can";
case 4899:
return L"radmin";
case 5004:
return L"rtp-data";
case 5005:
return L"rtp";
case 5009:
return L"airport-admin";
case 5051:
return L"ida-agent";
case 5060:
return L"sip";
case 5101:
return L"admdog";
case 5190:
return L"aol";
case 5350:
return L"nat-pmp-status";
case 5351:
return L"nat-pmp";
case 5352:
return L"dns-llq";
case 5353:
return L"mdns";
case 5354:
return L"mdnsresponder";
case 5355:
return L"llmnr";
case 5357:
return L"wsdapi";
case 5358:
return L"wsdapi-s";
case 5362:
return L"serverwsd2";
case 5432:
return L"postgresql";
case 5631:
return L"pcanywheredata";
case 5666:
return L"nrpe";
case 5687:
return L"gog-multiplayer";
case 5800:
return L"vnc-http";
case 5900:
return L"vnc";
case 5938:
return L"teamviewer";
case 6000:
case 6001:
case 6002:
case 6003:
return L"x11";
case 6222:
case 6662: // deprecated!
return L"radmind";
case 6346:
return L"gnutella";
case 6347:
return L"gnutella2";
case 6622:
return L"mcftp";
case 6665:
case 6666:
case 6667:
case 6668:
case 6669:
return L"ircu";
case 6881:
return L"bittorrent-tracker";
case 7070:
return L"realserver";
case 7235:
return L"aspcoordination";
case 8443:
return L"https-alt";
case 8021:
return L"ftp-proxy";
case 8333:
case 18333:
return L"bitcoin";
case 591:
case 8000:
case 8008:
case 8080:
case 8444:
return L"http-alt";
case 8999:
return L"bctp";
case 9418:
return L"git";
case 9800:
return L"webdav";
case 10107:
return L"bctp-server";
case 11371:
{
if (proto == IPPROTO_UDP)
return L"hkp";
return L"pksd";
}
case 25565:
return L"minecraft";
case 26000:
return L"quake";
case 27015:
return L"halflife";
case 27017:
case 27018:
case 27019:
case 28017:
return L"mongod";
case 27500:
return L"quakeworld";
case 27910:
return L"quake2";
case 27960:
return L"quake3";
case 28240:
return L"siemensgsm";
case 33434:
return L"traceroute";
}
return default_value;
}