new upstream release (3.3.0); modify package compatibility for Stretch
[ossec-hids.git] / src / os_crypto / shared / msgs.c
1 /* Copyright (C) 2009 Trend Micro Inc.
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 "shared.h"
11 #include "headers/sec.h"
12 #include "os_zlib/os_zlib.h"
13 #include "os_crypto/md5/md5_op.h"
14 #include "os_crypto/blowfish/bf_op.h"
15
16 /* Prototypes */
17 static void StoreSenderCounter(const keystore *keys, unsigned int global, unsigned int local) __attribute((nonnull));
18 static void StoreCounter(const keystore *keys, int id, unsigned int global, unsigned int local) __attribute((nonnull));
19 static char *CheckSum(char *msg) __attribute((nonnull));
20
21 /* Sending counts */
22 static unsigned int global_count = 0;
23 static unsigned int local_count  = 0;
24
25 /* Average compression rates */
26 static unsigned int evt_count = 0;
27 static unsigned int rcv_count = 0;
28 static size_t c_orig_size = 0;
29 static size_t c_comp_size = 0;
30
31 /* Static variables (read from define file) */
32 static unsigned int _s_comp_print = 0;
33 static unsigned int _s_recv_flush = 0;
34
35 static int _s_verify_counter = 1;
36
37
38 /* Read counters for each agent */
39 void OS_StartCounter(keystore *keys)
40 {
41     unsigned int i;
42     char rids_file[OS_FLSIZE + 1];
43
44     rids_file[OS_FLSIZE] = '\0';
45
46     debug1("%s: OS_StartCounter: keysize: %u", __local_name, keys->keysize);
47
48     /* Start receiving counter */
49     for (i = 0; i <= keys->keysize; i++) {
50         /* On i == keysize, we deal with the sender counter */
51         if (i == keys->keysize) {
52             snprintf(rids_file, OS_FLSIZE, "%s/%s",
53                      RIDS_DIR,
54                      SENDER_COUNTER);
55         } else {
56             snprintf(rids_file, OS_FLSIZE, "%s/%s",
57                      RIDS_DIR,
58                      keys->keyentries[i]->id);
59         }
60
61         keys->keyentries[i]->fp = fopen(rids_file, "r+");
62
63         /* If nothing is there, try to open as write only */
64         if (!keys->keyentries[i]->fp) {
65             keys->keyentries[i]->fp = fopen(rids_file, "w");
66             if (!keys->keyentries[i]->fp) {
67                 int my_error = errno;
68
69                 /* Just in case we run out of file descriptors */
70                 if ((i > 10) && (keys->keyentries[i - 1]->fp)) {
71                     fclose(keys->keyentries[i - 1]->fp);
72
73                     if (keys->keyentries[i - 2]->fp) {
74                         fclose(keys->keyentries[i - 2]->fp);
75                     }
76                 }
77
78                 merror("%s: Unable to open agent file. errno: %d",
79                        __local_name, my_error);
80                 ErrorExit(FOPEN_ERROR, __local_name, rids_file, errno, strerror(errno));
81             }
82         } else {
83             unsigned int g_c = 0, l_c = 0;
84             if (fscanf(keys->keyentries[i]->fp, "%u:%u", &g_c, &l_c) != 2) {
85                 if (i == keys->keysize) {
86                     verbose("%s: INFO: No previous sender counter.", __local_name);
87                 } else {
88                     verbose("%s: INFO: No previous counter available for '%s'.",
89                             __local_name,
90                             keys->keyentries[i]->name);
91                 }
92
93                 g_c = 0;
94                 l_c = 0;
95             }
96
97             if (i == keys->keysize) {
98                 verbose("%s: INFO: Assigning sender counter: %u:%u",
99                         __local_name, g_c, l_c);
100                 global_count = g_c;
101                 local_count = l_c;
102             } else {
103                 verbose("%s: INFO: Assigning counter for agent %s: '%u:%u'.",
104                         __local_name, keys->keyentries[i]->name, g_c, l_c);
105
106                 keys->keyentries[i]->global = g_c;
107                 keys->keyentries[i]->local = l_c;
108             }
109         }
110     }
111
112     debug2("%s: DEBUG: Stored counter.", __local_name);
113
114     /* Get counter values */
115     if (_s_recv_flush == 0) {
116         _s_recv_flush = (unsigned int) getDefine_Int("remoted",
117                         "recv_counter_flush",
118                         10, 999999);
119     }
120
121     /* Average printout values */
122     if (_s_comp_print == 0) {
123         _s_comp_print = (unsigned int) getDefine_Int("remoted",
124                         "comp_average_printout",
125                         10, 999999);
126     }
127
128
129     _s_verify_counter = getDefine_Int("remoted", "verify_msg_id" , 0, 1);
130 }
131
132 /* Remove the ID counter */
133 void OS_RemoveCounter(const char *id)
134 {
135     char rids_file[OS_FLSIZE + 1];
136     snprintf(rids_file, OS_FLSIZE, "%s/%s", RIDS_DIR, id);
137     unlink(rids_file);
138 }
139
140 /* Store sender counter */
141 static void StoreSenderCounter(const keystore *keys, unsigned int global, unsigned int local)
142 {
143     /* Write to the beginning of the file */
144     fseek(keys->keyentries[keys->keysize]->fp, 0, SEEK_SET);
145     fprintf(keys->keyentries[keys->keysize]->fp, "%u:%u:", global, local);
146 }
147
148 /* Store the global and local count of events */
149 static void StoreCounter(const keystore *keys, int id, unsigned int global, unsigned int local)
150 {
151     /* Write to the beginning of the file */
152     fseek(keys->keyentries[id]->fp, 0, SEEK_SET);
153     fprintf(keys->keyentries[id]->fp, "%u:%u:", global, local);
154 }
155
156 /* Verify the checksum of the message
157  * Returns NULL on error or the message on success
158  */
159 static char *CheckSum(char *msg)
160 {
161     os_md5 recvd_sum;
162     os_md5 checksum;
163
164     /* Better way */
165     strncpy(recvd_sum, msg, 32);
166     recvd_sum[32] = '\0';
167
168     msg += 32;
169
170     OS_MD5_Str(msg, checksum);
171     if (strncmp(checksum, recvd_sum, 32) != 0) {
172         return (NULL);
173     }
174
175     return (msg);
176 }
177
178 char *ReadSecMSG(keystore *keys, char *buffer, char *cleartext,
179                  int id, unsigned int buffer_size)
180 {
181     unsigned int msg_global = 0;
182     unsigned int msg_local = 0;
183     char *f_msg;
184
185     if (*buffer == ':') {
186         buffer++;
187     } else {
188         merror(ENCFORMAT_ERROR, __local_name, keys->keyentries[id]->ip->ip);
189         return (NULL);
190     }
191
192     /* Decrypt message */
193     if (!OS_BF_Str(buffer, cleartext, keys->keyentries[id]->key,
194                    buffer_size, OS_DECRYPT)) {
195         merror(ENCKEY_ERROR, __local_name, keys->keyentries[id]->ip->ip);
196         return (NULL);
197     }
198
199     /* Compressed */
200     else if (cleartext[0] == '!') {
201         cleartext[buffer_size] = '\0';
202         cleartext++;
203         buffer_size--;
204
205         /* Remove padding */
206         while (*cleartext == '!') {
207             cleartext++;
208             buffer_size--;
209         }
210
211         /* Uncompress */
212         if (!os_zlib_uncompress(cleartext, buffer, buffer_size, OS_MAXSTR)) {
213             merror(UNCOMPRESS_ERR, __local_name);
214             return (NULL);
215         }
216
217         /* Check checksum */
218         f_msg = CheckSum(buffer);
219         if (f_msg == NULL) {
220             merror(ENCSUM_ERROR, __local_name, keys->keyentries[id]->ip->ip);
221             return (NULL);
222         }
223
224         /* Remove random */
225         f_msg += 5;
226
227         /* Check count -- protect against replay attacks */
228         msg_global = (unsigned int) atoi(f_msg);
229         f_msg += 10;
230
231         /* Check for the right message format */
232         if (*f_msg != ':') {
233             merror(ENCFORMAT_ERROR, __local_name, keys->keyentries[id]->ip->ip);
234             return (NULL);
235         }
236         f_msg++;
237
238         msg_local = (unsigned int) atoi(f_msg);
239         f_msg += 5;
240
241         /* Return the message if we don't need to verify the counter */
242         if (!_s_verify_counter) {
243             /* Update current counts */
244             keys->keyentries[id]->global = msg_global;
245             keys->keyentries[id]->local = msg_local;
246             if (rcv_count >= _s_recv_flush) {
247                 StoreCounter(keys, id, msg_global, msg_local);
248                 rcv_count = 0;
249             }
250             rcv_count++;
251             return (f_msg);
252         }
253
254
255         if ((msg_global > keys->keyentries[id]->global) ||
256                 ((msg_global == keys->keyentries[id]->global) &&
257                  (msg_local > keys->keyentries[id]->local))) {
258             /* Update current counts */
259             keys->keyentries[id]->global = msg_global;
260             keys->keyentries[id]->local = msg_local;
261
262             if (rcv_count >= _s_recv_flush) {
263                 StoreCounter(keys, id, msg_global, msg_local);
264                 rcv_count = 0;
265             }
266             rcv_count++;
267             return (f_msg);
268         }
269
270         /* Check if it is a duplicated message */
271         if (msg_global == keys->keyentries[id]->global) {
272             /* Warn about duplicated messages */
273             merror("%s: WARN: Duplicate error:  global: %u, local: %u, "
274                "saved global: %u, saved local:%u",
275                __local_name,
276                msg_global,
277                msg_local,
278                keys->keyentries[id]->global,
279                keys->keyentries[id]->local);
280
281             merror(ENCTIME_ERROR, __local_name, keys->keyentries[id]->name);
282             return (NULL);
283         }
284     }
285
286     /* Old format */
287     else if (cleartext[0] == ':') {
288         unsigned int msg_count;
289         unsigned int msg_time;
290
291         /* Close string */
292         cleartext[buffer_size] = '\0';
293
294         /* Check checksum */
295         cleartext++;
296         f_msg = CheckSum(cleartext);
297         if (f_msg == NULL) {
298             merror(ENCSUM_ERROR, __local_name, keys->keyentries[id]->ip->ip);
299             return (NULL);
300         }
301
302         /* Check time -- protect against replay attacks */
303         msg_time = (unsigned int) atoi(f_msg);
304         f_msg += 11;
305
306         msg_count = (unsigned int) atoi(f_msg);
307         f_msg += 5;
308
309
310         /* Return the message if we don't need to verify the counter */
311         if (!_s_verify_counter) {
312             /* Update current counts */
313             keys->keyentries[id]->global = msg_time;
314             keys->keyentries[id]->local = msg_local;
315
316             f_msg = strchr(f_msg, ':');
317             if (f_msg) {
318                 f_msg++;
319                 return (f_msg);
320             } else {
321                 merror(ENCFORMAT_ERROR, __local_name, keys->keyentries[id]->ip->ip);
322                 return (NULL);
323             }
324         }
325
326
327         if ((msg_time > keys->keyentries[id]->global) ||
328                 ((msg_time == keys->keyentries[id]->global) &&
329                  (msg_count > keys->keyentries[id]->local))) {
330             /* Update current time and count */
331             keys->keyentries[id]->global = msg_time;
332             keys->keyentries[id]->local = msg_count;
333
334             f_msg = strchr(f_msg, ':');
335             if (f_msg) {
336                 f_msg++;
337                 return (f_msg);
338             } else {
339                 merror(ENCFORMAT_ERROR, __local_name, keys->keyentries[id]->ip->ip);
340                 return (NULL);
341             }
342         }
343
344         /* Check if it is a duplicated message */
345         if ((msg_count == keys->keyentries[id]->local) &&
346                 (msg_time == keys->keyentries[id]->global)) {
347             return (NULL);
348         }
349
350         /* Warn about duplicated message */
351         merror("%s: WARN: Duplicate error:  msg_count: %u, time: %u, "
352                "saved count: %u, saved_time:%u",
353                __local_name,
354                msg_count,
355                msg_time,
356                keys->keyentries[id]->local,
357                keys->keyentries[id]->global);
358
359         merror(ENCTIME_ERROR, __local_name, keys->keyentries[id]->name);
360         return (NULL);
361     }
362
363     merror(ENCFORMAT_ERROR, __local_name, keys->keyentries[id]->ip->ip);
364     return (NULL);
365 }
366
367 /* Create an encrypted message
368  * Returns the size
369  */
370 size_t CreateSecMSG(const keystore *keys, const char *msg, char *msg_encrypted, unsigned int id)
371 {
372     size_t bfsize;
373     size_t msg_size;
374     unsigned long int cmp_size;
375     u_int16_t rand1;
376     char _tmpmsg[OS_MAXSTR + 2];
377     char _finmsg[OS_MAXSTR + 2];
378     os_md5 md5sum;
379
380     msg_size = strlen(msg);
381
382     /* Check for invalid msg sizes */
383     if ((msg_size > (OS_MAXSTR - OS_HEADER_SIZE)) || (msg_size < 1)) {
384         merror(ENCSIZE_ERROR, __local_name, msg);
385         return (0);
386     }
387
388     /* Random number, take only 5 chars ~= 2^16=65536*/
389     rand1 = (u_int16_t) random();
390
391     _tmpmsg[OS_MAXSTR + 1] = '\0';
392     _finmsg[OS_MAXSTR + 1] = '\0';
393     msg_encrypted[OS_MAXSTR] = '\0';
394
395     /* Increase local and global counters */
396     if (local_count >= 9997) {
397         local_count = 0;
398         global_count++;
399     }
400     local_count++;
401
402     snprintf(_tmpmsg, OS_MAXSTR, "%05hu%010u:%04u:%s",
403              rand1, global_count, local_count,
404              msg);
405
406     /* Generate MD5 of the unencrypted string */
407     OS_MD5_Str(_tmpmsg, md5sum);
408
409     /* Generate final msg to be compressed */
410     snprintf(_finmsg, OS_MAXSTR, "%s%s", md5sum, _tmpmsg);
411     msg_size = strlen(_finmsg);
412
413     /* Compress the message
414      * We assign the first 8 bytes for padding
415      */
416     cmp_size = os_zlib_compress(_finmsg, _tmpmsg + 8, msg_size, OS_MAXSTR - 12);
417     if (!cmp_size) {
418         merror(COMPRESS_ERR, __local_name, _finmsg);
419         return (0);
420     }
421     cmp_size++;
422
423     /* Pad the message (needs to be div by 8) */
424     bfsize = 8 - (cmp_size % 8);
425     if (bfsize == 8) {
426         bfsize = 0;
427     }
428
429     _tmpmsg[0] = '!';
430     _tmpmsg[1] = '!';
431     _tmpmsg[2] = '!';
432     _tmpmsg[3] = '!';
433     _tmpmsg[4] = '!';
434     _tmpmsg[5] = '!';
435     _tmpmsg[6] = '!';
436     _tmpmsg[7] = '!';
437
438     cmp_size += bfsize;
439
440     /* Get average sizes */
441     c_orig_size += msg_size;
442     c_comp_size += cmp_size;
443     if (evt_count > _s_comp_print) {
444         verbose("%s: INFO: Event count after '%u': %lu->%lu (%lu%%)", __local_name,
445                 evt_count,
446                 (unsigned long)c_orig_size,
447                 (unsigned long)c_comp_size,
448                 (unsigned long)((c_comp_size * 100) / c_orig_size));
449         evt_count = 0;
450         c_orig_size = 0;
451         c_comp_size = 0;
452     }
453     evt_count++;
454
455     /* If the IP is dynamic (not single host), append agent ID to the message */
456     if (!isSingleHost(keys->keyentries[id]->ip) && isAgent) {
457         snprintf(msg_encrypted, 16, "!%s!:", keys->keyentries[id]->id);
458         msg_size = strlen(msg_encrypted);
459     } else {
460         /* Set beginning of the message */
461         msg_encrypted[0] = ':';
462         msg_size = 1;
463     }
464
465     /* msg_size is the amount of non-encrypted message appended to the buffer
466      * On dynamic IPs, it will include the agent ID
467      */
468
469     /* Encrypt everything */
470     OS_BF_Str(_tmpmsg + (7 - bfsize), msg_encrypted + msg_size,
471               keys->keyentries[id]->key,
472               (long) cmp_size,
473               OS_ENCRYPT);
474
475     /* Store before leaving */
476     StoreSenderCounter(keys, global_count, local_count);
477
478     return (cmp_size + msg_size);
479 }
480