1 /*
2 * Tunnel SIE data from an SRA or RAD server.
3 *
4 * Copyright (c) 2014-2018 by Farsight Security, Inc.
5 *
6 * Licensed under the Apache License, Version 2.0 (the "License");
7 * you may not use this file except in compliance with the License.
8 * You may obtain a copy of the License at
9 *
10 * http://www.apache.org/licenses/LICENSE-2.0
11 *
12 * Unless required by applicable law or agreed to in writing, software
13 * distributed under the License is distributed on an "AS IS" BASIS,
14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 * See the License for the specific language governing permissions and
16 * limitations under the License.
17 */
18
19 #include "sratunnel.h"
20
21 /* extern: output.c */
22 extern bool out_bar_on;
23 extern bool nmsg_zlib;
24 extern uint output_tsindex_write_interval;
25
26 /* extern: axalib/open_nmsg_out.c */
27 extern bool axa_out_file_append;
28
29 /* extern: server.c */
30 extern axa_client_t client;
31
32 /* extern signal.c */
33 extern int terminated;
34 extern int give_status;
35
36 /* global */
37 uint axa_debug; /* debug level */
38 int count; /* limit to this many */
39 unsigned long count_messages_sent = 0; /* how many messages we have sent */
40 unsigned long count_messages_rcvd = 0; /* how many messages we have received */
41 unsigned long count_hits = 0; /* how many hits received */
42 int trace = 0; /* server-side trace level */
43 int initial_count; /* initial count */
44 bool counting = false; /* true == limiting packets to count */
45 bool first_time; /* true == first time connecting */
46 nmsg_input_t nmsg_input; /* NMSG input object */
47 FILE *fp_pidfile = NULL; /* PID file FILE pointer */
48 FILE *fp_tsindex = NULL; /* timestamp index file pointer */
49 const char *pidfile; /* PID file file name */
50 const char *srvr_addr; /* SRA/RAD server string */
51 axa_mode_t mode; /* SRA or RAD */
52 axa_cnt_t rlimit = 0; /* rate limit, packets per second */
53 arg_t *chs = NULL; /* channels */
54 arg_t *watches = NULL; /* watches */
55 arg_t *anomalies = NULL; /* anomalies */
56 const char *out_addr; /* output address */
57 double sample = 0.0; /* sampling rate */
58 MDB_env *mdb_env; /* timestamp index db environment */
59 MDB_dbi mdb_dbi; /* timestamp index db handle */
60 MDB_txn *mdb_txn; /* timestamp index transaction handle */
61
62 /* private */
63 static bool version;
64 static struct timeval time_out_flush;
65 static uint acct_interval;
66 static struct timeval acct_timer;
67
68 void print_status(void);
69
70 static void
lmdb_shutdown(void)71 lmdb_shutdown(void)
72 {
73 int rc;
74
75 /* try to commit any outstanding data before closing the env */
76 rc = mdb_txn_commit(mdb_txn);
77 if (rc != 0)
78 fprintf(stderr, "lmdb_shutdown() couldn't perform final commit: mdb_txn_commit(): %s\n",
79 mdb_strerror(rc));
80
81 mdb_dbi_close(mdb_env, mdb_dbi);
82 mdb_env_close(mdb_env);
83 }
84
85 static void AXA_NORETURN AXA_PF(1,2)
usage(const char * msg,...)86 usage(const char *msg, ...)
87 {
88 const char *sra = "SIE Remote Access Tunnel (sratunnel)\n";
89 const char *rad = "Real-time Anomaly Detection Tunnel (radtunnel)\n";
90 va_list args;
91
92 if (msg != NULL) {
93 printf("%s: ", axa_prog_name);
94 va_start(args, msg);
95 axa_verror_msg(msg, args);
96 va_end(args);
97 printf("\n");
98 }
99
100 printf("%s", mode == SRA ? sra : rad);
101 printf("(c) 2014-2018 Farsight Security, Inc.\n");
102 printf("Usage: %s [options]\n", axa_prog_name);
103 if (mode == SRA) {
104 printf("-c channel\t\tenable channel\n");
105 printf("-o output\t\tspecify destination of SIE data\n");
106 printf("-s [user@]server|alias\tconnect to SRA server\n");
107 printf("-w watch\t\tset watch\n");
108 }
109 if (mode == RAD) {
110 printf("-a anomaly\t\tenable anomaly detection module\n");
111 printf("-o output\t\tspecify destination of SIE data\n");
112 printf("-s [user@]server|alias\tconnect to RAD server\n");
113 printf("-w watch\t\tset watch\n");
114 }
115 printf("\n[-A interval]\t\temit acct messages to stdout every interval seconds\n");
116 printf("[-C count]\t\tstop after processing count messages\n");
117 printf("[-d]\t\t\tincrement debug level, -ddd > -dd > -d\n");
118 printf("[-E ciphers]\t\tuse these TLS ciphers\n");
119 printf("[-h]\t\t\tdisplay this help and exit\n");
120 printf("[-i interval]\t\twrite timestamp indexes every interval nmsgs\n");
121 printf("[-V]\t\t\tprint version and quit\n");
122 printf("[-m rate]\t\tsampling %% of packets over 1 second, 0.01 - 100.0\n");
123 printf("[-n file]\t\tspecify AXA config file\n");
124 printf("[-O]\t\t\tenable spinning bar on output\n");
125 printf("[-P file]\t\twrite PID to pidfile\n");
126 printf("[-p]\t\t\tappend to output file (only valid for file outputs)\n");
127 printf("[-r limit]\t\trate limit to this many packets per second\n");
128 printf("[-S dir]\t\tspecify TLS certificates directory\n");
129 printf("[-t]\t\t\tincrement server trace level, -ttt > -tt > -t\n");
130 printf("[-u]\t\t\tunbuffer nmsg container output\n");
131 printf("[-z]\t\t\tenable nmsg zlib container compression\n");
132
133 exit(EX_USAGE);
134 }
135
136 int
main(int argc,char ** argv)137 main(int argc, char **argv)
138 {
139 const char *config_file = "";
140 arg_t *arg;
141 struct timeval now;
142 time_t ms;
143 nmsg_res res;
144 axa_emsg_t emsg;
145 char *p;
146 const char *cp;
147 int i;
148 size_t n = 0;
149 bool output_buffering = true;
150 char out_filename[BUFSIZ], lmdb_filename[BUFSIZ];
151
152 axa_set_me(argv[0]);
153 AXA_ASSERT(axa_parse_log_opt(&emsg, "trace,off,stdout"));
154 AXA_ASSERT(axa_parse_log_opt(&emsg, "error,off,stderr"));
155 axa_syslog_init();
156 axa_clean_stdio();
157 axa_set_core();
158 axa_client_backoff_reset(&client);
159 axa_client_init(&client);
160
161 if (strcmp(axa_prog_name, "radtunnel") == 0)
162 mode = RAD;
163
164 version = false;
165 pidfile = NULL;
166 while ((i = getopt(argc, argv, "hi:a:pA:VdtOC:r:E:P:S:o:s:c:w:m:n:uz"))
167 != -1) {
168 switch (i) {
169 case 'A':
170 acct_interval = atoi(optarg);
171 if (acct_interval <= 0) {
172 axa_error_msg("invalid \"-A %s\"", optarg);
173 exit(EX_USAGE);
174 }
175 gettimeofday(&acct_timer, NULL);
176 break;
177
178 case 'V':
179 version = true;
180 break;
181
182 case 'p':
183 axa_out_file_append = true;
184 break;
185
186 case 'd':
187 ++axa_debug;
188 break;
189
190 case 'h':
191 usage(NULL);
192 break;
193
194 case 'i':
195 output_tsindex_write_interval = atoi(optarg);
196 break;
197
198 case 't':
199 ++trace;
200 break;
201
202 case 'm':
203 sample = atof(optarg);
204 if (sample <= 0.0 || sample > 100.0) {
205 axa_error_msg("invalid \"-a %s\"", optarg);
206 exit(EX_USAGE);
207 }
208 break;
209 case 'n':
210 config_file = optarg;
211 break;
212
213 case 'O':
214 out_bar_on = true;
215 break;
216
217 case 'o':
218 out_addr = optarg;
219 break;
220
221 case 'P':
222 pidfile = optarg;
223 break;
224
225 case 'C':
226 count = strtoul(optarg, &p, 10);
227 if (*optarg == '\0'
228 || *p != '\0'
229 || count < 1) {
230 axa_error_msg("invalid \"-C %s\"", optarg);
231 exit(EX_USAGE);
232 }
233 initial_count = count;
234 counting = true;
235 break;
236
237 case 'r':
238 rlimit = strtoul(optarg, &p, 10);
239 if (*optarg == '\0'
240 || *p != '\0'
241 || rlimit < 1 || rlimit > AXA_RLIMIT_MAX) {
242 axa_error_msg("invalid \"-r %s\"", optarg);
243 exit(EX_USAGE);
244 }
245 break;
246
247 case 'E':
248 if (axa_tls_cipher_list(&emsg, optarg) == NULL)
249 axa_error_msg("%s", emsg.c);
250 break;
251
252 case 'S':
253 if (!axa_tls_certs_dir(&emsg, optarg))
254 axa_error_msg("%s", emsg.c);
255 break;
256
257 case 's':
258 srvr_addr = optarg;
259 break;
260
261 case 'c':
262 arg = AXA_SALLOC(arg_t);
263 arg->c = optarg;
264 arg->next = chs;
265 chs = arg;
266 break;
267
268 case 'w':
269 arg = AXA_SALLOC(arg_t);
270 arg->c = optarg;
271 arg->next = watches;
272 watches = arg;
273 break;
274
275 case 'a':
276 arg = AXA_SALLOC(arg_t);
277 arg->c = optarg;
278 arg->next = anomalies;
279 anomalies = arg;
280 break;
281
282 case 'u':
283 output_buffering = false;
284 break;
285
286 case 'z':
287 nmsg_zlib = true;
288 break;
289 default:
290 usage(NULL);
291 }
292 }
293 argc -= optind;
294 argv += optind;
295 if (argc != 0) {
296 usage("unrecognized \"%s\"", argv[0]);
297 }
298
299 if (version) {
300 #if AXA_P_PVERS_MIN != AXA_P_PVERS_MAX
301 axa_trace_msg("%s built using AXA library %s, AXA protocol %d in %d to %d\n",
302 axa_prog_name, axa_get_version(),
303 AXA_P_PVERS, AXA_P_PVERS_MIN, AXA_P_PVERS_MAX);
304 #else
305 axa_trace_msg("%s built using AXA library: %s, AXA protocol: %d\n",
306 axa_prog_name, axa_get_version(), AXA_P_PVERS);
307 #endif
308 if (srvr_addr == NULL && out_addr == NULL
309 && chs == NULL && watches == NULL
310 && anomalies == NULL)
311 exit(0);
312 }
313 if (srvr_addr == NULL)
314 usage("server not specified with -s");
315 if (out_addr == NULL)
316 usage("output not specified with -o");
317 if (watches == NULL)
318 usage("no watches specified with -w");
319 if (mode == RAD) {
320 if (anomalies == NULL)
321 usage("anomalies specified with -a");
322 if (chs != NULL) {
323 axa_error_msg("\"-c %s\" not allowed in RAD mode",
324 chs->c);
325 while ((arg = chs) != NULL) {
326 chs = arg->next;
327 free(arg);
328 }
329 exit(0);
330 }
331 } else {
332 if (anomalies != NULL) {
333 axa_error_msg("\"-a %s\" not allowed in SRA mode",
334 anomalies->c);
335 while ((arg = anomalies) != NULL) {
336 anomalies = arg->next;
337 free(arg);
338 }
339 exit(0);
340 }
341 }
342
343 if (!axa_load_client_config(&emsg, config_file)) {
344 axa_error_msg("can't load config file: %s", emsg.c);
345 exit(EXIT_FAILURE);
346 }
347
348 signal(SIGPIPE, SIG_IGN);
349 signal(SIGHUP, sigterm);
350 signal(SIGTERM, sigterm);
351 signal(SIGINT, sigterm);
352 #ifdef SIGXFSZ
353 signal(SIGXFSZ, SIG_IGN);
354 #endif
355 #ifdef SIGINFO
356 signal(SIGINFO, siginfo);
357 #endif
358 /* When appending, demand that the output file exists. */
359 if (axa_out_file_append) {
360 n = strlcpy(out_filename, strrchr(out_addr, ':') + 1,
361 sizeof (out_filename));
362
363 if (access(out_filename, F_OK) == -1) {
364 axa_error_msg("append mode expected to find output file \"%s\": %s\n",
365 out_filename, strerror(errno));
366 exit(EX_SOFTWARE);
367 }
368 else if (axa_debug > 0)
369 axa_trace_msg("appending to nmsg file \"%s\"\n", out_filename);
370 }
371
372 if (!out_open(output_buffering))
373 exit(EX_IOERR);
374
375 AXA_DEBUG_TO_NMSG(axa_debug);
376 res = nmsg_init();
377 if (res != nmsg_res_success) {
378 axa_error_msg("nmsg_init(): %s", nmsg_res_lookup(res));
379 exit(EX_SOFTWARE);
380 }
381 nmsg_input = nmsg_input_open_null();
382 AXA_ASSERT(nmsg_input != NULL);
383
384 if (pidfile) {
385 fp_pidfile = pidfile_open();
386 if (fp_pidfile == NULL)
387 exit(EX_SOFTWARE);
388 pidfile_write();
389 }
390
391 if (output_tsindex_write_interval > 0) {
392 int rc = 0, pagesize = 0;
393 bool idx_exists = false;
394
395 /* Timestamp indexing requires unbuffered filesystem writes. */
396 if (output_buffering == true) {
397 axa_error_msg("can't store timestamp indexes when buffering output (need `-u`)\n");
398 exit(EX_SOFTWARE);
399 }
400
401 n = strlcpy(lmdb_filename, strrchr(out_addr, ':') + 1,
402 sizeof (lmdb_filename));
403 strlcpy(lmdb_filename + n, ".mdb", sizeof (lmdb_filename) - n);
404
405 /* Try to find a tsindex file that would correspond to the
406 * name of the data file.
407 */
408 rc = access(lmdb_filename, F_OK);
409 if (rc == 0)
410 idx_exists = true;
411 else if (rc == -1 && errno != ENOENT) {
412 axa_error_msg("can't access: \"%s\": %s\n",
413 lmdb_filename, strerror(errno));
414 exit(EX_SOFTWARE);
415 }
416
417 /* Timestamp indexing + appending requires a previously created
418 * tsindex file (which should correspond in name to the output
419 * file being appended to).
420 */
421 if (axa_out_file_append == true) {
422 if (idx_exists == true) {
423 if (axa_debug > 0)
424 axa_trace_msg("found tsindex file \"%s\"\n", lmdb_filename);
425 } else {
426 axa_error_msg("tsindex mode expected to find tsindex file \"%s\": %s\n",
427 lmdb_filename, strerror(errno));
428 exit(EX_SOFTWARE);
429 }
430 }
431 else {
432 /* This orphaned tsindex file is clobbered lest we write
433 * to it and end up mixing with timestamps and offsets
434 * from a previous unrelated session.
435 */
436 if (idx_exists) {
437 if (unlink(lmdb_filename) == -1) {
438 axa_error_msg("found orphan tsindex file \"%s\" but can't delete it: %s\n",
439 lmdb_filename, strerror(errno));
440 exit(EX_SOFTWARE);
441 }
442 else if (axa_debug > 0)
443 axa_trace_msg("found and deleted orphan tsindex file \"%s\"\n",
444 lmdb_filename);
445 }
446 }
447
448 rc = mdb_env_create(&mdb_env);
449 if (rc != 0) {
450 axa_error_msg("mdb_env_create(): %s", mdb_strerror(rc));
451 exit(EX_SOFTWARE);
452 }
453 pagesize = getpagesize();
454 /* lmdb is a memory mapped database. The mapsize should be a
455 * multiple of the OS page size (usually 4,096 bytes). The
456 * size of the memory map is also the maximum size of the
457 * database. As such, we should periodically check to see if
458 * we're close to running out of space and resize.
459 */
460 rc = mdb_env_set_mapsize(mdb_env, pagesize * 2560);
461 if (rc != 0) {
462 axa_error_msg("mdb_env_set_mapsize(): %s",
463 mdb_strerror(rc));
464 exit(EX_SOFTWARE);
465 }
466
467 if (axa_debug != 0)
468 axa_trace_msg("writing timestamp offsets to %s every %u nmsgs\n",
469 lmdb_filename,
470 output_tsindex_write_interval);
471 rc = mdb_env_open(mdb_env, lmdb_filename,
472 MDB_NOSUBDIR, 0664);
473 if (rc != 0) {
474 axa_error_msg("mdb_env_open(): %s", mdb_strerror(rc));
475 exit(EX_SOFTWARE);
476 }
477 rc = mdb_txn_begin(mdb_env, NULL, 0, &mdb_txn);
478 if (rc != 0) {
479 axa_error_msg("mdb_txn_begin(): %s", mdb_strerror(rc));
480 exit(EX_SOFTWARE);
481 }
482 rc = mdb_dbi_open(mdb_txn, NULL, MDB_INTEGERKEY, &mdb_dbi);
483 if (rc != 0) {
484 axa_error_msg("mdb_dbi_open(): %s", mdb_strerror(rc));
485 exit(EX_SOFTWARE);
486 }
487 rc = mdb_set_compare(mdb_txn, mdb_dbi, axa_tsi_mdb_cmp);
488 if (rc != 0) {
489 axa_error_msg("mdb_set_compare(): %s", mdb_strerror(rc));
490 exit(EX_SOFTWARE);
491 }
492 atexit(lmdb_shutdown);
493 }
494
495 /*
496 * Continually reconnect to the SRA or RAD server and forward SIE data.
497 */
498 first_time = true;
499 for (;;) {
500 if (terminated != 0)
501 stop(terminated);
502 if (give_status != 0) {
503 give_status = 0;
504 print_status();
505 }
506
507 /* (Re)connect to the server if it is time. */
508 if (!AXA_CLIENT_CONNECTED(&client)) {
509 ms = axa_client_again(&client, &now);
510 if (ms <= 0) {
511 srvr_connect();
512 } else {
513 if (axa_debug != 0
514 && (ms >= 100
515 || axa_debug >= AXA_DEBUG_TRACE))
516 axa_trace_msg("delaying %.1f"
517 " seconds to re-connect",
518 ms/1000.0);
519 usleep(ms*1000);
520 }
521 continue;
522 }
523
524 /* Flush the output buffer after silence. */
525 gettimeofday(&now, NULL);
526 if (time_out_flush.tv_sec != 0) {
527 ms = (OUT_FLUSH_MS - axa_elapsed_ms(&now,
528 &time_out_flush));
529 if (ms <= 0) {
530 out_flush();
531 continue;
532 }
533 }
534 /* check to see if it's time for an accounting update */
535 if (acct_interval) {
536 gettimeofday(&now, NULL);
537 if (now.tv_sec - acct_timer.tv_sec >= acct_interval) {
538 if (!srvr_send(1, AXA_P_OP_ACCT, NULL, 0)) {
539 continue;
540 }
541 acct_timer.tv_sec = now.tv_sec;
542 }
543 }
544 switch (axa_io_wait(&emsg, &client.io, OUT_FLUSH_MS,
545 true, true)) {
546 case AXA_IO_ERR:
547 disconnect(true, "%s", emsg.c);
548 break;
549 case AXA_IO_TUNERR:
550 for (;;) {
551 cp = axa_io_tunerr(&client.io);
552 if (cp == NULL)
553 break;
554 axa_error_msg("%s", cp);
555 }
556 break;
557 case AXA_IO_BUSY:
558 break;
559 case AXA_IO_KEEPALIVE:
560 /* nothing to do here if srvr_send() fails, we just
561 * hope it was ephemeral and do our backoff and retry
562 * thing */
563 (void) srvr_send(AXA_TAG_NONE, AXA_P_OP_NOP, NULL, 0);
564 continue;
565 case AXA_IO_OK:
566 /* Process a message from the server. */
567 forward();
568 break;
569 default:
570 AXA_FAIL("impossible axa_io_wait() result");
571 }
572 }
573 }
574
575 void AXA_NORETURN
stop(int s)576 stop(int s)
577 {
578 arg_t *arg;
579
580 /* Free everything when we quit to check for memory leaks */
581
582 axa_client_close(&client);
583 if (nmsg_input != NULL)
584 nmsg_input_close(&nmsg_input);
585 out_close();
586
587 while ((arg = chs) != NULL) {
588 chs = arg->next;
589 free(arg);
590 }
591 while ((arg = watches) != NULL) {
592 watches = arg->next;
593 free(arg);
594 }
595 while ((arg = anomalies) != NULL) {
596 anomalies = arg->next;
597 free(arg);
598 }
599
600 axa_unload_client_config();
601 axa_io_cleanup();
602
603 if (pidfile)
604 if (unlink(pidfile) != 0)
605 fprintf(stderr, "unlink() failed: %s\n",
606 strerror(errno));
607 exit(s);
608 }
609
print_status(void)610 void print_status(void)
611 {
612 printf("%s ", mode == SRA ? "sra" : "rad");
613 if (!AXA_CLIENT_CONNECTED(&client))
614 printf("NOT-");
615 printf("connected, ");
616
617 /* print with the proper pluralization */
618 printf("sent %lu message%s, received %lu message%s, %lu hit%s\n",
619 count_messages_sent, count_messages_sent != 1 ? "s" : "",
620 count_messages_rcvd, count_messages_rcvd != 1 ? "s" : "",
621 count_hits, count_hits != 1 ? "s" : "");
622 }
623
624
625