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