diff options
-rwxr-xr-x | md5.h | 3 | ||||
-rw-r--r-- | options.c | 151 | ||||
-rw-r--r-- | options.h | 20 | ||||
-rwxr-xr-x | ptunnel.c | 59 | ||||
-rwxr-xr-x | ptunnel.h | 53 |
5 files changed, 186 insertions, 100 deletions
@@ -62,6 +62,7 @@ typedef unsigned char md5_byte_t; /* 8-bit byte */ typedef unsigned int md5_word_t; /* 32-bit word */ +#define MD5_LEN 16 /* Define the state of the MD5 Algorithm. */ typedef struct md5_state_s { @@ -82,7 +83,7 @@ void md5_init(md5_state_t *pms); void md5_append(md5_state_t *pms, const md5_byte_t *data, int nbytes); /* Finish the message and return the digest. */ -void md5_finish(md5_state_t *pms, md5_byte_t digest[16]); +void md5_finish(md5_state_t *pms, md5_byte_t digest[MD5_LEN]); #ifdef __cplusplus } /* end extern "C" */ @@ -8,6 +8,11 @@ #include "options.h" #include "utils.h" +#include "ptunnel.h" +#include "md5.h" + + +struct options opts; enum option_type { OPT_BOOL, OPT_DEC32, OPT_HEX32, OPT_STR @@ -90,11 +95,14 @@ static const struct option_usage usage[] = { {"directory", 0, OPT_STR, {.str = "/var/lib/ptunnel"}, "When started in privileged mode, restrict file access to the specified directory\n" }, +#endif +#ifdef HAVE_SELINUX {NULL, 0, OPT_BOOL, {.unum = 0}, "Set SELinux context when all there is left to do are network I/O operations\n" "To combine with -chroot you will have to `mount --bind /proc /chrootdir/proc`\n" }, #endif + {"help", 0, OPT_STR, {.str = NULL}, "this\n"}, {NULL,0,OPT_BOOL,{.unum=0},NULL} }; @@ -105,23 +113,29 @@ static struct option long_options[] = { {"remote", required_argument, 0, 'r'}, {"connections", required_argument, 0, 'c'}, {"verbosity", required_argument, 0, 'v'}, - {"libpcap", no_argument, 0, 0 }, + {"libpcap", required_argument, 0, 'a'}, {"logfile", required_argument, 0, 'o'}, {"statistics", no_argument, 0, 's'}, #ifndef WIN32 {"daemon", no_argument, 0, 'd'}, - {"syslog", no_argument, 0, 0 }, + {"syslog", no_argument, 0, 'S'}, #endif - {"udp", no_argument, 0, 0 }, - {"passwd", optional_argument, 0, 'x'}, - {"unprivileged", no_argument, 0, 0 }, + {"udp", no_argument, &opts.udp, 1 }, + {"passwd", required_argument, 0, 'x'}, + {"unprivileged", no_argument, &opts.unprivledged, 1 }, +#ifndef WIN32 {"user", required_argument, 0, 'u'}, {"group", required_argument, 0, 'g'}, - {"chroot", optional_argument, 0, 0 }, + {"chroot", required_argument, 0, 't'}, +#endif +#ifdef HAVE_SELINUX {"setcon", no_argument, 0, 'e'}, +#endif + {"help", no_argument, 0, 'h'}, {NULL,0,0,0} }; + static void print_multiline(const char *prefix, const char *multiline) { const char sep[] = "\n"; const char *start, *end; @@ -200,7 +214,7 @@ void print_usage(const char *arg0) { assert( ARRAY_SIZE(long_options) == ARRAY_SIZE(usage) ); - printf("ptunnel-ng v%d.%.2d.\n\nUsage: %s", 12, 22, arg0); + printf("ptunnel-ng v%d.%.2d\n\nUsage: %s", kMajor_version, kMinor_version, arg0); /* print (short)help argument line */ for (i = 0; i < ARRAY_SIZE(usage); ++i) { print_short_help(i, 1); @@ -217,10 +231,129 @@ void print_usage(const char *arg0) { for (i = 0; i < ARRAY_SIZE(usage); ++i) { print_long_help(i, 0); } - -_exit(0); } int parse_options(int argc, char **argv) { + int c = 0, optind = -1; + struct hostent *host_ent; + md5_state_t state; +#ifndef WIN32 + struct passwd *pwnam; + struct group *grnam; +#endif + + while (1) { + c = getopt_long(argc, argv, "m:p:l:r:c:v:a:o:sdSx:u:g:t:eh", &long_options[0], &optind); + if (c == -1) break; + + switch (c) { + case 'm': + opts.magic = strtoul(optarg, NULL, 16); + break; + case 'p': + if (NULL == (host_ent = gethostbyname(optarg))) { + pt_log(kLog_error, "Failed to look up %s as proxy address\n", optarg); + return 1; + } + opts.given_proxy_ip = *(uint32_t*)host_ent->h_addr_list[0]; + break; + case 'l': + opts.tcp_listen_port = strtoul(optarg, NULL, 10); + break; + case 'r': + if (NULL == (host_ent = gethostbyname(optarg))) { + pt_log(kLog_error, "Failed to look up %s as destination address\n", optarg); + return 1; + } + opts.given_dst_ip = *(uint32_t*)host_ent->h_addr_list[0]; + break; + case 'c': + opts.max_tunnels = strtoul(optarg, NULL,10); + if (opts.max_tunnels > kMax_tunnels) + opts.max_tunnels = kMax_tunnels; + break; + case 'v': + opts.log_level = strtoul(optarg, NULL, 10); + break; + case 'a': + opts.pcap_device = strdup(optarg); + break; + case 'o': + opts.log_file = fopen(optarg, "a"); + if (!opts.log_file) { + opts.log_file = stdout; + pt_log(kLog_error, "Failed to open log file: '%s'. Cause: % s\n", optarg, strerror(errno)); + pt_log(kLog_error, "Reverting log to standard out.\n"); + } + break; + case 's': + opts.print_stats = !opts.print_stats; + break; + case 'x': + opts.password_digest = (unsigned char *)calloc(MD5_LEN, sizeof(unsigned char)); + pt_log(kLog_debug, "Password set - unauthenicated connections will be refused.\n"); + // Compute the password digest + md5_init(&state); + md5_append(&state, (md5_byte_t*)optarg, strlen(optarg)); + md5_finish(&state, opts.password_digest); + // Hide the password in process listing + memset(optarg, '*', strlen(optarg)); + break; +#ifndef WIN32 + case 'd': + opts.daemonize = true; + if (NULL == (opts.pid_file = fopen(optarg, "w"))) + pt_log(kLog_error, "%s: %s\n", optarg, strerror(errno)); + break; + case 'S': + opts.syslog = 1; + break; + case 'u': + errno = 0; + if (NULL == (pwnam = getpwnam(optarg))) { + pt_log(kLog_error, "%s: %s\n", optarg, errno ? strerror(errno) : "unknown user"); + exit(1); + } + opts.uid = pwnam->pw_uid; + if (!opts.gid) + opts.gid = pwnam->pw_gid; + break; + case 'g': + errno = 0; + if (NULL == (grnam = getgrnam(optarg))) { + pt_log(kLog_error, "%s: %s\n", optarg, errno ? strerror(errno) : "unknown group"); + exit(1); + } + opts.gid = grnam->gr_gid; + break; + case 't': + opts.root_dir = strdup(optarg); + break; +#else + case 'd': + case 'S': + case 'U': + case 'g': + case 't': + pt_log(kLog_error, "%s: feature not supported", optarg); + exit(1); +#endif + case 'e': +#ifdef HAVE_SELINUX + opts.selinux_context = strdup(optarg); + break; +#else + pt_log(kLog_error, "%s: feature not supported", optarg); + exit(1); +#endif + case 'h': + print_usage(argv[0]); + _exit(EXIT_SUCCESS); + default: + printf("Opt ERROR\n"); + break; + } + } + return 0; } @@ -15,10 +15,10 @@ struct options { uint32_t magic; /** Proxy's internet address */ uint32_t given_proxy_ip; - /** Password (must be the same on proxy and client for authentication to succeed) */ - char password; + /** Password-Digest (must be the same on proxy and client for authentication to succeed) */ + unsigned char *password_digest; /** Port the client listens on */ - int tcp_listen_port; + uint16_t tcp_listen_port; /** Proxy's internet address */ uint32_t given_dst_ip; /** Port to send data to from the proxy */ @@ -29,15 +29,19 @@ struct options { int log_level; /** Device to capture packets from */ char *pcap_device; - /** True if user wants packet capturing */ - bool pcap; /** Usually stdout, but can be altered by the user */ FILE *log_file; + /** Print more detailed traffic statistics if non zero value */ + int print_stats; + /** use UDP instead of ICMP */ + int udp; + /** unpriviledged mode */ + int unprivledged; -#ifndef WIN32 #ifdef HAVE_SELINUX char *selinux_context; #endif +#ifndef WIN32 /** UID of the running process */ uid_t uid; /** GID of the running process */ @@ -45,9 +49,11 @@ struct options { /** CHROOT dir */ char *root_dir; /** PIDFILE */ - char *pid_file; + FILE *pid_file; /** run as daemon */ bool daemonize; + /** log to syslog if non zero value */ + int syslog; #endif }; @@ -165,6 +165,8 @@ int main(int argc, char *argv[]) { log_file = stdout; // Parse options + parse_options(argc, argv); + opt = kOpt_undefined; mode = kMode_proxy; for (i=1;i<argc;i++) { @@ -303,14 +305,14 @@ int main(int argc, char *argv[]) { break; #endif /* !WIN32 */ case kOpt_undefined: - usage(argv[0]); + print_usage(argv[0]); return 1; } opt = kOpt_undefined; } } if (opt != kOpt_undefined) { - usage(argv[0]); + print_usage(argv[0]); exit(1); } if (pcap && use_udp) { @@ -398,7 +400,7 @@ int main(int argc, char *argv[]) { if (mode == kMode_forward) { if (!given_proxy_ip || !given_dst_ip || !tcp_port || !tcp_listen_port) { printf("One of the options are missing or invalid.\n"); - usage(argv[0]); + print_usage(argv[0]); return -1; } pt_forwarder(); @@ -425,57 +427,6 @@ int main(int argc, char *argv[]) { return 0; } - -void usage(char *exec_name) { -print_usage(exec_name); - printf("ptunnel v %d.%.2d.\n", kMajor_version, kMinor_version); - printf("Usage: %s -p <addr> -lp <port> -da <dest_addr> -dp <dest_port> [-m max_tunnels] [-v verbosity] [-f logfile]\n", exec_name); - printf(" %s [-m max_threads] [-v verbosity] [-c <device>]\n", exec_name); - printf(" -a: Set ICMP Tunnel magic hexadecimal number e.g. %X\n", magic); - printf(" -p: Set address of peer running packet forwarder. This causes\n"); - printf(" ptunnel to operate in forwarding mode - the absence of this\n"); - printf(" option causes ptunnel to operate in proxy mode.\n"); - printf(" -lp: Set TCP listening port (only used when operating in forward mode)\n"); - printf(" -da: Set remote proxy destination address if client\n"); - printf(" Restrict to only this destination address if server\n"); - printf(" -dp: Set remote proxy destionation port if client\n"); - printf(" Restrict to only this destination port if server\n"); - printf(" -m: Set maximum number of concurrent tunnels\n"); - printf(" -v: Verbosity level (-1 to 4, where -1 is no output, and 4 is all output)\n"); - printf(" -c: Enable libpcap on the given device.\n"); - printf(" -f: Specify a file to log to, rather than printing to standard out.\n"); - printf(" -s: Client only. Enables continuous output of statistics (packet loss, etc.)\n"); - #ifndef WIN32 - printf("-daemon: Run in background, the PID will be written in the file supplied as argument\n"); - printf("-syslog: Output debug to syslog instead of standard out.\n"); - #endif /* !WIN32 */ - printf(" -udp: Toggle use of UDP instead of ICMP. Proxy will listen on port 53 (must be root).\n\n"); - - printf("Security features: [-x password] [-u] [-setuid user] [-setgid group] [-chroot dir]\n"); - printf(" -x: Set password (must be same on client and proxy)\n"); - printf(" -u: Run proxy in unprivileged mode. This causes the proxy to forward\n"); - printf(" packets using standard echo requests, instead of crafting custom echo replies.\n"); - printf(" Unprivileged mode will only work on some systems, and is in general less reliable\n"); - printf(" than running in privileged mode.\n"); - #ifndef WIN32 - printf(" Please consider combining the following three options instead:\n"); - printf("-setuid: When started in privileged mode, drop down to user's rights as soon as possible\n"); - printf("-setgid: When started in privileged mode, drop down to group's rights as soon as possible\n"); - printf("-chroot: When started in privileged mode, restrict file access to the specified directory\n"); - printf("-setcon: Set SELinux context when all there is left to do are network I/O operations\n"); - printf(" To combine with -chroot you will have to `mount --bind /proc /chrootdir/proc`\n"); - #endif /* !WIN32 */ - - printf("\nStarting the proxy (needs to run as root):\n"); - printf(" [root #] %s\n", exec_name); - printf("Starting a client (also needs root):\n"); - printf(" [root #] %s -p proxy.pingtunnel.com -lp 8000 -da login.domain.com -dp 22 -c eth0\n", exec_name); - printf("And then using the tunnel to ssh to login.domain.com:\n"); - printf(" [user $] ssh -p 8000 localhost\n"); - printf("And that's it. Enjoy your tunnel!\n\n"); -} - - /* pt_forwarder: Sets up a listening TCP socket, and forwards incoming connections over ping packets. @@ -45,32 +45,33 @@ // Includes #ifndef WIN32 -#include <sys/unistd.h> -#include <sys/types.h> -#include <sys/socket.h> -#include <netinet/in.h> -#include <arpa/inet.h> -#include <netdb.h> -#include <pthread.h> -#include <errno.h> -#include <net/ethernet.h> -#include <syslog.h> -#include <pwd.h> -#include <grp.h> +#include <sys/unistd.h> +#include <sys/types.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#include <netdb.h> +#include <pthread.h> +#include <errno.h> +#include <net/ethernet.h> +#include <syslog.h> +#include <pwd.h> +#include <grp.h> #endif /* !WIN32 */ -#include <stdarg.h> -#include <unistd.h> -#include <stdio.h> -#include <stdlib.h> -#include <string.h> -#include <time.h> -#include <sys/time.h> -#include <signal.h> -#include <stdint.h> -#include <pcap.h> +#include <stdarg.h> +#include <unistd.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <time.h> +#include <sys/time.h> +#include <signal.h> +#include <stdint.h> +#include <stdbool.h> +#include <pcap.h> #ifdef WIN32 -#include <winsock2.h> +#include <winsock2.h> typedef int socklen_t; typedef uint32_t in_addr_t; #define ETH_ALEN 6 /* Octets in one ethernet addr */ @@ -81,11 +82,6 @@ struct ether_header { }; #endif /* WIN32 */ -// Constants -#define false 0 -#define true 1 -#define bool char - enum { kOpt_undefined = 0, // Constants for parsing options kOpt_set_magic, @@ -427,7 +423,6 @@ typedef struct { // Prototypes (sorry about the long lines..) - void usage(char *exec_name); void* pt_proxy(void *args); void pcap_packet_handler(u_char *refcon, const struct pcap_pkthdr *hdr, const u_char* pkt); void handle_packet(char *buf, int bytes, int is_pcap, struct sockaddr_in *addr, int icmp_sock); |