blob: eb0d67e17bbac5ab325eab85f8e7d694fa39aeff [file] [log] [blame]
/* **********************************************************
* Copyright (c) 2012 Google, Inc. All rights reserved.
* Copyright (c) 2006-2010 VMware, Inc. All rights reserved.
* **********************************************************/
/*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
*
* * Redistributions of source code must retain the above copyright notice,
* this list of conditions and the following disclaimer.
*
* * Redistributions in binary form must reproduce the above copyright notice,
* this list of conditions and the following disclaimer in the documentation
* and/or other materials provided with the distribution.
*
* * Neither the name of VMware, Inc. nor the names of its contributors may be
* used to endorse or promote products derived from this software without
* specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL VMWARE, INC. OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
* SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
* CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH
* DAMAGE.
*/
/* Copyright (c) 2006-2007 Determina Corp. */
#include "globals.h"
#include "utils.h"
#include "moduledb.h"
#include "module_shared.h"
#include <string.h> /* for memset, strlen */
/* An array of pointers to the various exempt lists indexed by the
* moduledb_exempt_list_t enum. We have the ugliness of an extra indirection
* and dynamic sizing to move the array into the heap and avoid self
* protection changes. */
static char **exemption_lists = NULL;
#define GET_EXEMPT_LIST(list) (exemption_lists[(list)])
/* protects access to the above lists, we assume it is safe to check for NULL
* without holding the lock */
DECLARE_CXTSWPROT_VAR(read_write_lock_t moduledb_lock,
INIT_READWRITE_LOCK(moduledb_lock));
static char *
moduledb_add_to_exempt_list(char *list, const char *name)
{
size_t cur_size_less_null = (list == NULL) ? 0 : strlen(list);
size_t needed_size = cur_size_less_null +
((cur_size_less_null > 0) ? 1 /* ; */ : 0) +
strlen(name) + 1 /* NULL */;
char *new_list = (char *)
global_heap_realloc(list, cur_size_less_null + 1 /* NULL */,
needed_size, sizeof(*list) HEAPACCT(ACCT_OTHER));
if (cur_size_less_null == 0)
*new_list = '\0';
else
ASSERT(strlen(new_list) == cur_size_less_null);
if (cur_size_less_null > 0) {
/* add separator */
strncat(new_list, ";", needed_size - cur_size_less_null - 1 /* null */);
}
strncat(new_list, name, needed_size - strlen(new_list) - 1 /* NULL */);
return new_list;
}
static char *
moduledb_remove_from_exempt_list(char *list, const char *name)
{
/* FIXME - does nothing, worry is if enough unique names are added to the
* list it could use a substantial amt of memory and take a while to walk
* (though note we won't get duplicate entries in module churn situations)
* or we could an get an accidental name collision with a later module. */
return list;
}
static void
moduledb_update_exempt_list(char **list, const char *name, bool add)
{
LOG(GLOBAL, LOG_MODULEDB, 2,
"\tlist before update \"%s\"\n", (*list == NULL) ? "" : *list);
write_lock(&moduledb_lock);
if (add && (*list == NULL || !check_filter(*list, name))) {
*list = moduledb_add_to_exempt_list(*list, name);
} else if (!add && *list != NULL && check_filter(*list, name)) {
*list = moduledb_remove_from_exempt_list(*list, name);
}
write_unlock(&moduledb_lock);
LOG(GLOBAL, LOG_MODULEDB, 2,
"\tlist after update \"%s\"\n", (*list == NULL) ? "" : *list);
}
static void
moduledb_process_relaxation_flags(uint flags, const char *name, bool add)
{
ASSERT_NOT_IMPLEMENTED(!TESTANY(~(MODULEDB_ALL_SECTIONS_BITS|MODULEDB_RCT_EXEMPT_TO|
MODULEDB_REPORT_ON_LOAD|MODULEDB_DLL2HEAP|
MODULEDB_DLL2STACK), flags));
if (TEST(MODULEDB_RCT_EXEMPT_TO, flags)) {
LOG(GLOBAL, LOG_MODULEDB, 1,
"%s module %s to moduledb exempt rct\n",
add ? "Adding" : "Removing", name);
moduledb_update_exempt_list(&GET_EXEMPT_LIST(MODULEDB_EXEMPT_RCT),
name, add);
}
if (TEST(MODULEDB_DLL2HEAP, flags)) {
LOG(GLOBAL, LOG_MODULEDB, 1,
"%s module %s to moduledb exempt dll2heap\n",
add ? "Adding" : "Removing", name);
moduledb_update_exempt_list(&GET_EXEMPT_LIST(MODULEDB_EXEMPT_DLL2HEAP),
name, add);
}
if (TEST(MODULEDB_DLL2STACK, flags)) {
LOG(GLOBAL, LOG_MODULEDB, 1,
"%s module %s to moduledb exempt dll2stack\n",
add ? "Adding" : "Removing", name);
moduledb_update_exempt_list(&GET_EXEMPT_LIST(MODULEDB_EXEMPT_DLL2STACK),
name, add);
}
if (TESTANY(MODULEDB_ALL_SECTIONS_BITS, flags)) {
uint all_sections = (flags & MODULEDB_ALL_SECTIONS_BITS) >>
MODULEDB_ALL_SECTIONS_SHIFT;
ASSERT_NOT_IMPLEMENTED(all_sections == SECTION_ALLOW);
if (all_sections == SECTION_ALLOW) {
LOG(GLOBAL, LOG_MODULEDB, 1,
"%s module %s to moduledb exec if image\n",
add ? "Adding" : "Removing", name);
moduledb_update_exempt_list(&GET_EXEMPT_LIST(MODULEDB_EXEMPT_IMAGE),
name, add);
}
}
}
/* for DO_THRESHOLD_SAFE statics */
START_DATA_SECTION(FREQ_PROTECTED_SECTION, "w")
void
moduledb_report_exemption(const char *fmt, app_pc addr1, app_pc addr2,
const char *name)
{
ASSERT(exemption_lists != NULL);
/* FIXME - need a release version of this */
/* FIXME - should these respect some MODULEDB_REPORT flag? */
DODEBUG({
/* FIXME - would be nice to only report unique module names per type. */
DO_THRESHOLD_SAFE(DYNAMO_OPTION(moduledb_exemptions_report_max),
FREQ_PROTECTED_SECTION, {
/* < max */
SYSLOG_INTERNAL_WARNING(fmt, addr1, addr2, name);
}, { /* > max -> nothing */ });
});
}
/* somewhat arbitrary, but more than long enough for current usage */
#define MAX_COMPANY_NAME 256
void
moduledb_process_image(const char *name, app_pc base, bool add)
{
char company_name[MAX_COMPANY_NAME];
bool got_company_name;
bool relax = true;
ASSERT(exemption_lists != NULL);
if (!DYNAMO_OPTION(use_moduledb))
return;
/* caller has already verified this is a pe module */
got_company_name =
get_module_company_name(base, company_name,
BUFFER_SIZE_ELEMENTS(company_name));
if (!got_company_name)
company_name[0] = '\0';
if (got_company_name && company_name[0] != '\0' &&
(!IS_STRING_OPTION_EMPTY(whitelist_company_names_default) ||
!IS_STRING_OPTION_EMPTY(whitelist_company_names)) &&
check_list_default_and_append(dynamo_options.whitelist_company_names_default,
dynamo_options.whitelist_company_names,
company_name)) {
relax = false;
LOG(GLOBAL, LOG_MODULEDB, 1,
"Found module \"%s\" from whitelisted company \"%s\"\n",
name == NULL ? "no-name" : name, company_name);
/* FIXME - not all of our modules have the Company name
* field set (drpreinject & liveshields don't), need to avoid
* relaxing for those. Should add version info and also check
* nodgemgr and our other tools etc. xref case 8723 */
}
if (relax) {
if (name == NULL || *name == '\0') {
if (add) {
/* FIXME - not able to relax for these nameless dlls, there shouldn't be
* too many of these once we also fall back to the version original
* filename for modules with no exports and we'll eventually exempt by
* area in the modules list instead of by name anyways. */
/* FIXME - would be nice to use get_module_name to get the filename of
* the module at least, but this a bad time w/respect to the loader to
* be walking the lists. */
SYSLOG_INTERNAL_WARNING("Unable to relax for nameless unknown module "
"from \"%s\" @"PFX, company_name, base);
}
} else {
LOG(GLOBAL, LOG_MODULEDB, 1, "Loaded unknown module %s\n", name);
/* process the relaxations */
moduledb_process_relaxation_flags(DYNAMO_OPTION(unknown_module_policy),
name, add);
/* FIXME - prob. too noisy, on my machine there are
* usually 5 of these per process, two for logitech mouse hook
* dlls, 1 for drpreinject and 2 for Norton av. */
if (add && TEST(MODULEDB_REPORT_ON_LOAD,
DYNAMO_OPTION(unknown_module_policy))) {
/* FIXME - will prob. need a release version of this */
DODEBUG({
DO_THRESHOLD_SAFE(DYNAMO_OPTION(unknown_module_load_report_max),
FREQ_PROTECTED_SECTION, {
/* < max */
SYSLOG_INTERNAL_INFO("Relaxing protections for unknown module "
"%s @"PFX" from \"%s\"",
name, base, company_name);
}, { /* > max -> nothing */ });
});
}
}
}
}
/* back to normal section */
END_DATA_SECTION()
static const char *const
exempt_list_names[MODULEDB_EXEMPT_NUM_LISTS] = { "rct", "image", "dll2heap", "dll2stack" };
void
moduledb_init()
{
uint exempt_array_size = (MODULEDB_EXEMPT_NUM_LISTS) * sizeof(char *);
ASSERT(exemption_lists == NULL);
exemption_lists = (char **)
global_heap_alloc(exempt_array_size HEAPACCT(ACCT_OTHER));
ASSERT(exemption_lists != NULL);
memset(exemption_lists, 0, exempt_array_size);
DOCHECK(1, {
int i;
for (i = 0; i < MODULEDB_EXEMPT_NUM_LISTS; i++)
ASSERT(exempt_list_names[i] != NULL);
});
}
void
moduledb_exit()
{
int i;
ASSERT(exemption_lists != NULL);
DOLOG(1, LOG_MODULEDB, {
LOG(GLOBAL, LOG_MODULEDB, 1, "Moduledb exit:\n");
print_moduledb_exempt_lists(GLOBAL);
});
for (i = 0; i < MODULEDB_EXEMPT_NUM_LISTS; i++) {
char *list = GET_EXEMPT_LIST(i);
if (list != NULL) {
global_heap_free(list, strlen(list) + 1 /* NULL */
HEAPACCT(ACCT_OTHER));
}
}
global_heap_free(exemption_lists, (MODULEDB_EXEMPT_NUM_LISTS) * sizeof(char *)
HEAPACCT(ACCT_OTHER));
DELETE_READWRITE_LOCK(moduledb_lock);
}
bool
moduledb_exempt_list_empty(moduledb_exempt_list_t list)
{
return GET_EXEMPT_LIST(list) == NULL;
}
/* checks to see if module name is on the moduledb exempt list pointed
* to by list */
bool
moduledb_check_exempt_list(moduledb_exempt_list_t list, const char *name)
{
bool found = false;
ASSERT(exemption_lists != NULL);
read_lock(&moduledb_lock);
if (GET_EXEMPT_LIST(list) != NULL) {
LOG(GLOBAL, LOG_MODULEDB, 2,
"Moduledb checking %s exempt list =\"%s\" for module \"%s\"\n",
exempt_list_names[list],
GET_EXEMPT_LIST(list) == NULL ? "" : GET_EXEMPT_LIST(list), name);
found = check_filter(GET_EXEMPT_LIST(list), name);
}
read_unlock(&moduledb_lock);
return found;
}
void
print_moduledb_exempt_lists(file_t file)
{
int i;
ASSERT(exemption_lists != NULL);
read_lock(&moduledb_lock);
for (i = 0; i < MODULEDB_EXEMPT_NUM_LISTS; i++) {
ASSERT(exempt_list_names[i] != NULL);
print_file(file, "moduledb %s exemption list =\"%s\"\n",
exempt_list_names[i],
GET_EXEMPT_LIST(i) == NULL ? "" : GET_EXEMPT_LIST(i));
}
read_unlock(&moduledb_lock);
}
#ifdef PROCESS_CONTROL
/***************************************************************************/
/* Process control */
/* Note: if the process control/lockdown feature increases in size, then
* create a separate file; for now let it be here.
*/
/* As of today, either the blacklist or the whitelist can be used, not both,
* according to the PRD. In that case there is no need to distinguish between
* "not matched" and "no hashlist". However, if we decide that both need to
* co-exist (Windows sw restriction policies do a mix), then this is needed.
* The core is designed to be flexible to handle both.
*/
enum {
/* NOTE: this isn't the same as "empty" hashlist. This just means that
* the hashlist registry key doesn't exist.
*/
PROCESS_CONTROL_NO_HASHLIST,
PROCESS_CONTROL_HASHLIST_EMPTY,
/* Hash list is too big to fit in our buffer so no list was obtained, which
* means we can't use this to make any decision about process control,
* i.e., don't do any process control.
*/
PROCESS_CONTROL_LONG_LIST,
PROCESS_CONTROL_NOT_MATCHED,
PROCESS_CONTROL_MATCHED
};
/* This macro defines "matched" to either being on the hashlist or the hashlist
* being empty; empty hashlist is a wildcard match for white and black list
* modes, but not for whitelist integrity mode. Note, this macro is used only
* for white and black list modes, not whitelist integrity mode.
*/
# define IS_PROCESS_CONTROL_MATCHED(x) \
((x) == PROCESS_CONTROL_MATCHED || (x) == PROCESS_CONTROL_HASHLIST_EMPTY)
/* Records an event static that the hash list in reg_key is too long.
* Note: the reg_key here is (char *) as opposed to (wchar_t *) because our
* syslogging can only handle regular chars!
*/
static void
process_control_report_long_list(const char *reg_key)
{
char num_hash_str[6]; /* Won't need more than 5 digits for num hashes! */
snprintf(num_hash_str, BUFFER_SIZE_ELEMENTS(num_hash_str), "%d",
DYNAMO_OPTION(pc_num_hashes));
/* Be safe, in case some one uses more than 99999 hashes! */
NULL_TERMINATE_BUFFER(num_hash_str);
SYSLOG(SYSLOG_WARNING, PROC_CTL_HASH_LIST_TOO_LONG, 4,
get_application_name(), get_application_pid(),
reg_key, num_hash_str);
}
/* This routine reads the hash keys from either the app-specific or the anonymous
* reg_key for the app and checks if md5_hash is on that list of keys.
* If so it returns true, if not false.
*/
static int
process_control_match(const char *md5_hash,
#ifdef PARAMS_IN_REGISTRY
const IF_WINDOWS_ELSE_NP(wchar_t, char) *reg_key
#else
const char *reg_key
#endif
)
{
int ret_val, res;
char *hash_list;
/* The default size of the hash list is set to 100 hashes. It is unlikely
* that anyone will exceed it even for anonymous hashes. If they do then
* process control will be disabled, i.e., no enforcement will be done.
* Case 9252. BTW, the +1 is for the delimiting ';'.
* FIXME: when we read md5 from a file this should go.
*/
uint list_size = DYNAMO_OPTION(pc_num_hashes) * (MD5_STRING_LENGTH + 1);
/* Read the hash_list from the registry. The hash_list is nothing but a
* semicolon separated string, just like all core option string.
*/
hash_list = heap_alloc(GLOBAL_DCONTEXT, list_size HEAPACCT(ACCT_OTHER));
hash_list[0] = '\0'; /* be safe, in case there is no list */
/* xref case 9119, we want the value from the unqualified key since our usage of
* this only depends on the exe (not its cmdline). */
res = get_unqualified_parameter(reg_key, hash_list, list_size);
hash_list[list_size - 1] = '\0'; /* be safe */
if (IS_GET_PARAMETER_SUCCESS(res)) {
if (hash_list[0] == '\0') {
/* empty hash is a wildcard match only for white and black list
* modes, not for whitelist integrity mode. */
ret_val = PROCESS_CONTROL_HASHLIST_EMPTY;
} else if (check_filter(hash_list, md5_hash)) { /* hash matched */
ret_val = PROCESS_CONTROL_MATCHED;
} else {
ret_val = PROCESS_CONTROL_NOT_MATCHED;
}
/* FIXME: Anonymous hashes should be in a global registry key and app
* specific hashes in app specific keys. Though there is the facility
* to do otherwise, we should restrict them because combinations
* like app specific anonymous hashes don't make sense. But how to?
*/
} else if (res == GET_PARAMETER_BUF_TOO_SMALL) { /* Case 9252. */
ret_val = PROCESS_CONTROL_LONG_LIST;
} else {
ASSERT(res == GET_PARAMETER_FAILURE);
/* Couldn't read the key, so assuming there is no hashlist. */
ret_val = PROCESS_CONTROL_NO_HASHLIST;
}
heap_free(GLOBAL_DCONTEXT, hash_list, list_size HEAPACCT(ACCT_OTHER));
return ret_val;
}
/* In the regular whitelist mode, the process will be allowed to run if its
* executable's hash matches a hash either on the app specific list or the
* anonymous list, or if any of those lists are empty.
*
* In the whitelist integrity mode, the process will be allowed to run if its
* executable's hash matches a hash on its app specific hashlist or there is no
* app specific hashlist at all. The idea is to ascertain that an executable
* hasn't changed. If there is no need to track the change, then those exe's
* won't have a hashlist.
* Though the regular whitelist mode can be used to do the same, there are
* holes in it like the ones below that have to be manually fixed.
* 1. support for anonymous hashes; would have to be disabled or not used.
* 2. support exe names without hashes; same resolution as point #1.
* 3. apps would be killed if there is no hashlist; would have to add empty
* global hashlists.
*
* Note: xref case 10969 for whitelist integrity mode.
*/
static void
process_control_whitelist(const char *md5_hash)
{
security_option_t type_handling;
action_type_t desired_action;
const char *threat_id = NULL;
int anonymous = PROCESS_CONTROL_NOT_MATCHED;
int app_specific =
process_control_match(md5_hash, PARAM_STR(DYNAMORIO_VAR_APP_PROCESS_WHITELIST));
/* Do the pure whitelist mode check in case both modes were specified
* accidentally; a matter of precedence.
*/
if (IS_PROCESS_CONTROL_MODE_WHITELIST()) {
/* Allow the process if md5_hash matched a hash on either the app
* specific or anonymous hash list.
*/
if (IS_PROCESS_CONTROL_MATCHED(app_specific))
return;
anonymous = process_control_match(md5_hash,
PARAM_STR(DYNAMORIO_VAR_ANON_PROCESS_WHITELIST));
if (IS_PROCESS_CONTROL_MATCHED(anonymous))
return;
threat_id = "WHIT.NOMA"; /* WHIT.NOMA = WHITe list NOt MAtched. */
/* If there was no match on the anonymous list and if it was too
* long, then we can't decide to kill the process because we didn't
* search the full list. Do no harm and ignore process control. Case 9252.
*/
if (anonymous == PROCESS_CONTROL_LONG_LIST) {
process_control_report_long_list(DYNAMORIO_VAR_ANON_PROCESS_WHITELIST);
return;
}
} else if (IS_PROCESS_CONTROL_MODE_WHITELIST_INTEGRITY()) {
/* Allow the process only if md5_hash matched a hash on the app
* specific hash list; this is the integrity tracking part. If there
* is no hash list, it means that this process's exe wasn't added to
* the integrity mode, so let it run.
*/
if (app_specific == PROCESS_CONTROL_MATCHED ||
app_specific == PROCESS_CONTROL_NO_HASHLIST)
return;
/* There is a bug in the controller if app specific hashlist is empty
* for the integrity mode.
*/
ASSERT(app_specific != PROCESS_CONTROL_HASHLIST_EMPTY);
threat_id = "WHIT.INTG"; /* WHIT.NOMA = WHITe list INTeGrity mode. */
} else {
ASSERT_NOT_REACHED();
return; /* play it safe */
}
/* If there was no match on the app specific list and if it was too
* long, then we can't decide to kill the process because we didn't search
* the full list. Do no harm and ignore process control. Case 9252.
*/
if (app_specific == PROCESS_CONTROL_LONG_LIST) {
process_control_report_long_list(DYNAMORIO_VAR_APP_PROCESS_WHITELIST);
return;
}
/* At this point, it should either be not-matched or no-hashlist.
* Note: No hashlist is equivalent to no match; for white list, this means
* kill.
*/
ASSERT((app_specific == PROCESS_CONTROL_NOT_MATCHED ||
app_specific == PROCESS_CONTROL_NO_HASHLIST));
/* Anonymous lists aren't applicable for integrity mode. */
if (IS_PROCESS_CONTROL_MODE_WHITELIST()) {
ASSERT((anonymous == PROCESS_CONTROL_NOT_MATCHED ||
anonymous == PROCESS_CONTROL_NO_HASHLIST));
}
#ifdef PROGRAM_SHEPHERDING
/* Process control has its own detect_mode; case 10610. */
if (DYNAMO_OPTION(pc_detect_mode)) {
type_handling = OPTION_REPORT;
desired_action = ACTION_CONTINUE;
} else {
type_handling = OPTION_REPORT|OPTION_BLOCK_IGNORE_DETECT;
desired_action = ACTION_TERMINATE_PROCESS;
}
/* All process control violations will be .K. As the exe name and
* pid are already in the event, the threat ID has nothing else to
* convey, hence a constant string is used.
*/
security_violation_internal(GLOBAL_DCONTEXT, NULL,
PROCESS_CONTROL_VIOLATION,
type_handling, threat_id,
desired_action, NULL);
/* Can reach here only if process control is in detect mode. */
ASSERT(DYNAMO_OPTION(pc_detect_mode));
#endif
}
static void
process_control_blacklist(const char *md5_hash)
{
security_option_t type_handling;
action_type_t desired_action;
int app_specific =
process_control_match(md5_hash, PARAM_STR(DYNAMORIO_VAR_APP_PROCESS_BLACKLIST));
int anonymous =
process_control_match(md5_hash, PARAM_STR(DYNAMORIO_VAR_ANON_PROCESS_BLACKLIST));
ASSERT(IS_PROCESS_CONTROL_MODE_BLACKLIST());
if (IS_PROCESS_CONTROL_MATCHED(app_specific) ||
IS_PROCESS_CONTROL_MATCHED(anonymous)) {
#ifdef PROGRAM_SHEPHERDING
/* Process control has its own detect_mode; case 10610. */
if (DYNAMO_OPTION(pc_detect_mode)) {
type_handling = OPTION_REPORT;
desired_action = ACTION_CONTINUE;
} else {
type_handling = OPTION_REPORT|OPTION_BLOCK_IGNORE_DETECT;
desired_action = ACTION_TERMINATE_PROCESS;
}
/* All process control violations will be .K; for blacklist we kill
* if the current executable's process is either on the anonymous
* or on the app specific whitelist. As the exe name and
* pid are already in the event, the threat ID will only convey what
* list was used, anonymous ("ANON.BLAC") or app-specific ("ANON.BLAC").
*/
security_violation_internal(GLOBAL_DCONTEXT, NULL,
PROCESS_CONTROL_VIOLATION, type_handling,
app_specific == PROCESS_CONTROL_MATCHED ?
"BLAC.APPS" : "BLAC.ANON",
desired_action, NULL);
/* Can reach here only if process control is in detect mode. */
ASSERT(DYNAMO_OPTION(pc_detect_mode));
#endif
} else if (app_specific == PROCESS_CONTROL_LONG_LIST) { /* Case 9252. */
process_control_report_long_list(DYNAMORIO_VAR_APP_PROCESS_BLACKLIST);
} else if (anonymous == PROCESS_CONTROL_LONG_LIST){ /* Case 9252. */
process_control_report_long_list(DYNAMORIO_VAR_ANON_PROCESS_BLACKLIST);
} else {
/* At this point, it should either be not-matched or no-hashlist.
* Note: No hashlist is equivalent to no match; for black list, this
* means don't kill.
*/
ASSERT((app_specific == PROCESS_CONTROL_NOT_MATCHED ||
app_specific == PROCESS_CONTROL_NO_HASHLIST) &&
(anonymous == PROCESS_CONTROL_NOT_MATCHED ||
anonymous == PROCESS_CONTROL_NO_HASHLIST));
}
}
/* This routine does all the process control work. This work is the same for
* both the startup scenario and the nudge scenario. The bool is to identify
* whether or not we are in the startup phase because only in that phase we
* dump a process-start-event.
*/
void
process_control(void)
{
const char *md5_hash = get_application_md5();
bool is_black, is_white, is_white_intg;
if (strlen(md5_hash) != MD5_STRING_LENGTH) {
/* FIXME: what to do if we couldn't get md5 for the current process?
* today the default is to ignore and keep going, but is that ok?
* should ev be notified?
*/
ASSERT_NOT_REACHED();
return;
}
is_black = IS_PROCESS_CONTROL_MODE_BLACKLIST();
is_white = IS_PROCESS_CONTROL_MODE_WHITELIST();
is_white_intg = IS_PROCESS_CONTROL_MODE_WHITELIST_INTEGRITY();
/* Currently only one mode can be used; this is how our product is sold.
* However, there is nothing preventing us from using all of them (just
* make each process control mode below a separate if) with a precedence
* order. That was how it was originally; however if there is a bug in EV,
* we would accidentally run multiple modes at once. So to safeguard
* against that the if-else construct below is used.
*/
ASSERT((is_black && !is_white && !is_white_intg) ||
(!is_black && is_white && !is_white_intg) ||
(!is_black && !is_white && is_white_intg));
if (is_black)
process_control_blacklist(md5_hash);
else if (is_white || is_white_intg)
process_control_whitelist(md5_hash);
else
ASSERT_NOT_REACHED();
}
void process_control_init(void)
{
process_control();
}
/***************************************************************************/
#endif /* PROCESS_CONTROL */