Branch data Line data Source code
1 : : /**
2 : : * \file server/replay_cache.c
3 : : *
4 : : * \brief Cache packets for replay attack detection
5 : : *
6 : : * Provides the functions to check for possible replay attacks
7 : : * by using a cache of previously seen digests. This cache is a
8 : : * simple file by default, but can be made to use a dbm solution
9 : : * (ndbm or gdbm in ndbm compatibility mode) file to store the digest
10 : : * of a previously received SPA packets.
11 : : */
12 : :
13 : : /* Fwknop is developed primarily by the people listed in the file 'AUTHORS'.
14 : : * Copyright (C) 2009-2015 fwknop developers and contributors. For a full
15 : : * list of contributors, see the file 'CREDITS'.
16 : : *
17 : : * License (GNU General Public License):
18 : : *
19 : : * This program is free software; you can redistribute it and/or
20 : : * modify it under the terms of the GNU General Public License
21 : : * as published by the Free Software Foundation; either version 2
22 : : * of the License, or (at your option) any later version.
23 : : *
24 : : * This program is distributed in the hope that it will be useful,
25 : : * but WITHOUT ANY WARRANTY; without even the implied warranty of
26 : : * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
27 : : * GNU General Public License for more details.
28 : : *
29 : : * You should have received a copy of the GNU General Public License
30 : : * along with this program; if not, write to the Free Software
31 : : * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
32 : : * USA
33 : : *
34 : : *****************************************************************************
35 : : */
36 : : #include "replay_cache.h"
37 : : #include "log_msg.h"
38 : : #include "fwknopd_errors.h"
39 : : #include "utils.h"
40 : :
41 : : #include <sys/stat.h>
42 : : #include <fcntl.h>
43 : : #include <time.h>
44 : :
45 : : #if HAVE_LIBGDBM
46 : : #include <gdbm.h>
47 : :
48 : : #define MY_DBM_FETCH(d, k) gdbm_fetch(d, k)
49 : : #define MY_DBM_STORE(d, k, v, m) gdbm_store(d, k, v, m)
50 : : #define MY_DBM_STRERROR(x) gdbm_strerror(x)
51 : : #define MY_DBM_CLOSE(d) gdbm_close(d)
52 : :
53 : : #define MY_DBM_REPLACE GDBM_REPLACE
54 : : #define MY_DBM_INSERT GDBM_INSERT
55 : :
56 : : #elif HAVE_LIBNDBM
57 : : #include <ndbm.h>
58 : :
59 : : #define MY_DBM_FETCH(d, k) dbm_fetch(d, k)
60 : : #define MY_DBM_STORE(d, k, v, m) dbm_store(d, k, v, m)
61 : : #define MY_DBM_STRERROR(x) strerror(x)
62 : : #define MY_DBM_CLOSE(d) dbm_close(d)
63 : :
64 : : #define MY_DBM_REPLACE DBM_REPLACE
65 : : #define MY_DBM_INSERT DBM_INSERT
66 : :
67 : : #else
68 : : #if ! USE_FILE_CACHE
69 : : #error "File cache method disabled, and No GDBM or NDBM header file found. WTF?"
70 : : #endif
71 : : #endif
72 : :
73 : : #if HAVE_SYS_SOCKET_H
74 : : #include <sys/socket.h>
75 : : #endif
76 : : #include <arpa/inet.h>
77 : :
78 : : #include <fcntl.h>
79 : :
80 : : #define DATE_LEN 18
81 : : #define MAX_DIGEST_SIZE 64
82 : :
83 : : /* Rotate the digest file by simply renaming it.
84 : : */
85 : : static void
86 : 2 : rotate_digest_cache_file(fko_srv_options_t *opts)
87 : : {
88 : : #ifdef NO_DIGEST_CACHE
89 : : log_msg(LOG_WARNING, "Digest cache not supported. Nothing to rotate.");
90 : : #else
91 : : int res;
92 : 2 : char *new_file = NULL;
93 : :
94 : 2 : log_msg(LOG_INFO, "Rotating digest cache file.");
95 : :
96 : : #if USE_FILE_CACHE
97 : 2 : new_file = calloc(1, strlen(opts->config[CONF_DIGEST_FILE])+5);
98 : : #else
99 : : new_file = calloc(1, strlen(opts->config[CONF_DIGEST_DB_FILE])+5);
100 : : #endif
101 : :
102 [ - + ]: 2 : if(new_file == NULL)
103 : : {
104 : 0 : log_msg(LOG_ERR, "rotate_digest_cache_file: Memory allocation error.");
105 : 0 : clean_exit(opts, NO_FW_CLEANUP, EXIT_FAILURE);
106 : : }
107 : :
108 : : /* The new filename is just the original with a trailing '-old'.
109 : : */
110 : : #if USE_FILE_CACHE
111 : 2 : strlcpy(new_file, opts->config[CONF_DIGEST_FILE],
112 : 2 : strlen(opts->config[CONF_DIGEST_FILE])+5);
113 : 2 : strlcat(new_file, "-old",
114 : 2 : strlen(opts->config[CONF_DIGEST_FILE])+5);
115 : : #else
116 : : strlcpy(new_file, opts->config[CONF_DIGEST_DB_FILE],
117 : : strlen(opts->config[CONF_DIGEST_DB_FILE])+5);
118 : : strlcat(new_file, "-old",
119 : : strlen(opts->config[CONF_DIGEST_DB_FILE])+5);
120 : : #endif
121 : :
122 : : #if USE_FILE_CACHE
123 : 2 : res = rename(opts->config[CONF_DIGEST_FILE], new_file);
124 : : #else
125 : : res = rename(opts->config[CONF_DIGEST_DB_FILE], new_file);
126 : : #endif
127 : :
128 [ - + ]: 2 : if(res < 0)
129 : 0 : log_msg(LOG_ERR, "Unable to rename digest file: %s to %s: %s",
130 : : #if USE_FILE_CACHE
131 : 0 : opts->config[CONF_DIGEST_FILE], new_file, strerror(errno)
132 : : #else
133 : : opts->config[CONF_DIGEST_DB_FILE], new_file, strerror(errno)
134 : : #endif
135 : : );
136 : : #endif /* NO_DIGEST_CACHE */
137 : :
138 : 2 : free(new_file);
139 : 2 : return;
140 : : }
141 : :
142 : : static void
143 : 1504 : replay_warning(fko_srv_options_t *opts, digest_cache_info_t *digest_info)
144 : : {
145 : 1504 : char src_ip[INET_ADDRSTRLEN+1] = {0};
146 : 1504 : char orig_src_ip[INET_ADDRSTRLEN+1] = {0};
147 : 1504 : char created[DATE_LEN] = {0};
148 : :
149 : : #if ! USE_FILE_CACHE
150 : : char first[DATE_LEN] = {0}, last[DATE_LEN] = {0};
151 : : #endif
152 : :
153 : : /* Convert the IPs to a human readable form
154 : : */
155 : 1504 : inet_ntop(AF_INET, &(opts->spa_pkt.packet_src_ip),
156 : : src_ip, INET_ADDRSTRLEN);
157 : 1504 : inet_ntop(AF_INET, &(digest_info->src_ip), orig_src_ip, INET_ADDRSTRLEN);
158 : :
159 : : #if ! USE_FILE_CACHE
160 : : /* Mark the last_replay time.
161 : : */
162 : : digest_info->last_replay = time(NULL);
163 : :
164 : : /* Increment the replay count and check to see if it is the first one.
165 : : */
166 : : if(++(digest_info->replay_count) == 1)
167 : : {
168 : : /* This is the first replay so make it the same as last_replay
169 : : */
170 : : digest_info->first_replay = digest_info->last_replay;
171 : : }
172 : :
173 : : strftime(first, DATE_LEN, "%D %H:%M:%S", localtime(&(digest_info->first_replay)));
174 : : strftime(last, DATE_LEN, "%D %H:%M:%S", localtime(&(digest_info->last_replay)));
175 : : #endif
176 : :
177 : 1504 : strftime(created, DATE_LEN, "%D %H:%M:%S", localtime(&(digest_info->created)));
178 : :
179 : 1504 : log_msg(LOG_WARNING,
180 : : "Replay detected from source IP: %s, "
181 : : "Destination proto/port: %d/%d, "
182 : : "Original source IP: %s, "
183 : : "Original dst proto/port: %d/%d, "
184 : : #if USE_FILE_CACHE
185 : : "Entry created: %s",
186 : : #else
187 : : "Entry created: %s, "
188 : : "First replay: %s, "
189 : : "Last replay: %s, "
190 : : "Replay count: %i",
191 : : #endif
192 : : src_ip,
193 : : opts->spa_pkt.packet_proto,
194 : 1504 : opts->spa_pkt.packet_dst_port,
195 : : orig_src_ip,
196 : 1504 : digest_info->proto,
197 : 1504 : digest_info->dst_port,
198 : : #if USE_FILE_CACHE
199 : : created
200 : : #else
201 : : created,
202 : : first,
203 : : last,
204 : : digest_info->replay_count
205 : : #endif
206 : : );
207 : :
208 : 1504 : return;
209 : : }
210 : :
211 : : #if USE_FILE_CACHE
212 : : static int
213 : 508 : replay_file_cache_init(fko_srv_options_t *opts)
214 : : {
215 : 508 : FILE *digest_file_ptr = NULL;
216 : 508 : unsigned int num_lines = 0, digest_ctr = 0;
217 : 508 : char line_buf[MAX_LINE_LEN] = {0};
218 : 508 : char src_ip[INET_ADDRSTRLEN+1] = {0};
219 : 508 : char dst_ip[INET_ADDRSTRLEN+1] = {0};
220 : : long int time_tmp;
221 : 508 : int digest_file_fd = -1;
222 : 508 : char digest_header[] = "# <digest> <proto> <src_ip> <src_port> <dst_ip> <dst_port> <time>\n";
223 : :
224 : 508 : struct digest_cache_list *digest_elm = NULL;
225 : :
226 : : /* if the file exists, import the previous SPA digests into
227 : : * the cache list
228 : : */
229 [ + + ]: 508 : if (access(opts->config[CONF_DIGEST_FILE], F_OK) == 0)
230 : : {
231 : : /* Check permissions
232 : : */
233 [ - + ]: 502 : if (access(opts->config[CONF_DIGEST_FILE], R_OK|W_OK) != 0)
234 : : {
235 : 0 : log_msg(LOG_WARNING, "Digest file '%s' exists but: '%s'",
236 : 0 : opts->config[CONF_DIGEST_FILE], strerror(errno));
237 : 0 : return(-1);
238 : : }
239 : : }
240 : : else
241 : : {
242 : : /* the file does not exist yet, so it will be created when the first
243 : : * successful SPA packet digest is written to disk
244 : : */
245 : 12 : digest_file_fd = open(opts->config[CONF_DIGEST_FILE],
246 : : O_WRONLY|O_CREAT|O_EXCL, S_IRUSR|S_IWUSR);
247 : :
248 [ - + ]: 6 : if (digest_file_fd == -1)
249 : : {
250 : 0 : log_msg(LOG_WARNING, "Could not create digest cache: %s: %s",
251 : 0 : opts->config[CONF_DIGEST_FILE], strerror(errno));
252 : 0 : return(-1);
253 : : }
254 : : else
255 : : {
256 [ - + ]: 6 : if(write(digest_file_fd, digest_header, strlen(digest_header))
257 : 6 : != strlen(digest_header)) {
258 : 0 : log_msg(LOG_WARNING,
259 : : "Did not write expected number of bytes to digest cache: %s",
260 : : opts->config[CONF_DIGEST_FILE]);
261 : : }
262 : 6 : close(digest_file_fd);
263 : :
264 : 6 : return(0);
265 : : }
266 : : }
267 : :
268 [ + - ]: 502 : if(verify_file_perms_ownership(opts->config[CONF_DIGEST_FILE]) != 1)
269 : : return(-1);
270 : :
271 : : /* File exists, and we have access - create in-memory digest cache
272 : : */
273 [ + + ]: 502 : if ((digest_file_ptr = fopen(opts->config[CONF_DIGEST_FILE], "r")) == NULL)
274 : : {
275 : 2 : log_msg(LOG_WARNING, "Could not open digest cache: %s",
276 : : opts->config[CONF_DIGEST_FILE]);
277 : 2 : return(-1);
278 : : }
279 : :
280 : : /* Line format:
281 : : * <digest> <proto> <src_ip> <src_port> <dst_ip> <dst_port> <time>
282 : : * Example:
283 : : * 7XgadOyqv0tF5xG8uhg2iIrheeNKglCWKmxQDgYP1dY 17 127.0.0.1 40305 127.0.0.1 62201 1313283481
284 : : */
285 [ + + ]: 251542 : while ((fgets(line_buf, MAX_LINE_LEN, digest_file_ptr)) != NULL)
286 : : {
287 : 251042 : num_lines++;
288 : 251042 : line_buf[MAX_LINE_LEN-1] = '\0';
289 : :
290 [ + + ][ + - ]: 251042 : if(IS_EMPTY_LINE(line_buf[0]))
[ + - ][ - + ]
291 : 498 : continue;
292 : :
293 : : /* Initialize a digest cache list element, and add it into the list if
294 : : * valid.
295 : : */
296 [ + + ]: 250544 : if ((digest_elm = calloc(1, sizeof(struct digest_cache_list))) == NULL)
297 : : {
298 : 1045 : log_msg(LOG_ERR, "[*] Could not allocate digest list element");
299 : 1045 : continue;
300 : : }
301 [ + + ]: 249499 : if ((digest_elm->cache_info.digest = calloc(1, MAX_DIGEST_SIZE+1)) == NULL)
302 : : {
303 : 1060 : free(digest_elm);
304 : 1060 : log_msg(LOG_ERR, "[*] Could not allocate digest string");
305 : 1060 : continue;
306 : : }
307 : 248439 : src_ip[0] = '\0';
308 : 248439 : dst_ip[0] = '\0';
309 : :
310 [ + + ]: 248439 : if(sscanf(line_buf, "%64s %hhu %16s %hu %16s %hu %ld",
311 : : digest_elm->cache_info.digest, /* %64s, buffer size is MAX_DIGEST_SIZE+1 */
312 : : &(digest_elm->cache_info.proto),
313 : : src_ip, /* %16s, buffer size is INET_ADDRSTRLEN+1 */
314 : : &(digest_elm->cache_info.src_port),
315 : : dst_ip, /* %16s, buffer size is INET_ADDRSTRLEN+1 */
316 : : &(digest_elm->cache_info.dst_port),
317 : : &time_tmp) != 7)
318 : : {
319 : 1 : log_msg(LOG_INFO,
320 : : "*Skipping invalid digest file entry in %s at line %i.\n - %s",
321 : : opts->config[CONF_DIGEST_FILE], num_lines, line_buf
322 : : );
323 : 1 : free(digest_elm->cache_info.digest);
324 : 1 : free(digest_elm);
325 : 1 : continue;
326 : : }
327 : 248438 : digest_elm->cache_info.created = time_tmp;
328 : :
329 : :
330 [ + + ]: 248438 : if (inet_pton(AF_INET, src_ip, &(digest_elm->cache_info.src_ip)) != 1)
331 : : {
332 : 2 : free(digest_elm->cache_info.digest);
333 : 2 : free(digest_elm);
334 : 2 : continue;
335 : : }
336 : :
337 [ + + ]: 248436 : if (inet_pton(AF_INET, dst_ip, &(digest_elm->cache_info.dst_ip)) != 1)
338 : : {
339 : 1 : free(digest_elm->cache_info.digest);
340 : 1 : free(digest_elm);
341 : 1 : continue;
342 : : }
343 : :
344 : 248435 : digest_elm->next = opts->digest_cache;
345 : 248435 : opts->digest_cache = digest_elm;
346 : 248435 : digest_ctr++;
347 : :
348 [ + + ]: 248435 : if(opts->verbose > 3)
349 : 251042 : log_msg(LOG_DEBUG,
350 : : "DIGEST FILE: %s, VALID LINE: %s",
351 : : opts->config[CONF_DIGEST_FILE], line_buf
352 : : );
353 : :
354 : : }
355 : :
356 : 500 : fclose(digest_file_ptr);
357 : :
358 : 500 : return(digest_ctr);
359 : : }
360 : :
361 : : #else /* USE_FILE_CACHE */
362 : :
363 : : /* Check for the existence of the replay dbm file, and create it if it does
364 : : * not exist. Returns the number of db entries or -1 on error.
365 : : */
366 : : static int
367 : : replay_db_cache_init(fko_srv_options_t *opts)
368 : : {
369 : : #ifdef NO_DIGEST_CACHE
370 : : return(-1);
371 : : #else
372 : :
373 : : #ifdef HAVE_LIBGDBM
374 : : GDBM_FILE rpdb;
375 : : #elif HAVE_LIBNDBM
376 : : DBM *rpdb;
377 : : datum db_ent;
378 : : #endif
379 : :
380 : : datum db_key, db_next_key;
381 : : int db_count = 0;
382 : :
383 : : #ifdef HAVE_LIBGDBM
384 : : rpdb = gdbm_open(
385 : : opts->config[CONF_DIGEST_DB_FILE], 512, GDBM_WRCREAT, S_IRUSR|S_IWUSR, 0
386 : : );
387 : : #elif HAVE_LIBNDBM
388 : : rpdb = dbm_open(
389 : : opts->config[CONF_DIGEST_DB_FILE], O_RDWR|O_CREAT, S_IRUSR|S_IWUSR
390 : : );
391 : : #endif
392 : :
393 : : if(!rpdb)
394 : : {
395 : : log_msg(LOG_ERR,
396 : : "Unable to open digest cache file: '%s': %s",
397 : : opts->config[CONF_DIGEST_DB_FILE],
398 : : MY_DBM_STRERROR(errno)
399 : : );
400 : :
401 : : return(-1);
402 : : }
403 : :
404 : : #ifdef HAVE_LIBGDBM
405 : : db_key = gdbm_firstkey(rpdb);
406 : :
407 : : while (db_key.dptr != NULL)
408 : : {
409 : : db_count++;
410 : : db_next_key = gdbm_nextkey(rpdb, db_key);
411 : : free(db_key.dptr);
412 : : db_key = db_next_key;
413 : : }
414 : : #elif HAVE_LIBNDBM
415 : : for (db_key = dbm_firstkey(rpdb); db_ent.dptr != NULL; db_key = dbm_nextkey(rpdb))
416 : : db_count++;
417 : : #endif
418 : :
419 : : MY_DBM_CLOSE(rpdb);
420 : :
421 : : return(db_count);
422 : : #endif /* NO_DIGEST_CACHE */
423 : : }
424 : : #endif /* USE_FILE_CACHE */
425 : :
426 : : #if USE_FILE_CACHE
427 : : static int
428 : 16224 : is_replay_file_cache(fko_srv_options_t *opts, char *digest)
429 : : {
430 : 16224 : int digest_len = 0;
431 : :
432 : 16224 : struct digest_cache_list *digest_list_ptr = NULL;
433 : :
434 : 16224 : digest_len = strlen(digest);
435 : :
436 : : /* Check the cache for the SPA packet digest
437 : : */
438 [ + + ]: 10160100 : for (digest_list_ptr = opts->digest_cache;
439 : : digest_list_ptr != NULL;
440 : 10143876 : digest_list_ptr = digest_list_ptr->next) {
441 : :
442 [ + + ]: 10145380 : if (constant_runtime_cmp(digest_list_ptr->cache_info.digest,
443 : : digest, digest_len) == 0) {
444 : :
445 : 1504 : replay_warning(opts, &(digest_list_ptr->cache_info));
446 : :
447 : 1504 : return(SPA_MSG_REPLAY);
448 : : }
449 : : }
450 : : return(SPA_MSG_SUCCESS);
451 : : }
452 : :
453 : : static int
454 : 811 : add_replay_file_cache(fko_srv_options_t *opts, char *digest)
455 : : {
456 : 811 : FILE *digest_file_ptr = NULL;
457 : 811 : int digest_len = 0;
458 : 811 : char src_ip[INET_ADDRSTRLEN+1] = {0};
459 : 811 : char dst_ip[INET_ADDRSTRLEN+1] = {0};
460 : :
461 : 811 : struct digest_cache_list *digest_elm = NULL;
462 : :
463 : 811 : digest_len = strlen(digest);
464 : :
465 [ - + ]: 811 : if ((digest_elm = calloc(1, sizeof(struct digest_cache_list))) == NULL)
466 : : {
467 : 0 : log_msg(LOG_WARNING, "Error calloc() returned NULL for digest cache element",
468 : : fko_errstr(SPA_MSG_ERROR));
469 : :
470 : 0 : return(SPA_MSG_ERROR);
471 : : }
472 [ - + ]: 811 : if ((digest_elm->cache_info.digest = calloc(1, digest_len+1)) == NULL)
473 : : {
474 : 0 : log_msg(LOG_WARNING, "Error calloc() returned NULL for digest cache string",
475 : : fko_errstr(SPA_MSG_ERROR));
476 : 0 : free(digest_elm);
477 : 0 : return(SPA_MSG_ERROR);
478 : : }
479 : :
480 : 811 : strlcpy(digest_elm->cache_info.digest, digest, digest_len+1);
481 : 811 : digest_elm->cache_info.proto = opts->spa_pkt.packet_proto;
482 : 811 : digest_elm->cache_info.src_ip = opts->spa_pkt.packet_src_ip;
483 : 811 : digest_elm->cache_info.dst_ip = opts->spa_pkt.packet_dst_ip;
484 : 811 : digest_elm->cache_info.src_port = opts->spa_pkt.packet_src_port;
485 : 811 : digest_elm->cache_info.dst_port = opts->spa_pkt.packet_dst_port;
486 : 811 : digest_elm->cache_info.created = time(NULL);
487 : :
488 : : /* First, add the digest at the head of the in-memory list
489 : : */
490 : 811 : digest_elm->next = opts->digest_cache;
491 : 811 : opts->digest_cache = digest_elm;
492 : :
493 : : /* Now, write the digest to disk
494 : : */
495 [ - + ]: 811 : if ((digest_file_ptr = fopen(opts->config[CONF_DIGEST_FILE], "a")) == NULL)
496 : : {
497 : 0 : log_msg(LOG_WARNING, "Could not open digest cache: %s",
498 : : opts->config[CONF_DIGEST_FILE]);
499 : 0 : return(SPA_MSG_DIGEST_CACHE_ERROR);
500 : : }
501 : :
502 : 811 : inet_ntop(AF_INET, &(digest_elm->cache_info.src_ip),
503 : : src_ip, INET_ADDRSTRLEN);
504 : 811 : inet_ntop(AF_INET, &(digest_elm->cache_info.dst_ip),
505 : : dst_ip, INET_ADDRSTRLEN);
506 : 3244 : fprintf(digest_file_ptr, "%s %d %s %d %s %d %d\n",
507 : : digest,
508 : 811 : digest_elm->cache_info.proto,
509 : : src_ip,
510 : 811 : (int) digest_elm->cache_info.src_port,
511 : : dst_ip,
512 : 811 : digest_elm->cache_info.dst_port,
513 : 811 : (int) digest_elm->cache_info.created);
514 : :
515 : 811 : fclose(digest_file_ptr);
516 : :
517 : 811 : return(SPA_MSG_SUCCESS);
518 : : }
519 : : #endif /* USE_FILE_CACHE */
520 : :
521 : : #if !USE_FILE_CACHE
522 : : static int
523 : : is_replay_dbm_cache(fko_srv_options_t *opts, char *digest)
524 : : {
525 : : #ifdef NO_DIGEST_CACHE
526 : : return 0;
527 : : #else
528 : :
529 : : #ifdef HAVE_LIBGDBM
530 : : GDBM_FILE rpdb;
531 : : #elif HAVE_LIBNDBM
532 : : DBM *rpdb;
533 : : #endif
534 : : datum db_key, db_ent;
535 : :
536 : : int digest_len, res = SPA_MSG_SUCCESS;
537 : :
538 : : digest_len = strlen(digest);
539 : :
540 : : db_key.dptr = digest;
541 : : db_key.dsize = digest_len;
542 : :
543 : : /* Check the db for the key
544 : : */
545 : : #ifdef HAVE_LIBGDBM
546 : : rpdb = gdbm_open(
547 : : opts->config[CONF_DIGEST_DB_FILE], 512, GDBM_WRCREAT, S_IRUSR|S_IWUSR, 0
548 : : );
549 : : #elif HAVE_LIBNDBM
550 : : rpdb = dbm_open(opts->config[CONF_DIGEST_DB_FILE], O_RDWR, 0);
551 : : #endif
552 : :
553 : : if(!rpdb)
554 : : {
555 : : log_msg(LOG_WARNING, "Error opening digest_cache: '%s': %s",
556 : : opts->config[CONF_DIGEST_DB_FILE],
557 : : MY_DBM_STRERROR(errno)
558 : : );
559 : :
560 : : return(SPA_MSG_DIGEST_CACHE_ERROR);
561 : : }
562 : :
563 : : db_ent = MY_DBM_FETCH(rpdb, db_key);
564 : :
565 : : /* If the datum is not null, we have a match. Otherwise, we add
566 : : * this entry to the cache.
567 : : */
568 : : if(db_ent.dptr != NULL)
569 : : {
570 : : replay_warning(opts, (digest_cache_info_t *)db_ent.dptr);
571 : :
572 : : /* Save it back to the digest cache
573 : : */
574 : : if(MY_DBM_STORE(rpdb, db_key, db_ent, MY_DBM_REPLACE) != 0)
575 : : log_msg(LOG_WARNING, "Error updating entry in digest_cache: '%s': %s",
576 : : opts->config[CONF_DIGEST_DB_FILE],
577 : : MY_DBM_STRERROR(errno)
578 : : );
579 : :
580 : : #ifdef HAVE_LIBGDBM
581 : : free(db_ent.dptr);
582 : : #endif
583 : : res = SPA_MSG_REPLAY;
584 : : }
585 : :
586 : : MY_DBM_CLOSE(rpdb);
587 : :
588 : : return(res);
589 : : #endif /* NO_DIGEST_CACHE */
590 : : }
591 : :
592 : : static int
593 : : add_replay_dbm_cache(fko_srv_options_t *opts, char *digest)
594 : : {
595 : : #ifdef NO_DIGEST_CACHE
596 : : return 0;
597 : : #else
598 : :
599 : : #ifdef HAVE_LIBGDBM
600 : : GDBM_FILE rpdb;
601 : : #elif HAVE_LIBNDBM
602 : : DBM *rpdb;
603 : : #endif
604 : : datum db_key, db_ent;
605 : :
606 : : int digest_len, res = SPA_MSG_SUCCESS;
607 : :
608 : : digest_cache_info_t dc_info;
609 : :
610 : : digest_len = strlen(digest);
611 : :
612 : : db_key.dptr = digest;
613 : : db_key.dsize = digest_len;
614 : :
615 : : /* Check the db for the key
616 : : */
617 : : #ifdef HAVE_LIBGDBM
618 : : rpdb = gdbm_open(
619 : : opts->config[CONF_DIGEST_DB_FILE], 512, GDBM_WRCREAT, S_IRUSR|S_IWUSR, 0
620 : : );
621 : : #elif HAVE_LIBNDBM
622 : : rpdb = dbm_open(opts->config[CONF_DIGEST_DB_FILE], O_RDWR, 0);
623 : : #endif
624 : :
625 : : if(!rpdb)
626 : : {
627 : : log_msg(LOG_WARNING, "Error opening digest_cache: '%s': %s",
628 : : opts->config[CONF_DIGEST_DB_FILE],
629 : : MY_DBM_STRERROR(errno)
630 : : );
631 : :
632 : : return(SPA_MSG_DIGEST_CACHE_ERROR);
633 : : }
634 : :
635 : : db_ent = MY_DBM_FETCH(rpdb, db_key);
636 : :
637 : : /* If the datum is null, we have a new entry.
638 : : */
639 : : if(db_ent.dptr == NULL)
640 : : {
641 : : /* This is a new SPA packet that needs to be added to the cache.
642 : : */
643 : : dc_info.src_ip = opts->spa_pkt.packet_src_ip;
644 : : dc_info.dst_ip = opts->spa_pkt.packet_dst_ip;
645 : : dc_info.src_port = opts->spa_pkt.packet_src_port;
646 : : dc_info.dst_port = opts->spa_pkt.packet_dst_port;
647 : : dc_info.proto = opts->spa_pkt.packet_proto;
648 : : dc_info.created = time(NULL);
649 : : dc_info.first_replay = dc_info.last_replay = dc_info.replay_count = 0;
650 : :
651 : : db_ent.dsize = sizeof(digest_cache_info_t);
652 : : db_ent.dptr = (char*)&(dc_info);
653 : :
654 : : if(MY_DBM_STORE(rpdb, db_key, db_ent, MY_DBM_INSERT) != 0)
655 : : {
656 : : log_msg(LOG_WARNING, "Error adding entry digest_cache: %s",
657 : : MY_DBM_STRERROR(errno)
658 : : );
659 : :
660 : : res = SPA_MSG_DIGEST_CACHE_ERROR;
661 : : }
662 : :
663 : : res = SPA_MSG_SUCCESS;
664 : : }
665 : : else
666 : : res = SPA_MSG_DIGEST_CACHE_ERROR;
667 : :
668 : : MY_DBM_CLOSE(rpdb);
669 : :
670 : : return(res);
671 : : #endif /* NO_DIGEST_CACHE */
672 : : }
673 : : #endif /* USE_FILE_CACHE */
674 : :
675 : : #if USE_FILE_CACHE
676 : : /* Free replay list memory
677 : : */
678 : : void
679 : 7115 : free_replay_list(fko_srv_options_t *opts)
680 : : {
681 : 7115 : struct digest_cache_list *digest_list_ptr = NULL, *digest_tmp = NULL;
682 : :
683 : : #ifdef NO_DIGEST_CACHE
684 : : return;
685 : : #endif
686 : :
687 : : #if AFL_FUZZING
688 : : if(opts->afl_fuzzing)
689 : : return;
690 : : #endif
691 : :
692 [ + + ]: 7115 : if (opts->digest_cache == NULL)
693 : : return;
694 : :
695 : : digest_list_ptr = opts->digest_cache;
696 [ + + ]: 247743 : while (digest_list_ptr != NULL)
697 : : {
698 : 247266 : digest_tmp = digest_list_ptr->next;
699 [ + - ]: 247266 : if (digest_list_ptr->cache_info.digest != NULL
700 [ + - ]: 247266 : && digest_list_ptr->cache_info.digest[0] != '\0')
701 : : {
702 : 247266 : free(digest_list_ptr->cache_info.digest);
703 : : }
704 : 247266 : free(digest_list_ptr);
705 : 247266 : digest_list_ptr = digest_tmp;
706 : : }
707 : :
708 : : return;
709 : : }
710 : : #endif
711 : :
712 : : int
713 : 508 : replay_cache_init(fko_srv_options_t *opts)
714 : : {
715 : : #ifdef NO_DIGEST_CACHE
716 : : return(-1);
717 : : #else
718 : :
719 : : /* If rotation was specified, do it.
720 : : */
721 [ + + ]: 508 : if(opts->rotate_digest_cache)
722 : 2 : rotate_digest_cache_file(opts);
723 : :
724 : : #if USE_FILE_CACHE
725 : 508 : return replay_file_cache_init(opts);
726 : : #else
727 : : return replay_db_cache_init(opts);
728 : : #endif
729 : :
730 : : #endif /* NO_DIGEST_CACHE */
731 : : }
732 : :
733 : : int
734 : 811 : add_replay(fko_srv_options_t *opts, char *digest)
735 : : {
736 : : #ifdef NO_DIGEST_CACHE
737 : : return(-1);
738 : : #else
739 : :
740 [ - + ]: 811 : if(digest == NULL)
741 : : {
742 : 0 : log_msg(LOG_WARNING, "NULL digest passed into add_replay()");
743 : 0 : return(SPA_MSG_DIGEST_CACHE_ERROR);
744 : : }
745 : :
746 : : #if USE_FILE_CACHE
747 : 811 : return add_replay_file_cache(opts, digest);
748 : : #else
749 : : return add_replay_dbm_cache(opts, digest);
750 : : #endif
751 : : #endif /* NO_DIGEST_CACHE */
752 : : }
753 : :
754 : : /* Take an fko context, pull the digest and use it as the key to check the
755 : : * replay db (digest cache).
756 : : */
757 : : int
758 : 16224 : is_replay(fko_srv_options_t *opts, char *digest)
759 : : {
760 : : #ifdef NO_DIGEST_CACHE
761 : : return(-1);
762 : : #else
763 : :
764 : : #if USE_FILE_CACHE
765 : 16224 : return is_replay_file_cache(opts, digest);
766 : : #else
767 : : return is_replay_dbm_cache(opts, digest);
768 : : #endif
769 : : #endif /* NO_DIGEST_CACHE */
770 : : }
771 : :
772 : : /***EOF***/
|