Imported Upstream version 2.3
[ossec-hids.git] / src / agentlessd / agentlessd.c
1 /* @(#) $Id: agentlessd.c,v 1.13 2009/06/24 17:06:20 dcid Exp $ */
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 3) 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 #include "agentlessd.h"
17
18
19
20 /* Saves agentless entry for the control tools to gather. */
21 int save_agentless_entry(char *host, char *script, char *agttype)
22 {
23     FILE *fp;
24     char sys_location[1024 +1];
25
26     sys_location[1024] = '\0';
27     snprintf(sys_location, 1024, "%s/(%s) %s", 
28              AGENTLESS_ENTRYDIRPATH, script, host);
29
30     fp = fopen(sys_location, "w");
31     if(fp)
32     {
33         fprintf(fp, "type: %s\n", agttype);
34         fclose(fp);
35     }
36     else
37     {
38         merror(FOPEN_ERROR, ARGV0, sys_location);
39     }
40
41     return(0);
42 }
43
44
45
46 /* send integrity checking message. */
47 int send_intcheck_msg(char *script, char *host, char *msg)
48 {
49     char sys_location[1024 +1];
50
51     sys_location[1024] = '\0';
52     snprintf(sys_location, 1024, "(%s) %s->%s", script, host, SYSCHECK);
53     
54     if(SendMSG(lessdc.queue, msg, sys_location, SYSCHECK_MQ) < 0)
55     {
56         merror(QUEUE_SEND, ARGV0);
57
58         if((lessdc.queue = StartMQ(DEFAULTQPATH,WRITE)) < 0)
59         {
60             ErrorExit(QUEUE_FATAL, ARGV0, DEFAULTQPATH);
61         }
62
63         /* If we reach here, we can try to send it again */
64         SendMSG(lessdc.queue, msg, sys_location, SYSCHECK_MQ);
65     }
66
67     return(0);
68 }
69
70
71
72 /* Generate diffs alerts. */
73 int gen_diff_alert(char *host, char *script, int alert_diff_time)
74 {
75     int n = 0;
76     FILE *fp;
77     char *tmp_str;
78     char buf[2048 +1];
79     char diff_alert[4096 +1];
80
81     buf[2048] = '\0';
82     diff_alert[4096] = '\0';
83
84     snprintf(buf, 2048, "%s/%s->%s/diff.%d",
85              DIFF_DIR_PATH, host, script,  alert_diff_time);
86     
87     fp = fopen(buf, "r");
88     if(!fp)
89     {
90         merror("%s: ERROR: Unable to generate diff alert.", ARGV0);
91         return(0);
92     }
93
94     n = fread(buf, 1, 2048 -1, fp);
95     if(n <= 0)
96     {
97         merror("%s: ERROR: Unable to generate diff alert (fread).", ARGV0);
98         fclose(fp);
99         return(0);
100     }
101     else if(n >= 2040)
102     {
103         /* We need to clear the last new line. */
104         buf[n] = '\0';
105         tmp_str = strrchr(buf, '\n');
106         if(tmp_str)
107             *tmp_str = '\0';
108         else
109         {
110             /* Weird diff with only one large line. */
111             buf[256] = '\0';    
112         }
113     }
114     else
115     {
116         buf[n] = '\0';
117     }
118
119     n = 0;
120
121
122     /* Getting up to 8 line changes. */
123     tmp_str = buf;
124     
125     while(tmp_str && (*tmp_str != '\0'))
126     {
127         tmp_str = strchr(tmp_str, '\n');
128         if(!tmp_str)
129             break;    
130         else if(n >= 7)
131         {
132             *tmp_str = '\0';    
133             break;
134         }
135         n++;
136         tmp_str++;    
137     }
138
139
140     /* Creating alert. */
141     snprintf(diff_alert, 4096 -1, "ossec: agentless: Change detected:\n%s%s",
142              buf, n>=7?
143              "\nMore changes..":
144              "");
145     
146     
147     snprintf(buf, 1024, "(%s) %s->agentless", script, host);
148     
149     if(SendMSG(lessdc.queue, diff_alert, buf, LOCALFILE_MQ) < 0)
150     {
151         merror(QUEUE_SEND, ARGV0);
152
153         if((lessdc.queue = StartMQ(DEFAULTQPATH, WRITE)) < 0)
154         {
155             ErrorExit(QUEUE_FATAL, ARGV0, DEFAULTQPATH);
156         }
157
158         /* If we reach here, we can try to send it again */
159         SendMSG(lessdc.queue, diff_alert, buf, LOCALFILE_MQ);
160     }
161
162     save_agentless_entry(host, script, "diff");
163
164     fclose(fp);
165     return(0);
166 }
167
168
169
170 /* Checks if the file has changed */
171 int check_diff_file(char *host, char *script)
172 {
173     int date_of_change;
174     char old_location[1024 +1];
175     char new_location[1024 +1];
176     char tmp_location[1024 +1];
177     char diff_cmd[2048 +1];
178
179     os_md5 md5sum_old;
180     os_md5 md5sum_new;
181     
182     old_location[1024] = '\0';
183     new_location[1024] = '\0';
184     tmp_location[1024] = '\0';
185     diff_cmd[2048] = '\0';
186
187     snprintf(new_location, 1024, "%s/%s->%s/%s", DIFF_DIR_PATH, host, script,
188              DIFF_NEW_FILE);
189     snprintf(old_location, 1024, "%s/%s->%s/%s", DIFF_DIR_PATH, host, script,
190              DIFF_LAST_FILE);
191
192
193     /* If the file is not there, rename new location to last location. */
194     if(OS_MD5_File(old_location, md5sum_old) != 0)
195     {
196         if(rename(new_location, old_location) != 0)
197         {
198             merror(RENAME_ERROR, ARGV0, new_location);
199         }
200         return(0);
201     }
202
203     /* Get md5sum of the new file. */
204     if(OS_MD5_File(new_location, md5sum_new) != 0)
205     {
206         merror("%s: ERROR: Invalid internal state (missing '%s').",
207                ARGV0, new_location); 
208         return(0);
209     }
210
211     /* If they match, keep the old file and remove the new. */
212     if(strcmp(md5sum_new, md5sum_old) == 0)
213     {
214         unlink(new_location);
215         return(0);
216     }
217
218
219     /* Saving the old file at timestamp and renaming new to last. */
220     date_of_change = File_DateofChange(old_location);
221     snprintf(tmp_location, 1024, "%s/%s->%s/state.%d", DIFF_DIR_PATH, host, script,
222              date_of_change);
223     rename(old_location, tmp_location);
224     rename(new_location, old_location);
225
226
227     /* Run diff. */
228     date_of_change = File_DateofChange(old_location);
229     snprintf(diff_cmd, 2048, "diff \"%s\" \"%s\" > \"%s/%s->%s/diff.%d\" " 
230              "2>/dev/null",
231              tmp_location, old_location, 
232              DIFF_DIR_PATH, host, script, date_of_change);
233     if(system(diff_cmd) != 256)
234     {
235         merror("%s: ERROR: Unable to run diff for %s->%s",
236                ARGV0,  host, script);
237         return(0); 
238     }
239
240
241     /* Generate alert. */
242     gen_diff_alert(host, script, date_of_change);
243
244
245     return(0);
246 }
247
248
249
250 /* get the diff file. */
251 FILE *open_diff_file(char *host, char *script)
252 {
253     FILE *fp = NULL;
254     char sys_location[1024 +1];
255           
256     sys_location[1024] = '\0';
257     snprintf(sys_location, 1024, "%s/%s->%s/%s", DIFF_DIR_PATH, host, script,
258              DIFF_NEW_FILE);
259
260
261     fp = fopen(sys_location, "w");
262
263     /* If we can't open, try creating the directory. */
264     if(!fp)
265     {
266         snprintf(sys_location, 1024, "%s/%s->%s", DIFF_DIR_PATH, host, script);
267         if(IsDir(sys_location) == -1)
268         {
269             if(mkdir(sys_location, 0770) == -1)
270             {
271                 merror(MKDIR_ERROR, ARGV0, sys_location);
272                 return(NULL);
273             }
274         }
275
276         snprintf(sys_location, 1024, "%s/%s->%s/%s", DIFF_DIR_PATH, host, 
277                  script, DIFF_NEW_FILE);
278         fp = fopen(sys_location, "w");
279         if(!fp)
280         {
281             merror(FOPEN_ERROR, ARGV0, sys_location);
282             return(NULL);
283         }
284     }
285
286     return(fp);
287 }
288
289
290
291 /* Run periodic commands. */
292 int run_periodic_cmd(agentlessd_entries *entry, int test_it)
293 {
294     int i = 0;
295     char *tmp_str;
296     char buf[OS_SIZE_2048 +1];
297     char command[OS_SIZE_1024 +1];
298     FILE *fp;
299     FILE *fp_store = NULL;
300     
301     
302     buf[0] = '\0';
303     command[0] = '\0';
304     command[OS_SIZE_1024] = '\0'; 
305     
306     
307     while(entry->server[i])
308     {
309         /* Ignored entry. */
310         if(entry->server[i][0] == '\0')
311         {
312             i++;
313             continue;
314         }
315         
316         
317         /* We only test for the first server entry. */ 
318         else if(test_it)
319         {
320             int ret_code = 0;
321             snprintf(command, OS_SIZE_1024, 
322                     "%s/%s test test >/dev/null 2>&1", 
323                     AGENTLESSDIRPATH, entry->type);
324             ret_code = system(command);
325
326             /* Checking if the test worked. */
327             if(ret_code != 0)
328             {
329                 if(ret_code == 32512)
330                 {
331                     merror("%s: ERROR: Expect command not found (or bad "
332                            "arguments) for '%s'.",
333                            ARGV0, entry->type); 
334                 }
335                 merror("%s: ERROR: Test failed for '%s' (%d). Ignoring.",
336                        ARGV0, entry->type, ret_code/256);
337                 entry->error_flag = 99;
338                 return(-1);
339             }
340
341             verbose("%s: INFO: Test passed for '%s'.", ARGV0, entry->type);
342             return(0);
343         }
344         
345         if(entry->server[i][0] == 's')
346         {
347             snprintf(command, OS_SIZE_1024, "%s/%s \"use_su\" \"%s\" %s 2>&1", 
348                 AGENTLESSDIRPATH, entry->type, entry->server[i] +1, 
349                 entry->options);
350         }
351         else if(entry->server[i][0] == 'o')
352         {
353             snprintf(command, OS_SIZE_1024, "%s/%s \"use_sudo\" \"%s\" %s 2>&1", 
354                 AGENTLESSDIRPATH, entry->type, entry->server[i] +1, 
355                 entry->options);
356         }
357         else
358         {
359             snprintf(command, OS_SIZE_1024, "%s/%s \"%s\" %s 2>&1", 
360                 AGENTLESSDIRPATH, entry->type, entry->server[i] +1, 
361                 entry->options);
362         }
363
364         fp = popen(command, "r");
365         if(fp)
366         {
367             while(fgets(buf, OS_SIZE_2048, fp) != NULL)
368             {
369                 /* Removing new lines or carriage returns. */
370                 tmp_str = strchr(buf, '\r');
371                 if(tmp_str)
372                     *tmp_str = '\0';
373                 tmp_str = strchr(buf, '\n');
374                 if(tmp_str)
375                     *tmp_str = '\0';
376                     
377                 if(strncmp(buf, "ERROR: ", 7) == 0)
378                 {
379                     merror("%s: ERROR: %s: %s: %s", ARGV0, 
380                            entry->type, entry->server[i] +1, buf +7);
381                     entry->error_flag++;
382                     break;
383                 }
384                 else if(strncmp(buf, "INFO: ", 6) == 0)
385                 {
386                     verbose("%s: INFO: %s: %s: %s", ARGV0, 
387                             entry->type, entry->server[i] +1, buf +6);
388                 }
389                 else if(strncmp(buf, "FWD: ", 4) == 0)
390                 {
391                     tmp_str = buf + 5;
392                     send_intcheck_msg(entry->type, entry->server[i]+1, 
393                                       tmp_str);
394                 }
395                 else if((entry->state & LESSD_STATE_DIFF) &&
396                         (strncmp(buf, "STORE: ", 7) == 0))
397                 {
398                     fp_store = open_diff_file(entry->server[i]+1, 
399                                               entry->type);
400                 }
401                 else if(fp_store)
402                 {
403                     fprintf(fp_store, "%s\n", buf);
404                 }
405                 else
406                 {
407                     debug1("%s: DEBUG: buffer: %s", ARGV0, buf);
408                 }
409             }
410
411             if(fp_store)
412             {
413                 fclose(fp_store);
414                 fp_store = NULL;
415
416                 check_diff_file(entry->server[i] +1, entry->type);
417             }
418             else
419             {
420                 save_agentless_entry(entry->server[i] +1, 
421                                      entry->type, "syscheck");
422             }
423             pclose(fp);
424         }
425         else
426         {
427             merror("%s: ERROR: popen failed on '%s' for '%s'.", ARGV0, 
428                    entry->type, entry->server[i] +1);
429             entry->error_flag++;
430         }
431
432         i++;
433     }
434
435     if(fp_store)
436     {
437         fclose(fp_store);
438     }
439     
440     return(0);
441 }
442
443
444
445 /* Main agentlessd */
446 void Agentlessd()
447 {
448     time_t tm;     
449     struct tm *p;       
450
451     int today = 0;                      
452     int thismonth = 0;
453     int thisyear = 0;
454     int test_it = 1;
455
456     char str[OS_SIZE_1024 +1];
457
458
459     /* Waiting a few seconds to settle */
460     sleep(2);
461     memset(str, '\0', OS_SIZE_1024 +1);
462     
463     
464     /* Getting currently time before starting */
465     tm = time(NULL);
466     p = localtime(&tm); 
467     
468     today = p->tm_mday;
469     thismonth = p->tm_mon;
470     thisyear = p->tm_year+1900;
471                 
472
473     /* Connecting to the message queue
474      * Exit if it fails.
475      */
476     if((lessdc.queue = StartMQ(DEFAULTQPATH, WRITE)) < 0)
477     {
478         ErrorExit(QUEUE_FATAL, ARGV0, DEFAULTQUEUE);
479     }
480
481
482
483     /* Main monitor loop */
484     while(1)
485     {
486         int i = 0;
487         tm = time(NULL);
488         p = localtime(&tm);
489
490
491         /* Day changed, deal with log files */
492         if(today != p->tm_mday)
493         {
494             today = p->tm_mday;
495             thismonth = p->tm_mon;
496             thisyear = p->tm_year+1900;
497         }
498
499
500         while(lessdc.entries[i])
501         {
502             if(lessdc.entries[i]->error_flag >= 10)
503             {
504                 if(lessdc.entries[i]->error_flag != 99)
505                 {
506                     merror("%s: ERROR: Too many failures for '%s'. Ignoring it.",
507                            ARGV0, lessdc.entries[i]->type); 
508                     lessdc.entries[i]->error_flag = 99;
509                 }
510
511                 i++;
512                 sleep(i);
513                 continue;
514             }
515
516             
517             /* Run the check again if the frequency has elapsed. */
518             if((lessdc.entries[i]->state & LESSD_STATE_PERIODIC) &&
519                ((lessdc.entries[i]->current_state + 
520                  lessdc.entries[i]->frequency) < tm))
521             {
522                 run_periodic_cmd(lessdc.entries[i], test_it);
523                 if(!test_it)
524                     lessdc.entries[i]->current_state = tm;
525             }
526             
527             i++;
528
529             sleep(i);
530         }
531         
532         /* We only check every minute */
533         test_it = 0;
534         sleep(60);
535     }
536 }
537
538 /* EOF */