Imported Upstream version 2.7
[ossec-hids.git] / src / syscheckd / seechanges.c
1 /* @(#) $Id: ./src/syscheckd/seechanges.c, 2011/09/08 dcid Exp $
2  */
3
4 /* Copyright (C) 2009 Trend Micro Inc.
5  * All rights reserved.
6  *
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
10  * Foundation
11  */
12
13
14
15 #include "shared.h"
16 #include "os_crypto/md5/md5_op.h"
17
18
19
20 /* Generate diffs alerts. */
21 char *gen_diff_alert(char *filename, int alert_diff_time)
22 {
23     int n = 0;
24     FILE *fp;
25     char *tmp_str;
26     char buf[OS_MAXSTR +1];
27     char diff_alert[OS_MAXSTR +1];
28
29     buf[OS_MAXSTR] = '\0';
30     diff_alert[OS_MAXSTR] = '\0';
31
32     snprintf(buf, OS_MAXSTR, "%s/local/%s/diff.%d",
33              DIFF_DIR_PATH, filename,  alert_diff_time);
34
35     fp = fopen(buf, "r");
36     if(!fp)
37     {
38         merror("%s: ERROR: Unable to generate diff alert.", ARGV0);
39         return(NULL);
40     }
41
42     n = fread(buf, 1, 4096 -1, fp);
43     if(n <= 0)
44     {
45         merror("%s: ERROR: Unable to generate diff alert (fread).", ARGV0);
46         fclose(fp);
47         return(NULL);
48     }
49     else if(n >= 4000)
50     {
51         /* We need to clear the last new line. */
52         buf[n] = '\0';
53         tmp_str = strrchr(buf, '\n');
54         if(tmp_str)
55             *tmp_str = '\0';
56         else
57         {
58             /* Weird diff with only one large line. */
59             buf[256] = '\0';
60         }
61     }
62     else
63     {
64         buf[n] = '\0';
65     }
66
67     n = 0;
68
69
70     /* Getting up to 20 line changes. */
71     tmp_str = buf;
72
73
74     while(tmp_str && (*tmp_str != '\0'))
75     {
76         tmp_str = strchr(tmp_str, '\n');
77         if(!tmp_str)
78             break;
79         else if(n >= 19)
80         {
81             *tmp_str = '\0';
82             break;
83         }
84         n++;
85         tmp_str++;
86     }
87
88
89     /* Creating alert. */
90     snprintf(diff_alert, 4096 -1, "%s%s",
91              buf, n>=19?
92              "\nMore changes..":
93              "");
94
95
96     fclose(fp);
97     return(strdup(diff_alert));
98 }
99
100
101 int seechanges_dupfile(char *old, char *new)
102 {
103     int n;
104     FILE *fpr;
105     FILE *fpw;
106     unsigned char buf[2048 +1];
107
108     buf[2048] = '\0';
109
110     fpr = fopen(old,"r");
111     fpw = fopen(new,"w");
112
113     if(!fpr || !fpw)
114     {
115         return(0);
116     }
117
118     while((n = fread(buf, 1, 2048, fpr)) > 0)
119     {
120         buf[n] = '\0';
121         fwrite(buf, n, 1, fpw);
122
123     }
124
125     fclose(fpr);
126     fclose(fpw);
127     return(1);
128 }
129
130
131 int seechanges_createpath(char *filename)
132 {
133     char *buffer = NULL;
134     char *tmpstr = NULL;
135     char *newdir = NULL;
136
137
138     os_strdup(filename, buffer);
139     newdir = buffer;
140     tmpstr = strchr(buffer +1, '/');
141     if(!tmpstr)
142     {
143         merror("%s: ERROR: Invalid path name: '%s'", ARGV0, filename);
144         free(buffer);
145         return(0);
146     }
147     *tmpstr = '\0';
148     tmpstr++;
149
150
151     while(1)
152     {
153         if(IsDir(newdir) != 0)
154         {
155             #ifndef WIN32
156             if(mkdir(newdir, 0770) == -1)
157             #else
158             if(mkdir(newdir) == -1)
159             #endif
160             {
161                 merror(MKDIR_ERROR, ARGV0, newdir);
162                 free(buffer);
163                 return(0);
164             }
165         }
166
167         if(*tmpstr == '\0')
168         {
169             break;
170         }
171
172         tmpstr[-1] = '/';
173         tmpstr = strchr(tmpstr, '/');
174         if(!tmpstr)
175         {
176             break;
177         }
178         *tmpstr = '\0';
179         tmpstr++;
180     }
181
182     free(buffer);
183     return(1);
184 }
185
186
187 /* Checks if the file has changed */
188 char *seechanges_addfile(char *filename)
189 {
190     int date_of_change;
191     char old_location[OS_MAXSTR +1];
192     char tmp_location[OS_MAXSTR +1];
193     char diff_cmd[OS_MAXSTR +1];
194
195     os_md5 md5sum_old;
196     os_md5 md5sum_new;
197
198     old_location[OS_MAXSTR] = '\0';
199     tmp_location[OS_MAXSTR] = '\0';
200     diff_cmd[OS_MAXSTR] = '\0';
201     md5sum_new[0] = '\0';
202     md5sum_old[0] = '\0';
203
204
205     snprintf(old_location, OS_MAXSTR, "%s/local/%s/%s", DIFF_DIR_PATH, filename +1,
206              DIFF_LAST_FILE);
207
208
209     /* If the file is not there, rename new location to last location. */
210     if(OS_MD5_File(old_location, md5sum_old) != 0)
211     {
212         seechanges_createpath(old_location);
213         if(seechanges_dupfile(filename, old_location) != 1)
214         {
215             merror(RENAME_ERROR, ARGV0, filename);
216         }
217         return(NULL);
218     }
219
220
221     /* Get md5sum of the new file. */
222     if(OS_MD5_File(filename, md5sum_new) != 0)
223     {
224         //merror("%s: ERROR: Invalid internal state (missing '%s').",
225         //       ARGV0, filename);
226         return(NULL);
227     }
228
229
230     /* If they match, keep the old file and remove the new. */
231     if(strcmp(md5sum_new, md5sum_old) == 0)
232     {
233         return(NULL);
234     }
235
236
237     /* Saving the old file at timestamp and renaming new to last. */
238     date_of_change = File_DateofChange(old_location);
239     snprintf(tmp_location, OS_MAXSTR, "%s/local/%s/state.%d", DIFF_DIR_PATH, filename +1,
240              date_of_change);
241     rename(old_location, tmp_location);
242     if(seechanges_dupfile(filename, old_location) != 1)
243     {
244         merror("%s: ERROR: Unable to create snapshot for %s",ARGV0, filename);
245         return(NULL);
246     }
247
248
249     /* Run diff. */
250     date_of_change = File_DateofChange(old_location);
251     snprintf(diff_cmd, 2048, "diff \"%s\" \"%s\" > \"%s/local/%s/diff.%d\" "
252              "2>/dev/null",
253              tmp_location, old_location,
254              DIFF_DIR_PATH, filename +1, date_of_change);
255     if(system(diff_cmd) != 256)
256     {
257         merror("%s: ERROR: Unable to run diff for %s",
258                ARGV0,  filename);
259         return(NULL);
260     }
261
262
263     /* Generate alert. */
264     return(gen_diff_alert(filename, date_of_change));
265
266
267     return(NULL);
268 }
269
270
271
272 /* EOF */