-/* @(#) $Id$ */
-
/* Copyright (C) 2009 Trend Micro Inc.
* All rights reserved.
*
* Foundation
*/
-
-
#include "shared.h"
#include "os_crypto/md5/md5_op.h"
+#include "syscheck.h"
+
+/* Prototypes */
+static char *gen_diff_alert(const char *filename, time_t alert_diff_time) __attribute__((nonnull));
+static int seechanges_dupfile(const char *old, const char *current) __attribute__((nonnull));
+static int seechanges_createpath(const char *filename) __attribute__((nonnull));
+
+#ifdef USE_MAGIC
+#include <magic.h>
+/* Global variables */
+extern magic_t magic_cookie;
+
+
+int is_text(magic_t cookie, const void *buf, size_t len)
+{
+ const char *magic = magic_buffer(cookie, buf, len);
+
+ if (!magic) {
+ const char *err = magic_error(cookie);
+ merror("%s: ERROR: magic_buffer: %s", ARGV0, err ? err : "unknown");
+ return (1); // TODO default to true?
+ } else {
+ if (strncmp(magic, "text/", 5) == 0) {
+ return (1);
+ }
+ }
+ return (0);
+}
+#endif
+
+/* Return TRUE if the file name match one of the ``nodiff`` entries.
+ Return FALSE otherwise */
+int is_nodiff(const char *filename){
+ if (syscheck.nodiff){
+ int i;
+ for (i = 0; syscheck.nodiff[i] != NULL; i++){
+ if (strncasecmp(syscheck.nodiff[i], filename,
+ strlen(syscheck.nodiff[i])) == 0) {
+ return (TRUE);
+ }
+ }
+ }
+ if (syscheck.nodiff_regex) {
+ int i;
+ for (i = 0; syscheck.nodiff_regex[i] != NULL; i++) {
+ if (OSMatch_Execute(filename, strlen(filename),
+ syscheck.nodiff_regex[i])) {
+ return (TRUE);
+ }
+ }
+ }
+ return (FALSE);
+}
-/* Generate diffs alerts. */
-char *gen_diff_alert(char *filename, int alert_diff_time)
+/* Generate diffs alerts */
+static char *gen_diff_alert(const char *filename, time_t alert_diff_time)
{
- int n = 0;
+ size_t n = 0;
FILE *fp;
char *tmp_str;
- char buf[OS_MAXSTR +1];
- char diff_alert[OS_MAXSTR +1];
+ char buf[OS_MAXSTR + 1];
+ char diff_alert[OS_MAXSTR + 1];
buf[OS_MAXSTR] = '\0';
diff_alert[OS_MAXSTR] = '\0';
snprintf(buf, OS_MAXSTR, "%s/local/%s/diff.%d",
- DIFF_DIR_PATH, filename, alert_diff_time);
-
+ DIFF_DIR_PATH, filename, (int)alert_diff_time);
+
fp = fopen(buf, "r");
- if(!fp)
- {
+ if (!fp) {
merror("%s: ERROR: Unable to generate diff alert.", ARGV0);
- return(NULL);
+ return (NULL);
}
- n = fread(buf, 1, 4096 -1, fp);
- if(n <= 0)
- {
+ n = fread(buf, 1, 4096 - 1, fp);
+ if (n <= 0) {
merror("%s: ERROR: Unable to generate diff alert (fread).", ARGV0);
fclose(fp);
- return(NULL);
- }
- else if(n >= 4000)
- {
- /* We need to clear the last new line. */
+ return (NULL);
+ } else if (n >= 4000) {
+ /* Clear the last newline */
buf[n] = '\0';
tmp_str = strrchr(buf, '\n');
- if(tmp_str)
+ if (tmp_str) {
*tmp_str = '\0';
- else
- {
- /* Weird diff with only one large line. */
- buf[256] = '\0';
+ } else {
+ /* Weird diff with only one large line */
+ buf[256] = '\0';
}
- }
- else
- {
+ } else {
buf[n] = '\0';
}
n = 0;
-
- /* Getting up to 20 line changes. */
+ /* Get up to 20 line changes */
tmp_str = buf;
-
- while(tmp_str && (*tmp_str != '\0'))
- {
+ while (tmp_str && (*tmp_str != '\0')) {
tmp_str = strchr(tmp_str, '\n');
- if(!tmp_str)
- break;
- else if(n >= 19)
- {
- *tmp_str = '\0';
+ if (!tmp_str) {
+ break;
+ } else if (n >= 19) {
+ *tmp_str = '\0';
break;
}
n++;
- tmp_str++;
+ tmp_str++;
}
-
- /* Creating alert. */
- snprintf(diff_alert, 4096 -1, "%s%s",
- buf, n>=19?
- "\nMore changes..":
+ /* Create alert */
+ snprintf(diff_alert, 4096 - 1, "%s%s",
+ buf, n >= 19 ?
+ "\nMore changes.." :
"");
-
-
+
fclose(fp);
- return(strdup(diff_alert));
+ return (strdup(diff_alert));
}
-
-int seechanges_dupfile(char *old, char *new)
+static int seechanges_dupfile(const char *old, const char *current)
{
- int n;
+ size_t n;
FILE *fpr;
FILE *fpw;
- unsigned char buf[2048 +1];
+ unsigned char buf[2048 + 1];
buf[2048] = '\0';
- fpr = fopen(old,"r");
- fpw = fopen(new,"w");
+ fpr = fopen(old, "r");
+ if (!fpr) {
+ return (0);
+ }
- if(!fpr || !fpw)
- {
- return(0);
+ fpw = fopen(current, "w");
+ if (!fpw) {
+ fclose(fpr);
+ return (0);
}
- while((n = fread(buf, 1, 2048, fpr)) > 0)
- {
+ n = fread(buf, 1, 2048, fpr);
+#ifdef USE_MAGIC
+ if (is_text(magic_cookie, buf, n) == 0) {
+ goto cleanup;
+ }
+#endif
+
+ do {
buf[n] = '\0';
fwrite(buf, n, 1, fpw);
+ } while ((n = fread(buf, 1, 2048, fpr)) > 0);
- }
-
+#ifdef USE_MAGIC
+cleanup:
+#endif
fclose(fpr);
fclose(fpw);
- return(1);
+ return (1);
}
-
-int seechanges_createpath(char *filename)
+static int seechanges_createpath(const char *filename)
{
char *buffer = NULL;
char *tmpstr = NULL;
char *newdir = NULL;
-
os_strdup(filename, buffer);
newdir = buffer;
- tmpstr = strchr(buffer +1, '/');
- if(!tmpstr)
- {
+ tmpstr = strchr(buffer + 1, '/');
+ if (!tmpstr) {
merror("%s: ERROR: Invalid path name: '%s'", ARGV0, filename);
free(buffer);
- return(0);
+ return (0);
}
*tmpstr = '\0';
tmpstr++;
-
- while(1)
- {
- if(IsDir(newdir) != 0)
- {
- #ifndef WIN32
- if(mkdir(newdir, 0770) == -1)
- #else
- if(mkdir(newdir) == -1)
- #endif
+ while (1) {
+ if (IsDir(newdir) != 0) {
+#ifndef WIN32
+ if (mkdir(newdir, 0770) == -1)
+#else
+ if (mkdir(newdir) == -1)
+#endif
{
- merror(MKDIR_ERROR, ARGV0, newdir);
+ merror(MKDIR_ERROR, ARGV0, newdir, errno, strerror(errno));
free(buffer);
- return(0);
+ return (0);
}
}
- if(*tmpstr == '\0')
- {
+ if (*tmpstr == '\0') {
break;
}
tmpstr[-1] = '/';
tmpstr = strchr(tmpstr, '/');
- if(!tmpstr)
- {
+ if (!tmpstr) {
break;
}
*tmpstr = '\0';
}
free(buffer);
- return(1);
+ return (1);
}
-
-/* Checks if the file has changed */
-char *seechanges_addfile(char *filename)
+/* Check if the file has changed */
+char *seechanges_addfile(const char *filename)
{
- int date_of_change;
- char old_location[OS_MAXSTR +1];
- char tmp_location[OS_MAXSTR +1];
- char diff_cmd[OS_MAXSTR +1];
-
+ time_t old_date_of_change;
+ time_t new_date_of_change;
+ char old_location[OS_MAXSTR + 1];
+ char tmp_location[OS_MAXSTR + 1];
+ char diff_location[OS_MAXSTR + 1];
+ char old_tmp[OS_MAXSTR + 1];
+ char new_tmp[OS_MAXSTR + 1];
+ char diff_tmp[OS_MAXSTR + 1];
+ char diff_cmd[OS_MAXSTR + 1];
os_md5 md5sum_old;
os_md5 md5sum_new;
-
+ int status = -1;
+
old_location[OS_MAXSTR] = '\0';
tmp_location[OS_MAXSTR] = '\0';
+ diff_location[OS_MAXSTR] = '\0';
+ old_tmp[OS_MAXSTR] = '\0';
+ new_tmp[OS_MAXSTR] = '\0';
+ diff_tmp[OS_MAXSTR] = '\0';
diff_cmd[OS_MAXSTR] = '\0';
md5sum_new[0] = '\0';
md5sum_old[0] = '\0';
-
- snprintf(old_location, OS_MAXSTR, "%s/local/%s/%s", DIFF_DIR_PATH, filename +1,
- DIFF_LAST_FILE);
-
-
- /* If the file is not there, rename new location to last location. */
- if(OS_MD5_File(old_location, md5sum_old) != 0)
- {
+ snprintf(
+ old_location,
+ OS_MAXSTR,
+ "%s/local/%s/%s",
+ DIFF_DIR_PATH,
+ filename + 1,
+ DIFF_LAST_FILE
+ );
+
+ /* If the file is not there, rename new location to last location */
+ if (OS_MD5_File(old_location, md5sum_old, OS_BINARY) != 0) {
seechanges_createpath(old_location);
- if(seechanges_dupfile(filename, old_location) != 1)
- {
- merror(RENAME_ERROR, ARGV0, filename);
+ if (seechanges_dupfile(filename, old_location) != 1) {
+ merror(RENAME_ERROR, ARGV0, filename, old_location, errno, strerror(errno));
}
- return(NULL);
+ return (NULL);
}
-
- /* Get md5sum of the new file. */
- if(OS_MD5_File(filename, md5sum_new) != 0)
- {
- //merror("%s: ERROR: Invalid internal state (missing '%s').",
- // ARGV0, filename);
- return(NULL);
+ /* Get md5sum of the new file */
+ if (OS_MD5_File(filename, md5sum_new, OS_BINARY) != 0) {
+ return (NULL);
}
-
- /* If they match, keep the old file and remove the new. */
- if(strcmp(md5sum_new, md5sum_old) == 0)
- {
- return(NULL);
+ /* If they match, keep the old file and remove the new */
+ if (strcmp(md5sum_new, md5sum_old) == 0) {
+ return (NULL);
}
+ /* Save the old file at timestamp and rename new to last */
+ old_date_of_change = File_DateofChange(old_location);
+
+ snprintf(
+ tmp_location,
+ OS_MAXSTR,
+ "%s/local/%s/state.%d",
+ DIFF_DIR_PATH,
+ filename + 1,
+ (int)old_date_of_change
+ );
/* Saving the old file at timestamp and renaming new to last. */
- date_of_change = File_DateofChange(old_location);
- snprintf(tmp_location, OS_MAXSTR, "%s/local/%s/state.%d", DIFF_DIR_PATH, filename +1,
- date_of_change);
- rename(old_location, tmp_location);
+ old_date_of_change = File_DateofChange(old_location);
+
+ snprintf(
+ tmp_location,
+ sizeof(tmp_location),
+ "%s/local/%s/state.%d",
+ DIFF_DIR_PATH,
+ filename + 1,
+ (int)old_date_of_change
+ );
+
+
+ if((rename(old_location, tmp_location)) < 0) {
+ merror("%s: ERROR rename of %s failed: %s", ARGV0, old_location, strerror(errno));
+ }
if(seechanges_dupfile(filename, old_location) != 1)
{
merror("%s: ERROR: Unable to create snapshot for %s",ARGV0, filename);
return(NULL);
}
+ if (seechanges_dupfile(filename, old_location) != 1) {
+ merror("%s: ERROR: Unable to create snapshot for %s", ARGV0, filename);
+ return (NULL);
+ }
- /* Run diff. */
- date_of_change = File_DateofChange(old_location);
- snprintf(diff_cmd, 2048, "diff \"%s\" \"%s\" > \"%s/local/%s/diff.%d\" "
- "2>/dev/null",
- tmp_location, old_location,
- DIFF_DIR_PATH, filename +1, date_of_change);
- if(system(diff_cmd) != 256)
- {
- merror("%s: ERROR: Unable to run diff for %s",
- ARGV0, filename);
- return(NULL);
+ new_date_of_change = File_DateofChange(old_location);
+
+ /* Create file names */
+ snprintf(
+ old_tmp,
+ OS_MAXSTR,
+ "%s/%s/syscheck-changes-%s-%d",
+ DEFAULTDIR,
+ TMP_DIR,
+ md5sum_old,
+ (int)old_date_of_change
+ );
+
+ snprintf(
+ new_tmp,
+ OS_MAXSTR,
+ "%s/%s/syscheck-changes-%s-%d",
+ DEFAULTDIR,
+ TMP_DIR,
+ md5sum_new,
+ (int)new_date_of_change
+ );
+
+ snprintf(
+ diff_tmp,
+ OS_MAXSTR,
+ "%s/%s/syscheck-changes-%s-%d-%s-%d",
+ DEFAULTDIR,
+ TMP_DIR,
+ md5sum_old,
+ (int)old_date_of_change,
+ md5sum_new,
+ (int)new_date_of_change
+ );
+ /* Create diff location */
+ snprintf(
+ diff_location,
+ OS_MAXSTR,
+ "%s/local/%s/diff.%d",
+ DIFF_DIR_PATH,
+ filename + 1,
+ (int)new_date_of_change
+ );
+
+ /* Create symlinks */
+ if (symlink(old_location, old_tmp) == -1) {
+ merror(LINK_ERROR, ARGV0, old_location, old_tmp, errno, strerror(errno));
+ goto cleanup;
}
+ if (symlink(tmp_location, new_tmp) == -1) {
+ merror(LINK_ERROR, ARGV0, tmp_location, new_tmp, errno, strerror(errno));
+ goto cleanup;
+ }
- /* Generate alert. */
- return(gen_diff_alert(filename, date_of_change));
+ if (symlink(diff_location, diff_tmp) == -1) {
+ merror(LINK_ERROR, ARGV0, diff_location, diff_tmp, errno, strerror(errno));
+ goto cleanup;
+ }
+ if (is_nodiff((filename))) {
+ /* Dont leak sensible data with a diff hanging around */
+ FILE *fdiff;
+ char* nodiff_message = "<Diff truncated because nodiff option>";
+ fdiff = fopen(diff_location, "w");
+ if (!fdiff){
+ merror("%s: ERROR: Unable to open file for writing `%s`", ARGV0, diff_location);
+ goto cleanup;
+ }
+ fwrite(nodiff_message, strlen(nodiff_message) + 1, 1, fdiff);
+ fclose(fdiff);
+ /* Success */
+ status = 0;
+ } else {
+ /* OK, run diff */
+ snprintf(
+ diff_cmd,
+ 2048,
+ "diff \"%s\" \"%s\" > \"%s\" 2> /dev/null",
+ new_tmp,
+ old_tmp,
+ diff_tmp
+ );
+
+ if (system(diff_cmd) != 256) {
+ merror("%s: ERROR: Unable to run `%s`", ARGV0, diff_cmd);
+ goto cleanup;
+ }
- return(NULL);
-}
+ /* Success */
+ status = 0;
+ };
+cleanup:
+ unlink(old_tmp);
+ unlink(new_tmp);
+ unlink(diff_tmp);
+ if (status == -1)
+ return (NULL);
-/* EOF */
+ /* Generate alert */
+ return (gen_diff_alert(filename, new_date_of_change));
+}