3 /* Copyright (C) 2009 Trend Micro Inc.
6 * This program is a free software; you can redistribute it
7 * and/or modify it under the terms of the GNU General Public
8 * License (version 2) as published by the FSF - Free Software
13 /* Syscheck decoder */
15 #include "eventinfo.h"
16 #include "os_regex/os_regex.h"
18 #include "alerts/alerts.h"
24 char buf[OS_MAXSTR + 1];
25 char comment[OS_MAXSTR +1];
27 char size[OS_FLSIZE +1];
28 char perm[OS_FLSIZE +1];
29 char owner[OS_FLSIZE +1];
30 char gowner[OS_FLSIZE +1];
31 char md5[OS_FLSIZE +1];
32 char sha1[OS_FLSIZE +1];
34 char agent_cp[MAX_AGENTS +1][1];
35 char *agent_ips[MAX_AGENTS +1];
36 FILE *agent_fps[MAX_AGENTS +1];
50 OSDecoderInfo *syscheck_dec;
53 /* File search variables */
56 }_sdb; /* syscheck db information */
65 * Initialize the necessary information to process the syscheck information
73 for(;i <= MAX_AGENTS;i++)
75 sdb.agent_ips[i] = NULL;
76 sdb.agent_fps[i] = NULL;
77 sdb.agent_cp[i][0] = '0';
80 /* Clearing db memory */
81 memset(sdb.buf, '\0', OS_MAXSTR +1);
82 memset(sdb.comment, '\0', OS_MAXSTR +1);
84 memset(sdb.size, '\0', OS_FLSIZE +1);
85 memset(sdb.perm, '\0', OS_FLSIZE +1);
86 memset(sdb.owner, '\0', OS_FLSIZE +1);
87 memset(sdb.gowner, '\0', OS_FLSIZE +1);
88 memset(sdb.md5, '\0', OS_FLSIZE +1);
89 memset(sdb.sha1, '\0', OS_FLSIZE +1);
92 /* Creating decoder */
93 os_calloc(1, sizeof(OSDecoderInfo), sdb.syscheck_dec);
94 sdb.syscheck_dec->id = getDecoderfromlist(SYSCHECK_MOD);
95 sdb.syscheck_dec->name = SYSCHECK_MOD;
96 sdb.syscheck_dec->type = OSSEC_RL;
97 sdb.syscheck_dec->fts = 0;
99 sdb.id1 = getDecoderfromlist(SYSCHECK_MOD);
100 sdb.id2 = getDecoderfromlist(SYSCHECK_MOD2);
101 sdb.id3 = getDecoderfromlist(SYSCHECK_MOD3);
102 sdb.idn = getDecoderfromlist(SYSCHECK_NEW);
103 sdb.idd = getDecoderfromlist(SYSCHECK_DEL);
105 debug1("%s: SyscheckInit completed.", ARGV0);
110 * Checks if the db is completed for that specific agent.
112 #define DB_IsCompleted(x) (sdb.agent_cp[x][0] == '1')?1:0
115 void __setcompleted(char *agent)
119 /* Getting agent file */
120 snprintf(sdb.buf, OS_FLSIZE , "%s/.%s.cpt", SYSCHECK_DIR, agent);
122 fp = fopen(sdb.buf,"w");
131 int __iscompleted(char *agent)
135 /* Getting agent file */
136 snprintf(sdb.buf, OS_FLSIZE , "%s/.%s.cpt", SYSCHECK_DIR, agent);
138 fp = fopen(sdb.buf,"r");
148 /* void DB_SetCompleted(Eventinfo *lf).
149 * Set the database of a specific agent as completed.
151 void DB_SetCompleted(Eventinfo *lf)
155 /* Finding file pointer */
156 while(sdb.agent_ips[i] != NULL)
158 if(strcmp(sdb.agent_ips[i], lf->location) == 0)
160 /* Return if already set as completed. */
161 if(DB_IsCompleted(i))
166 __setcompleted(lf->location);
169 /* Setting as completed in memory */
170 sdb.agent_cp[i][0] = '1';
180 * Return the file pointer to be used to verify the integrity
182 FILE *DB_File(char *agent, int *agent_id)
186 /* Finding file pointer */
187 while(sdb.agent_ips[i] != NULL)
189 if(strcmp(sdb.agent_ips[i], agent) == 0)
191 /* Pointing to the beginning of the file */
192 fseek(sdb.agent_fps[i],0, SEEK_SET);
194 return(sdb.agent_fps[i]);
200 /* If here, our agent wasn't found */
201 os_strdup(agent, sdb.agent_ips[i]);
204 /* Getting agent file */
205 snprintf(sdb.buf, OS_FLSIZE , "%s/%s", SYSCHECK_DIR,agent);
208 /* r+ to read and write. Do not truncate */
209 sdb.agent_fps[i] = fopen(sdb.buf,"r+");
210 if(!sdb.agent_fps[i])
212 /* try opening with a w flag, file probably does not exist */
213 sdb.agent_fps[i] = fopen(sdb.buf, "w");
216 fclose(sdb.agent_fps[i]);
217 sdb.agent_fps[i] = fopen(sdb.buf, "r+");
222 if(!sdb.agent_fps[i])
224 merror("%s: Unable to open '%s'",ARGV0, sdb.buf);
226 free(sdb.agent_ips[i]);
227 sdb.agent_ips[i] = NULL;
232 /* Returning the opened pointer (the beginning of it) */
233 fseek(sdb.agent_fps[i],0, SEEK_SET);
237 /* Getting if the agent was completed */
238 if(__iscompleted(agent))
240 sdb.agent_cp[i][0] = '1';
243 return(sdb.agent_fps[i]);
248 * Search the DB for any entry related to the file being received
250 int DB_Search(char *f_name, char *c_sum, Eventinfo *lf)
262 /* Getting db pointer */
263 fp = DB_File(lf->location, &agent_id);
266 merror("%s: Error handling integrity database.",ARGV0);
267 sdb.db_err++; /* Increment db error */
273 /* Reads the integrity file and search for a possible
276 if(fgetpos(fp, &sdb.init_pos) == -1)
278 merror("%s: Error handling integrity database (fgetpos).",ARGV0);
283 /* Looping the file */
284 while(fgets(sdb.buf, OS_MAXSTR, fp) != NULL)
286 /* Ignore blank lines and lines with a comment */
287 if(sdb.buf[0] == '\n' || sdb.buf[0] == '#')
289 fgetpos(fp, &sdb.init_pos); /* getting next location */
295 saved_name = strchr(sdb.buf, ' ');
296 if(saved_name == NULL)
298 merror("%s: Invalid integrity message in the database.",ARGV0);
299 fgetpos(fp, &sdb.init_pos); /* getting next location */
306 /* New format - with a timestamp */
307 if(*saved_name == '!')
309 saved_name = strchr(saved_name, ' ');
310 if(saved_name == NULL)
312 merror("%s: Invalid integrity message in the database",ARGV0);
313 fgetpos(fp, &sdb.init_pos); /* getting next location */
320 /* Removing new line from saved_name */
321 sn_size = strlen(saved_name);
323 if(saved_name[sn_size] == '\n')
324 saved_name[sn_size] = '\0';
327 /* If name is different, go to next one. */
328 if(strcmp(f_name,saved_name) != 0)
330 /* Saving currently location */
331 fgetpos(fp, &sdb.init_pos);
339 /* First three bytes are for frequency check */
343 /* checksum match, we can just return and keep going */
344 if(strcmp(saved_sum, c_sum) == 0)
351 /* If we reached here, the checksum of the file has changed */
352 if(saved_sum[-3] == '!')
355 if(saved_sum[-2] == '!')
358 if(saved_sum[-1] == '!')
360 else if(saved_sum[-1] == '?')
366 /* Checking the number of changes */
367 if(!Config.syscheck_auto_ignore)
369 sdb.syscheck_dec->id = sdb.id1;
376 sdb.syscheck_dec->id = sdb.id1;
380 sdb.syscheck_dec->id = sdb.id2;
384 sdb.syscheck_dec->id = sdb.id3;
395 /* Adding new checksum to the database */
396 /* Commenting the file entry and adding a new one latter */
397 fsetpos(fp, &sdb.init_pos);
401 /* Adding the new entry at the end of the file */
402 fseek(fp, 0, SEEK_END);
403 fprintf(fp,"%c%c%c%s !%d %s\n",
406 p == 2? '!' : (p > 2)?'?':'+',
414 if(c_sum[0] == '-' && c_sum[1] == '1')
416 sdb.syscheck_dec->id = sdb.idd;
417 snprintf(sdb.comment, OS_MAXSTR,
418 "File '%.756s' was deleted. Unable to retrieve "
419 "checksum.", f_name);
422 /* If file was re-added, do not compare changes */
423 else if(saved_sum[0] == '-' && saved_sum[1] == '1')
425 sdb.syscheck_dec->id = sdb.idn;
426 snprintf(sdb.comment, OS_MAXSTR,
427 "File '%.756s' was re-added.", f_name);
432 int oldperm = 0, newperm = 0;
434 /* Providing more info about the file change */
435 char *oldsize = NULL, *newsize = NULL;
436 char *olduid = NULL, *newuid = NULL;
437 char *c_oldperm = NULL, *c_newperm = NULL;
438 char *oldgid = NULL, *newgid = NULL;
439 char *oldmd5 = NULL, *newmd5 = NULL;
440 char *oldsha1 = NULL, *newsha1 = NULL;
445 c_oldperm = strchr(saved_sum, ':');
446 c_newperm = strchr(c_sum, ':');
448 /* Get old/new permissions */
449 if(c_oldperm && c_newperm)
457 /* Get old/new uid/gid */
458 olduid = strchr(c_oldperm, ':');
459 newuid = strchr(c_newperm, ':');
469 oldgid = strchr(olduid, ':');
470 newgid = strchr(newuid, ':');
482 oldmd5 = strchr(oldgid, ':');
483 newmd5 = strchr(newgid, ':');
494 oldsha1 = strchr(oldmd5, ':');
495 newsha1 = strchr(newmd5, ':');
497 if(oldsha1 && newsha1)
510 /* Getting integer values */
511 if(c_newperm && c_oldperm)
513 newperm = atoi(c_newperm);
514 oldperm = atoi(c_oldperm);
517 /* Generating size message */
518 if(!oldsize || !newsize || strcmp(oldsize, newsize) == 0)
524 snprintf(sdb.size, OS_FLSIZE,
525 "Size changed from '%s' to '%s'\n",
529 os_strdup(oldsize, lf->size_before);
530 os_strdup(newsize, lf->size_after);
534 /* Permission message */
535 if(oldperm == newperm)
539 else if(oldperm > 0 && newperm > 0)
542 snprintf(sdb.perm, OS_FLSIZE, "Permissions changed from "
543 "'%c%c%c%c%c%c%c%c%c' "
544 "to '%c%c%c%c%c%c%c%c%c'\n",
545 (oldperm & S_IRUSR)? 'r' : '-',
546 (oldperm & S_IWUSR)? 'w' : '-',
548 (oldperm & S_ISUID)? 's' :
549 (oldperm & S_IXUSR)? 'x' : '-',
551 (oldperm & S_IRGRP)? 'r' : '-',
552 (oldperm & S_IWGRP)? 'w' : '-',
554 (oldperm & S_ISGID)? 's' :
555 (oldperm & S_IXGRP)? 'x' : '-',
557 (oldperm & S_IROTH)? 'r' : '-',
558 (oldperm & S_IWOTH)? 'w' : '-',
560 (oldperm & S_ISVTX)? 't' :
561 (oldperm & S_IXOTH)? 'x' : '-',
565 (newperm & S_IRUSR)? 'r' : '-',
566 (newperm & S_IWUSR)? 'w' : '-',
568 (newperm & S_ISUID)? 's' :
569 (newperm & S_IXUSR)? 'x' : '-',
572 (newperm & S_IRGRP)? 'r' : '-',
573 (newperm & S_IWGRP)? 'w' : '-',
575 (newperm & S_ISGID)? 's' :
576 (newperm & S_IXGRP)? 'x' : '-',
578 (newperm & S_IROTH)? 'r' : '-',
579 (newperm & S_IWOTH)? 'w' : '-',
581 (newperm & S_ISVTX)? 't' :
582 (newperm & S_IXOTH)? 'x' : '-');
585 lf->perm_before = oldperm;
586 lf->perm_after = newperm;
590 /* Ownership message */
591 if(!newuid || !olduid || strcmp(newuid, olduid) == 0)
597 snprintf(sdb.owner, OS_FLSIZE, "Ownership was '%s', "
603 os_strdup(olduid, lf->owner_before);
604 os_strdup(newuid, lf->owner_after);
608 /* group ownership message */
609 if(!newgid || !oldgid || strcmp(newgid, oldgid) == 0)
611 sdb.gowner[0] = '\0';
615 snprintf(sdb.gowner, OS_FLSIZE,"Group ownership was '%s', "
619 os_strdup(oldgid, lf->gowner_before);
620 os_strdup(newgid, lf->gowner_after);
625 if(!newmd5 || !oldmd5 || strcmp(newmd5, oldmd5) == 0)
631 snprintf(sdb.md5, OS_FLSIZE, "Old md5sum was: '%s'\n"
632 "New md5sum is : '%s'\n",
635 os_strdup(oldmd5, lf->md5_before);
636 os_strdup(newmd5, lf->md5_after);
641 if(!newsha1 || !oldsha1 || strcmp(newsha1, oldsha1) == 0)
647 snprintf(sdb.sha1, OS_FLSIZE, "Old sha1sum was: '%s'\n"
648 "New sha1sum is : '%s'\n",
651 os_strdup(oldsha1, lf->sha1_before);
652 os_strdup(newsha1, lf->sha1_after);
656 os_strdup(f_name, lf->filename);
660 /* Provide information about the file */
661 snprintf(sdb.comment, OS_MAXSTR, "Integrity checksum changed for: "
677 lf->data == NULL?"":"What changed:\n",
678 lf->data == NULL?"":lf->data
683 /* Creating a new log message */
685 os_strdup(sdb.comment, lf->full_log);
686 lf->log = lf->full_log;
690 /* Setting decoder */
691 lf->decoder_info = sdb.syscheck_dec;
696 } /* continuiing... */
699 /* If we reach here, this file is not present on our database */
700 fseek(fp, 0, SEEK_END);
702 fprintf(fp,"+++%s !%d %s\n", c_sum, lf->time, f_name);
705 /* Alert if configured to notify on new files */
706 if((Config.syscheck_alert_new == 1) && (DB_IsCompleted(agent_id)))
708 sdb.syscheck_dec->id = sdb.idn;
710 /* New file message */
711 snprintf(sdb.comment, OS_MAXSTR,
713 "added to the file system.", f_name);
716 /* Creating a new log message */
718 os_strdup(sdb.comment, lf->full_log);
719 lf->log = lf->full_log;
722 /* Setting decoder */
723 lf->decoder_info = sdb.syscheck_dec;
734 /* Special decoder for syscheck
735 * Not using the default decoding lib for simplicity
736 * and to be less resource intensive
738 int DecodeSyscheck(Eventinfo *lf)
744 /* Every syscheck message must be in the following format:
747 f_name = strchr(lf->log, ' ');
750 /* If we don't have a valid syscheck message, it may be
751 * a database completed message.
753 if(strcmp(lf->log, HC_SK_DB_COMPLETED) == 0)
759 merror(SK_INV_MSG, ARGV0);
764 /* Zeroing to get the check sum */
770 lf->data = strchr(f_name, '\n');
783 /* Checking if file is supposed to be ignored */
784 if(Config.syscheck_ignore)
786 char **ff_ig = Config.syscheck_ignore;
790 if(strncasecmp(*ff_ig, f_name, strlen(*ff_ig)) == 0)
801 /* Checksum is at the beginning of the log */
805 /* Searching for file changes */
806 return(DB_Search(f_name, c_sum, lf));