new upstream release (3.3.0); modify package compatibility for Stretch
[ossec-hids.git] / src / addagent / manage_agents.c
1 /* Copyright (C) 2019 OSSEC Foundation
2  * All rights 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 /* Manage agents tool
11  * Add/extract and remove agents from a server
12  */
13
14 #include "manage_agents.h"
15 #include "os_crypto/md5/md5_op.h"
16 #include "external/cJSON/cJSON.h"
17 #include <stdlib.h>
18
19 /* Global variables */
20 int restart_necessary;
21 time_t time1;
22 time_t time2;
23 time_t time3;
24 long int rand1;
25 long int rand2;
26
27
28 /* Remove spaces, newlines, etc from a string */
29 char *chomp(char *str)
30 {
31     char *tmp_str;
32     ssize_t size;
33
34     /* Remove spaces from the beginning */
35     while (*str == ' ' || *str == '\t') {
36         str++;
37     }
38
39     /* Remove any trailing newlines or \r */
40     do {
41         tmp_str = strchr(str, '\n');
42         if (tmp_str) {
43             *tmp_str = '\0';
44             continue;
45         }
46
47         tmp_str = strchr(str, '\r');
48         if (tmp_str) {
49             *tmp_str = '\0';
50         }
51     } while (tmp_str != NULL);
52
53     /* Remove spaces at the end of the string */
54     tmp_str = str;
55     size = (ssize_t) strlen(str) - 1;
56
57     while ((size >= 0) && (tmp_str[size] == ' ' || tmp_str[size] == '\t')) {
58         tmp_str[size] = '\0';
59         size--;
60     }
61
62     return (str);
63 }
64
65 int add_agent(int json_output)
66 {
67     int i = 1;
68     FILE *fp;
69     char str1[STR_SIZE + 1];
70     char str2[STR_SIZE + 1];
71
72     os_md5 md1;
73     os_md5 md2;
74
75     char *user_input;
76     char *_name;
77     char *_id;
78     char *_ip;
79
80     char name[FILE_SIZE + 1];
81     char id[FILE_SIZE + 1] = { '\0' };
82     char ip[FILE_SIZE + 1];
83     os_ip c_ip;
84     c_ip.ip = NULL;
85
86     char authfile[257];
87
88     if(willchroot > 0) {
89         snprintf(authfile, 256, "%s", AUTH_FILE);       //XXX
90     } else {
91         const char *dir = DEFAULTDIR;
92         snprintf(authfile, 256, "%s/%s", dir, AUTH_FILE);       //XXX
93     }
94
95
96     char *id_exist = NULL;
97     int force_antiquity;
98
99     /* Check if we can open the auth_file */
100     fp = fopen(authfile, "a");
101     if (!fp) {
102         if (json_output) {
103             char buffer[1024];
104             cJSON *json_root = cJSON_CreateObject();
105             snprintf(buffer, 1023, "Could not open file '%s' due to [(%d)-(%s)]", AUTH_FILE, errno, strerror(errno));
106             cJSON_AddNumberToObject(json_root, "error", 71);
107             cJSON_AddStringToObject(json_root, "description", buffer);
108             printf("%s", cJSON_PrintUnformatted(json_root));
109             exit(1);
110         } else
111             ErrorExit(FOPEN_ERROR, ARGV0, AUTH_FILE, errno, strerror(errno));
112     }
113     fclose(fp);
114
115
116 #ifndef WIN32
117     if (chmod(authfile, 0440) == -1) {
118         if (json_output) {
119             char buffer[1024];
120             cJSON *json_root = cJSON_CreateObject();
121             snprintf(buffer, 1023, "Could not chmod object '%s' due to [(%d)-(%s)]", AUTH_FILE, errno, strerror(errno));
122             cJSON_AddNumberToObject(json_root, "error", 71);
123             cJSON_AddStringToObject(json_root, "description", buffer);
124             printf("%s", cJSON_PrintUnformatted(json_root));
125             exit(1);
126         } else
127             ErrorExit(CHMOD_ERROR, ARGV0, AUTH_FILE, errno, strerror(errno));
128
129     }
130 #endif
131
132     /* Set time 2 */
133     time2 = time(0);
134
135     rand1 = random();
136
137     /* Zero strings */
138     memset(str1, '\0', STR_SIZE + 1);
139     memset(str2, '\0', STR_SIZE + 1);
140
141     if (!json_output)
142         printf(ADD_NEW);
143
144     /* Get the name */
145     memset(name, '\0', FILE_SIZE + 1);
146
147     do {
148         if (!json_output) {
149             printf(ADD_NAME);
150             fflush(stdout);
151         }
152         /* Read the agent's name from user environment. If it is invalid
153          * we should force user to provide a name from input device.
154          */
155         _name = getenv("OSSEC_AGENT_NAME");
156         if (_name == NULL || NameExist(_name) || !OS_IsValidName(_name)) {
157             if (json_output) {
158                 cJSON *json_root = cJSON_CreateObject();
159                 cJSON_AddNumberToObject(json_root, "error", 76);
160                 cJSON_AddStringToObject(json_root, "description", "Invalid name for agent");
161                 printf("%s", cJSON_PrintUnformatted(json_root));
162                 exit(1);
163             } else
164                 _name = read_from_user();
165
166         }
167
168         if (strcmp(_name, QUIT) == 0) {
169             goto cleanup;
170         }
171
172         strncpy(name, _name, FILE_SIZE - 1);
173
174         /* Check the name */
175         if (!OS_IsValidName(name)) {
176             printf(INVALID_NAME, name);
177         }
178
179         /* Search for name  -- no duplicates */
180         if (NameExist(name)) {
181             printf(ADD_ERROR_NAME, name);
182         }
183     } while (NameExist(name) || !OS_IsValidName(name));
184
185     /* Get IP */
186     memset(ip, '\0', FILE_SIZE + 1);
187
188     do {
189         if (!json_output) {
190             printf(ADD_IP);
191             fflush(stdout);
192         }
193
194
195         /* Read IP address from user's environment. If that IP is invalid,
196          * force user to provide IP from input device */
197         _ip = getenv("OSSEC_AGENT_IP");
198         if (_ip == NULL || !OS_IsValidIP(_ip, &c_ip)) {
199             if (json_output) {
200                 cJSON *json_root = cJSON_CreateObject();
201                 cJSON_AddNumberToObject(json_root, "error", 77);
202                 cJSON_AddStringToObject(json_root, "description", "Invalid IP for agent");
203                 printf("%s", cJSON_PrintUnformatted(json_root));
204                 exit(1);
205             } else
206                 _ip = read_from_user();
207
208         }
209
210         /* Quit */
211         if (strcmp(_ip, QUIT) == 0) {
212             goto cleanup;
213         }
214
215         strncpy(ip, _ip, FILE_SIZE - 1);
216
217         if (!OS_IsValidIP(ip, &c_ip)) {
218             printf(IP_ERROR, ip);
219             _ip = NULL;
220         } else if ((id_exist = IPExist(ip))) {
221             double antiquity = -1;
222
223             const char *env_remove_dup = getenv("OSSEC_REMOVE_DUPLICATED");
224
225             if (env_remove_dup) {
226                 force_antiquity = strtol(env_remove_dup, NULL, 10);
227                 antiquity = OS_AgentAntiquity(id_exist);
228             }
229
230             if (env_remove_dup && (antiquity >= force_antiquity || antiquity < 0)) {
231                 /* TODO: Save backup */
232 #ifdef REUSE_ID
233                 strncpy(id, id_exist, FILE_SIZE);
234 #endif
235                 OS_RemoveAgent(id_exist);
236             } else {
237                 /* TODO: Send alert */
238
239                 if (json_output) {
240                     cJSON *json_root = cJSON_CreateObject();
241                     cJSON_AddNumberToObject(json_root, "error", 79);
242                     cJSON_AddStringToObject(json_root, "description", "Duplicated IP for agent");
243                     printf("%s", cJSON_PrintUnformatted(json_root));
244                     exit(1);
245                 } else {
246                     printf(IP_DUP_ERROR, ip);
247                     _ip = NULL;
248                 }
249             }
250         }
251
252     } while (!_ip);
253
254     if (!*id) {
255         do {
256             /* Default ID */
257             i = MAX_AGENTS + 32512;
258             snprintf(id, 8, "%03d", i); //XXX
259             while (!IDExist(id)) {
260                 i--;
261                 snprintf(id, 8, "%03d", i);
262
263                 /* No key present, use id 0 */
264                 if (i <= 0) {
265                     i = 0;
266                     break;
267                 }
268             }
269             snprintf(id, 8, "%03d", i + 1);
270
271             /* Get ID */
272             if (!json_output) {
273                 printf(ADD_ID, id);
274                 fflush(stdout);
275             }
276
277
278             /* Get Agent ID from environment. If 0, use default ID. If null,
279              * get from user input. If value from environment is invalid,
280              * we force user to specify an ID from the terminal. Otherwise,
281              * our program goes to infinite loop.
282              */
283             _id = getenv("OSSEC_AGENT_ID");
284             if (_id == NULL || IDExist(_id) || !OS_IsValidID(_id)) {
285                 _id = read_from_user();
286             }
287
288             /* Quit */
289             if (strcmp(_id, QUIT) == 0) {
290                 goto cleanup;
291             }
292
293             if (_id[0] != '\0' && strcmp(_id, "0")) {
294                 strncpy(id, _id, FILE_SIZE - 1);
295             }
296
297             if (OS_IsValidID(id)) {
298                 FormatID(id);
299             } else {
300                 printf(INVALID_ID, id);
301             }
302
303             /* Search for ID KEY  -- no duplicates */
304             if (IDExist(id)) {
305                 printf(ADD_ERROR_ID, id);
306             }
307
308         } while (IDExist(id) || !OS_IsValidID(id));
309     }
310
311     if (!json_output) {
312         printf(AGENT_INFO, id, name, ip);
313         fflush(stdout);
314     }
315
316
317     do {
318         if (!json_output)
319             printf(ADD_CONFIRM);
320
321         /* Confirmation by an environment variable. The valid value is y/Y.
322          * If the user provides anything other string, it is considered as
323          * n/N; please note that the old code only accepts y/Y/n/N. So if
324          * the variable OSSEC_ACTION_CONFIRMED is 'foobar', the program will
325          * go into an infinite loop.
326          */
327         user_input = getenv("OSSEC_ACTION_CONFIRMED");
328         if (user_input == NULL) {
329             user_input = read_from_user();
330         }
331
332         /* If user accepts to add */
333         if (user_input[0] == 'y' || user_input[0] == 'Y') {
334             time3 = time(0);
335             rand2 = random();
336
337             fp = fopen(authfile, "a");
338             if (!fp) {
339                 if (json_output) {
340                     char buffer[1024];
341                     cJSON *json_root = cJSON_CreateObject();
342                     snprintf(buffer, 1023, "Could not open file '%s' due to [(%d)-(%s)]", KEYS_FILE, errno, strerror(errno));
343                     cJSON_AddNumberToObject(json_root, "error", 71);
344                     cJSON_AddStringToObject(json_root, "description", buffer);
345                     printf("%s", cJSON_PrintUnformatted(json_root));
346                     exit(1);
347                 } else
348                     ErrorExit(FOPEN_ERROR, ARGV0, KEYS_FILE, errno, strerror(errno));
349             }
350 #ifndef WIN32
351             if ((chmod(authfile, 0440)) != 0) {
352                 if(json_output) {
353                     char buffer[1024];
354                     snprintf(buffer, 1023, "%s: Could not chmod file %s due to [(%d)-(%s)]", ARGV0, authfile, errno, strerror(errno));
355                     cJSON *json_root = cJSON_CreateObject();
356                     cJSON_AddNumberToObject(json_root, "error", 76);
357                     cJSON_AddStringToObject(json_root, "description", buffer);
358                     printf("%s", cJSON_PrintUnformatted(json_root));
359                     exit(errno);
360                 } else {
361                     ErrorExit("%s: Cannot chmod %s: %s", ARGV0, authfile, strerror(errno));
362                 }
363             }
364 #endif
365
366             /* Random 1: Time took to write the agent information
367              * Random 2: Time took to choose the action
368              * Random 3: All of this + time + pid
369              * Random 4: Md5 all of this + the name, key and IP
370              * Random 5: Final key
371              */
372
373             snprintf(str1, STR_SIZE, "%d%s%d", (int)(time3 - time2), name, (int)rand1);
374             snprintf(str2, STR_SIZE, "%d%s%s%d", (int)(time2 - time1), ip, id, (int)rand2);
375
376             OS_MD5_Str(str1, md1);
377             OS_MD5_Str(str2, md2);
378
379             snprintf(str1, STR_SIZE, "%s%d%d%d", md1, (int)getpid(), (int)random(),
380                      (int)time3);
381             OS_MD5_Str(str1, md1);
382
383             fprintf(fp, "%s %s %s %s%s\n", id, name, c_ip.ip, md1, md2);
384             fclose(fp);
385
386             if (json_output) {
387                 char buffer[1024];
388                 cJSON *json_root = cJSON_CreateObject();
389                 snprintf(buffer, 1023, "Agent added with ID %s", id);
390                 cJSON_AddNumberToObject(json_root, "error", 0);
391                 cJSON_AddStringToObject(json_root, "response", buffer);
392                 printf("%s", cJSON_PrintUnformatted(json_root));
393             } else
394                 printf(AGENT_ADD, id);
395
396
397             restart_necessary = 1;
398             break;
399         } else { /* if(user_input[0] == 'n' || user_input[0] == 'N') */
400             printf(ADD_NOT);
401             break;
402         }
403     } while (1);
404
405     cleanup:
406     free(c_ip.ip);
407
408     return (0);
409 }
410
411 int remove_agent(int json_output)
412 {
413     FILE *fp;
414     char *user_input;
415     char u_id[FILE_SIZE + 1];
416     int id_exist;
417
418     u_id[FILE_SIZE] = '\0';
419
420     extern int willchroot;
421     char authfile[257];
422     if(willchroot > 0) {
423         snprintf(authfile, 256, "%s", AUTH_FILE);       //XXX
424     } else {
425         const char *dir = DEFAULTDIR;
426         snprintf(authfile, 256, "%s/%s", dir, AUTH_FILE);       //XXX
427     }
428         
429
430
431     if (!(json_output || print_agents(0, 0, 0, 0))) {
432         printf(NO_AGENT);
433         return (0);
434     }
435
436     do {
437         if (!json_output) {
438             printf(REMOVE_ID);
439             fflush(stdout);
440         }
441
442         user_input = getenv("OSSEC_AGENT_ID");
443         if (user_input == NULL) {
444             user_input = read_from_user();
445         } else if (!json_output) {
446             printf("%s\n", user_input);
447         }
448
449         if (strcmp(user_input, QUIT) == 0) {
450             return (0);
451         }
452
453         FormatID(user_input);
454         strncpy(u_id, user_input, FILE_SIZE);
455
456         id_exist = IDExist(user_input);
457
458         if (!id_exist) {
459             if (json_output) {
460                 char buffer[1024];
461                 cJSON *json_root = cJSON_CreateObject();
462                 snprintf(buffer, 1023, "Invalid ID '%s' given. ID is not present", user_input);
463                 cJSON_AddNumberToObject(json_root, "error", 78);
464                 cJSON_AddStringToObject(json_root, "description", buffer);
465                 printf("%s", cJSON_PrintUnformatted(json_root));
466                 exit(1);
467             } else
468                 printf(NO_ID, user_input);
469
470
471             /* Exit here if we are using environment variables
472              * and our ID does not exist
473              */
474             if (getenv("OSSEC_AGENT_ID")) {
475                 return (1);
476             }
477         }
478     } while (!id_exist);
479
480     do {
481         if (!json_output) {
482             printf(REMOVE_CONFIRM);
483             fflush(stdout);
484         }
485
486
487         user_input = getenv("OSSEC_ACTION_CONFIRMED");
488         if (user_input == NULL) {
489             user_input = read_from_user();
490         } else if (!json_output) {
491             printf("%s\n", user_input);
492         }
493
494         /* If user confirms */
495         if (user_input[0] == 'y' || user_input[0] == 'Y') {
496             /* Get full agent name */
497             char *full_name = getFullnameById(u_id);
498             if (!full_name) {
499                 if (json_output) {
500                     char buffer[1024];
501                     cJSON *json_root = cJSON_CreateObject();
502                     snprintf(buffer, 1023, "Invalid ID '%s' given. ID is not present", u_id);
503                     cJSON_AddNumberToObject(json_root, "error", 78);
504                     cJSON_AddStringToObject(json_root, "description", buffer);
505                     printf("%s", cJSON_PrintUnformatted(json_root));
506                     exit(1);
507                 } else
508                     printf(NO_ID, u_id);
509                 return (1);
510             }
511
512             fp = fopen(authfile, "r+");
513             if (!fp) {
514                 free(full_name);
515                 ErrorExit(FOPEN_ERROR, ARGV0, authfile, errno, strerror(errno));
516             }
517 #ifndef WIN32
518             chmod(authfile, 0440);
519 #endif
520
521             /* Remove the agent, but keep the id */
522             fsetpos(fp, &fp_pos);
523 #ifdef REUSE_ID
524             fprintf(fp, "#%s #*#*#*#*#*#*#*#*#*#*#", u_id);
525 #else
526             fprintf(fp, "%s #*#*#*#*#*#*#*#*#*#*#", u_id);
527 #endif
528
529             fclose(fp);
530
531             /* Remove counter for ID */
532             delete_agentinfo(full_name);
533             OS_RemoveCounter(u_id);
534             free(full_name);
535             full_name = NULL;
536
537             if (json_output) {
538                 cJSON *json_root = cJSON_CreateObject();
539                 cJSON_AddNumberToObject(json_root, "error", 0);
540                 cJSON_AddStringToObject(json_root, "response", "Agent removed");
541                 printf("%s", cJSON_PrintUnformatted(json_root));
542             } else
543                 printf(REMOVE_DONE, u_id);
544
545             restart_necessary = 1;
546             break;
547         } else { /* if(user_input[0] == 'n' || user_input[0] == 'N') */
548             printf(REMOVE_NOT);
549             break;
550         }
551     } while (1);
552
553     return (0);
554 }
555
556 int list_agents(int cmdlist)
557 {
558     if (!print_agents(0, 0, 0, 0)) {
559         printf(NO_AGENT);
560     }
561
562     printf("\n");
563     if (!cmdlist) {
564         printf(PRESS_ENTER);
565         read_from_user();
566     }
567
568     return (0);
569 }