15fc42caf30aab3671a60da4441c72b1a6618f46
[ossec-hids.git] / execd.c
1 /* @(#) $Id: ./src/os_execd/execd.c, 2011/09/08 dcid Exp $
2  */
3
4 /* Copyright (C) 2009 Trend Micro Inc.
5  * All right 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 "list_op.h"
17 #include "os_regex/os_regex.h"
18 #include "os_net/os_net.h"
19
20 #include "execd.h"
21
22
23
24 /* Timeout data structure */
25 typedef struct _timeout_data
26 {
27     time_t time_of_addition;
28     int time_to_block;
29     char **command;
30 }timeout_data;
31
32
33 /* Timeout list */
34 OSList *timeout_list;
35 OSListNode *timeout_node;
36 OSHash *repeated_hash;
37 int repeated_offenders_timeout[] = {0,0,0,0,0,0,0};
38
39
40
41 /**
42  * Shutdowns execd properly.
43  */
44 void execd_shutdown()
45 {
46     /* Removing pending active responses. */
47     merror(EXEC_SHUTDOWN, ARGV0);
48
49     timeout_node = OSList_GetFirstNode(timeout_list);
50     while(timeout_node)
51     {
52         timeout_data *list_entry;
53
54         list_entry = (timeout_data *)timeout_node->data;
55
56         ExecCmd(list_entry->command);
57
58         /* Delete currently node - already sets the pointer to next */
59         OSList_DeleteCurrentlyNode(timeout_list);
60         timeout_node = OSList_GetCurrentlyNode(timeout_list);
61     }
62
63     #ifndef WIN32
64     HandleSIG();
65     #endif
66
67 }
68
69
70 #ifndef WIN32
71
72 /** int main(int argc, char **argv) v0.1
73  */
74 int main(int argc, char **argv)
75 {
76     int c;
77     int test_config = 0,run_foreground = 0;
78     int gid = 0,m_queue = 0;
79
80     // TODO: delete or implement
81     char *dir __attribute__((unused)) = DEFAULTDIR;
82     char *group = GROUPGLOBAL;
83     // TODO: delete or implement
84     char *cfg __attribute__((unused)) = DEFAULTARPATH;
85     char *xmlcfg = DEFAULTCPATH;
86
87
88     /* Setting the name */
89     OS_SetName(ARGV0);
90
91
92     while((c = getopt(argc, argv, "Vtdhfu:g:D:c:")) != -1){
93         switch(c){
94             case 'V':
95                 print_version();
96                 break;
97             case 'h':
98                 help(ARGV0);
99                 break;
100             case 'd':
101                 nowDebug();
102                 break;
103             case 'f':
104                 run_foreground = 1;
105                 break;
106             case 'g':
107                 if(!optarg)
108                     ErrorExit("%s: -g needs an argument.",ARGV0);
109                 group = optarg;
110                 break;
111             case 'D':
112                 if(!optarg)
113                     ErrorExit("%s: -D needs an argument.",ARGV0);
114                 dir = optarg;
115                 break;
116             case 'c':
117                 if(!optarg)
118                     ErrorExit("%s: -c needs an argument.",ARGV0);
119                 cfg = optarg;
120                 break;
121             case 't':
122                 test_config = 1;
123                 break;
124             default:
125                 help(ARGV0);
126                 break;
127         }
128
129     }
130
131
132
133     /* Check if the group given are valid */
134     gid = Privsep_GetGroup(group);
135     if(gid < 0)
136         ErrorExit(USER_ERROR,ARGV0,"",group);
137
138
139     /* Privilege separation */
140     if(Privsep_SetGroup(gid) < 0)
141         ErrorExit(SETGID_ERROR,ARGV0,group);
142
143
144     /* Reading config */
145     if((c = ExecdConfig(xmlcfg)) < 0)
146     {
147         ErrorExit(CONFIG_ERROR, ARGV0, xmlcfg);
148     }
149
150
151     /* Exit if test_config */
152     if(test_config)
153         exit(0);
154
155
156     /* Signal manipulation */
157     StartSIG2(ARGV0, execd_shutdown);
158
159
160     if (!run_foreground)
161     {
162         /* Going daemon */
163         nowDaemon();
164         goDaemon();
165     }
166
167
168     /* Active response disabled */
169     if(c == 1)
170     {
171         verbose(EXEC_DISABLED, ARGV0);
172         exit(0);
173     }
174
175     /* Creating the PID file */
176     if(CreatePID(ARGV0, getpid()) < 0)
177         merror(PID_ERROR, ARGV0);
178
179
180     /* Starting queue (exec queue) */
181     if((m_queue = StartMQ(EXECQUEUEPATH,READ)) < 0)
182         ErrorExit(QUEUE_ERROR, ARGV0, EXECQUEUEPATH, strerror(errno));
183
184
185     /* Start up message */
186     verbose(STARTUP_MSG, ARGV0, (int)getpid());
187
188
189     /* The real daemon Now */
190     ExecdStart(m_queue);
191
192     exit(0);
193 }
194
195
196 #endif
197
198
199
200 /** void FreeTimeoutEntry(timeout_data *timeout_entry) v0.1
201  * Free the timeout entry. Must be called after popping it
202  * from the timeout list
203  */
204 void FreeTimeoutEntry(void *timeout_entry_pt)
205 {
206     timeout_data *timeout_entry;
207     char **tmp_str;
208
209     timeout_entry = (timeout_data *)timeout_entry_pt;
210
211     if(!timeout_entry)
212     {
213         return;
214     }
215
216     tmp_str = timeout_entry->command;
217
218     /* Clearing the command arguments */
219     if(tmp_str)
220     {
221         while(*tmp_str)
222         {
223             os_free(*tmp_str);
224             *tmp_str = NULL;
225             tmp_str++;
226         }
227         os_free(timeout_entry->command);
228         timeout_entry->command = NULL;
229     }
230
231     os_free(timeout_entry);
232     timeout_entry = NULL;
233
234     return;
235 }
236
237
238 #ifndef WIN32
239
240
241 /** void ExecdStart(int q) v0.2
242  * Main function on the execd. Does all the data receiving ,etc.
243  */
244 void ExecdStart(int q)
245 {
246     int i, childcount = 0;
247     time_t curr_time;
248
249     char buffer[OS_MAXSTR + 1];
250     char *tmp_msg = NULL;
251     char *name;
252     char *command;
253     char *cmd_args[MAX_ARGS +2];
254
255
256     /* Select */
257     fd_set fdset;
258     struct timeval socket_timeout;
259
260
261     /* Clearing the buffer */
262     memset(buffer, '\0', OS_MAXSTR +1);
263
264
265     /* Initializing the cmd arguments */
266     for(i = 0; i<= MAX_ARGS +1; i++)
267     {
268         cmd_args[i] = NULL;
269     }
270
271
272     /* Creating list for timeout */
273     timeout_list = OSList_Create();
274     if(!timeout_list)
275     {
276         ErrorExit(LIST_ERROR, ARGV0);
277     }
278
279
280     if(repeated_offenders_timeout[0] != 0)
281     {
282         repeated_hash = OSHash_Create();
283     }
284     else
285     {
286         repeated_hash = NULL;
287     }
288
289
290
291     /* Main loop. */
292     while(1)
293     {
294         int timeout_value;
295         int added_before = 0;
296
297         char **timeout_args;
298         timeout_data *timeout_entry;
299
300
301         /* Cleaning up any child. */
302         while (childcount)
303         {
304             int wp;
305             wp = waitpid((pid_t) -1, NULL, WNOHANG);
306             if (wp < 0)
307             {
308                 merror(WAITPID_ERROR, ARGV0);
309                 break;
310             }
311
312             /* if = 0, we still need to wait for the child process */
313             else if (wp == 0)
314             {
315                 break;
316             }
317             /* Child completed if wp > 0 */
318             else
319             {
320                 childcount--;
321             }
322         }
323
324
325         /* Getting currently time */
326         curr_time = time(0);
327
328
329         /* Checking if there is any timeouted command to execute. */
330         timeout_node = OSList_GetFirstNode(timeout_list);
331         while(timeout_node)
332         {
333             timeout_data *list_entry;
334
335             list_entry = (timeout_data *)timeout_node->data;
336
337             /* Timeouted */
338             if((curr_time - list_entry->time_of_addition) >
339                     list_entry->time_to_block)
340             {
341                 ExecCmd(list_entry->command);
342
343                 /* Deletecurrently node already sets the pointer to next */
344                 OSList_DeleteCurrentlyNode(timeout_list);
345                 timeout_node = OSList_GetCurrentlyNode(timeout_list);
346
347                 /* Clearing the memory */
348                 FreeTimeoutEntry(list_entry);
349
350                 childcount++;
351             }
352
353             else
354             {
355                 timeout_node = OSList_GetNextNode(timeout_list);
356             }
357         }
358
359
360         /* Setting timeout to EXECD_TIMEOUT */
361         socket_timeout.tv_sec = EXECD_TIMEOUT;
362         socket_timeout.tv_usec= 0;
363
364
365
366         /* Setting FD values */
367         FD_ZERO(&fdset);
368         FD_SET(q, &fdset);
369
370         /* Adding timeout */
371         if(select(q+1, &fdset, NULL, NULL, &socket_timeout) == 0)
372         {
373             /* Timeout .. */
374             continue;
375         }
376
377
378         /* Checking for error */
379         if(!FD_ISSET(q, &fdset))
380         {
381             merror(SELECT_ERROR, ARGV0);
382             continue;
383         }
384
385
386         /* Receiving the message */
387         if(recv(q, buffer, OS_MAXSTR, 0) == -1)
388         {
389             merror(QUEUE_ERROR, ARGV0, EXECQUEUEPATH, strerror(errno));
390             continue;
391         }
392
393
394         /* Currently time */
395         curr_time = time(0);
396
397
398         /* Getting application name */
399         name = buffer;
400
401
402         /* Zeroing the name */
403         tmp_msg = strchr(buffer, ' ');
404         if(!tmp_msg)
405         {
406             merror(EXECD_INV_MSG, ARGV0, buffer);
407             continue;
408         }
409         *tmp_msg = '\0';
410         tmp_msg++;
411
412
413         /* Getting the command to execute (valid name) */
414         command = GetCommandbyName(name, &timeout_value);
415         if(!command)
416         {
417             ReadExecConfig();
418             command = GetCommandbyName(name, &timeout_value);
419             if(!command)
420             {
421                 merror(EXEC_INV_NAME, ARGV0, name);
422                 continue;
423             }
424         }
425
426
427         /* Command not present. */
428         if(command[0] == '\0')
429             continue;
430
431
432         /* Allocating memory for the timeout argument */
433         os_calloc(MAX_ARGS+2, sizeof(char *), timeout_args);
434
435
436         /* Adding initial variables to the cmd_arg and to the timeout cmd */
437         cmd_args[0] = command;
438         cmd_args[1] = ADD_ENTRY;
439         os_strdup(command, timeout_args[0]);
440         os_strdup(DELETE_ENTRY, timeout_args[1]);
441
442         cmd_args[2] = NULL;
443         timeout_args[2] = NULL;
444
445
446         /* Getting the arguments. */
447         i = 2;
448         while(i < (MAX_ARGS -1))
449         {
450             cmd_args[i] = tmp_msg;
451             cmd_args[i+1] = NULL;
452
453             tmp_msg = strchr(tmp_msg, ' ');
454             if(!tmp_msg)
455             {
456                 timeout_args[i] = strdup(cmd_args[i]);
457                 timeout_args[i+1] = NULL;
458                 break;
459             }
460             *tmp_msg = '\0';
461             tmp_msg++;
462
463             timeout_args[i] = strdup(cmd_args[i]);
464             timeout_args[i+1] = NULL;
465
466             i++;
467         }
468
469
470         /* Check this command was already executed. */
471         timeout_node = OSList_GetFirstNode(timeout_list);
472         added_before = 0;
473
474
475         /* Checking for the username and ip argument */
476         if(!timeout_args[2] || !timeout_args[3])
477         {
478             added_before = 1;
479             merror("%s: Invalid number of arguments.", ARGV0);
480         }
481
482
483
484         while(timeout_node)
485         {
486             timeout_data *list_entry;
487
488             list_entry = (timeout_data *)timeout_node->data;
489             if((strcmp(list_entry->command[3], timeout_args[3]) == 0) &&
490                (strcmp(list_entry->command[0], timeout_args[0]) == 0))
491             {
492                 /* Means we executed this command before
493                  * and we don't need to add it again.
494                  */
495                 added_before = 1;
496
497
498                 /* updating the timeout */
499                 list_entry->time_of_addition = curr_time;
500
501                 if(repeated_offenders_timeout[0] != 0 &&
502                    repeated_hash != NULL &&
503                    strncmp(timeout_args[3],"-", 1) != 0)
504                 {
505                     char *ntimes = NULL;
506                     char rkey[256];
507                     rkey[255] = '\0';
508                     snprintf(rkey, 255, "%s%s", list_entry->command[0],
509                                                 timeout_args[3]);
510
511                     if((ntimes = OSHash_Get(repeated_hash, rkey)))
512                     {
513                         int ntimes_int = 0;
514                         int i2 = 0;
515                         int new_timeout = 0;
516                         ntimes_int = atoi(ntimes);
517                         while(repeated_offenders_timeout[i2] != 0)
518                         {
519                             i2++;
520                         }
521                         if(ntimes_int >= i2)
522                         {
523                             new_timeout = repeated_offenders_timeout[i2 - 1]*60;
524                         }
525                         else
526                         {
527                             os_calloc(10, sizeof(char), ntimes);
528                             new_timeout = repeated_offenders_timeout[ntimes_int]*60;
529                             ntimes_int++;
530                             snprintf(ntimes, 9, "%d", ntimes_int);
531                             OSHash_Update(repeated_hash,rkey,ntimes);
532                         }
533                         list_entry->time_to_block = new_timeout;
534                     }
535                 }
536                 break;
537             }
538
539             /* Continue with the next entry in timeout list*/
540             timeout_node = OSList_GetNextNode(timeout_list);
541         }
542
543
544         /* If it wasn't added before, do it now */
545         if(!added_before)
546         {
547             /* executing command */
548             ExecCmd(cmd_args);
549
550             /* We don't need to add to the list if the timeout_value == 0 */
551             if(timeout_value)
552             {
553                 char *ntimes;
554                 char rkey[256];
555                 rkey[255] = '\0';
556                 snprintf(rkey, 255, "%s%s", timeout_args[0],
557                                             timeout_args[3]);
558
559                 if(repeated_hash != NULL)
560                 {
561                   if((ntimes = OSHash_Get(repeated_hash, rkey)))
562                   {
563                     int ntimes_int = 0;
564                     int i2 = 0;
565                     int new_timeout = 0;
566
567                     ntimes_int = atoi(ntimes);
568                     while(repeated_offenders_timeout[i2] != 0)
569                     {
570                         i2++;
571                     }
572                     if(ntimes_int >= i2)
573                     {
574                         new_timeout = repeated_offenders_timeout[i2 - 1]*60;
575                     }
576                     else
577                     {
578                         os_calloc(10, sizeof(char), ntimes);
579                         new_timeout = repeated_offenders_timeout[ntimes_int]*60;
580                         ntimes_int++;
581                         snprintf(ntimes, 9, "%d", ntimes_int);
582                         OSHash_Update(repeated_hash, rkey, ntimes);
583                     }
584                     timeout_value = new_timeout;
585                   }
586                   else
587                   {
588                       /* Adding to the repeated offenders list. */
589                       OSHash_Add(repeated_hash,
590                            strdup(rkey),strdup("0"));
591                   }
592                 }
593
594
595                 /* Creating the timeout entry */
596                 os_calloc(1, sizeof(timeout_data), timeout_entry);
597                 timeout_entry->command = timeout_args;
598                 timeout_entry->time_of_addition = curr_time;
599                 timeout_entry->time_to_block = timeout_value;
600
601
602                 /* Adding command to the timeout list */
603                 if(!OSList_AddData(timeout_list, timeout_entry))
604                 {
605                     merror(LIST_ADD_ERROR, ARGV0);
606                     FreeTimeoutEntry(timeout_entry);
607                 }
608             }
609
610             /* If no timeout, we still need to free it in here */
611             else
612             {
613                 char **ss_ta = timeout_args;
614                 while(*timeout_args)
615                 {
616                     os_free(*timeout_args);
617                     *timeout_args = NULL;
618                     timeout_args++;
619                 }
620                 os_free(ss_ta);
621             }
622
623             childcount++;
624         }
625
626         /* We didn't add it to the timeout list */
627         else
628         {
629             char **ss_ta = timeout_args;
630
631             /* Clear the timeout arguments */
632             while(*timeout_args)
633             {
634                 os_free(*timeout_args);
635                 *timeout_args = NULL;
636                 timeout_args++;
637             }
638
639             os_free(ss_ta);
640         }
641
642         /* Some cleanup */
643         while(i > 0)
644         {
645             cmd_args[i] = NULL;
646             i--;
647         }
648     }
649 }
650
651
652 #endif
653
654 /* EOF */