7bf2ee1920f78c719a7431c064474b07dacf6fd8
[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 #ifdef USE_MAGIC
19 #include <magic.h>
20 extern magic_t magic_cookie;
21
22 int is_text(magic_t cookie, const void* buf, size_t len)
23 {
24     const char* magic = magic_buffer(cookie, buf, len);
25
26     if(!magic)
27     {
28         const char* err = magic_error(cookie);
29         merror("%s: ERROR: magic_buffer: %s", ARGV0, err ? err : "unknown");
30         return(1); // TODO default to true?
31     }
32     else
33     {
34         if(strncmp(magic, "text/", 5) == 0) return(1);
35     }
36
37     return(0);
38 }
39 #endif
40
41 /* Generate diffs alerts. */
42 char *gen_diff_alert(char *filename, int alert_diff_time)
43 {
44     int n = 0;
45     FILE *fp;
46     char *tmp_str;
47     char buf[OS_MAXSTR +1];
48     char diff_alert[OS_MAXSTR +1];
49
50     buf[OS_MAXSTR] = '\0';
51     diff_alert[OS_MAXSTR] = '\0';
52
53     snprintf(buf, OS_MAXSTR, "%s/local/%s/diff.%d",
54              DIFF_DIR_PATH, filename,  alert_diff_time);
55
56     fp = fopen(buf, "r");
57     if(!fp)
58     {
59         merror("%s: ERROR: Unable to generate diff alert.", ARGV0);
60         return(NULL);
61     }
62
63     n = fread(buf, 1, 4096 -1, fp);
64     if(n <= 0)
65     {
66         merror("%s: ERROR: Unable to generate diff alert (fread).", ARGV0);
67         fclose(fp);
68         return(NULL);
69     }
70     else if(n >= 4000)
71     {
72         /* We need to clear the last new line. */
73         buf[n] = '\0';
74         tmp_str = strrchr(buf, '\n');
75         if(tmp_str)
76             *tmp_str = '\0';
77         else
78         {
79             /* Weird diff with only one large line. */
80             buf[256] = '\0';
81         }
82     }
83     else
84     {
85         buf[n] = '\0';
86     }
87
88     n = 0;
89
90
91     /* Getting up to 20 line changes. */
92     tmp_str = buf;
93
94
95     while(tmp_str && (*tmp_str != '\0'))
96     {
97         tmp_str = strchr(tmp_str, '\n');
98         if(!tmp_str)
99             break;
100         else if(n >= 19)
101         {
102             *tmp_str = '\0';
103             break;
104         }
105         n++;
106         tmp_str++;
107     }
108
109
110     /* Creating alert. */
111     snprintf(diff_alert, 4096 -1, "%s%s",
112              buf, n>=19?
113              "\nMore changes..":
114              "");
115
116
117     fclose(fp);
118     return(strdup(diff_alert));
119 }
120
121
122 int seechanges_dupfile(char *old, char *new)
123 {
124     int n;
125     FILE *fpr;
126     FILE *fpw;
127     unsigned char buf[2048 +1];
128
129     buf[2048] = '\0';
130
131     fpr = fopen(old,"r");
132     fpw = fopen(new,"w");
133
134     if(!fpr || !fpw)
135     {
136         return(0);
137     }
138
139     n = fread(buf, 1, 2048, fpr);
140     #ifdef USE_MAGIC
141     if(is_text(magic_cookie, buf, n) == 0)
142     {
143         goto cleanup;
144     }
145     #endif
146
147     do
148     {
149         buf[n] = '\0';
150         fwrite(buf, n, 1, fpw);
151     }
152     while((n = fread(buf, 1, 2048, fpr)) > 0);
153
154 #ifdef USE_MAGIC
155 cleanup:
156 #endif
157     fclose(fpr);
158     fclose(fpw);
159     return(1);
160 }
161
162
163 int seechanges_createpath(char *filename)
164 {
165     char *buffer = NULL;
166     char *tmpstr = NULL;
167     char *newdir = NULL;
168
169
170     os_strdup(filename, buffer);
171     newdir = buffer;
172     tmpstr = strchr(buffer +1, '/');
173     if(!tmpstr)
174     {
175         merror("%s: ERROR: Invalid path name: '%s'", ARGV0, filename);
176         free(buffer);
177         return(0);
178     }
179     *tmpstr = '\0';
180     tmpstr++;
181
182
183     while(1)
184     {
185         if(IsDir(newdir) != 0)
186         {
187             #ifndef WIN32
188             if(mkdir(newdir, 0770) == -1)
189             #else
190             if(mkdir(newdir) == -1)
191             #endif
192             {
193                 merror(MKDIR_ERROR, ARGV0, newdir);
194                 free(buffer);
195                 return(0);
196             }
197         }
198
199         if(*tmpstr == '\0')
200         {
201             break;
202         }
203
204         tmpstr[-1] = '/';
205         tmpstr = strchr(tmpstr, '/');
206         if(!tmpstr)
207         {
208             break;
209         }
210         *tmpstr = '\0';
211         tmpstr++;
212     }
213
214     free(buffer);
215     return(1);
216 }
217
218
219 /* Checks if the file has changed */
220 char *seechanges_addfile(char *filename)
221 {
222     time_t old_date_of_change;
223     time_t new_date_of_change;
224
225     char old_location[OS_MAXSTR +1];
226     char tmp_location[OS_MAXSTR +1];
227     char diff_location[OS_MAXSTR + 1];
228     char old_tmp[OS_MAXSTR + 1];
229     char new_tmp[OS_MAXSTR + 1];
230     char diff_tmp[OS_MAXSTR + 1];
231
232     char diff_cmd[OS_MAXSTR +1];
233
234     os_md5 md5sum_old;
235     os_md5 md5sum_new;
236     int status = -1;
237
238
239     old_location[OS_MAXSTR] = '\0';
240     tmp_location[OS_MAXSTR] = '\0';
241     diff_location[OS_MAXSTR] = '\0';
242     old_tmp[OS_MAXSTR] = '\0';
243     new_tmp[OS_MAXSTR] = '\0';
244     diff_tmp[OS_MAXSTR] = '\0';
245     diff_cmd[OS_MAXSTR] = '\0';
246     md5sum_new[0] = '\0';
247     md5sum_old[0] = '\0';
248
249
250     snprintf(
251         old_location,
252         OS_MAXSTR,
253         "%s/local/%s/%s",
254         DIFF_DIR_PATH,
255         filename + 1,
256         DIFF_LAST_FILE
257     );
258
259
260     /* If the file is not there, rename new location to last location. */
261     if(OS_MD5_File(old_location, md5sum_old) != 0)
262     {
263         seechanges_createpath(old_location);
264         if(seechanges_dupfile(filename, old_location) != 1)
265         {
266             merror(RENAME_ERROR, ARGV0, filename);
267         }
268         return(NULL);
269     }
270
271
272     /* Get md5sum of the new file. */
273     if(OS_MD5_File(filename, md5sum_new) != 0)
274     {
275         //merror("%s: ERROR: Invalid internal state (missing '%s').",
276         //       ARGV0, filename);
277         return(NULL);
278     }
279
280
281     /* If they match, keep the old file and remove the new. */
282     if(strcmp(md5sum_new, md5sum_old) == 0)
283     {
284         return(NULL);
285     }
286
287
288     /* Saving the old file at timestamp and renaming new to last. */
289     old_date_of_change = File_DateofChange(old_location);
290
291     snprintf(
292         tmp_location,
293         OS_MAXSTR,
294         "%s/local/%s/state.%d",
295         DIFF_DIR_PATH,
296         filename + 1,
297        (int)old_date_of_change
298     );
299
300
301     rename(old_location, tmp_location);
302     if(seechanges_dupfile(filename, old_location) != 1)
303     {
304         merror("%s: ERROR: Unable to create snapshot for %s",ARGV0, filename);
305         return(NULL);
306     }
307
308
309     new_date_of_change = File_DateofChange(old_location);
310
311     /* Create file names */
312     snprintf(
313         old_tmp,
314         OS_MAXSTR,
315         "%s/%s/syscheck-changes-%s-%d",
316         DEFAULTDIR,
317         TMP_DIR,
318         md5sum_old,
319         (int)old_date_of_change
320     );
321
322     snprintf(
323         new_tmp,
324         OS_MAXSTR,
325         "%s/%s/syscheck-changes-%s-%d",
326         DEFAULTDIR,
327         TMP_DIR,
328         md5sum_new,
329         (int)new_date_of_change
330     );
331
332     snprintf(
333         diff_tmp,
334         OS_MAXSTR,
335         "%s/%s/syscheck-changes-%s-%d-%s-%d",
336         DEFAULTDIR,
337         TMP_DIR,
338         md5sum_old,
339         (int)old_date_of_change,
340         md5sum_new,
341         (int)new_date_of_change
342     );
343
344     /* Create diff location */
345     snprintf(
346         diff_location,
347         OS_MAXSTR,
348         "%s/local/%s/diff.%d",
349         DIFF_DIR_PATH,
350         filename + 1,
351         (int)new_date_of_change
352     );
353
354     /* Create symlinks */
355     if (symlink(old_location, old_tmp) == -1) {
356         merror(LINK_ERROR, ARGV0, old_location, old_tmp, errno, strerror(errno));
357         goto cleanup;
358     }
359
360     if (symlink(tmp_location, new_tmp) == -1) {
361         merror(LINK_ERROR, ARGV0, tmp_location, new_tmp, errno, strerror(errno));
362         goto cleanup;
363     }
364
365     if (symlink(diff_location, diff_tmp) == -1) {
366         merror(LINK_ERROR, ARGV0, diff_location, diff_tmp, errno, strerror(errno));
367         goto cleanup;
368     }
369
370
371
372     /* Run diff. */
373     snprintf(
374         diff_cmd,
375         2048,
376         "diff \"%s\" \"%s\" > \"%s\" 2> /dev/null",
377         new_tmp,
378         old_tmp,
379         diff_tmp
380     );
381
382
383
384     if(system(diff_cmd) != 256) {
385         merror("%s: ERROR: Unable to run diff for %s", ARGV0, filename);
386         goto cleanup;
387
388     }
389
390     /* Success */
391     status = 0;
392
393 cleanup:
394     unlink(old_tmp);
395     unlink(new_tmp);
396     unlink(diff_tmp);
397
398     if (status == -1)
399         return (NULL);
400
401     /* Generate alert. */
402     return (gen_diff_alert(filename, new_date_of_change));
403 }
404
405
406
407 /* EOF */