1 /* @(#) $Id: ./src/analysisd/decoders/syscheck.c, 2012/02/07 dcid Exp $
4 /* Copyright (C) 2009 Trend Micro Inc.
7 * This program is a free software; you can redistribute it
8 * and/or modify it under the terms of the GNU General Public
9 * License (version 2) as published by the FSF - Free Software
14 /* Syscheck decoder */
16 #include "eventinfo.h"
17 #include "os_regex/os_regex.h"
19 #include "alerts/alerts.h"
25 char buf[OS_MAXSTR + 1];
26 char comment[OS_MAXSTR +1];
28 char size[OS_FLSIZE +1];
29 char perm[OS_FLSIZE +1];
30 char owner[OS_FLSIZE +1];
31 char gowner[OS_FLSIZE +1];
32 char md5[OS_FLSIZE +1];
33 char sha1[OS_FLSIZE +1];
35 char agent_cp[MAX_AGENTS +1][1];
36 char *agent_ips[MAX_AGENTS +1];
37 FILE *agent_fps[MAX_AGENTS +1];
51 OSDecoderInfo *syscheck_dec;
54 /* File search variables */
57 }_sdb; /* syscheck db information */
66 * Initialize the necessary information to process the syscheck information
74 for(;i <= MAX_AGENTS;i++)
76 sdb.agent_ips[i] = NULL;
77 sdb.agent_fps[i] = NULL;
78 sdb.agent_cp[i][0] = '0';
81 /* Clearing db memory */
82 memset(sdb.buf, '\0', OS_MAXSTR +1);
83 memset(sdb.comment, '\0', OS_MAXSTR +1);
85 memset(sdb.size, '\0', OS_FLSIZE +1);
86 memset(sdb.perm, '\0', OS_FLSIZE +1);
87 memset(sdb.owner, '\0', OS_FLSIZE +1);
88 memset(sdb.gowner, '\0', OS_FLSIZE +1);
89 memset(sdb.md5, '\0', OS_FLSIZE +1);
90 memset(sdb.sha1, '\0', OS_FLSIZE +1);
93 /* Creating decoder */
94 os_calloc(1, sizeof(OSDecoderInfo), sdb.syscheck_dec);
95 sdb.syscheck_dec->id = getDecoderfromlist(SYSCHECK_MOD);
96 sdb.syscheck_dec->name = SYSCHECK_MOD;
97 sdb.syscheck_dec->type = OSSEC_RL;
98 sdb.syscheck_dec->fts = 0;
100 sdb.id1 = getDecoderfromlist(SYSCHECK_MOD);
101 sdb.id2 = getDecoderfromlist(SYSCHECK_MOD2);
102 sdb.id3 = getDecoderfromlist(SYSCHECK_MOD3);
103 sdb.idn = getDecoderfromlist(SYSCHECK_NEW);
104 sdb.idd = getDecoderfromlist(SYSCHECK_DEL);
106 debug1("%s: SyscheckInit completed.", ARGV0);
111 * Checks if the db is completed for that specific agent.
113 #define DB_IsCompleted(x) (sdb.agent_cp[x][0] == '1')?1:0
116 void __setcompleted(char *agent)
120 /* Getting agent file */
121 snprintf(sdb.buf, OS_FLSIZE , "%s/.%s.cpt", SYSCHECK_DIR, agent);
123 fp = fopen(sdb.buf,"w");
132 int __iscompleted(char *agent)
136 /* Getting agent file */
137 snprintf(sdb.buf, OS_FLSIZE , "%s/.%s.cpt", SYSCHECK_DIR, agent);
139 fp = fopen(sdb.buf,"r");
149 /* void DB_SetCompleted(Eventinfo *lf).
150 * Set the database of a specific agent as completed.
152 void DB_SetCompleted(Eventinfo *lf)
156 /* Finding file pointer */
157 while(sdb.agent_ips[i] != NULL && i < MAX_AGENTS)
159 if(strcmp(sdb.agent_ips[i], lf->location) == 0)
161 /* Return if already set as completed. */
162 if(DB_IsCompleted(i))
167 __setcompleted(lf->location);
170 /* Setting as completed in memory */
171 sdb.agent_cp[i][0] = '1';
181 * Return the file pointer to be used to verify the integrity
183 FILE *DB_File(char *agent, int *agent_id)
187 /* Finding file pointer */
188 while(sdb.agent_ips[i] != NULL && i < MAX_AGENTS)
190 if(strcmp(sdb.agent_ips[i], agent) == 0)
192 /* Pointing to the beginning of the file */
193 fseek(sdb.agent_fps[i],0, SEEK_SET);
195 return(sdb.agent_fps[i]);
201 /* If here, our agent wasn't found */
204 merror("%s: Unable to open integrity file. Increase MAX_AGENTS.",ARGV0);
208 os_strdup(agent, sdb.agent_ips[i]);
211 /* Getting agent file */
212 snprintf(sdb.buf, OS_FLSIZE , "%s/%s", SYSCHECK_DIR,agent);
215 /* r+ to read and write. Do not truncate */
216 sdb.agent_fps[i] = fopen(sdb.buf,"r+");
217 if(!sdb.agent_fps[i])
219 /* try opening with a w flag, file probably does not exist */
220 sdb.agent_fps[i] = fopen(sdb.buf, "w");
223 fclose(sdb.agent_fps[i]);
224 sdb.agent_fps[i] = fopen(sdb.buf, "r+");
229 if(!sdb.agent_fps[i])
231 merror("%s: Unable to open '%s'",ARGV0, sdb.buf);
233 free(sdb.agent_ips[i]);
234 sdb.agent_ips[i] = NULL;
239 /* Returning the opened pointer (the beginning of it) */
240 fseek(sdb.agent_fps[i],0, SEEK_SET);
244 /* Getting if the agent was completed */
245 if(__iscompleted(agent))
247 sdb.agent_cp[i][0] = '1';
250 return(sdb.agent_fps[i]);
255 * Search the DB for any entry related to the file being received
257 int DB_Search(char *f_name, char *c_sum, Eventinfo *lf)
269 /* Getting db pointer */
270 fp = DB_File(lf->location, &agent_id);
273 merror("%s: Error handling integrity database.",ARGV0);
274 sdb.db_err++; /* Increment db error */
280 /* Reads the integrity file and search for a possible
283 if(fgetpos(fp, &sdb.init_pos) == -1)
285 merror("%s: Error handling integrity database (fgetpos).",ARGV0);
290 /* Looping the file */
291 while(fgets(sdb.buf, OS_MAXSTR, fp) != NULL)
293 /* Ignore blank lines and lines with a comment */
294 if(sdb.buf[0] == '\n' || sdb.buf[0] == '#')
296 fgetpos(fp, &sdb.init_pos); /* getting next location */
302 saved_name = strchr(sdb.buf, ' ');
303 if(saved_name == NULL)
305 merror("%s: Invalid integrity message in the database.",ARGV0);
306 fgetpos(fp, &sdb.init_pos); /* getting next location */
313 /* New format - with a timestamp */
314 if(*saved_name == '!')
316 saved_name = strchr(saved_name, ' ');
317 if(saved_name == NULL)
319 merror("%s: Invalid integrity message in the database",ARGV0);
320 fgetpos(fp, &sdb.init_pos); /* getting next location */
327 /* Removing new line from saved_name */
328 sn_size = strlen(saved_name);
330 if(saved_name[sn_size] == '\n')
331 saved_name[sn_size] = '\0';
334 /* If name is different, go to next one. */
335 if(strcmp(f_name,saved_name) != 0)
337 /* Saving currently location */
338 fgetpos(fp, &sdb.init_pos);
346 /* First three bytes are for frequency check */
350 /* checksum match, we can just return and keep going */
351 if(strcmp(saved_sum, c_sum) == 0)
358 /* If we reached here, the checksum of the file has changed */
359 if(saved_sum[-3] == '!')
362 if(saved_sum[-2] == '!')
365 if(saved_sum[-1] == '!')
367 else if(saved_sum[-1] == '?')
373 /* Checking the number of changes */
374 if(!Config.syscheck_auto_ignore)
376 sdb.syscheck_dec->id = sdb.id1;
383 sdb.syscheck_dec->id = sdb.id1;
387 sdb.syscheck_dec->id = sdb.id2;
391 sdb.syscheck_dec->id = sdb.id3;
402 /* Adding new checksum to the database */
403 /* Commenting the file entry and adding a new one latter */
404 fsetpos(fp, &sdb.init_pos);
408 /* Adding the new entry at the end of the file */
409 fseek(fp, 0, SEEK_END);
410 fprintf(fp,"%c%c%c%s !%d %s\n",
413 p == 2? '!' : (p > 2)?'?':'+',
421 if(c_sum[0] == '-' && c_sum[1] == '1')
423 sdb.syscheck_dec->id = sdb.idd;
424 snprintf(sdb.comment, OS_MAXSTR,
425 "File '%.756s' was deleted. Unable to retrieve "
426 "checksum.", f_name);
429 /* If file was re-added, do not compare changes */
430 else if(saved_sum[0] == '-' && saved_sum[1] == '1')
432 sdb.syscheck_dec->id = sdb.idn;
433 snprintf(sdb.comment, OS_MAXSTR,
434 "File '%.756s' was re-added.", f_name);
439 int oldperm = 0, newperm = 0;
441 /* Providing more info about the file change */
442 char *oldsize = NULL, *newsize = NULL;
443 char *olduid = NULL, *newuid = NULL;
444 char *c_oldperm = NULL, *c_newperm = NULL;
445 char *oldgid = NULL, *newgid = NULL;
446 char *oldmd5 = NULL, *newmd5 = NULL;
447 char *oldsha1 = NULL, *newsha1 = NULL;
452 c_oldperm = strchr(saved_sum, ':');
453 c_newperm = strchr(c_sum, ':');
455 /* Get old/new permissions */
456 if(c_oldperm && c_newperm)
464 /* Get old/new uid/gid */
465 olduid = strchr(c_oldperm, ':');
466 newuid = strchr(c_newperm, ':');
476 oldgid = strchr(olduid, ':');
477 newgid = strchr(newuid, ':');
489 oldmd5 = strchr(oldgid, ':');
490 newmd5 = strchr(newgid, ':');
501 oldsha1 = strchr(oldmd5, ':');
502 newsha1 = strchr(newmd5, ':');
504 if(oldsha1 && newsha1)
517 /* Getting integer values */
518 if(c_newperm && c_oldperm)
520 newperm = atoi(c_newperm);
521 oldperm = atoi(c_oldperm);
524 /* Generating size message */
525 if(!oldsize || !newsize || strcmp(oldsize, newsize) == 0)
531 snprintf(sdb.size, OS_FLSIZE,
532 "Size changed from '%s' to '%s'\n",
536 os_strdup(oldsize, lf->size_before);
537 os_strdup(newsize, lf->size_after);
541 /* Permission message */
542 if(oldperm == newperm)
546 else if(oldperm > 0 && newperm > 0)
549 snprintf(sdb.perm, OS_FLSIZE, "Permissions changed from "
550 "'%c%c%c%c%c%c%c%c%c' "
551 "to '%c%c%c%c%c%c%c%c%c'\n",
552 (oldperm & S_IRUSR)? 'r' : '-',
553 (oldperm & S_IWUSR)? 'w' : '-',
555 (oldperm & S_ISUID)? 's' :
556 (oldperm & S_IXUSR)? 'x' : '-',
558 (oldperm & S_IRGRP)? 'r' : '-',
559 (oldperm & S_IWGRP)? 'w' : '-',
561 (oldperm & S_ISGID)? 's' :
562 (oldperm & S_IXGRP)? 'x' : '-',
564 (oldperm & S_IROTH)? 'r' : '-',
565 (oldperm & S_IWOTH)? 'w' : '-',
567 (oldperm & S_ISVTX)? 't' :
568 (oldperm & S_IXOTH)? 'x' : '-',
572 (newperm & S_IRUSR)? 'r' : '-',
573 (newperm & S_IWUSR)? 'w' : '-',
575 (newperm & S_ISUID)? 's' :
576 (newperm & S_IXUSR)? 'x' : '-',
579 (newperm & S_IRGRP)? 'r' : '-',
580 (newperm & S_IWGRP)? 'w' : '-',
582 (newperm & S_ISGID)? 's' :
583 (newperm & S_IXGRP)? 'x' : '-',
585 (newperm & S_IROTH)? 'r' : '-',
586 (newperm & S_IWOTH)? 'w' : '-',
588 (newperm & S_ISVTX)? 't' :
589 (newperm & S_IXOTH)? 'x' : '-');
592 lf->perm_before = oldperm;
593 lf->perm_after = newperm;
597 /* Ownership message */
598 if(!newuid || !olduid || strcmp(newuid, olduid) == 0)
604 snprintf(sdb.owner, OS_FLSIZE, "Ownership was '%s', "
610 os_strdup(olduid, lf->owner_before);
611 os_strdup(newuid, lf->owner_after);
615 /* group ownership message */
616 if(!newgid || !oldgid || strcmp(newgid, oldgid) == 0)
618 sdb.gowner[0] = '\0';
622 snprintf(sdb.gowner, OS_FLSIZE,"Group ownership was '%s', "
626 os_strdup(oldgid, lf->gowner_before);
627 os_strdup(newgid, lf->gowner_after);
632 if(!newmd5 || !oldmd5 || strcmp(newmd5, oldmd5) == 0)
638 snprintf(sdb.md5, OS_FLSIZE, "Old md5sum was: '%s'\n"
639 "New md5sum is : '%s'\n",
642 os_strdup(oldmd5, lf->md5_before);
643 os_strdup(newmd5, lf->md5_after);
648 if(!newsha1 || !oldsha1 || strcmp(newsha1, oldsha1) == 0)
654 snprintf(sdb.sha1, OS_FLSIZE, "Old sha1sum was: '%s'\n"
655 "New sha1sum is : '%s'\n",
658 os_strdup(oldsha1, lf->sha1_before);
659 os_strdup(newsha1, lf->sha1_after);
663 os_strdup(f_name, lf->filename);
667 /* Provide information about the file */
668 snprintf(sdb.comment, OS_MAXSTR, "Integrity checksum changed for: "
684 lf->data == NULL?"":"What changed:\n",
685 lf->data == NULL?"":lf->data
690 /* Creating a new log message */
692 os_strdup(sdb.comment, lf->full_log);
693 lf->log = lf->full_log;
697 /* Setting decoder */
698 lf->decoder_info = sdb.syscheck_dec;
703 } /* continuiing... */
706 /* If we reach here, this file is not present on our database */
707 fseek(fp, 0, SEEK_END);
709 fprintf(fp,"+++%s !%d %s\n", c_sum, lf->time, f_name);
713 /* Alert if configured to notify on new files */
714 if((Config.syscheck_alert_new == 1) && (DB_IsCompleted(agent_id)))
716 sdb.syscheck_dec->id = sdb.idn;
718 /* New file message */
719 snprintf(sdb.comment, OS_MAXSTR,
721 "added to the file system.", f_name);
724 /* Creating a new log message */
726 os_strdup(sdb.comment, lf->full_log);
727 lf->log = lf->full_log;
730 /* Setting decoder */
731 lf->decoder_info = sdb.syscheck_dec;
742 /* Special decoder for syscheck
743 * Not using the default decoding lib for simplicity
744 * and to be less resource intensive
746 int DecodeSyscheck(Eventinfo *lf)
752 /* Every syscheck message must be in the following format:
755 f_name = strchr(lf->log, ' ');
758 /* If we don't have a valid syscheck message, it may be
759 * a database completed message.
761 if(strcmp(lf->log, HC_SK_DB_COMPLETED) == 0)
767 merror(SK_INV_MSG, ARGV0);
772 /* Zeroing to get the check sum */
778 lf->data = strchr(f_name, '\n');
791 /* Checking if file is supposed to be ignored */
792 if(Config.syscheck_ignore)
794 char **ff_ig = Config.syscheck_ignore;
798 if(strncasecmp(*ff_ig, f_name, strlen(*ff_ig)) == 0)
809 /* Checksum is at the beginning of the log */
813 /* Searching for file changes */
814 return(DB_Search(f_name, c_sum, lf));