new upstream release (3.3.0); modify package compatibility for Stretch
[ossec-hids.git] / src / util / syscheck_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
13 #undef ARGV0
14 #define ARGV0 "syscheck_control"
15
16 /* Prototypes */
17 static void helpmsg(void) __attribute__((noreturn));
18
19
20 static void helpmsg()
21 {
22     printf("\nOSSEC HIDS %s: Manages the integrity checking database.\n",
23            ARGV0);
24     printf("Available options:\n");
25     printf("\t-h          This help message.\n");
26     printf("\t-l          List available (active or inactive) agents.\n");
27     printf("\t-lc         List only active agents.\n");
28     printf("\t-u <id>     Updates (clears) the database for the agent.\n");
29     printf("\t-u all      Updates (clears) the database for all agents.\n");
30     printf("\t-i <id>     List modified files for the agent.\n");
31     printf("\t-r -i <id>  List modified registry entries for the agent "
32            "(Windows only).\n");
33     printf("\t-f <file>   Prints information about a modified file.\n");
34     printf("\t            Must be used with -i <id>.\n");
35     printf("\t-z          Used with -f, zeroes the auto-ignore counter.\n");
36     printf("\t-d          Used with -f, ignores that file.\n");
37     printf("\t-s          Changes the output to CSV (comma delimited).\n");
38     printf("\t-j          Changes the output to JSON.\n");
39     printf("\n");
40     printf("\tExamples:\n");
41     printf("\n");
42     printf("\t'Show information about /etc/passwd from agent with ID 019'\n");
43     printf("\t%s -i 019 -f /etc/passwd\n", ARGV0);
44     exit(1);
45 }
46
47 int main(int argc, char **argv)
48 {
49     const char *dir = DEFAULTDIR;
50     const char *group = GROUPGLOBAL;
51     const char *user = USER;
52     const char *agent_id = NULL;
53     const char *fname = NULL;
54
55     gid_t gid;
56     uid_t uid;
57     int c = 0, info_agent = 0, update_syscheck = 0,
58         list_agents = 0, zero_counter = 0,
59         registry_only = 0;
60     int active_only = 0, csv_output = 0, json_output = 0;
61
62     char shost[512];
63     cJSON *json_root;
64
65     /* Set the name */
66     OS_SetName(ARGV0);
67
68     /* User arguments */
69     if (argc < 2) {
70         helpmsg();
71     }
72
73     while ((c = getopt(argc, argv, "VhzrDdlcsju:i:f:")) != -1) {
74         switch (c) {
75             case 'V':
76                 print_version();
77                 break;
78             case 'h':
79                 helpmsg();
80                 break;
81             case 'D':
82                 nowDebug();
83                 break;
84             case 'l':
85                 list_agents++;
86                 break;
87             case 'z':
88                 zero_counter = 1;
89                 break;
90             case 'd':
91                 zero_counter = 2;
92                 break;
93             case 's':
94                 csv_output = 1;
95                 break;
96             case 'j':
97                 json_output = 1;
98                 break;
99             case 'c':
100                 active_only++;
101                 break;
102             case 'r':
103                 registry_only = 1;
104                 break;
105             case 'i':
106                 info_agent++;
107                 if (!optarg) {
108                     merror("%s: -u needs an argument", ARGV0);
109                     helpmsg();
110                 }
111                 agent_id = optarg;
112                 break;
113             case 'f':
114                 if (!optarg) {
115                     merror("%s: -u needs an argument", ARGV0);
116                     helpmsg();
117                 }
118                 fname = optarg;
119                 break;
120             case 'u':
121                 if (!optarg) {
122                     merror("%s: -u needs an argument", ARGV0);
123                     helpmsg();
124                 }
125                 agent_id = optarg;
126                 update_syscheck = 1;
127                 break;
128             default:
129                 helpmsg();
130                 break;
131         }
132     }
133
134     /* Get the group name */
135     gid = Privsep_GetGroup(group);
136     uid = Privsep_GetUser(user);
137     if (uid == (uid_t) - 1 || gid == (gid_t) - 1) {
138         ErrorExit(USER_ERROR, ARGV0, user, group);
139     }
140
141     /* Set the group */
142     if (Privsep_SetGroup(gid) < 0) {
143         ErrorExit(SETGID_ERROR, ARGV0, group, errno, strerror(errno));
144     }
145
146     /* Chroot to the default directory */
147     if (Privsep_Chroot(dir) < 0) {
148         ErrorExit(CHROOT_ERROR, ARGV0, dir, errno, strerror(errno));
149     }
150
151     /* Inside chroot now */
152     nowChroot();
153
154     /* Set the user */
155     if (Privsep_SetUser(uid) < 0) {
156         ErrorExit(SETUID_ERROR, ARGV0, user, errno, strerror(errno));
157     }
158
159     /* Get server hostname */
160     memset(shost, '\0', 512);
161     if (gethostname(shost, 512 - 1) != 0) {
162         strncpy(shost, "localhost", 32);
163         return (0);
164     }
165
166     if (json_output)
167         json_root = cJSON_CreateObject();
168
169     /* List available agents */
170     if (list_agents) {
171         cJSON *json_agents = NULL;
172
173         if (json_output) {
174             cJSON *first = cJSON_CreateObject();
175             json_agents = cJSON_CreateArray();
176             cJSON_AddNumberToObject(json_root, "error", 0);
177             cJSON_AddStringToObject(first, "id", "000");
178             cJSON_AddStringToObject(first, "name", shost);
179             cJSON_AddStringToObject(first, "ip", "127.0.0.1");
180             cJSON_AddStringToObject(first, "status", "Active");
181             cJSON_AddItemToArray(json_agents, first);
182         } else if (csv_output)
183             printf("000,%s (server),127.0.0.1,Active/Local,\n", shost);
184         else {
185             printf("\nOSSEC HIDS %s. List of available agents:", ARGV0);
186             printf("\n   ID: 000, Name: %s (server), IP: 127.0.0.1, "
187                    "Active/Local\n", shost);
188         }
189
190         print_agents(1, active_only, csv_output, json_agents);
191
192         if (json_output) {
193             cJSON_AddItemToObject(json_root, "response", json_agents);
194             printf("%s", cJSON_PrintUnformatted(json_root));
195         } else
196             printf("\n");
197
198         exit(0);
199     }
200
201     /* Update syscheck database */
202     if (update_syscheck) {
203         /* Clean all agents (and server) db */
204         if (strcmp(agent_id, "all") == 0) {
205             DIR *sys_dir;
206             struct dirent *entry;
207
208             sys_dir = opendir(SYSCHECK_DIR);
209             if (!sys_dir) {
210                 if (json_output) {
211                     char buffer[1024];
212                     cJSON_AddNumberToObject(json_root, "error", 31);
213                     snprintf(buffer, 1023, "%s: Unable to open: '%s'", ARGV0, SYSCHECK_DIR);
214                     cJSON_AddStringToObject(json_root, "description", buffer);
215                     printf("%s", cJSON_PrintUnformatted(json_root));
216                     exit(1);
217                 } else
218                     ErrorExit("%s: Unable to open: '%s'", ARGV0, SYSCHECK_DIR);
219             }
220
221             while ((entry = readdir(sys_dir)) != NULL) {
222                 FILE *fp;
223                 char full_path[OS_MAXSTR + 1];
224
225                 /* Do not even attempt to delete . and .. :) */
226                 if ((strcmp(entry->d_name, ".") == 0) ||
227                         (strcmp(entry->d_name, "..") == 0)) {
228                     continue;
229                 }
230
231                 snprintf(full_path, OS_MAXSTR, "%s/%s", SYSCHECK_DIR,
232                          entry->d_name);
233
234                 fp = fopen(full_path, "w");
235                 if (fp) {
236                     fclose(fp);
237                 }
238                 if (entry->d_name[0] == '.') {
239                     unlink(full_path);
240                 }
241             }
242
243             closedir(sys_dir);
244
245             if (json_output) {
246                 cJSON_AddNumberToObject(json_root, "error", 0);
247                 cJSON_AddStringToObject(json_root, "response", "Integrity check database updated");
248                 printf("%s", cJSON_PrintUnformatted(json_root));
249             } else
250                 printf("\n** Integrity check database updated.\n\n");
251
252             exit(0);
253         }
254
255         else if ((strcmp(agent_id, "000") == 0) ||
256                  (strcmp(agent_id, "local") == 0)) {
257             char final_dir[1024];
258             FILE *fp;
259             snprintf(final_dir, 1020, "/%s/syscheck", SYSCHECK_DIR);
260
261             fp = fopen(final_dir, "w");
262             if (fp) {
263                 fclose(fp);
264             }
265             unlink(final_dir);
266
267
268             /* Deleting cpt file */
269             snprintf(final_dir, 1020, "/%s/.syscheck.cpt", SYSCHECK_DIR);
270
271             fp = fopen(final_dir, "w");
272             if (fp) {
273                 fclose(fp);
274             }
275             unlink(final_dir);
276
277             if (json_output) {
278                 cJSON_AddNumberToObject(json_root, "error", 0);
279                 cJSON_AddStringToObject(json_root, "response", "Integrity check database updated");
280                 printf("%s", cJSON_PrintUnformatted(json_root));
281             } else
282                 printf("\n** Integrity check database updated.\n\n");
283
284             exit(0);
285         }
286
287         /* Database from remote agents */
288         else {
289             int i;
290             keystore keys;
291
292             OS_ReadKeys(&keys);
293
294             i = OS_IsAllowedID(&keys, agent_id);
295             if (i < 0) {
296                 if (json_output) {
297                     char buffer[1024];
298                     cJSON_AddNumberToObject(json_root, "error", 33);
299                     snprintf(buffer, 1023, "Invalid agent id '%s'.", agent_id);
300                     cJSON_AddStringToObject(json_root, "description", buffer);
301                     printf("%s", cJSON_PrintUnformatted(json_root));
302                     exit(1);
303                 } else {
304                     printf("\n** Invalid agent id '%s'.\n", agent_id);
305                     helpmsg();
306                 }
307             }
308
309             /* Delete syscheck */
310             delete_syscheck(keys.keyentries[i]->name,
311                             keys.keyentries[i]->ip->ip, 0);
312
313             if (json_output) {
314                 cJSON_AddNumberToObject(json_root, "error", 0);
315                 cJSON_AddStringToObject(json_root, "response", "Integrity check database updated");
316                 printf("%s", cJSON_PrintUnformatted(json_root));
317             } else
318                 printf("\n** Integrity check database updated.\n\n");
319
320             exit(0);
321         }
322     }
323
324     /* Print information from an agent */
325     if (info_agent) {
326         int i;
327         char final_ip[IPSIZE + 4];
328         keystore keys;
329         cJSON *json_entries = NULL;
330
331         if (json_output)
332             json_entries = cJSON_CreateArray();
333
334         if ((strcmp(agent_id, "000") == 0) ||
335                 (strcmp(agent_id, "local") == 0)) {
336             if (!(csv_output || json_output)) {
337                 printf("\nIntegrity checking changes for local system '%s - %s':\n",
338                        shost, "127.0.0.1");
339                 if (fname) {
340                     printf("Detailed information for entries matching: '%s'\n",
341                            fname);
342                 }
343             }
344
345             print_syscheck(NULL, NULL, fname, 0, 0, csv_output, json_entries, zero_counter);
346         } else if (strchr(agent_id, '@')) {
347             if (fname && ! (csv_output || json_output)) {
348                 printf("Detailed information for entries matching: '%s'\n",
349                        fname);
350             }
351             print_syscheck(agent_id, NULL, fname, registry_only, 0,
352                            csv_output, json_entries, zero_counter);
353         } else {
354
355             OS_ReadKeys(&keys);
356
357             i = OS_IsAllowedID(&keys, agent_id);
358             if (i < 0) {
359                 if (json_output) {
360                     char buffer[1024];
361                     cJSON_AddNumberToObject(json_root, "error", 32);
362                     snprintf(buffer, 1023, "Invalid agent id '%s'.", agent_id);
363                     cJSON_AddStringToObject(json_root, "description", buffer);
364                     printf("%s", cJSON_PrintUnformatted(json_root));
365                     exit(1);
366                 } else {
367                     printf("\n** Invalid agent id '%s'.\n", agent_id);
368                     helpmsg();
369                 }
370             }
371
372             /* Getting full address/prefix length from ip. */
373             final_ip[(sizeof final_ip) - 1] = '\0';
374             snprintf(final_ip, sizeof final_ip, "%s/%u",
375                      keys.keyentries[i]->ip->ip,
376                      keys.keyentries[i]->ip->prefixlength);
377
378             if (!(csv_output || json_output)) {
379                 if (registry_only) {
380                     printf("\nIntegrity changes for 'Windows Registry' of"
381                            " agent '%s (%s) - %s':\n",
382                            keys.keyentries[i]->name, keys.keyentries[i]->id,
383                            final_ip);
384                 } else {
385                     printf("\nIntegrity changes for agent "
386                            "'%s (%s) - %s':\n",
387                            keys.keyentries[i]->name, keys.keyentries[i]->id,
388                            final_ip);
389                 }
390
391                 if (fname) {
392                     printf("Detailed information for entries matching: '%s'\n",
393                            fname);
394                 }
395             }
396
397             print_syscheck(keys.keyentries[i]->name, keys.keyentries[i]->ip->ip, fname,
398                            registry_only, 0, csv_output, json_entries, zero_counter);
399         }
400
401         if (json_output) {
402             cJSON_AddNumberToObject(json_root, "error", 0);
403             cJSON_AddItemToObject(json_root, "response", json_entries);
404             printf("%s", cJSON_PrintUnformatted(json_root));
405         }
406
407         exit(0);
408     }
409
410     if (json_output) {
411         cJSON_AddNumberToObject(json_root, "error", 30);
412         cJSON_AddStringToObject(json_root, "description", "Invalid argument combination");
413         printf("%s", cJSON_PrintUnformatted(json_root));
414         exit(1);
415     } else {
416         printf("\n** Invalid argument combination.\n");
417         helpmsg();
418     }
419
420     return (0);
421 }