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