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