new upstream release (3.3.0); modify package compatibility for Stretch
[ossec-hids.git] / src / util / agent_control.c
1 /* Copyright (C) 2019 OSSEC Foundation
2  * All right reserved.
3  *
4  * This program is a free software; you can redistribute it
5  * and/or modify it under the terms of the GNU General Public
6  * License (version 2) as published by the FSF - Free Software
7  * Foundation
8  */
9
10 #include "addagent/manage_agents.h"
11 #include "sec.h"
12 #include "external/cJSON/cJSON.h"
13
14 #undef ARGV0
15 #define ARGV0 "agent_control"
16
17 /* Prototypes */
18 static void helpmsg(void) __attribute__((noreturn));
19
20 static void helpmsg()
21 {
22     printf("\nOSSEC HIDS %s: Control remote agents.\n", ARGV0);
23     printf("Available options:\n");
24     printf("\t-h          This help message.\n");
25     printf("\t-l          List available (active or not) agents.\n");
26     printf("\t-lc         List active agents.\n");
27     printf("\t-i <id>     Extracts information from an agent.\n");
28     printf("\t-R <id>     Restarts agent.\n");
29     printf("\t-r -a       Runs the integrity/rootkit checking on all agents now.\n");
30     printf("\t-r          Runs the integrity/rootkit checking on one agent now.\n\n");
31     printf("\t-b <ip>     Blocks the specified ip address.\n");
32     printf("\t-f <ar>     Used with -b, specifies which response to run.\n");
33     printf("\t-L          List available active responses.\n");
34     printf("\t-m          Show the limit of agents that can be added.\n");
35     printf("\t-s          Changes the output to CSV (comma delimited).\n");
36     printf("\t-j          Changes the output to JSON .\n");
37     printf("\t-u <id>     Used with -r and -b Specifies the agent to use.\n");
38     exit(1);
39 }
40
41 int main(int argc, char **argv)
42 {
43     const char *dir = DEFAULTDIR;
44     const char *group = GROUPGLOBAL;
45     const char *user = USER;
46     const char *agent_id = NULL;
47     const char *ip_address = NULL;
48     const char *ar = NULL;
49
50     cJSON *root = NULL;
51
52     int arq = 0;
53     gid_t gid;
54     uid_t uid;
55     int c = 0;
56     int restart_syscheck = 0;
57     int restart_all_agents = 0;
58     int list_agents = 0;
59     int info_agent = 0;
60     int agt_id = 0;
61     int active_only = 0;
62     int csv_output = 0;
63     int json_output = 0;
64     int list_responses = 0;
65     int end_time = 0;
66     int restart_agent = 0;
67     int show_max_agents = 0;
68
69     char shost[512];
70
71     keystore keys;
72
73     /* Set the name */
74     OS_SetName(ARGV0);
75
76     /* User arguments */
77     if (argc < 2) {
78         helpmsg();
79     }
80
81     while ((c = getopt(argc, argv, "VehdlLcsjarmu:i:b:f:R:")) != -1) {
82         switch (c) {
83             case 'V':
84                 print_version();
85                 break;
86             case 'h':
87                 helpmsg();
88                 break;
89             case 'd':
90                 nowDebug();
91                 break;
92             case 'L':
93                 list_responses = 1;
94                 break;
95             case 'e':
96                 end_time = 1;
97                 break;
98             case 'r':
99                 restart_syscheck = 1;
100                 break;
101             case 'l':
102                 list_agents++;
103                 break;
104             case 'm':
105                 show_max_agents++;
106                 break;
107             case 's':
108                 csv_output = 1;
109                 json_output = 0;
110                 break;
111             case 'j':
112                 json_output = 1;
113                 csv_output = 0;
114                 break;
115             case 'c':
116                 active_only++;
117                 break;
118             case 'i':
119                 info_agent++;
120                 //FALLTHRU
121             case 'u':
122                 if (!optarg) {
123                     merror("%s: -u needs an argument", ARGV0);
124                     helpmsg();
125                 }
126                 agent_id = optarg;
127                 break;
128             case 'b':
129                 if (!optarg) {
130                     merror("%s: -b needs an argument", ARGV0);
131                     helpmsg();
132                 }
133                 ip_address = optarg;
134                 break;
135             case 'f':
136                 if (!optarg) {
137                     merror("%s: -e needs an argument", ARGV0);
138                     helpmsg();
139                 }
140                 ar = optarg;
141                 break;
142             case 'R':
143                 if (!optarg) {
144                     merror("%s: -R needs an argument", ARGV0);
145                     helpmsg();
146                 }
147                 agent_id = optarg;
148                 restart_agent = 1;
149                 break;
150             case 'a':
151                 restart_all_agents = 1;
152                 break;
153             default:
154                 helpmsg();
155                 break;
156         }
157
158     }
159
160     /* Prepare JSON Structure */
161     if(json_output)
162         root = cJSON_CreateObject();
163
164     /* Get the group name */
165     gid = Privsep_GetGroup(group);
166     uid = Privsep_GetUser(user);
167     if (uid == (uid_t) - 1 || gid == (gid_t) - 1) {
168         ErrorExit(USER_ERROR, ARGV0, user, group);
169     }
170
171     /* Set the group */
172     if (Privsep_SetGroup(gid) < 0) {
173         ErrorExit(SETGID_ERROR, ARGV0, group, errno, strerror(errno));
174     }
175
176     /* Chroot to the default directory */
177     if (Privsep_Chroot(dir) < 0) {
178         ErrorExit(CHROOT_ERROR, ARGV0, dir, errno, strerror(errno));
179     }
180
181     /* Inside chroot now */
182     nowChroot();
183
184     /* Set the user */
185     if (Privsep_SetUser(uid) < 0) {
186         ErrorExit(SETUID_ERROR, ARGV0, user, errno, strerror(errno));
187     }
188
189     /* Get server hostname */
190     memset(shost, '\0', 512);
191     if (gethostname(shost, 512 - 1) != 0) {
192         strncpy(shost, "localhost", 32);
193         return (0);
194     }
195
196     /* List responses */
197     if (list_responses) {
198         FILE *fp;
199         if (!csv_output && !json_output) {
200             printf("\nOSSEC HIDS %s. Available active responses:\n", ARGV0);
201         }
202
203         fp = fopen(DEFAULTAR, "r");
204         if (fp) {
205             char buffer[256];
206
207             while (fgets(buffer, 255, fp) != NULL) {
208                 char *r_name;
209                 char *r_cmd;
210                 char *r_timeout;
211
212                 r_name = buffer;
213                 r_cmd = strchr(buffer, ' ');
214                 if (!r_cmd) {
215                     continue;
216                 }
217
218                 *r_cmd = '\0';
219                 r_cmd++;
220                 if (*r_cmd == '-') {
221                     r_cmd++;
222                 }
223                 if (*r_cmd == ' ') {
224                     r_cmd++;
225                 }
226
227                 r_timeout = strchr(r_cmd, ' ');
228                 if (!r_timeout) {
229                     continue;
230                 }
231                 *r_timeout = '\0';
232
233                 if (strcmp(r_name, "restart-ossec0") == 0) {
234                     continue;
235                 }
236                 printf("\n   Response name: %s, command: %s", r_name, r_cmd);
237             }
238
239             printf("\n\n");
240             fclose(fp);
241         } else {
242             printf("\n   No active response available.\n\n");
243         }
244
245         exit(0);
246     }
247
248     /* List available agents */
249     if (list_agents) {
250         cJSON *agents = NULL;
251         
252         if (!csv_output && !json_output) {
253             printf("\nOSSEC HIDS %s. List of available agents:",
254                    ARGV0);
255             printf("\n   ID: 000, Name: %s (server), IP: 127.0.0.1, Active/Local\n",
256                    shost);
257         } else if(json_output){
258                 cJSON *first = cJSON_CreateObject();
259                 agents = cJSON_CreateArray();
260                 
261                 if (!(first && root && agents))
262                     exit(1);
263                 
264                 cJSON_AddNumberToObject(root, "error", 0);
265                 cJSON_AddItemToObject(root, "response", agents);
266                 
267                 cJSON_AddStringToObject(first, "id", "000");
268                 cJSON_AddStringToObject(first, "name", shost);
269                 cJSON_AddStringToObject(first, "ip", "127.0.0.1");
270                 cJSON_AddStringToObject(first, "status", "Active");
271                 cJSON_AddItemToArray(agents, first);
272         } else {
273             printf("000,%s (server),127.0.0.1,Active/Local,\n", shost);
274         }
275         
276         print_agents(1, active_only, csv_output, agents);
277         // Closing JSON Object array
278         if(json_output) {
279             char *render = cJSON_PrintUnformatted(root);
280             printf("%s", render);
281             cJSON_Delete(root);
282             free(render);
283         } else
284             printf("\n");
285         exit(0);
286     }
287
288     /* Show limit of agents */
289
290     if (show_max_agents) {
291         if (json_output) {
292             cJSON *data = cJSON_CreateObject();
293
294             if (!(root && data)) {
295                 exit(1);
296             }
297
298             cJSON_AddNumberToObject(data, "max_agents", MAX_AGENTS);
299             cJSON_AddNumberToObject(root, "error", 0);
300             cJSON_AddItemToObject(root, "data", data);
301
302             char *render = cJSON_PrintUnformatted(root);
303             printf("%s", render);
304             cJSON_Delete(root);
305             free(render);
306         } else if (csv_output) {
307             printf("%d\n", MAX_AGENTS);
308         } else {
309             printf("Limit of agents: %d\n", MAX_AGENTS);
310         }
311
312         exit(0);
313     }
314
315     /* Check if the provided ID is valid */
316     if (agent_id != NULL) {
317         if (strcmp(agent_id, "000") != 0) {
318             OS_ReadKeys(&keys);
319
320             agt_id = OS_IsAllowedID(&keys, agent_id);
321             if (agt_id < 0) {
322                 if(json_output){
323                     cJSON_AddNumberToObject(root, "error", 40);
324                     cJSON_AddStringToObject(root, "description", "Invalid agent id");
325                     printf("%s",cJSON_PrintUnformatted(root));
326                     cJSON_Delete(root);
327                     exit(0);
328                 }else{
329                   printf("\n** Invalid agent id '%s'.\n", agent_id);
330                   helpmsg();
331                 }
332             }
333         } else {
334             /* server */
335             agt_id = -1;
336         }
337     }
338
339     /* Print information from an agent */
340     if (info_agent) {
341         int agt_status = 0;
342         char final_ip[IPSIZE + 4];
343         agent_info *agt_info;
344
345         final_ip[(sizeof final_ip) - 1] = '\0';
346         cJSON *response = cJSON_CreateObject();
347
348
349         if (!csv_output && !json_output) {
350             printf("\nOSSEC HIDS %s. Agent information:", ARGV0);
351         }
352
353         if (agt_id != -1) {
354             agt_status = get_agent_status(keys.keyentries[agt_id]->name,
355                                           keys.keyentries[agt_id]->ip->ip);
356
357             agt_info = get_agent_info(keys.keyentries[agt_id]->name,
358                                       keys.keyentries[agt_id]->ip->ip);
359
360             /* Getting full address/prefix length from ip. */
361             snprintf(final_ip, sizeof final_ip, "%s/%u",
362                      keys.keyentries[agt_id]->ip->ip,
363                      keys.keyentries[agt_id]->ip->prefixlength);
364
365             if (!csv_output && !json_output) {
366                 printf("\n   Agent ID:   %s\n", keys.keyentries[agt_id]->id);
367                 printf("   Agent Name: %s\n", keys.keyentries[agt_id]->name);
368                 printf("   IP address: %s\n", final_ip);
369                 printf("   Status:     %s\n\n", print_agent_status(agt_status));
370             }else if(json_output){
371                 cJSON_AddStringToObject(response, "id", keys.keyentries[agt_id]->id);
372                 cJSON_AddStringToObject(response, "name", keys.keyentries[agt_id]->name);
373                 cJSON_AddStringToObject(response, "ip", final_ip);
374                 cJSON_AddStringToObject(response, "status", print_agent_status(agt_status));
375
376             } else {
377                 printf("%s,%s,%s,%s,",
378                        keys.keyentries[agt_id]->id,
379                        keys.keyentries[agt_id]->name,
380                        final_ip,
381                        print_agent_status(agt_status));
382             }
383         } else {
384             agt_status = get_agent_status(NULL, NULL);
385             agt_info = get_agent_info(NULL, "127.0.0.1");
386
387             if (!csv_output && !json_output) {
388                 printf("\n   Agent ID:   000 \n");
389                 printf("   Agent Name: %s\n", shost);
390                 printf("   IP address: 127.0.0.1\n");
391                 printf("   Status:     %s/Local\n\n", print_agent_status(agt_status));
392             }else if(json_output){
393                 cJSON_AddStringToObject(response, "id", "000");
394                 cJSON_AddStringToObject(response, "name", shost);
395                 cJSON_AddStringToObject(response, "ip", "127.0.0.1");
396                 cJSON_AddStringToObject(response, "status", print_agent_status(agt_status));
397             } else {
398                 printf("000,%s,127.0.0.1,%s/Local,",
399                        shost,
400                        print_agent_status(agt_status));
401             }
402         }
403
404         if (!csv_output && !json_output) {
405             printf("   Operating system:    %s\n", agt_info->os);
406             printf("   Client version:      %s\n", agt_info->version);
407             printf("   Last keep alive:     %s\n\n", agt_info->last_keepalive);
408
409             if (end_time) {
410                 printf("   Syscheck last started at:  %s\n", agt_info->syscheck_time);
411                 printf("   Syscheck last ended   at:  %s\n", agt_info->syscheck_endtime);
412                 printf("   Rootcheck last started at: %s\n", agt_info->rootcheck_time);
413                 printf("   Rootcheck last ended   at: %s\n\n", agt_info->rootcheck_endtime);
414             } else {
415                 printf("   Syscheck last started  at: %s\n", agt_info->syscheck_time);
416                 printf("   Rootcheck last started at: %s\n", agt_info->rootcheck_time);
417             }
418         }else if(json_output){
419                 cJSON_AddStringToObject(response, "os", agt_info->os);
420                 cJSON_AddStringToObject(response, "version", agt_info->version);
421                 cJSON_AddStringToObject(response, "lastKeepAlive", agt_info->last_keepalive);
422                 cJSON_AddStringToObject(response, "syscheckTime", agt_info->syscheck_time);
423                 cJSON_AddStringToObject(response, "syscheckEndTime", end_time ? agt_info->syscheck_endtime : "");
424                 cJSON_AddStringToObject(response, "rootcheckTime", agt_info->rootcheck_time);
425                 cJSON_AddStringToObject(response, "rootcheckEndTime", end_time ? agt_info->rootcheck_endtime : "");
426
427         } else {
428             printf("%s,%s,%s,%s,%s,\n",
429                    agt_info->os,
430                    agt_info->version,
431                    agt_info->last_keepalive,
432                    agt_info->syscheck_time,
433                    agt_info->rootcheck_time);
434         }
435         if(json_output){
436             cJSON_AddNumberToObject(root, "error", 0);
437             cJSON_AddItemToObject(root, "response", response);
438             printf("%s",cJSON_PrintUnformatted(root));
439             cJSON_Delete(root);
440         }
441         exit(0);
442     }
443
444     /* Restart syscheck everywhere */
445     if (restart_all_agents && restart_syscheck) {
446         /* Connect to remoted */
447         debug1("%s: DEBUG: Connecting to remoted...", ARGV0);
448         arq = connect_to_remoted();
449         if (arq < 0) {
450             if(json_output){
451                 cJSON_AddNumberToObject(root, "error", 41);
452                 cJSON_AddStringToObject(root, "description", "Unable to connect to remoted");
453                 printf("%s",cJSON_PrintUnformatted(root));
454                 cJSON_Delete(root);
455             }else{
456                 printf("\n** Unable to connect to remoted.\n");
457             }
458             exit(1);
459         }
460         debug1("%s: DEBUG: Connected...", ARGV0);
461
462         /* Send restart message to all agents */
463         if (send_msg_to_agent(arq, HC_SK_RESTART, NULL, NULL) == 0) {
464             if(json_output){
465                 cJSON_AddNumberToObject(root, "error", 0);
466                 cJSON_AddStringToObject(root, "response", "Restarting Syscheck/Rootcheck on all agents");
467                 printf("%s",cJSON_PrintUnformatted(root));
468                 cJSON_Delete(root);
469             }else{
470                 printf("\nOSSEC HIDS %s: Restarting Syscheck/Rootcheck on all agents.",ARGV0);
471             }
472         } else {
473             if(json_output){
474                 cJSON_AddNumberToObject(root, "error", 42);
475                 cJSON_AddStringToObject(root, "description", "Unable to restart syscheck on all agents");
476                 printf("%s",cJSON_PrintUnformatted(root));
477                 cJSON_Delete(root);
478             }else{
479                 printf("\n** Unable to restart syscheck on all agents.\n");
480             }
481             exit(1);
482         }
483
484         exit(0);
485     }
486
487     if (restart_syscheck && agent_id) {
488         /* Restart on the server */
489         if (strcmp(agent_id, "000") == 0) {
490             os_set_restart_syscheck();
491             if(json_output){
492                 cJSON_AddNumberToObject(root, "error", 0);
493                 cJSON_AddStringToObject(root, "response", "Restarting Syscheck/Rootcheck locally");
494                 printf("%s",cJSON_PrintUnformatted(root));
495                 cJSON_Delete(root);
496             }else{
497                 printf("\nOSSEC HIDS %s: Restarting Syscheck/Rootcheck ""locally.\n", ARGV0);
498             }
499             exit(0);
500         }
501
502         /* Connect to remoted */
503         debug1("%s: DEBUG: Connecting to remoted...", ARGV0);
504         arq = connect_to_remoted();
505         if (arq < 0) {
506             if(json_output){
507                 cJSON_AddNumberToObject(root, "error", 43);
508                 cJSON_AddStringToObject(root, "description", "Unable to connect to remoted");
509                 printf("%s",cJSON_PrintUnformatted(root));
510                 cJSON_Delete(root);
511             }else{
512                 printf("\n** Unable to connect to remoted.\n");
513             }
514             exit(1);
515         }
516         debug1("%s: DEBUG: Connected...", ARGV0);
517
518         if (send_msg_to_agent(arq, HC_SK_RESTART, agent_id, NULL) == 0) {
519             if(json_output){
520                 cJSON_AddNumberToObject(root, "error", 0);
521                 cJSON_AddStringToObject(root, "response", "Restarting Syscheck/Rootcheck on agent");
522                 printf("%s",cJSON_PrintUnformatted(root));
523                 cJSON_Delete(root);
524             }else{
525                 printf("\nOSSEC HIDS %s: Restarting Syscheck/Rootcheck on agent: %s\n",ARGV0, agent_id);
526             }
527         } else {
528             if(json_output){
529                 cJSON_AddNumberToObject(root, "error", 44);
530                 cJSON_AddStringToObject(root, "description", "Unable to restart syscheck on agent");
531                 printf("%s",cJSON_PrintUnformatted(root));
532                 cJSON_Delete(root);
533             }else{
534                 printf("\n** Unable to restart syscheck on agent: %s\n", agent_id);
535             }
536             exit(1);
537         }
538
539         exit(0);
540     }
541
542     if (restart_agent && agent_id) {
543
544         /* Connect to remoted */
545         debug1("%s: DEBUG: Connecting to remoted...", ARGV0);
546         arq = connect_to_remoted();
547         if (arq < 0) {
548             if(json_output){
549                 cJSON_AddNumberToObject(root, "error", 45);
550                 cJSON_AddStringToObject(root, "description", "Unable to connect to remoted");
551                 printf("%s",cJSON_PrintUnformatted(root));
552                 cJSON_Delete(root);
553             }else{
554                 printf("\n** Unable to connect to remoted.\n");
555             }
556             exit(1);
557         }
558         debug1("%s: DEBUG: Connected...", ARGV0);
559
560         if (send_msg_to_agent(arq, "restart-ossec0", agent_id, "null") == 0) {
561             if(json_output){
562
563                 cJSON_AddNumberToObject(root, "error", 0);
564                 cJSON_AddStringToObject(root, "response", "Restarting agent");
565                 printf("%s",cJSON_PrintUnformatted(root));
566                 cJSON_Delete(root);
567             }else{
568                 printf("\nOSSEC HIDS %s: Restarting agent: %s\n",ARGV0, agent_id);
569             }
570         } else {
571             if(json_output){
572                 cJSON_AddNumberToObject(root, "error", 46);
573                 cJSON_AddStringToObject(root, "description", "Unable to restart agent");
574                 printf("%s",cJSON_PrintUnformatted(root));
575                 cJSON_Delete(root);
576             }else{
577                 printf("\n** Unable to restart agent: %s\n", agent_id);
578             }
579             exit(1);
580         }
581
582         exit(0);
583     }
584
585     /* Run active response on the specified agent id */
586     if (ip_address && ar && agent_id) {
587         /* Connect to remoted */
588         debug1("%s: DEBUG: Connecting to remoted...", ARGV0);
589         arq = connect_to_remoted();
590         if (arq < 0) {
591             printf("\n** Unable to connect to remoted.\n");
592             exit(1);
593         }
594         debug1("%s: DEBUG: Connected...", ARGV0);
595
596         if (send_msg_to_agent(arq, ar, agent_id, ip_address) == 0) {
597             printf("\nOSSEC HIDS %s: Running active response '%s' on: %s\n",
598                    ARGV0, ar, agent_id);
599         } else {
600             printf("\n** Unable to restart syscheck on agent: %s\n", agent_id);
601             exit(1);
602         }
603
604         exit(0);
605     }
606
607     printf("\n** Invalid argument combination.\n");
608     helpmsg();
609
610     return (0);
611 }
612