Source

ossec-hids / src / syscheckd / seechanges.c

/* @(#) $Id: ./src/syscheckd/seechanges.c, 2011/09/08 dcid Exp $
 */

/* Copyright (C) 2009 Trend Micro Inc.
 * All rights 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
 */



#include "shared.h"
#include "os_crypto/md5/md5_op.h"



/* Generate diffs alerts. */
char *gen_diff_alert(char *filename, int alert_diff_time)
{
    int n = 0;
    FILE *fp;
    char *tmp_str;
    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);
    
    fp = fopen(buf, "r");
    if(!fp)
    {
        merror("%s: ERROR: Unable to generate diff alert.", ARGV0);
        return(NULL);
    }

    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. */
        buf[n] = '\0';
        tmp_str = strrchr(buf, '\n');
        if(tmp_str)
            *tmp_str = '\0';
        else
        {
            /* Weird diff with only one large line. */
            buf[256] = '\0';    
        }
    }
    else
    {
        buf[n] = '\0';
    }

    n = 0;


    /* Getting up to 20 line changes. */
    tmp_str = buf;

    
    while(tmp_str && (*tmp_str != '\0'))
    {
        tmp_str = strchr(tmp_str, '\n');
        if(!tmp_str)
            break;    
        else if(n >= 19)
        {
            *tmp_str = '\0';    
            break;
        }
        n++;
        tmp_str++;    
    }


    /* Creating alert. */
    snprintf(diff_alert, 4096 -1, "%s%s",
             buf, n>=19?
             "\nMore changes..":
             "");
    
    
    fclose(fp);
    return(strdup(diff_alert));
}


int seechanges_dupfile(char *old, char *new)
{
    int n;
    FILE *fpr;
    FILE *fpw;
    unsigned char buf[2048 +1];

    buf[2048] = '\0';

    fpr = fopen(old,"r");
    fpw = fopen(new,"w");

    if(!fpr || !fpw)
    {
        return(0);
    }

    while((n = fread(buf, 1, 2048, fpr)) > 0)
    {
        buf[n] = '\0';
        fwrite(buf, n, 1, fpw);

    }

    fclose(fpr);
    fclose(fpw);
    return(1);
}


int seechanges_createpath(char *filename)
{
    char *buffer = NULL;
    char *tmpstr = NULL;
    char *newdir = NULL;

    
    os_strdup(filename, buffer);
    newdir = buffer;
    tmpstr = strchr(buffer +1, '/');
    if(!tmpstr)
    {
        merror("%s: ERROR: Invalid path name: '%s'", ARGV0, filename);
        free(buffer);
        return(0);
    }
    *tmpstr = '\0';
    tmpstr++;


    while(1)
    {
        if(IsDir(newdir) != 0)
        {
            #ifndef WIN32
            if(mkdir(newdir, 0770) == -1)
            #else    
            if(mkdir(newdir) == -1)
            #endif    
            {
                merror(MKDIR_ERROR, ARGV0, newdir);
                free(buffer);
                return(0);
            }
        }

        if(*tmpstr == '\0')
        {
            break;
        }

        tmpstr[-1] = '/';
        tmpstr = strchr(tmpstr, '/');
        if(!tmpstr)
        {
            break;
        }
        *tmpstr = '\0';
        tmpstr++;
    }

    free(buffer);
    return(1);
}


/* Checks if the file has changed */
char *seechanges_addfile(char *filename)
{
    int date_of_change;
    char old_location[OS_MAXSTR +1];
    char tmp_location[OS_MAXSTR +1];
    char diff_cmd[OS_MAXSTR +1];

    os_md5 md5sum_old;
    os_md5 md5sum_new;
    
    old_location[OS_MAXSTR] = '\0';
    tmp_location[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)
    {
        seechanges_createpath(old_location);
        if(seechanges_dupfile(filename, old_location) != 1)
        {
            merror(RENAME_ERROR, ARGV0, filename);
        }
        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);
    }


    /* If they match, keep the old file and remove the new. */
    if(strcmp(md5sum_new, md5sum_old) == 0)
    {
        return(NULL);
    }


    /* 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);
    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); 
    }


    /* Generate alert. */
    return(gen_diff_alert(filename, date_of_change));


    return(NULL);
}



/* EOF */