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