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