From 9f4efa1a36b5c0ff50732242488ea4546f6c74ee Mon Sep 17 00:00:00 2001 From: Alexander Partanen <80092501+alex-pentagrid@users.noreply.github.com> Date: Mon, 7 Aug 2023 06:14:00 +0300 Subject: [PATCH] Procmon environment empty vars (#1692) * procmon: move environment variable parse to separate method, do not break on empty vars * procmon: add unit test for environment variable parsing --- src/plugins/Makefile.am | 4 +- src/plugins/procmon/check.cpp | 31 +++++++ src/plugins/procmon/linux.cpp | 34 +------ src/plugins/procmon/linux_utils.cpp | 132 ++++++++++++++++++++++++++++ src/plugins/procmon/linux_utils.h | 120 +++++++++++++++++++++++++ 5 files changed, 290 insertions(+), 31 deletions(-) create mode 100644 src/plugins/procmon/linux_utils.cpp create mode 100644 src/plugins/procmon/linux_utils.h diff --git a/src/plugins/Makefile.am b/src/plugins/Makefile.am index 319042ca7..23ba7f579 100644 --- a/src/plugins/Makefile.am +++ b/src/plugins/Makefile.am @@ -198,6 +198,8 @@ sources += procmon/procmon.cpp sources += procmon/procmon.h sources += procmon/linux.cpp sources += procmon/linux.h +sources += procmon/linux_utils.cpp +sources += procmon/linux_utils.h sources += procmon/private.h sources += procmon/win.cpp sources += procmon/win.h @@ -487,7 +489,7 @@ if PLUGIN_PROCMON check_PROGRAMS += procmon/check procmon_check_SOURCES = procmon/check.cpp procmon_check_CFLAGS = $(CHECK_CFLAGS) -procmon_check_LDADD = $(CHECK_LIBS) procmon/winnt.lo +procmon_check_LDADD = $(CHECK_LIBS) procmon/linux_utils.lo procmon/winnt.lo endif # Unit tests for output format diff --git a/src/plugins/procmon/check.cpp b/src/plugins/procmon/check.cpp index d621425a0..492223e59 100644 --- a/src/plugins/procmon/check.cpp +++ b/src/plugins/procmon/check.cpp @@ -104,6 +104,7 @@ #include +#include "linux_utils.h" #include "winnt.h" START_TEST(test_protection_attributes_stringify) @@ -114,6 +115,19 @@ START_TEST(test_protection_attributes_stringify) } END_TEST +START_TEST(test_environment_variable_parse) +{ + ck_assert(parse_environment_variable("PATH=/usr/local/bin:/usr/bin:/bin") == make_pair(std::string("PATH"), std::string("/usr/local/bin:/usr/bin:/bin"))); + ck_assert(parse_environment_variable("empty=") == make_pair(std::string("empty"), std::string())); + ck_assert(parse_environment_variable("empty= ") == make_pair(std::string("empty"), std::string())); + ck_assert(parse_environment_variable("spaces= test") == make_pair(std::string("spaces"), std::string("test"))); + ck_assert(parse_environment_variable("not variable") == make_pair(std::string(), std::string())); + ck_assert(parse_environment_variable("notvar") == make_pair(std::string(), std::string())); + ck_assert(parse_environment_variable("") == make_pair(std::string(), std::string())); + ck_assert(parse_environment_variable(nullptr) == make_pair(std::string(), std::string())); +} +END_TEST + Suite* protection_attributes_suite(void) { Suite* s; @@ -130,6 +144,22 @@ Suite* protection_attributes_suite(void) return s; } +Suite* environment_variables_suite(void) +{ + Suite* s; + TCase* tc_core; + + s = suite_create("Parse environment variables"); + + /* Core test case */ + tc_core = tcase_create("Core"); + + tcase_add_test(tc_core, test_environment_variable_parse); + suite_add_tcase(s, tc_core); + + return s; +} + int main(void) { int number_failed; @@ -138,6 +168,7 @@ int main(void) s = protection_attributes_suite(); sr = srunner_create(s); + srunner_add_suite(sr, environment_variables_suite()); srunner_run_all(sr, CK_NORMAL); number_failed = srunner_ntests_failed(sr); diff --git a/src/plugins/procmon/linux.cpp b/src/plugins/procmon/linux.cpp index 58c21a5cd..0cda23a0e 100644 --- a/src/plugins/procmon/linux.cpp +++ b/src/plugins/procmon/linux.cpp @@ -110,6 +110,7 @@ #include #include "linux.h" +#include "linux_utils.h" #include "plugins/output_format.h" #include "plugins/helpers/hooks.h" @@ -215,11 +216,6 @@ static std::map parse_environment(drakvuf_t drakvuf, d for (uint32_t envpc = 0; envpc < ARG_MAX; envpc++, envp += sizeof(addr_t)) { - std::string::size_type key_pos = 0; - std::string::size_type key_end; - std::string::size_type val_pos; - std::string::size_type val_end; - addr_t env_str_ptr; ctx.addr = envp; if (VMI_FAILURE == vmi_read_addr(vmi, &ctx, &env_str_ptr)) @@ -230,35 +226,13 @@ static std::map parse_environment(drakvuf_t drakvuf, d if (env_string == nullptr) break; - // based on: https://stackoverflow.com/questions/38812780/split-string-into-key-value-pairs-using-c - std::string s(env_string); - key_pos = 0; - - key_end = s.find('=', key_pos); - if (key_end == std::string::npos) - { - g_free(env_string); - break; - } - - if ((val_pos = s.find_first_not_of("= ", key_end)) == std::string::npos) - { - g_free(env_string); - break; - } + auto[key, val] = parse_environment_variable(env_string); + g_free(env_string); - val_end = s.find('\1', val_pos); - std::string key = s.substr(key_pos, key_end - key_pos); - std::string val = s.substr(val_pos, val_end - val_pos); - if (filter.find(key) == filter.end()) - { - g_free(env_string); + if (key.empty() || filter.find(key) == filter.end()) continue; - } envp_map.emplace(key, val); - - g_free(env_string); } return envp_map; } diff --git a/src/plugins/procmon/linux_utils.cpp b/src/plugins/procmon/linux_utils.cpp new file mode 100644 index 000000000..48a293619 --- /dev/null +++ b/src/plugins/procmon/linux_utils.cpp @@ -0,0 +1,132 @@ +/*********************IMPORTANT DRAKVUF LICENSE TERMS*********************** + * * + * DRAKVUF (C) 2014-2023 Tamas K Lengyel. * + * Tamas K Lengyel is hereinafter referred to as the author. * + * This program is free software; you may redistribute and/or modify it * + * under the terms of the GNU General Public License as published by the * + * Free Software Foundation; Version 2 ("GPL"), BUT ONLY WITH ALL OF THE * + * CLARIFICATIONS AND EXCEPTIONS DESCRIBED HEREIN. This guarantees your * + * right to use, modify, and redistribute this software under certain * + * conditions. If you wish to embed DRAKVUF technology into proprietary * + * software, alternative licenses can be acquired from the author. * + * * + * Note that the GPL places important restrictions on "derivative works", * + * yet it does not provide a detailed definition of that term. To avoid * + * misunderstandings, we interpret that term as broadly as copyright law * + * allows. For example, we consider an application to constitute a * + * derivative work for the purpose of this license if it does any of the * + * following with any software or content covered by this license * + * ("Covered Software"): * + * * + * o Integrates source code from Covered Software. * + * * + * o Reads or includes copyrighted data files. * + * * + * o Is designed specifically to execute Covered Software and parse the * + * results (as opposed to typical shell or execution-menu apps, which will * + * execute anything you tell them to). * + * * + * o Includes Covered Software in a proprietary executable installer. The * + * installers produced by InstallShield are an example of this. Including * + * DRAKVUF with other software in compressed or archival form does not * + * trigger this provision, provided appropriate open source decompression * + * or de-archiving software is widely available for no charge. For the * + * purposes of this license, an installer is considered to include Covered * + * Software even if it actually retrieves a copy of Covered Software from * + * another source during runtime (such as by downloading it from the * + * Internet). * + * * + * o Links (statically or dynamically) to a library which does any of the * + * above. * + * * + * o Executes a helper program, module, or script to do any of the above. * + * * + * This list is not exclusive, but is meant to clarify our interpretation * + * of derived works with some common examples. Other people may interpret * + * the plain GPL differently, so we consider this a special exception to * + * the GPL that we apply to Covered Software. Works which meet any of * + * these conditions must conform to all of the terms of this license, * + * particularly including the GPL Section 3 requirements of providing * + * source code and allowing free redistribution of the work as a whole. * + * * + * Any redistribution of Covered Software, including any derived works, * + * must obey and carry forward all of the terms of this license, including * + * obeying all GPL rules and restrictions. For example, source code of * + * the whole work must be provided and free redistribution must be * + * allowed. All GPL references to "this License", are to be treated as * + * including the terms and conditions of this license text as well. * + * * + * Because this license imposes special exceptions to the GPL, Covered * + * Work may not be combined (even as part of a larger work) with plain GPL * + * software. The terms, conditions, and exceptions of this license must * + * be included as well. This license is incompatible with some other open * + * source licenses as well. In some cases we can relicense portions of * + * DRAKVUF or grant special permissions to use it in other open source * + * software. Please contact tamas.k.lengyel@gmail.com with any such * + * requests. Similarly, we don't incorporate incompatible open source * + * software into Covered Software without special permission from the * + * copyright holders. * + * * + * If you have any questions about the licensing restrictions on using * + * DRAKVUF in other works, are happy to help. As mentioned above, * + * alternative license can be requested from the author to integrate * + * DRAKVUF into proprietary applications and appliances. Please email * + * tamas.k.lengyel@gmail.com for further information. * + * * + * If you have received a written license agreement or contract for * + * Covered Software stating terms other than these, you may choose to use * + * and redistribute Covered Software under those terms instead of these. * + * * + * Source is provided to this software because we believe users have a * + * right to know exactly what a program is going to do before they run it. * + * This also allows you to audit the software for security holes. * + * * + * Source code also allows you to port DRAKVUF to new platforms, fix bugs, * + * and add new features. You are highly encouraged to submit your changes * + * on https://github.com/tklengyel/drakvuf, or by other methods. * + * By sending these changes, it is understood (unless you specify * + * otherwise) that you are offering unlimited, non-exclusive right to * + * reuse, modify, and relicense the code. DRAKVUF will always be * + * available Open Source, but this is important because the inability to * + * relicense code has caused devastating problems for other Free Software * + * projects (such as KDE and NASM). * + * To specify special license conditions of your contributions, just say * + * so when you send them. * + * * + * 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 DRAKVUF * + * license file for more details (it's in a COPYING file included with * + * DRAKVUF, and also available from * + * https://github.com/tklengyel/drakvuf/COPYING) * + * * + ***************************************************************************/ + +#include "linux_utils.h" + +std::pair parse_environment_variable(const char* var) +{ + // based on: https://stackoverflow.com/questions/38812780/split-string-into-key-value-pairs-using-c + std::string key, value; + std::string::size_type key_pos = 0; + std::string::size_type key_end; + std::string::size_type val_pos; + std::string::size_type val_end; + + std::string s(var ?: ""); + + key_end = s.find('=', key_pos); + if (key_end == std::string::npos) + goto out; + + key = s.substr(key_pos, key_end - key_pos); + + if ((val_pos = s.find_first_not_of("= ", key_end)) != std::string::npos) + { + val_end = s.find('\1', val_pos); + value = s.substr(val_pos, val_end - val_pos); + } + +out: + return make_pair(key, value); +} diff --git a/src/plugins/procmon/linux_utils.h b/src/plugins/procmon/linux_utils.h new file mode 100644 index 000000000..8a4a32958 --- /dev/null +++ b/src/plugins/procmon/linux_utils.h @@ -0,0 +1,120 @@ +/*********************IMPORTANT DRAKVUF LICENSE TERMS*********************** + * * + * DRAKVUF (C) 2014-2023 Tamas K Lengyel. * + * Tamas K Lengyel is hereinafter referred to as the author. * + * This program is free software; you may redistribute and/or modify it * + * under the terms of the GNU General Public License as published by the * + * Free Software Foundation; Version 2 ("GPL"), BUT ONLY WITH ALL OF THE * + * CLARIFICATIONS AND EXCEPTIONS DESCRIBED HEREIN. This guarantees your * + * right to use, modify, and redistribute this software under certain * + * conditions. If you wish to embed DRAKVUF technology into proprietary * + * software, alternative licenses can be acquired from the author. * + * * + * Note that the GPL places important restrictions on "derivative works", * + * yet it does not provide a detailed definition of that term. To avoid * + * misunderstandings, we interpret that term as broadly as copyright law * + * allows. For example, we consider an application to constitute a * + * derivative work for the purpose of this license if it does any of the * + * following with any software or content covered by this license * + * ("Covered Software"): * + * * + * o Integrates source code from Covered Software. * + * * + * o Reads or includes copyrighted data files. * + * * + * o Is designed specifically to execute Covered Software and parse the * + * results (as opposed to typical shell or execution-menu apps, which will * + * execute anything you tell them to). * + * * + * o Includes Covered Software in a proprietary executable installer. The * + * installers produced by InstallShield are an example of this. Including * + * DRAKVUF with other software in compressed or archival form does not * + * trigger this provision, provided appropriate open source decompression * + * or de-archiving software is widely available for no charge. For the * + * purposes of this license, an installer is considered to include Covered * + * Software even if it actually retrieves a copy of Covered Software from * + * another source during runtime (such as by downloading it from the * + * Internet). * + * * + * o Links (statically or dynamically) to a library which does any of the * + * above. * + * * + * o Executes a helper program, module, or script to do any of the above. * + * * + * This list is not exclusive, but is meant to clarify our interpretation * + * of derived works with some common examples. Other people may interpret * + * the plain GPL differently, so we consider this a special exception to * + * the GPL that we apply to Covered Software. Works which meet any of * + * these conditions must conform to all of the terms of this license, * + * particularly including the GPL Section 3 requirements of providing * + * source code and allowing free redistribution of the work as a whole. * + * * + * Any redistribution of Covered Software, including any derived works, * + * must obey and carry forward all of the terms of this license, including * + * obeying all GPL rules and restrictions. For example, source code of * + * the whole work must be provided and free redistribution must be * + * allowed. All GPL references to "this License", are to be treated as * + * including the terms and conditions of this license text as well. * + * * + * Because this license imposes special exceptions to the GPL, Covered * + * Work may not be combined (even as part of a larger work) with plain GPL * + * software. The terms, conditions, and exceptions of this license must * + * be included as well. This license is incompatible with some other open * + * source licenses as well. In some cases we can relicense portions of * + * DRAKVUF or grant special permissions to use it in other open source * + * software. Please contact tamas.k.lengyel@gmail.com with any such * + * requests. Similarly, we don't incorporate incompatible open source * + * software into Covered Software without special permission from the * + * copyright holders. * + * * + * If you have any questions about the licensing restrictions on using * + * DRAKVUF in other works, are happy to help. As mentioned above, * + * alternative license can be requested from the author to integrate * + * DRAKVUF into proprietary applications and appliances. Please email * + * tamas.k.lengyel@gmail.com for further information. * + * * + * If you have received a written license agreement or contract for * + * Covered Software stating terms other than these, you may choose to use * + * and redistribute Covered Software under those terms instead of these. * + * * + * Source is provided to this software because we believe users have a * + * right to know exactly what a program is going to do before they run it. * + * This also allows you to audit the software for security holes. * + * * + * Source code also allows you to port DRAKVUF to new platforms, fix bugs, * + * and add new features. You are highly encouraged to submit your changes * + * on https://github.com/tklengyel/drakvuf, or by other methods. * + * By sending these changes, it is understood (unless you specify * + * otherwise) that you are offering unlimited, non-exclusive right to * + * reuse, modify, and relicense the code. DRAKVUF will always be * + * available Open Source, but this is important because the inability to * + * relicense code has caused devastating problems for other Free Software * + * projects (such as KDE and NASM). * + * To specify special license conditions of your contributions, just say * + * so when you send them. * + * * + * 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 DRAKVUF * + * license file for more details (it's in a COPYING file included with * + * DRAKVUF, and also available from * + * https://github.com/tklengyel/drakvuf/COPYING) * + * * + ***************************************************************************/ + +#ifndef PROCMON_LINUX_UTILS_H +#define PROCMON_LINUX_UTILS_H + +#include +#include + +/** + * Parses "variable=value" string and return pair of key and value strings. + * Omit spaces after equal sign. Value may be an empty string. + * + * @param var string to parse + * @return pair of key and value strings or pair of empty strings in case of errors + */ +std::pair parse_environment_variable(const char* var); + +#endif