Skip to content
7 changes: 7 additions & 0 deletions etc/rules/ossec_rules.xml
Original file line number Diff line number Diff line change
Expand Up @@ -211,6 +211,13 @@
<group>syscheck,agentless</group>
</rule>

<rule id="556" level="3">
<category>ossec</category>
<decoded_as>syscheck_allowed</decoded_as>
<description>Integrity checksum changed (allowed).</description>
<group>syscheck,</group>
</rule>

<!-- Hostinfo rules -->
<rule id="580" level="8">
<category>ossec</category>
Expand Down
1 change: 1 addition & 0 deletions src/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -379,6 +379,7 @@ install-common: build
install -d -m 0770 -o ${OSSEC_USER} -g ${OSSEC_GROUP} ${PREFIX}/queue/alerts
install -d -m 0750 -o ${OSSEC_USER} -g ${OSSEC_GROUP} ${PREFIX}/queue/ossec
install -d -m 0750 -o ${OSSEC_USER} -g ${OSSEC_GROUP} ${PREFIX}/queue/syscheck
install -d -m 0750 -o ${OSSEC_USER} -g ${OSSEC_GROUP} ${PREFIX}/queue/syscheck-allowchange
install -d -m 0750 -o ${OSSEC_USER} -g ${OSSEC_GROUP} ${PREFIX}/queue/diff

install -d -m 0550 -o root -g ${OSSEC_GROUP} ${PREFIX}/etc
Expand Down
10 changes: 10 additions & 0 deletions src/analysisd/analysisd.c
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
#include "cleanevent.h"
#include "dodiff.h"
#include "output/jsonout.h"
#include "decoders/syscheck-allow.h"

#ifdef PICVIZ_OUTPUT_ENABLED
#include "output/picviz.h"
Expand Down Expand Up @@ -755,6 +756,15 @@ void OS_ReadMSG_analysisd(int m_queue)
lf->size = strlen(lf->log);
}

/* Allowchange event decoding */
else if (msg[0] == ALLOWCHANGE_MQ) {
if (!DecodeAllowchange(lf)) {
/* We don't process allowchange events further */
goto CLMEM;
}
lf->size = strlen(lf->log);
}

/* Host information special decoder */
else if (msg[0] == HOSTINFO_MQ) {
if (!DecodeHostinfo(lf)) {
Expand Down
1 change: 1 addition & 0 deletions src/analysisd/decoders/decode-xml.c
Original file line number Diff line number Diff line change
Expand Up @@ -691,6 +691,7 @@ int SetDecodeXML()
addDecoder2list(SYSCHECK_MOD);
addDecoder2list(SYSCHECK_MOD2);
addDecoder2list(SYSCHECK_MOD3);
addDecoder2list(SYSCHECK_ALLOWED);
addDecoder2list(SYSCHECK_NEW);
addDecoder2list(SYSCHECK_DEL);
addDecoder2list(HOSTINFO_NEW);
Expand Down
110 changes: 110 additions & 0 deletions src/analysisd/decoders/syscheck-allow.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
/* Copyright (C) 2009 Trend Micro Inc.
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since this is an entirely new file, you should probably take ownership.

* All right reserved.
*
* This program is a free software; you can redistribute it
* and/or modify it under the terms of the GNU General Public
* License (version 2) as published by the FSF - Free Software
* Foundation
*/

/* Syscheck decoder */

#include "eventinfo.h"
#include "os_regex/os_regex.h"
#include "config.h"
#include "decoder.h"

/* We write allowed filenames in a database located at /queue/syscheck-allowchange/<agent>
* Each line represents an entry which is structured as
* <is_valid> <validity_timestamp> <path>
* where <is_valid> is either the char '0' or '1',
* and indicate whereas this entry have already been consummed.
*/


/* Determine if this filename have at least one allowed change, and
consumme it */
int consumeAllowchange(const char *filename, Eventinfo *lf){
FILE *db_file;
char db_filename[OS_FLSIZE];
char line[OS_FLSIZE*2];
int allowed = 0;
time_t current;
snprintf(db_filename, OS_FLSIZE , "%s/%s", ALLOWCHANGE_DIR, lf->hostname);
db_file = fopen(db_filename, "r+");
if (!db_file) {
db_file = fopen(db_filename, "w+");
if (!db_file) {
verbose("failed to open %s", db_filename);
return 0;
}
}
rewind(db_file);
current = time(0);

while (fgets(line, OS_FLSIZE*2, db_file) != NULL) {
/* Attempt to parse the line */
int validity;
time_t until;
char path[OS_FLSIZE];
sscanf(line, "%d %ld %s", &validity, &until, path);
if (validity) {
if (until > current) {
if (strcmp(path, filename) == 0){
int len = strlen(line);
/* Rewrite current entry */
fseek(db_file, -len, SEEK_CUR);
fprintf(db_file, "0");
fclose(db_file);
return until;
}
}
}
}

fclose(db_file);
return allowed;
}

/* Save an Allowchange record in our "database"
*/
int produceAllowchange(time_t timestamp, const char *filename, Eventinfo *lf){
FILE *db_file;
char db_filename[OS_FLSIZE];
snprintf(db_filename, OS_FLSIZE , "%s/%s", ALLOWCHANGE_DIR, lf->hostname);
db_file = fopen(db_filename, "a");
if (db_file) {
fprintf(db_file, "%d %ld %s\n", 1, timestamp, filename);
fclose(db_file);
return timestamp;
}
verbose("Failed to open %s", db_filename);
return 0;
}



/* A special decoder to read an AllowChange event */
int DecodeAllowchange(Eventinfo *lf)
{
time_t timestamp;
char *f_name;

/* Allowchange messages must be in the following format:
* timestamp filename
*/
f_name = strchr(lf->log, ' ');
if (f_name == NULL) {
merror(SK_INV_MSG, ARGV0);
return (0);
}

/* Zero to get the timestamp */
*f_name = '\0';
f_name++;

/* Get timestamp */
timestamp = atoi(lf->log);
produceAllowchange(timestamp, f_name, lf);
return timestamp;
}
10 changes: 10 additions & 0 deletions src/analysisd/decoders/syscheck-allow.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
#ifndef __SYSCHECK_ALLOW_H
#define __SYSCHECK_ALLOW_H

#include "shared.h"

int consumeAllowchange(const char *filename, Eventinfo *lf);
int produceAllowchange(time_t timestamp, const char *filename, Eventinfo *lf);
int DecodeAllowchange(Eventinfo *lf);

#endif
19 changes: 13 additions & 6 deletions src/analysisd/decoders/syscheck.c
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
#include "config.h"
#include "alerts/alerts.h"
#include "decoder.h"
#include "syscheck-allow.h"

typedef struct __sdb {
char buf[OS_MAXSTR + 1];
Expand Down Expand Up @@ -124,6 +125,7 @@ static int __iscompleted(const char *agent)
return (0);
}


/* Set the database of a specific agent as completed */
static void DB_SetCompleted(const Eventinfo *lf)
{
Expand Down Expand Up @@ -176,7 +178,6 @@ static FILE *DB_File(const char *agent, int *agent_id)

/* Get agent file */
snprintf(sdb.buf, OS_FLSIZE , "%s/%s", SYSCHECK_DIR, agent);

/* r+ to read and write. Do not truncate */
sdb.agent_fps[i] = fopen(sdb.buf, "r+");
if (!sdb.agent_fps[i]) {
Expand Down Expand Up @@ -304,9 +305,8 @@ static int DB_Search(const char *f_name, const char *c_sum, Eventinfo *lf)
}

/* Check the number of changes */
if (!Config.syscheck_auto_ignore) {
sdb.syscheck_dec->id = sdb.id1;
} else {
sdb.syscheck_dec->id = sdb.id1;
if (Config.syscheck_auto_ignore) {
switch (p) {
case 0:
sdb.syscheck_dec->id = sdb.id1;
Expand Down Expand Up @@ -619,6 +619,7 @@ int DecodeSyscheck(Eventinfo *lf)
{
const char *c_sum;
char *f_name;
int status;

/* Every syscheck message must be in the following format:
* checksum filename
Expand Down Expand Up @@ -668,6 +669,12 @@ int DecodeSyscheck(Eventinfo *lf)
c_sum = lf->log;

/* Search for file changes */
return (DB_Search(f_name, c_sum, lf));
}
status = DB_Search(f_name, c_sum, lf);

/* Check if the file have been allowed to change */
if (consumeAllowchange(f_name, lf)){
lf->decoder_info->id = getDecoderfromlist(SYSCHECK_ALLOWED);
lf->decoder_info->name = SYSCHECK_ALLOWED;
}
return status;
}
1 change: 1 addition & 0 deletions src/analysisd/rules.h
Original file line number Diff line number Diff line change
Expand Up @@ -232,6 +232,7 @@ int _setlevels(RuleNode *node, int nnode);
#define SYSCHECK_MOD3 "syscheck_integrity_changed_3rd"
#define SYSCHECK_NEW "syscheck_new_entry"
#define SYSCHECK_DEL "syscheck_deleted"
#define SYSCHECK_ALLOWED "syscheck_allowed"

/* Global variables */
extern int _max_freq;
Expand Down
5 changes: 5 additions & 0 deletions src/headers/defs.h
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@
#define OS_FLSIZE OS_SIZE_256 /* Maximum file size */
#define OS_HEADER_SIZE OS_SIZE_128 /* Maximum header size */
#define OS_LOG_HEADER OS_SIZE_256 /* Maximum log header size */
#define OS_MAXPATH OS_SIZE_1024 /* Maximum filepath length */
#define IPSIZE INET6_ADDRSTRLEN /* IP Address size */

/* Some global names */
Expand Down Expand Up @@ -117,6 +118,9 @@ published by the Free Software Foundation. For more details, go to \n\
/* Rootcheck directory */
#define ROOTCHECK_DIR "/queue/rootcheck"

/* Allowchange directory */
#define ALLOWCHANGE_DIR "/queue/syscheck-allowchange"

/* Diff queue */
#define DIFF_DIR "/queue/diff"
#define DIFF_DIR_PATH DEFAULTDIR DIFF_DIR
Expand All @@ -126,6 +130,7 @@ published by the Free Software Foundation. For more details, go to \n\
/* Syscheck data */
#define SYSCHECK "syscheck"
#define SYSCHECK_REG "syscheck-registry"
#define ALLOWCHANGE "syscheck-allowchange"

/* Rule path */
#define RULEPATH "/rules"
Expand Down
1 change: 1 addition & 0 deletions src/headers/mq_op.h
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
#define SECURE_MQ '4'
#define SYSCHECK_MQ '8'
#define ROOTCHECK_MQ '9'
#define ALLOWCHANGE_MQ 'c'

/* Queues for additional log types */
#define MYSQL_MQ 'a'
Expand Down
79 changes: 68 additions & 11 deletions src/syscheckd/syscheck.c
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,23 @@ static void read_internal(int debug_level)
return;
}

/* Send a message to the monitor that we allow one change events on
this file until timestamp
*/
static int allowChange(char* filename, time_t timestamp)
{
char msg[OS_MAXPATH*2];
sprintf(msg, "%ld %s", timestamp, filename);
if ((syscheck.queue = StartMQ(DEFAULTQPATH, WRITE)) < 0) {
ErrorExit(QUEUE_FATAL, ARGV0, DEFAULTQPATH);
}
if (SendMSG(syscheck.queue, msg, ALLOWCHANGE, ALLOWCHANGE_MQ) < 0) {
merror(QUEUE_SEND, ARGV0);
}
debug1("%s: send_allowchange_msg: %s to %s\n", ARGV0, msg, DEFAULTQPATH);
return 0;
}

#ifdef WIN32
/* syscheck main for Windows */
int Start_win32_Syscheck()
Expand Down Expand Up @@ -167,15 +184,17 @@ int Start_win32_Syscheck()
static void help_syscheckd()
{
print_header();
print_out(" %s: -[Vhdtf] [-c config]", ARGV0);
print_out(" -V Version and license message");
print_out(" -h This help message");
print_out(" -d Execute in debug mode. This parameter");
print_out(" can be specified multiple times");
print_out(" to increase the debug level.");
print_out(" -t Test configuration");
print_out(" -f Run in foreground");
print_out(" -c <config> Configuration file to use (default: %s)", DEFAULTCPATH);
print_out(" %s: -[Vhdtf] [-c config] [-a filename -u timestamp]", ARGV0);
print_out(" -V Version and license message");
print_out(" -h This help message");
print_out(" -d Execute in debug mode. This parameter");
print_out(" can be specified multiple times");
print_out(" to increase the debug level.");
print_out(" -t Test configuration");
print_out(" -f Run in foreground");
print_out(" -c <config> Configuration file to use (default: %s)", DEFAULTCPATH);
print_out(" -a <filename> Allow changes on filename");
print_out(" -u <timestamp> Allow changes until timestamp");
print_out(" ");
exit(1);
}
Expand All @@ -187,12 +206,16 @@ int main(int argc, char **argv)
int c, r;
int debug_level = 0;
int test_config = 0, run_foreground = 0;
int allow_change = 0;
const char *cfg = DEFAULTCPATH;
char allow_filename[OS_MAXPATH];
time_t allow_timestamp = 0;

/* Set the name */
OS_SetName(ARGV0);
*allow_filename = '\0';

while ((c = getopt(argc, argv, "Vtdhfc:")) != -1) {
while ((c = getopt(argc, argv, "Vtdhfc:a:u:")) != -1) {
switch (c) {
case 'V':
print_version();
Expand All @@ -213,6 +236,20 @@ int main(int argc, char **argv)
}
cfg = optarg;
break;
case 'a':
if (!optarg) {
ErrorExit("%s: -a needs a filename", ARGV0);
}
strncpy(allow_filename, optarg, OS_MAXPATH);
allow_change = 1;
break;
case 'u':
if (!optarg) {
ErrorExit("%s: -w needs a timestamp", ARGV0);
}
allow_timestamp = atoi(optarg);
allow_change = 1;
break;
case 't':
test_config = 1;
break;
Expand Down Expand Up @@ -252,6 +289,27 @@ int main(int argc, char **argv)
}
}


if (allow_change){
if (allow_timestamp == 0){
merror("%s: WARN: Missing timestamp for allow change", ARGV0);
exit(1);
} else if (*allow_filename != '\0') {
allowChange(allow_filename, allow_timestamp);
exit(0);
} else {
debug1("%s: Reading filenames from stdin, one path per line", ARGV0);
while (fgets(allow_filename, OS_MAXPATH, stdin)) {
/* Remove the newline character */
if (allow_filename[strlen(allow_filename) - 1] == '\n') {
allow_filename[strlen(allow_filename) - 1] = '\0';
}
allowChange(allow_filename, allow_timestamp);
}
exit(0);
}
}

/* Rootcheck config */
if (rootcheck_init(test_config) == 0) {
syscheck.rootcheck = 1;
Expand Down Expand Up @@ -360,4 +418,3 @@ int main(int argc, char **argv)
}

#endif /* !WIN32 */