new upstream release (3.3.0); modify package compatibility for Stretch
[ossec-hids.git] / src / addagent / manage_keys.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 #include "manage_agents.h"
11 #include "os_crypto/md5/md5_op.h"
12 #include "external/cJSON/cJSON.h"
13 #include <stdlib.h>
14
15
16 /* Prototypes */
17 static char *trimwhitespace(char *str);
18
19
20 static char *trimwhitespace(char *str)
21 {
22     char *end;
23
24     /* Trim leading space */
25     while (isspace(*str)) {
26         str++;
27     }
28
29     if (*str == 0) { /* All spaces? */
30         return str;
31     }
32
33     /* Trim trailing space */
34     end = str + strlen(str) - 1;
35     while (end > str && isspace(*end)) {
36         end--;
37     }
38
39     /* Write new null terminator */
40     *(end + 1) = 0;
41
42     return str;
43 }
44
45 /* Import a key */
46 int k_import(const char *cmdimport)
47 {
48     FILE *fp;
49     const char *user_input;
50     char *b64_dec;
51
52     char *name;
53     char *ip;
54     char *tmp_key;
55
56     char line_read[FILE_SIZE + 1];
57
58     char auth_file_tmp[] = AUTH_FILE;
59     char *keys_file = basename_ex(auth_file_tmp);
60
61     char tmp_path[strlen(TMP_DIR) + 1 + strlen(keys_file) + 6 + 1];
62
63     snprintf(tmp_path, sizeof(tmp_path), "%s/%sXXXXXX", TMP_DIR, keys_file);
64
65     /* Parse user argument */
66     if (cmdimport) {
67         user_input = cmdimport;
68     } else {
69         printf(IMPORT_KEY);
70
71         user_input = getenv("OSSEC_AGENT_KEY");
72         if (user_input == NULL) {
73             user_input = read_from_user();
74         }
75     }
76
77     /* Quit */
78     if (strcmp(user_input, QUIT) == 0) {
79         return (0);
80     }
81
82     b64_dec = decode_base64(user_input);
83     if (b64_dec == NULL) {
84         printf(NO_KEY);
85         printf(PRESS_ENTER);
86         read_from_user();
87         return (0);
88     }
89
90     memset(line_read, '\0', FILE_SIZE + 1);
91     strncpy(line_read, b64_dec, FILE_SIZE);
92
93     name = strchr(b64_dec, ' ');
94     if (name && strlen(line_read) < FILE_SIZE) {
95         *name = '\0';
96         name++;
97         ip = strchr(name, ' ');
98         if (ip) {
99             *ip = '\0';
100             ip++;
101
102             tmp_key = strchr(ip, ' ');
103             if (!tmp_key) {
104                 printf(NO_KEY);
105                 free(b64_dec);
106                 return (0);
107             }
108             *tmp_key = '\0';
109
110             printf("\n");
111             printf(AGENT_INFO, b64_dec, name, ip);
112
113             while (1) {
114                 printf(ADD_CONFIRM);
115                 fflush(stdout);
116
117                 user_input = getenv("OSSEC_ACTION_CONFIRMED");
118                 if (user_input == NULL) {
119                     user_input = read_from_user();
120                 }
121
122                 if (user_input[0] == 'y' || user_input[0] == 'Y') {
123                     if (mkstemp_ex(tmp_path)) {
124                         ErrorExit(MKSTEMP_ERROR, ARGV0, tmp_path, errno, strerror(errno));
125                     }
126
127 #ifndef WIN32
128                     if (chmod(tmp_path, 0440) == -1) {
129                         if (unlink(tmp_path)) {
130                             verbose(DELETE_ERROR, ARGV0, tmp_path, errno, strerror(errno));
131                         }
132
133                         ErrorExit(CHMOD_ERROR, ARGV0, tmp_path, errno, strerror(errno));
134                     }
135 #endif
136
137                     fp = fopen(tmp_path, "w");
138                     if (!fp) {
139                         if (unlink(tmp_path)) {
140                             verbose(DELETE_ERROR, ARGV0, tmp_path, errno, strerror(errno));
141                         }
142
143                         ErrorExit(FOPEN_ERROR, ARGV0, tmp_path, errno, strerror(errno));
144                     }
145                     fprintf(fp, "%s\n", line_read);
146                     fclose(fp);
147
148                     if (rename_ex(tmp_path, KEYS_FILE)) {
149                         if (unlink(tmp_path)) {
150                             verbose(DELETE_ERROR, ARGV0, tmp_path, errno, strerror(errno));
151                         }
152
153                         ErrorExit(RENAME_ERROR, ARGV0, tmp_path, KEYS_FILE, errno, strerror(errno));
154                     }
155
156                     /* Remove sender counter */
157                     OS_RemoveCounter("sender");
158
159                     printf(ADDED);
160                     printf(PRESS_ENTER);
161                     read_from_user();
162                     restart_necessary = 1;
163
164                     free(b64_dec);
165                     return (1);
166                 } else { /* if(user_input[0] == 'n' || user_input[0] == 'N') */
167                     printf("%s", ADD_NOT);
168
169                     free(b64_dec);
170                     return (0);
171                 }
172             }
173         }
174     }
175
176     printf(NO_KEY);
177     printf(PRESS_ENTER);
178     read_from_user();
179
180     free(b64_dec);
181     return (0);
182 }
183
184 /* Extract base64 for a specific agent */
185 int k_extract(const char *cmdextract, int json_output)
186 {
187     FILE *fp;
188     char *user_input;
189     char *b64_enc;
190     char line_read[FILE_SIZE + 1];
191     char n_id[USER_SIZE + 1];
192     cJSON *json_root = NULL;
193
194     if (json_output)
195         json_root = cJSON_CreateObject();
196
197     if (cmdextract) {
198         user_input = strdup(cmdextract);
199         FormatID(user_input);
200
201         if (!IDExist(user_input)) {
202             if (json_output) {
203                 char buffer[1024];
204                 snprintf(buffer, 1023, "Invalid ID '%s' given. ID is not present", user_input);
205                 cJSON_AddNumberToObject(json_root, "error", 70);
206                 cJSON_AddStringToObject(json_root, "description", buffer);
207                 printf("%s", cJSON_PrintUnformatted(json_root));
208             } else
209                 printf(NO_ID, user_input);
210             exit(1);
211         }
212     } else {
213         if (!print_agents(0, 0, 0, 0)) {
214             printf(NO_AGENT);
215             printf(PRESS_ENTER);
216             read_from_user();
217             return (0);
218         }
219
220         while (1) {
221             printf(EXTRACT_KEY);
222             fflush(stdout);
223             user_input = read_from_user();
224
225             /* quit */
226             if (strcmp(user_input, QUIT) == 0) {
227                 return (0);
228             }
229
230             FormatID(user_input);
231
232             if (IDExist(user_input)) {
233                 break;
234             } else {
235                 printf(NO_ID, user_input);
236             }
237
238         }
239     }
240
241     /* Try to open the auth file */
242     char authfile[257];
243     extern int willchroot;
244     if(willchroot > 0) {
245         snprintf(authfile, 256, "%s", AUTH_FILE);       //XXX
246     } else {
247         const char *dir = DEFAULTDIR;
248         snprintf(authfile, 256, "%s/%s", dir, AUTH_FILE);       //XXX
249     }
250
251     fp = fopen(authfile, "r");
252     if (!fp) {
253         if (json_output) {
254             char buffer[1024];
255             snprintf(buffer, 1023, "Could not open file '%s' due to [(%d)-(%s)]", AUTH_FILE, errno, strerror(errno));
256             cJSON_AddNumberToObject(json_root, "error", 71);
257             cJSON_AddStringToObject(json_root, "description", buffer);
258             printf("%s", cJSON_PrintUnformatted(json_root));
259             exit(1);
260         } else
261             ErrorExit(FOPEN_ERROR, ARGV0, AUTH_FILE, errno, strerror(errno));
262
263
264     }
265
266     if (fsetpos(fp, &fp_pos)) {
267         if (json_output) {
268             cJSON_AddNumberToObject(json_root, "error", 72);
269             cJSON_AddStringToObject(json_root, "description", "Can not set fileposition");
270             printf("%s", cJSON_PrintUnformatted(json_root));
271         } else
272             merror("%s: Can not set fileposition.", ARGV0);
273         exit(1);
274     }
275
276     memset(n_id, '\0', USER_SIZE + 1);
277     strncpy(n_id, user_input, USER_SIZE - 1);
278
279     if (fgets(line_read, FILE_SIZE, fp) == NULL) {
280         if (json_output) {
281             cJSON_AddNumberToObject(json_root, "error", 73);
282             cJSON_AddStringToObject(json_root, "description", "Unable to handle keys file");
283             printf("%s", cJSON_PrintUnformatted(json_root));
284         } else
285             printf(ERROR_KEYS);
286
287
288         exit(1);
289     }
290     chomp(line_read);
291
292     b64_enc = encode_base64(strlen(line_read), line_read);
293     if (b64_enc == NULL) {
294         if (json_output) {
295             cJSON_AddNumberToObject(json_root, "error", 74);
296             cJSON_AddStringToObject(json_root, "description", "Unable to extract agent key");
297             printf("%s", cJSON_PrintUnformatted(json_root));
298         } else
299             printf(EXTRACT_ERROR);
300         exit(1);
301     }
302
303     if (json_output) {
304         cJSON_AddNumberToObject(json_root, "error", 0);
305         cJSON_AddStringToObject(json_root, "response", b64_enc);
306         printf("%s", cJSON_PrintUnformatted(json_root));
307     } else
308         printf(EXTRACT_MSG, n_id, b64_enc);
309
310
311     if (!cmdextract) {
312         printf("\n" PRESS_ENTER);
313         read_from_user();
314     }
315
316     free(b64_enc);
317     fclose(fp);
318
319     return (0);
320 }
321
322 /* Bulk generate client keys from file */
323 int k_bulkload(const char *cmdbulk)
324 {
325     int i = 1;
326     FILE *fp, *infp;
327     char str1[STR_SIZE + 1];
328     char str2[STR_SIZE + 1];
329
330     os_md5 md1;
331     os_md5 md2;
332     char line[FILE_SIZE + 1];
333     char name[FILE_SIZE + 1];
334     char id[FILE_SIZE + 1];
335     char ip[FILE_SIZE + 1];
336     char delims[] = ",";
337     char *token = NULL;
338
339     /* Check if we can open the input file */
340     printf("Opening: [%s]\n", cmdbulk);
341     infp = fopen(cmdbulk, "r");
342     if (!infp) {
343         perror("Failed.");
344         ErrorExit(FOPEN_ERROR, ARGV0, cmdbulk, errno, strerror(errno));
345     }
346
347     /* Check if we can open the auth_file */
348     char authfile[257];
349     if(willchroot > 0) {
350         snprintf(authfile, 256, "%s", AUTH_FILE);       //XXX
351     } else {
352         const char *dir = DEFAULTDIR;
353         snprintf(authfile, 256, "%s/%s", dir, AUTH_FILE);       //XXX
354     }
355
356     fp = fopen(authfile, "a");
357     if (!fp) {
358         ErrorExit(FOPEN_ERROR, ARGV0, authfile, errno, strerror(errno));
359     }
360     fclose(fp);
361
362     while (fgets(line, FILE_SIZE - 1, infp) != NULL) {
363         os_ip c_ip;
364         c_ip.ip = NULL;
365
366         if (1 >= strlen(trimwhitespace(line))) {
367             continue;
368         }
369
370         memset(ip, '\0', FILE_SIZE + 1);
371         token = strtok(line, delims);
372         strncpy(ip, trimwhitespace(token), FILE_SIZE - 1);
373
374         memset(name, '\0', FILE_SIZE + 1);
375         token = strtok(NULL, delims);
376         strncpy(name, trimwhitespace(token), FILE_SIZE - 1);
377
378 #ifndef WIN32
379         if (chmod(authfile, 0440) == -1) {
380             ErrorExit(CHMOD_ERROR, ARGV0, authfile, errno, strerror(errno));
381         }
382 #endif
383
384         /* Set time 2 */
385         time2 = time(0);
386
387         srandom_init();
388         rand1 = random();
389
390         /* Zero strings */
391         memset(str1, '\0', STR_SIZE + 1);
392         memset(str2, '\0', STR_SIZE + 1);
393
394         /* Check the name */
395         if (!OS_IsValidName(name)) {
396             printf(INVALID_NAME, name);
397             continue;
398         }
399
400         /* Search for name  -- no duplicates */
401         if (NameExist(name)) {
402             printf(ADD_ERROR_NAME, name);
403             continue;
404         }
405
406         if (!OS_IsValidIP(ip, &c_ip)) {
407             printf(IP_ERROR, ip);
408             continue;
409         }
410
411         /* Default ID */
412         i = MAX_AGENTS + 32512;
413         snprintf(id, 8, "%03d", i);
414         while (!IDExist(id)) {
415             i--;
416             snprintf(id, 8, "%03d", i);
417
418             /* No key present, use id 0 */
419             if (i <= 0) {
420                 i = 0;
421                 break;
422             }
423         }
424         snprintf(id, 8, "%03d", i + 1);
425
426         if (!OS_IsValidID(id)) {
427             printf(INVALID_ID, id);
428             goto cleanup;
429         }
430
431         /* Search for ID KEY  -- no duplicates */
432         if (IDExist(id)) {
433             printf(NO_DEFAULT, i + 1);
434             goto cleanup;
435         }
436
437         printf(AGENT_INFO, id, name, ip);
438         fflush(stdout);
439
440         time3 = time(0);
441         rand2 = random();
442
443         fp = fopen(authfile, "a");
444         if (!fp) {
445             ErrorExit(FOPEN_ERROR, ARGV0, KEYS_FILE, errno, strerror(errno));
446         }
447 #ifndef WIN32
448         if (chmod(authfile, 0440) == -1) {
449             ErrorExit(CHMOD_ERROR, ARGV0, authfile, errno, strerror(errno));
450         }
451 #endif
452
453         /* Random 1: Time took to write the agent information
454          * Random 2: Time took to choose the action
455          * Random 3: All of this + time + pid
456          * Random 4: MD5 all of this + the name, key and IP
457          * Random 5: Final key
458          */
459
460         snprintf(str1, STR_SIZE, "%d%s%d", (int)(time3 - time2), name, (int)rand1);
461         snprintf(str2, STR_SIZE, "%d%s%s%d", (int)(time2 - time1), ip, id, (int)rand2);
462
463         OS_MD5_Str(str1, md1);
464         OS_MD5_Str(str2, md2);
465
466         snprintf(str1, STR_SIZE, "%s%d%d%d", md1, (int)getpid(), (int)random(),
467                  (int)time3);
468         OS_MD5_Str(str1, md1);
469
470         fprintf(fp, "%s %s %s %s%s\n", id, name, c_ip.ip, md1, md2);
471         fclose(fp);
472
473         printf(AGENT_ADD, id);
474         restart_necessary = 1;
475
476 cleanup:
477         free(c_ip.ip);
478     };
479
480     fclose(infp);
481     return (0);
482 }