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