Skip to content

Commit

Permalink
Change how to Windows agent process permissions of a file, from char …
Browse files Browse the repository at this point in the history
…to cJSON
  • Loading branch information
jotacarma90 committed Jul 1, 2021
1 parent a3d2d1a commit 1e5e17d
Show file tree
Hide file tree
Showing 6 changed files with 226 additions and 23 deletions.
3 changes: 3 additions & 0 deletions src/config/syscheck-config.h
Original file line number Diff line number Diff line change
Expand Up @@ -278,6 +278,9 @@ typedef struct registry_ignore_regex {
typedef struct fim_file_data {
// Checksum attributes
unsigned int size;
#ifdef WIN32
cJSON * perm_json;
#endif
char * perm;
char * attributes;
char * uid;
Expand Down
2 changes: 2 additions & 0 deletions src/error_messages/warning_messages.h
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,8 @@
#define FIM_AUDIT_DISABLED "(6946): Audit is disabled."
#define FIM_WARN_FORMAT_PATH "(6947): Error formatting path: '%s'"
#define FIM_DATABASE_NODES_COUNT_FAIL "(6948): Unable to get the number of entries in database."
#define FIM_CJSON_ERROR_CREATE_ITEM "(6949): Cannot create a cJSON item"


/* Monitord warning messages */
#define ROTATE_LOG_LONG_PATH "(7500): The path of the rotated log is too long."
Expand Down
12 changes: 9 additions & 3 deletions src/headers/syscheck_op.h
Original file line number Diff line number Diff line change
Expand Up @@ -360,11 +360,10 @@ unsigned int w_get_file_attrs(const char *file_path);
* @brief Retrieves the permissions of a specific file (Windows)
*
* @param [in] file_path The path of the file from which to check permissions
* @param [out] permissions Buffer in which to write the permissions
* @param [in] perm_size The size of the permissions buffer
* @param [out] acl_json cJSON in which to write the acl
* @return 0 on success, the error code on failure, -2 if ACE could not be obtained
*/
int w_get_file_permissions(const char *file_path, char *permissions, int perm_size);
int w_get_file_permissions(const char *file_path, cJSON *acl_json);

/**
* @brief Retrieves the group name from a group ID in windows
Expand Down Expand Up @@ -441,6 +440,13 @@ void decode_win_attributes(char *str, unsigned int attrs);
*/
char *decode_win_permissions(char *raw_perm);

/**
* @brief Decodes a permission string and converts it to a human readable format
*
* @param [out] acl_json A cJSON with the permissions to decode
*/
void decode_win_acl_json(cJSON *acl_json);

/**
* @brief Transforms a bit mask of attributes into a human readable cJSON
*
Expand Down
203 changes: 188 additions & 15 deletions src/shared/syscheck_op.c
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,36 @@ extern void mock_assert(const int result, const char* const expression,
#endif
#endif

/**
* @brief Retrieves the permissions of a specific file (Windows)
*
* @param [in] ace ACE structure
* @param [out] acl_json cJSON to write the permissions
* @return 0 on success, the error code on failure, -2 if ACE could not be obtained
*/
static int process_ace_info(void *ace, cJSON *acl_json);

/**
* @brief Retrieves the permissions of a specific file (Windows)
*
* @param [out] acl_json cJSON to write the permissions
* @param [in] sid The user ID associated to the user
* @param [in] account_name The account name associated to the sid
* @param [in] ace_type int with 0 if "allowed" ace, 1 if "denied" ace
* @param [in] mask Mask with the permissions
*/
static void add_ace_to_json(cJSON *acl_json, char *sid, char *account_name, const char *ace_type, int mask);

/**
* @brief Retrieves the permissions of a specific file (Windows)
*
* @param [out] ace_json cJSON with the mask to process
* @param [in] mask Mask with the permissions
* @param [in] ace_type string "allowed" or "denied" depends on ace type
*/
static void make_mask_readable (cJSON *ace_json, int mask, char *ace_type);


char *escape_syscheck_field(char *field) {
char *esc_it;

Expand Down Expand Up @@ -765,7 +795,7 @@ char *get_user(const char *path, char **sid, HANDLE hndl, SE_OBJECT_TYPE object_
return result;
}

int w_get_file_permissions(const char *file_path, char *permissions, int perm_size) {
int w_get_file_permissions(const char *file_path, cJSON *acl_json) {
int retval = 0;
int error;
unsigned int i;
Expand All @@ -775,9 +805,6 @@ int w_get_file_permissions(const char *file_path, char *permissions, int perm_si
int has_dacl, default_dacl;
unsigned long size = 0;
ACL_SIZE_INFORMATION acl_size;
char *perm_it = permissions;

*permissions = '\0';

if (!GetFileSecurity(file_path, DACL_SECURITY_INFORMATION, 0, 0, &size)) {
// We must have this error at this point
Expand Down Expand Up @@ -813,30 +840,98 @@ int w_get_file_permissions(const char *file_path, char *permissions, int perm_si
}

for (i = 0; i < acl_size.AceCount; i++) {
int written;

if (!GetAce(f_acl, i, &f_ace)) {
mdebug1("ACE number %d could not be obtained.", i);
retval = -2;
*permissions = '\0';
goto end;
}
written = copy_ace_info(f_ace, perm_it, perm_size);
if (written > 0) {
perm_it += written;
perm_size -= written;
if (perm_size > 0) {
continue;
}
if (process_ace_info(f_ace, acl_json)) {
mdebug1("ACE number %d could not be processed.", i);
}
mdebug1("The parameters of ACE number %d from '%s' could not be extracted. %d bytes remaining.", i, file_path, perm_size);
}

end:
free(s_desc);
return retval;
}

int process_ace_info(void *ace, cJSON *acl_json) {
SID *sid;
char *sid_str = NULL;
char *account_name = NULL;
char *domain_name = NULL;
int mask;
int ace_type;
int error;

if (((ACCESS_ALLOWED_ACE *)ace)->Header.AceType == ACCESS_ALLOWED_ACE_TYPE) {
ACCESS_ALLOWED_ACE *allowed_ace = (ACCESS_ALLOWED_ACE *)ace;
sid = (SID *)&allowed_ace->SidStart;
mask = allowed_ace->Mask;
ace_type = 0;
} else if (((ACCESS_DENIED_ACE *)ace)->Header.AceType == ACCESS_DENIED_ACE_TYPE) {
ACCESS_DENIED_ACE *denied_ace = (ACCESS_DENIED_ACE *)ace;
sid = (SID *)&denied_ace->SidStart;
mask = denied_ace->Mask;
ace_type = 1;
} else {
mdebug2("Invalid ACE type.");
return 1;
}

if (!IsValidSid(sid)) {
mdebug2("Invalid SID found in ACE.");
return 1;
}

if (error = w_get_account_info(sid, &account_name, &domain_name), error) {
mdebug2("No information could be extracted from the account linked to the SID. Error: %d.", error);
}

if (!ConvertSidToStringSid(sid, &sid_str)) {
mdebug2("Could not extract the SID.");
free(account_name);
free(domain_name);
return 1;
}

add_ace_to_json(acl_json, sid_str, account_name, ace_type ? "denied" : "allowed", mask);
LocalFree(sid_str);

return 0;
}

void add_ace_to_json(cJSON *acl_json, char *sid, char *account_name, const char *ace_type, int mask) {
cJSON *ace_json = NULL;
cJSON *mask_json = NULL;
int saved_mask;

assert(acl_json != NULL);
assert(strcmp(ace_type, "allowed") == 0 || strcmp(ace_type, "denied") == 0);

ace_json = cJSON_GetObjectItem(acl_json, sid);
if (ace_json == NULL) {
ace_json = cJSON_CreateObject();
if (ace_json == NULL) {
mwarn(FIM_CJSON_ERROR_CREATE_ITEM);
return;
}
cJSON_AddStringToObject(ace_json, "name", account_name);
cJSON_AddItemToObject(acl_json, sid, ace_json);
}

mask_json = cJSON_GetObjectItem(ace_json, ace_type);
if (mask_json == NULL) {
cJSON_AddNumberToObject(ace_json, ace_type, mask);
return;
}

saved_mask = mask_json->valueint;
cJSON_SetNumberValue(mask_json, (saved_mask | mask));

return;
}

int copy_ace_info(void *ace, char *perm, int perm_size) {
SID *sid;
char *sid_str = NULL;
Expand Down Expand Up @@ -1170,6 +1265,84 @@ void decode_win_attributes(char *str, unsigned int attrs) {
}
}

void decode_win_acl_json (cJSON *acl_json) {
cJSON *json_object = NULL;
cJSON *allowed_item = NULL;
cJSON *denied_item = NULL;

assert(acl_json != NULL);

cJSON_ArrayForEach(json_object, acl_json) {
allowed_item = cJSON_GetObjectItem(json_object, "allowed");
if (allowed_item) {
make_mask_readable(json_object, allowed_item->valueint, "allowed");
}
denied_item = cJSON_GetObjectItem(json_object, "denied");
if (denied_item) {
make_mask_readable(json_object, denied_item->valueint, "denied");
}
}
}

void make_mask_readable (cJSON *ace_json, int mask, char *ace_type) {
int i;
int perm_bits[] = {
GENERIC_READ,
GENERIC_WRITE,
GENERIC_EXECUTE,
GENERIC_ALL,
DELETE,
READ_CONTROL,
WRITE_DAC,
WRITE_OWNER,
SYNCHRONIZE,
FILE_READ_DATA,
FILE_WRITE_DATA,
FILE_APPEND_DATA,
FILE_READ_EA,
FILE_WRITE_EA,
FILE_EXECUTE,
FILE_READ_ATTRIBUTES,
FILE_WRITE_ATTRIBUTES,
0
};

static const char * const perm_strings[] = {
"generic_read",
"generic_write",
"generic_execute",
"generic_all",
"delete",
"read_control",
"write_dac",
"write_owner",
"synchronize",
"read_data",
"write_data",
"append_data",
"read_ea",
"write_ea",
"execute",
"read_attributes",
"write_attributes",
NULL
};

cJSON *perm_array = cJSON_CreateArray();
if (perm_array == NULL) {
mwarn(FIM_CJSON_ERROR_CREATE_ITEM);
return;
}

for (i = 0; perm_bits[i]; i++) {
if (mask & perm_bits[i]) {
cJSON_AddItemToArray(perm_array, cJSON_CreateString(perm_strings[i]));
}
}

cJSON_ReplaceItemInObject(ace_json, ace_type, perm_array);
}

char *decode_win_permissions(char *raw_perm) {
int written = 0;
int size = 0;
Expand Down
26 changes: 21 additions & 5 deletions src/syscheckd/create_db.c
Original file line number Diff line number Diff line change
Expand Up @@ -1153,15 +1153,23 @@ fim_file_data *fim_get_data(const char *file, const directory_t *configuration,
if (configuration->options & CHECK_PERM) {
#ifdef WIN32
int error;
char perm[OS_SIZE_6144 + 1];
cJSON *acl_json = cJSON_CreateObject();
if (acl_json == NULL) {
mwarn(FIM_CJSON_ERROR_CREATE_ITEM);
return NULL;
}

if (error = w_get_file_permissions(file, perm, OS_SIZE_6144), error) {
error = w_get_file_permissions(file, acl_json);
if (error) {
mdebug1(FIM_EXTRACT_PERM_FAIL, file, error);
free_file_data(data);
return NULL;
} else {
data->perm = decode_win_permissions(perm);
}

decode_win_acl_json(acl_json);

data->perm_json = acl_json;
data->perm = cJSON_PrintUnformatted(acl_json);
#else
data->perm = agent_file_perm(statbuf->st_mode);
#endif
Expand Down Expand Up @@ -1259,8 +1267,8 @@ void init_fim_data_entry(fim_file_data *data) {
}

void fim_get_checksum (fim_file_data * data) {
char *checksum = NULL;
int size;
char *checksum = NULL;

size = snprintf(0,
0,
Expand Down Expand Up @@ -1411,7 +1419,11 @@ cJSON * fim_attributes_json(const fim_file_data * data) {
}

if (data->options & CHECK_PERM) {
#ifndef WIN32
cJSON_AddStringToObject(attributes, "perm", data->perm);
#else
cJSON_AddItemToObject(attributes, "perm", cJSON_Duplicate(data->perm_json, 1));
#endif
}

if (data->options & CHECK_OWNER) {
Expand Down Expand Up @@ -1524,6 +1536,7 @@ cJSON * fim_json_compare_attrs(const fim_file_data * old_data, const fim_file_da
cJSON_AddItemToArray(changed_attributes, cJSON_CreateString("sha256"));
}


return changed_attributes;
}

Expand Down Expand Up @@ -1618,6 +1631,9 @@ void free_file_data(fim_file_data * data) {
return;
}

#ifdef WIN32
cJSON_Delete(data->perm_json);
#endif
os_free(data->perm);
os_free(data->attributes);
os_free(data->uid);
Expand Down
3 changes: 3 additions & 0 deletions src/syscheckd/db/fim_db_files.c
Original file line number Diff line number Diff line change
Expand Up @@ -180,6 +180,9 @@ fim_entry *fim_db_decode_full_row(sqlite3_stmt *stmt) {
entry->file_entry.data->inode = (unsigned long int)sqlite3_column_int64(stmt, 8);
entry->file_entry.data->size = (unsigned int)sqlite3_column_int(stmt, 9);
sqlite_strdup((char *)sqlite3_column_text(stmt, 10), entry->file_entry.data->perm);
#ifdef WIN32
entry->file_entry.data->perm_json = cJSON_Parse((char *)sqlite3_column_text(stmt, 10));
#endif
sqlite_strdup((char *)sqlite3_column_text(stmt, 11), entry->file_entry.data->attributes);
sqlite_strdup((char *)sqlite3_column_text(stmt, 12), entry->file_entry.data->uid);
sqlite_strdup((char *)sqlite3_column_text(stmt, 13), entry->file_entry.data->gid);
Expand Down

0 comments on commit 1e5e17d

Please sign in to comment.