Home | History | Annotate | Download | only in util
      1 #include <stdio.h>
      2 #include <stdlib.h>
      3 #include <unistd.h>
      4 #include <string.h>
      5 #include <stdarg.h>
      6 #include <errno.h>
      7 #include <fcntl.h>
      8 #include <libgen.h>
      9 #include <signal.h>
     10 #include <net/if.h>
     11 #include <net/ethernet.h>
     12 #include <sys/select.h>
     13 #include <sys/socket.h>
     14 #include <sys/stat.h>
     15 #include <sys/un.h>
     16 #include <syslog.h>
     17 #include <getopt.h>
     18 #include <pcap.h>
     19 
     20 #define SNAPLEN 1600
     21 
     22 /*
     23  * FIXME: is there a way to detect the version of the libpcap library?
     24  * Version 0.9 has pcap_inject; version 0.8 doesn't, but both report
     25  * their version number as 2.4.
     26  */
     27 #define HAVE_PCAP_INJECT 0
     28 
     29 struct hijack {
     30 	pcap_t *pcap;
     31 	int fd;
     32 	int datalink;
     33 	int filtered;
     34 	unsigned long rx_count;
     35 	unsigned long tx_count;
     36 };
     37 
     38 struct hijack_listener {
     39 	struct sockaddr_un sun;
     40 	int fd;
     41 };
     42 
     43 struct hijack_options {
     44 	char interface[IF_NAMESIZE];
     45 	int daemonise;
     46 };
     47 
     48 static int daemonised = 0;
     49 
     50 static int signalled = 0;
     51 
     52 static void flag_signalled ( int signal __attribute__ (( unused )) ) {
     53 	signalled = 1;
     54 }
     55 
     56 #if ! HAVE_PCAP_INJECT
     57 /**
     58  * Substitute for pcap_inject(), if this version of libpcap doesn't
     59  * have it.  Will almost certainly only work under Linux.
     60  *
     61  */
     62 int pcap_inject ( pcap_t *pcap, const void *data, size_t len ) {
     63 	int fd;
     64 	char *errbuf = pcap_geterr ( pcap );
     65 
     66 	fd = pcap_get_selectable_fd ( pcap );
     67 	if ( fd < 0 ) {
     68 		snprintf ( errbuf, PCAP_ERRBUF_SIZE,
     69 			   "could not get file descriptor" );
     70 		return -1;
     71 	}
     72 	if ( write ( fd, data, len ) != len ) {
     73 		snprintf ( errbuf, PCAP_ERRBUF_SIZE,
     74 			   "could not write data: %s", strerror ( errno ) );
     75 		return -1;
     76 	}
     77 	return len;
     78 }
     79 #endif /* ! HAVE_PCAP_INJECT */
     80 
     81 /**
     82  * Log error message
     83  *
     84  */
     85 static __attribute__ (( format ( printf, 2, 3 ) )) void
     86 logmsg ( int level, const char *format, ... ) {
     87 	va_list ap;
     88 
     89 	va_start ( ap, format );
     90 	if ( daemonised ) {
     91 		vsyslog ( ( LOG_DAEMON | level ), format, ap );
     92 	} else {
     93 		vfprintf ( stderr, format, ap );
     94 	}
     95 	va_end ( ap );
     96 }
     97 
     98 /**
     99  * Open pcap device
    100  *
    101  */
    102 static int hijack_open ( const char *interface, struct hijack *hijack ) {
    103 	char errbuf[PCAP_ERRBUF_SIZE];
    104 
    105 	/* Open interface via pcap */
    106 	errbuf[0] = '\0';
    107 	hijack->pcap = pcap_open_live ( interface, SNAPLEN, 1, 0, errbuf );
    108 	if ( ! hijack->pcap ) {
    109 		logmsg ( LOG_ERR, "Failed to open %s: %s\n",
    110 			 interface, errbuf );
    111 		goto err;
    112 	}
    113 	if ( errbuf[0] )
    114 		logmsg ( LOG_WARNING, "Warning: %s\n", errbuf );
    115 
    116 	/* Set capture interface to non-blocking mode */
    117 	if ( pcap_setnonblock ( hijack->pcap, 1, errbuf ) < 0 ) {
    118 		logmsg ( LOG_ERR, "Could not make %s non-blocking: %s\n",
    119 			 interface, errbuf );
    120 		goto err;
    121 	}
    122 
    123 	/* Get file descriptor for select() */
    124 	hijack->fd = pcap_get_selectable_fd ( hijack->pcap );
    125 	if ( hijack->fd < 0 ) {
    126 		logmsg ( LOG_ERR, "Cannot get selectable file descriptor "
    127 			 "for %s\n", interface );
    128 		goto err;
    129 	}
    130 
    131 	/* Get link layer type */
    132 	hijack->datalink = pcap_datalink ( hijack->pcap );
    133 
    134 	return 0;
    135 
    136  err:
    137 	if ( hijack->pcap )
    138 		pcap_close ( hijack->pcap );
    139 	return -1;
    140 }
    141 
    142 /**
    143  * Close pcap device
    144  *
    145  */
    146 static void hijack_close ( struct hijack *hijack ) {
    147 	pcap_close ( hijack->pcap );
    148 }
    149 
    150 /**
    151  * Install filter for hijacked connection
    152  *
    153  */
    154 static int hijack_install_filter ( struct hijack *hijack,
    155 				   char *filter ) {
    156 	struct bpf_program program;
    157 
    158 	/* Compile filter */
    159 	if ( pcap_compile ( hijack->pcap, &program, filter, 1, 0 ) < 0 ) {
    160 		logmsg ( LOG_ERR, "could not compile filter \"%s\": %s\n",
    161 			 filter, pcap_geterr ( hijack->pcap ) );
    162 		goto err_nofree;
    163 	}
    164 
    165 	/* Install filter */
    166 	if ( pcap_setfilter ( hijack->pcap, &program ) < 0 ) {
    167 		logmsg ( LOG_ERR, "could not install filter \"%s\": %s\n",
    168 			 filter, pcap_geterr ( hijack->pcap ) );
    169 		goto err;
    170 	}
    171 
    172 	logmsg ( LOG_INFO, "using filter \"%s\"\n", filter );
    173 
    174 	pcap_freecode ( &program );
    175 	return 0;
    176 
    177  err:
    178 	pcap_freecode ( &program );
    179  err_nofree:
    180 	return -1;
    181 }
    182 
    183 /**
    184  * Set up filter for hijacked ethernet connection
    185  *
    186  */
    187 static int hijack_filter_ethernet ( struct hijack *hijack, const char *buf,
    188 				    size_t len ) {
    189 	char filter[55]; /* see format string */
    190 	struct ether_header *ether_header = ( struct ether_header * ) buf;
    191 	unsigned char *hwaddr = ether_header->ether_shost;
    192 
    193 	if ( len < sizeof ( *ether_header ) )
    194 		return -1;
    195 
    196 	snprintf ( filter, sizeof ( filter ), "broadcast or multicast or "
    197 		   "ether host %02x:%02x:%02x:%02x:%02x:%02x", hwaddr[0],
    198 		   hwaddr[1], hwaddr[2], hwaddr[3], hwaddr[4], hwaddr[5] );
    199 
    200 	return hijack_install_filter ( hijack, filter );
    201 }
    202 
    203 /**
    204  * Set up filter for hijacked connection
    205  *
    206  */
    207 static int hijack_filter ( struct hijack *hijack, const char *buf,
    208 			   size_t len ) {
    209 	switch ( hijack->datalink ) {
    210 	case DLT_EN10MB:
    211 		return hijack_filter_ethernet ( hijack, buf, len );
    212 	default:
    213 		logmsg ( LOG_ERR, "unsupported protocol %s: cannot filter\n",
    214 			 ( pcap_datalink_val_to_name ( hijack->datalink ) ?
    215 			   pcap_datalink_val_to_name ( hijack->datalink ) :
    216 			   "UNKNOWN" ) );
    217 		/* Return success so we don't get called again */
    218 		return 0;
    219 	}
    220 }
    221 
    222 /**
    223  * Forward data from hijacker
    224  *
    225  */
    226 static ssize_t forward_from_hijacker ( struct hijack *hijack, int fd ) {
    227 	char buf[SNAPLEN];
    228 	ssize_t len;
    229 
    230 	/* Read packet from hijacker */
    231 	len = read ( fd, buf, sizeof ( buf ) );
    232 	if ( len < 0 ) {
    233 		logmsg ( LOG_ERR, "read from hijacker failed: %s\n",
    234 			 strerror ( errno ) );
    235 		return -1;
    236 	}
    237 	if ( len == 0 )
    238 		return 0;
    239 
    240 	/* Set up filter if not already in place */
    241 	if ( ! hijack->filtered ) {
    242 		if ( hijack_filter ( hijack, buf, len ) == 0 )
    243 			hijack->filtered = 1;
    244 	}
    245 
    246 	/* Transmit packet to network */
    247 	if ( pcap_inject ( hijack->pcap, buf, len ) != len ) {
    248 		logmsg ( LOG_ERR, "write to hijacked port failed: %s\n",
    249 			 pcap_geterr ( hijack->pcap ) );
    250 		return -1;
    251 	}
    252 
    253 	hijack->tx_count++;
    254 	return len;
    255 };
    256 
    257 /**
    258  * Forward data to hijacker
    259  *
    260  */
    261 static ssize_t forward_to_hijacker ( int fd, struct hijack *hijack ) {
    262 	struct pcap_pkthdr *pkt_header;
    263 	const unsigned char *pkt_data;
    264 	ssize_t len;
    265 
    266 	/* Receive packet from network */
    267 	if ( pcap_next_ex ( hijack->pcap, &pkt_header, &pkt_data ) < 0 ) {
    268 		logmsg ( LOG_ERR, "read from hijacked port failed: %s\n",
    269 			 pcap_geterr ( hijack->pcap ) );
    270 		return -1;
    271 	}
    272 	if ( pkt_header->caplen != pkt_header->len ) {
    273 		logmsg ( LOG_ERR, "read partial packet (%d of %d bytes)\n",
    274 			 pkt_header->caplen, pkt_header->len );
    275 		return -1;
    276 	}
    277 	if ( pkt_header->caplen == 0 )
    278 		return 0;
    279 	len = pkt_header->caplen;
    280 
    281 	/* Write packet to hijacker */
    282 	if ( write ( fd, pkt_data, len ) != len ) {
    283 		logmsg ( LOG_ERR, "write to hijacker failed: %s\n",
    284 			 strerror ( errno ) );
    285 		return -1;
    286 	}
    287 
    288 	hijack->rx_count++;
    289 	return len;
    290 };
    291 
    292 
    293 /**
    294  * Run hijacker
    295  *
    296  */
    297 static int run_hijacker ( const char *interface, int fd ) {
    298 	struct hijack hijack;
    299 	fd_set fdset;
    300 	int max_fd;
    301 	ssize_t len;
    302 
    303 	logmsg ( LOG_INFO, "new connection for %s\n", interface );
    304 
    305 	/* Open connection to network */
    306 	memset ( &hijack, 0, sizeof ( hijack ) );
    307 	if ( hijack_open ( interface, &hijack ) < 0 )
    308 		goto err;
    309 
    310 	/* Do the forwarding */
    311 	max_fd = ( ( fd > hijack.fd ) ? fd : hijack.fd );
    312 	while ( 1 ) {
    313 		/* Wait for available data */
    314 		FD_ZERO ( &fdset );
    315 		FD_SET ( fd, &fdset );
    316 		FD_SET ( hijack.fd, &fdset );
    317 		if ( select ( ( max_fd + 1 ), &fdset, NULL, NULL, 0 ) < 0 ) {
    318 			logmsg ( LOG_ERR, "select failed: %s\n",
    319 				 strerror ( errno ) );
    320 			goto err;
    321 		}
    322 		if ( FD_ISSET ( fd, &fdset ) ) {
    323 			len = forward_from_hijacker ( &hijack, fd );
    324 			if ( len < 0 )
    325 				goto err;
    326 			if ( len == 0 )
    327 				break;
    328 		}
    329 		if ( FD_ISSET ( hijack.fd, &fdset ) ) {
    330 			len = forward_to_hijacker ( fd, &hijack );
    331 			if ( len < 0 )
    332 				goto err;
    333 			if ( len == 0 )
    334 				break;
    335 		}
    336 	}
    337 
    338 	hijack_close ( &hijack );
    339 	logmsg ( LOG_INFO, "closed connection for %s\n", interface );
    340 	logmsg ( LOG_INFO, "received %ld packets, sent %ld packets\n",
    341 		 hijack.rx_count, hijack.tx_count );
    342 
    343 	return 0;
    344 
    345  err:
    346 	if ( hijack.pcap )
    347 		hijack_close ( &hijack );
    348 	return -1;
    349 }
    350 
    351 /**
    352  * Open listener socket
    353  *
    354  */
    355 static int open_listener ( const char *interface,
    356 			   struct hijack_listener *listener ) {
    357 
    358 	/* Create socket */
    359 	listener->fd = socket ( PF_UNIX, SOCK_SEQPACKET, 0 );
    360 	if ( listener->fd < 0 ) {
    361 		logmsg ( LOG_ERR, "Could not create socket: %s\n",
    362 			 strerror ( errno ) );
    363 		goto err;
    364 	}
    365 
    366 	/* Bind to local filename */
    367 	listener->sun.sun_family = AF_UNIX,
    368 	snprintf ( listener->sun.sun_path, sizeof ( listener->sun.sun_path ),
    369 		   "/var/run/hijack-%s", interface );
    370 	if ( bind ( listener->fd, ( struct sockaddr * ) &listener->sun,
    371 		    sizeof ( listener->sun ) ) < 0 ) {
    372 		logmsg ( LOG_ERR, "Could not bind socket to %s: %s\n",
    373 			 listener->sun.sun_path, strerror ( errno ) );
    374 		goto err;
    375 	}
    376 
    377 	/* Set as a listening socket */
    378 	if ( listen ( listener->fd, 0 ) < 0 ) {
    379 		logmsg ( LOG_ERR, "Could not listen to %s: %s\n",
    380 			 listener->sun.sun_path, strerror ( errno ) );
    381 		goto err;
    382 	}
    383 
    384 	return 0;
    385 
    386  err:
    387 	if ( listener->fd >= 0 )
    388 		close ( listener->fd );
    389 	return -1;
    390 }
    391 
    392 /**
    393  * Listen on listener socket
    394  *
    395  */
    396 static int listen_for_hijackers ( struct hijack_listener *listener,
    397 				  const char *interface ) {
    398 	int fd;
    399 	pid_t child;
    400 	int rc;
    401 
    402 	logmsg ( LOG_INFO, "Listening on %s\n", listener->sun.sun_path );
    403 
    404 	while ( ! signalled ) {
    405 		/* Accept new connection, interruptibly */
    406 		siginterrupt ( SIGINT, 1 );
    407 		siginterrupt ( SIGHUP, 1 );
    408 		fd = accept ( listener->fd, NULL, 0 );
    409 		siginterrupt ( SIGINT, 0 );
    410 		siginterrupt ( SIGHUP, 0 );
    411 		if ( fd < 0 ) {
    412 			if ( errno == EINTR ) {
    413 				continue;
    414 			} else {
    415 				logmsg ( LOG_ERR, "accept failed: %s\n",
    416 					 strerror ( errno ) );
    417 				goto err;
    418 			}
    419 		}
    420 
    421 		/* Fork child process */
    422 		child = fork();
    423 		if ( child < 0 ) {
    424 			logmsg ( LOG_ERR, "fork failed: %s\n",
    425 				 strerror ( errno ) );
    426 			goto err;
    427 		}
    428 		if ( child == 0 ) {
    429 			/* I am the child; run the hijacker */
    430 			rc = run_hijacker ( interface, fd );
    431 			close ( fd );
    432 			exit ( rc );
    433 		}
    434 
    435 		close ( fd );
    436 	}
    437 
    438 	logmsg ( LOG_INFO, "Stopped listening on %s\n",
    439 		 listener->sun.sun_path );
    440 	return 0;
    441 
    442  err:
    443 	if ( fd >= 0 )
    444 		close ( fd );
    445 	return -1;
    446 }
    447 
    448 /**
    449  * Close listener socket
    450  *
    451  */
    452 static void close_listener ( struct hijack_listener *listener ) {
    453 	close ( listener->fd );
    454 	unlink ( listener->sun.sun_path );
    455 }
    456 
    457 /**
    458  * Print usage
    459  *
    460  */
    461 static void usage ( char **argv ) {
    462 	logmsg ( LOG_ERR,
    463 		 "Usage: %s [options]\n"
    464 		 "\n"
    465 		 "Options:\n"
    466 		 "  -h|--help               Print this help message\n"
    467 		 "  -i|--interface intf     Use specified network interface\n"
    468 		 "  -n|--nodaemon           Run in foreground\n",
    469 		 argv[0] );
    470 }
    471 
    472 /**
    473  * Parse command-line options
    474  *
    475  */
    476 static int parse_options ( int argc, char **argv,
    477 			   struct hijack_options *options ) {
    478 	static struct option long_options[] = {
    479 		{ "interface", 1, NULL, 'i' },
    480 		{ "nodaemon", 0, NULL, 'n' },
    481 		{ "help", 0, NULL, 'h' },
    482 		{ },
    483 	};
    484 	int c;
    485 
    486 	/* Set default options */
    487 	memset ( options, 0, sizeof ( *options ) );
    488 	strncpy ( options->interface, "eth0", sizeof ( options->interface ) );
    489 	options->daemonise = 1;
    490 
    491 	/* Parse command-line options */
    492 	while ( 1 ) {
    493 		int option_index = 0;
    494 
    495 		c = getopt_long ( argc, argv, "i:hn", long_options,
    496 				  &option_index );
    497 		if ( c < 0 )
    498 			break;
    499 
    500 		switch ( c ) {
    501 		case 'i':
    502 			strncpy ( options->interface, optarg,
    503 				  sizeof ( options->interface ) );
    504 			break;
    505 		case 'n':
    506 			options->daemonise = 0;
    507 			break;
    508 		case 'h':
    509 			usage( argv );
    510 			return -1;
    511 		case '?':
    512 			/* Unrecognised option */
    513 			return -1;
    514 		default:
    515 			logmsg ( LOG_ERR, "Unrecognised option '-%c'\n", c );
    516 			return -1;
    517 		}
    518 	}
    519 
    520 	/* Check there's nothing left over on the command line */
    521 	if ( optind != argc ) {
    522 		usage ( argv );
    523 		return -1;
    524 	}
    525 
    526 	return 0;
    527 }
    528 
    529 /**
    530  * Daemonise
    531  *
    532  */
    533 static int daemonise ( const char *interface ) {
    534 	char pidfile[16 + IF_NAMESIZE + 4]; /* "/var/run/hijack-<intf>.pid" */
    535 	char pid[16];
    536 	int pidlen;
    537 	int fd = -1;
    538 
    539 	/* Daemonise */
    540 	if ( daemon ( 0, 0 ) < 0 ) {
    541 		logmsg ( LOG_ERR, "Could not daemonise: %s\n",
    542 			 strerror ( errno ) );
    543 		goto err;
    544 	}
    545 	daemonised = 1; /* Direct messages to syslog now */
    546 
    547 	/* Open pid file */
    548 	snprintf ( pidfile, sizeof ( pidfile ), "/var/run/hijack-%s.pid",
    549 		   interface );
    550 	fd = open ( pidfile, ( O_WRONLY | O_CREAT | O_TRUNC ),
    551 		    ( S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH ) );
    552 	if ( fd < 0 ) {
    553 		logmsg ( LOG_ERR, "Could not open %s for writing: %s\n",
    554 			 pidfile, strerror ( errno ) );
    555 		goto err;
    556 	}
    557 
    558 	/* Write pid to file */
    559 	pidlen = snprintf ( pid, sizeof ( pid ), "%d\n", getpid() );
    560 	if ( write ( fd, pid, pidlen ) != pidlen ) {
    561 		logmsg ( LOG_ERR, "Could not write %s: %s\n",
    562 			 pidfile, strerror ( errno ) );
    563 		goto err;
    564 	}
    565 
    566 	close ( fd );
    567 	return 0;
    568 
    569  err:
    570 	if ( fd >= 0 )
    571 		close ( fd );
    572 	return -1;
    573 }
    574 
    575 int main ( int argc, char **argv ) {
    576 	struct hijack_options options;
    577 	struct hijack_listener listener;
    578 	struct sigaction sa;
    579 
    580 	/* Parse command-line options */
    581 	if ( parse_options ( argc, argv, &options ) < 0 )
    582 		exit ( 1 );
    583 
    584 	/* Set up syslog connection */
    585 	openlog ( basename ( argv[0] ), LOG_PID, LOG_DAEMON );
    586 
    587 	/* Set up listening socket */
    588 	if ( open_listener ( options.interface, &listener ) < 0 )
    589 		exit ( 1 );
    590 
    591 	/* Daemonise on demand */
    592 	if ( options.daemonise ) {
    593 		if ( daemonise ( options.interface ) < 0 )
    594 			exit ( 1 );
    595 	}
    596 
    597 	/* Avoid creating zombies */
    598 	memset ( &sa, 0, sizeof ( sa ) );
    599 	sa.sa_handler = SIG_IGN;
    600 	sa.sa_flags = SA_RESTART | SA_NOCLDWAIT;
    601 	if ( sigaction ( SIGCHLD, &sa, NULL ) < 0 ) {
    602 		logmsg ( LOG_ERR, "Could not set SIGCHLD handler: %s",
    603 			 strerror ( errno ) );
    604 		exit ( 1 );
    605 	}
    606 
    607 	/* Set 'signalled' flag on SIGINT or SIGHUP */
    608 	sa.sa_handler = flag_signalled;
    609 	sa.sa_flags = SA_RESTART | SA_RESETHAND;
    610 	if ( sigaction ( SIGINT, &sa, NULL ) < 0 ) {
    611 		logmsg ( LOG_ERR, "Could not set SIGINT handler: %s",
    612 			 strerror ( errno ) );
    613 		exit ( 1 );
    614 	}
    615 	if ( sigaction ( SIGHUP, &sa, NULL ) < 0 ) {
    616 		logmsg ( LOG_ERR, "Could not set SIGHUP handler: %s",
    617 			 strerror ( errno ) );
    618 		exit ( 1 );
    619 	}
    620 
    621 	/* Listen for hijackers */
    622 	if ( listen_for_hijackers ( &listener, options.interface ) < 0 )
    623 		exit ( 1 );
    624 
    625 	close_listener ( &listener );
    626 
    627 	return 0;
    628 }
    629