diff options
author | Toni Uhlig <matzeton@googlemail.com> | 2017-12-12 00:17:40 +0100 |
---|---|---|
committer | Toni Uhlig <matzeton@googlemail.com> | 2017-12-12 00:17:40 +0100 |
commit | 8cb97f22fec130583b245326faee023fc4cfcd8f (patch) | |
tree | 5c7f4633039d1ae81344048bb69acd7dd25170ad | |
parent | 2c5bf52173f92ce1f7cad8ba793290dc9dfa0bdd (diff) |
ptunnel-ng:
* options parsing
-rw-r--r-- | options.c | 109 | ||||
-rw-r--r-- | options.h | 9 | ||||
-rw-r--r-- | ptunnel.c | 76 |
3 files changed, 119 insertions, 75 deletions
@@ -31,51 +31,64 @@ struct option_usage { }; static const struct option_usage usage[] = { + /** --magic */ {"magic", 0, OPT_HEX32, {.unum = 0xdeadc0de}, "Set ptunnel magic hexadecimal number. (32-bit unsigned)\n" - "This will be prefixed in all ICMP packets\n" - "and can be used to bypass Cisco IPS\n" + "It is an identifier for all ICMP/UDP packets\n" + "and can be used to bypass Cisco IPS fingerprint scan.\n" "This value has to be the same on the server and client!\n" }, - {"address", 1, OPT_DEC32, {.unum = 0}, + /** --proxy */ + {"address", 1, OPT_DEC32, {.unum = 0}, "Set address of peer running packet forwarder. This causes\n" "ptunnel to operate in forwarding mode - the absence of this\n" "option causes ptunnel to operate in proxy mode.\n" }, + /** --listen */ {"port", 1, OPT_DEC32, {.unum = 2222}, "Set TCP listening port (only used when operating in forward mode)\n" }, + /** --remote-adr */ {"address", 1, OPT_STR, {.str = "127.0.0.1"}, "Set remote proxy destination address if client\n" "Restrict to only this destination address if server\n" }, + /** --remote-port */ {"port", 1, OPT_DEC32, {.unum = 22}, "Set remote proxy destination port if client\n" "Restrict to only this destination port if server\n" }, + /** --connections */ {"connections", 0, OPT_DEC32, {.unum = kMax_tunnels}, "Set maximum number of concurrent tunnels\n" }, + /** --verbosity */ {"level", 0, OPT_DEC32, {.num = kLog_event}, "Verbosity level (-1 to 4, where -1 is no output, and 4 is all output)\n" "The special level 5 (or higher) includes xfer logging (lots of output)\n" }, - {"interface", 0, OPT_STR, {.str = "eth0"}, + /** --libpcap */ + {"interface", 0, OPT_STR, {.str = "eth0"}, "Enable libpcap on the given device.\n" }, + /** --logfile */ {"file", 0, OPT_STR, {.str = "/var/log/ptunnel.log"}, "Specify a file to log to, rather than printing to standard out.\n" }, + /** --statistics */ {NULL, 0, OPT_BOOL, {.num = 0}, "Client only. Enables continuous output of statistics (packet loss, etc.)\n" }, + /** --passwd */ {"password", 0, OPT_STR, {.str = NULL}, "Set password (must be same on client and proxy)\n" "If no password is set, you will be asked during runtime.\n" }, + /** --udp */ {NULL, 0, OPT_BOOL, {.num = 0}, "Toggle use of UDP instead of ICMP. Proxy will listen on port 53 (must be root).\n" }, + /** --unprivileged */ {NULL, 0, OPT_BOOL, {.num = 0}, "Run proxy in unprivileged mode. This causes the proxy to forward\n" "packets using standard echo requests, instead of crafting custom echo replies.\n" @@ -83,28 +96,35 @@ static const struct option_usage usage[] = { "than running in privileged mode.\n" }, #ifndef WIN32 - {NULL, 0, OPT_BOOL, {.num = 0}, + /** --daemon */ + {"pidfile", 0, OPT_STR, {.str = "/run/ptunnel.pid"}, "Run in background, the PID will be written in the file supplied as argument\n" }, + /** --syslog */ {NULL, 0, OPT_BOOL, {.num = 0}, "Output debug to syslog instead of standard out.\n" }, + /** --user */ {"user", 0, OPT_STR, {.str = "nobody"}, "When started in privileged mode, drop down to user's rights as soon as possible\n" }, + /** --group */ {"group", 0, OPT_STR, {.str = "nogroup"}, "When started in privileged mode, drop down to group's rights as soon as possible\n" }, + /** --chroot */ {"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, {.num = 0}, + /** --setcon */ + {NULL, 0, OPT_STR, {.num = 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 */ {"help", 0, OPT_STR, {.str = NULL}, "this\n"}, {NULL,0,OPT_BOOL,{.unum=0},NULL} }; @@ -124,11 +144,11 @@ static struct option long_options[] = { {"udp", no_argument, &opts.udp, 1 }, {"unprivileged", no_argument, &opts.unprivileged, 1 }, #ifndef WIN32 - {"daemon", no_argument, 0, 'd'}, + {"daemon", optional_argument, 0, 'd'}, {"syslog", no_argument, 0, 'S'}, - {"user", required_argument, 0, 'u'}, - {"group", required_argument, 0, 'g'}, - {"chroot", required_argument, 0, 't'}, + {"user", optional_argument, 0, 'u'}, + {"group", optional_argument, 0, 'g'}, + {"chroot", optional_argument, 0, 'C'}, #endif #ifdef HAVE_SELINUX {"setcon", no_argument, 0, 'e'}, @@ -172,6 +192,8 @@ static void set_options_defaults(void) { opts.udp = *(int *) get_default_optval(OPT_BOOL, "udp"); opts.unprivileged = *(int *) get_default_optval(OPT_BOOL, "unprivileged"); #ifndef WIN32 + opts.pid_path = strdup(*(char **)get_default_optval(OPT_STR, "daemon")); + errno = 0; tmp = *(char **) get_default_optval(OPT_STR, "user"); if (NULL == (pwnam = getpwnam(tmp))) @@ -188,6 +210,8 @@ static void set_options_defaults(void) { pt_log(kLog_error, "%s: %s\n", tmp, errno ? strerror(errno) : "unknown group"); else opts.gid = grnam->gr_gid; + + opts.root_dir = strdup(*(char **)get_default_optval(OPT_STR, "chroot")); #endif } @@ -303,7 +327,7 @@ int parse_options(int argc, char **argv) { /* parse command line arguments */ while (1) { - c = getopt_long(argc, argv, "m:p:l::r::R::c:v:a:o::sdSx:u::g::t:eh", &long_options[0], &optind); + c = getopt_long(argc, argv, "m:p:l::r::R::c:v:a::o::sd::Sx:u::g::C::eh", &long_options[0], &optind); if (c == -1) break; switch (c) { @@ -312,19 +336,20 @@ int parse_options(int argc, char **argv) { break; case 'p': opts.mode = kMode_forward; - 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]; + if (opts.given_proxy_hostname) + free(opts.given_proxy_hostname); + opts.given_proxy_hostname = strdup(optarg); break; case 'l': if (optarg) opts.tcp_listen_port = strtoul(optarg, NULL, 10); break; case 'r': - if (optarg) - opts.given_dst_hostname = strdup(optarg); + if (!optarg) + break; + if (opts.given_dst_hostname) + free(opts.given_dst_hostname); + opts.given_dst_hostname = strdup(optarg); break; case 'R': if (optarg) @@ -339,22 +364,27 @@ int parse_options(int argc, char **argv) { opts.log_level = strtol(optarg, NULL, 10); break; case 'a': + opts.pcap = 1; + if (!optarg) + break; if (opts.pcap_device) free(opts.pcap_device); opts.pcap_device = strdup(optarg); break; case 'o': - if (optarg) { - if (opts.log_path) - free(opts.log_path); - opts.log_path = strdup(optarg); - } has_logfile = 1; + if (!optarg) + break; + if (opts.log_path) + free(opts.log_path); + opts.log_path = strdup(optarg); break; case 's': opts.print_stats = !opts.print_stats; break; case 'x': + if (opts.password) + free(opts.password); opts.password = strdup(optarg); pt_log(kLog_debug, "Password set - unauthenicated connections will be refused.\n"); // Compute the password digest @@ -367,8 +397,11 @@ int parse_options(int argc, char **argv) { #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)); + if (!optarg) + break; + if (opts.pid_path) + free(opts.pid_path); + opts.pid_path = strdup(optarg); break; case 'S': opts.use_syslog = 1; @@ -395,7 +428,12 @@ int parse_options(int argc, char **argv) { } opts.gid = grnam->gr_gid; break; - case 't': + case 'C': + opts.chroot = 1; + if (!optarg) + break; + if (opts.root_dir) + free(opts.root_dir); opts.root_dir = strdup(optarg); break; #else @@ -404,34 +442,45 @@ int parse_options(int argc, char **argv) { case 'u': case 'g': case 't': - pt_log(kLog_error, "%s: feature not supported\n", optarg); + pt_log(kLog_error, "%s: feature not supported\n", argv[optind - 1]); exit(1); #endif case 'e': #ifdef HAVE_SELINUX + if (opts.selinux_context) + free(opts.selinux_context); opts.selinux_context = strdup(optarg); break; #else - pt_log(kLog_error, "%s: feature not supported\n", optarg); + pt_log(kLog_error, "%s: feature not supported\n", argv[optind - 1]); exit(1); #endif case 'h': print_usage(argv[0]); _exit(EXIT_SUCCESS); case 0: /* long opt only */ - break; default: - pt_log(kLog_error, "\"%s\": option unknown\n", argv[optind - 1]); break; } } + if (opts.given_proxy_hostname) { + if (NULL == (host_ent = gethostbyname(opts.given_proxy_hostname))) { + pt_log(kLog_error, "Failed to look up %s as proxy address\n", opts.given_proxy_hostname); + return 1; + } + opts.given_proxy_ip = *(uint32_t*)host_ent->h_addr_list[0]; + } + if (NULL == (host_ent = gethostbyname(opts.given_dst_hostname))) { pt_log(kLog_error, "Failed to look up %s as destination address\n", opts.given_dst_hostname); return 1; } opts.given_dst_ip = *(uint32_t*)host_ent->h_addr_list[0]; + if (NULL == (opts.pid_file = fopen(opts.pid_path, "w"))) + pt_log(kLog_error, "Failed to open pidfile: \"%s\", Cause: %s\n", opts.pid_path, strerror(errno)); + if (has_logfile && opts.log_path) { pt_log(kLog_info, "Open Logfile: \"%s\"\n", opts.log_path); tmp_log = fopen(opts.log_path, "a"); @@ -20,6 +20,7 @@ struct options { /** proxy or forwarder? */ int mode; /** Proxy's internet address */ + char *given_proxy_hostname; uint32_t given_proxy_ip; /** Port the client listens on */ uint32_t tcp_listen_port; @@ -32,6 +33,8 @@ struct options { uint32_t max_tunnels; /** Default log level */ int log_level; + /** Non zero value if user wants packet capturing */ + int pcap; /** Device to capture packets from */ char *pcap_device; /** Usually stdout, but can be altered by the user */ @@ -51,6 +54,9 @@ struct options { #ifndef WIN32 /** run as daemon if non zero value */ int daemonize; + /** PIDFILE if running as daemon */ + char *pid_path; + FILE *pid_file; /** log to syslog if non zero value */ int use_syslog; /** UID of the running process */ @@ -58,9 +64,8 @@ struct options { /** GID of the running process */ gid_t gid; /** CHROOT dir */ + int chroot; char *root_dir; - /** PIDFILE */ - FILE *pid_file; #endif #ifdef HAVE_SELINUX @@ -43,6 +43,9 @@ #include "ptunnel.h" #include "options.h" #include "md5.h" +#ifdef HAVE_SELINUX +#include <selinux/selinux.h> +#endif #ifdef WIN32 /* pthread porting to windows */ @@ -69,13 +72,6 @@ } #define strerror(x) print_last_windows_error() #else -#ifdef HAVE_SELINUX - #include <selinux/selinux.h> - static char *selinux_context = NULL; -#endif - static char *root_dir = NULL; - static bool daemonize = false; - static FILE *pid_file = NULL; #endif /* WIN32 */ @@ -83,15 +79,11 @@ pthread_mutex_t chain_lock, // Lock protecting the chain of connections num_threads_lock; // Lock protecting the num_threads variable -bool - pcap = false; // True if user wants packet capturing int num_threads = 0, // Current thread count num_tunnels = 0; // Current tunnel count uint32_t *seq_expiry_tbl = 0; // Table indicating when a connection ID is allowable (used by proxy) -char - *pcap_device = 0; // Device to capture packets from // Some buffer constants const int @@ -139,11 +131,12 @@ int main(int argc, char *argv[]) { seq_expiry_tbl = calloc(65536, sizeof(uint32_t)); // Parse options - parse_options(argc, argv); + if (parse_options(argc, argv)) + return -1; - if (pcap && opts.udp) { + if (opts.pcap && opts.udp) { pt_log(kLog_error, "Packet capture is not supported (or needed) when using UDP for transport.\n"); - pcap = 0; + opts.pcap = 0; } pt_log(kLog_info, "Starting ptunnel v %d.%.2d.\n", kMajor_version, kMinor_version); pt_log(kLog_info, "(c) 2004-2011 Daniel Stoedle, <daniels@cs.uit.no>\n"); @@ -169,14 +162,14 @@ int main(int argc, char *argv[]) { } openlog("ptunnel", LOG_PID, LOG_USER); } - if (NULL != root_dir) { - pt_log(kLog_info, "Restricting file access to %s\n", root_dir); - if (-1 == chdir(root_dir) || -1 == chroot(root_dir)) { - pt_log(kLog_error, "%s: %s\n", root_dir, strerror(errno)); + if (opts.chroot) { + pt_log(kLog_info, "Restricting file access to %s\n", opts.root_dir); + if (-1 == chdir(opts.root_dir) || -1 == chroot(opts.root_dir)) { + pt_log(kLog_error, "%s: %s\n", opts.root_dir, strerror(errno)); exit(1); } } - if (daemonize) { + if (opts.daemonize) { pt_log(kLog_info, "Going to the background.\n"); if (0 < (pid = fork())) exit(0); @@ -191,9 +184,9 @@ int main(int argc, char *argv[]) { if (0 > pid) pt_log(kLog_error, "fork: %s\n", strerror(errno)); else { - if (NULL != pid_file) { - fprintf(pid_file, "%d\n", getpid()); - fclose(pid_file); + if (NULL != opts.pid_file) { + fprintf(opts.pid_file, "%d\n", getpid()); + fclose(opts.pid_file); } if (! freopen("/dev/null", "r", stdin) || ! freopen("/dev/null", "w", stdout) || @@ -237,18 +230,14 @@ int main(int argc, char *argv[]) { else pt_proxy(0); - // Clean up - if (opts.log_file != stdout) - fclose(opts.log_file); - #ifdef WIN32 WSACleanup(); #else - if (NULL != root_dir) - free(root_dir); + if (opts.root_dir) + free(opts.root_dir); #ifdef HAVE_SELINUX - if (NULL != selinux_context) - free(selinux_context); + if (NULL != opts.selinux_context) + free(opts.selinux_context); #endif #endif /* WIN32 */ @@ -426,45 +415,46 @@ void* pt_proxy(void *args) { } } max_sock = fwd_sock+1; - if (pcap) { + if (opts.pcap) { if (opts.udp) { pt_log(kLog_error, "Packet capture is not useful with UDP [should not get here!]!\n"); close(fwd_sock); return 0; } if (!opts.unprivileged) { + memset(&pc, 0, sizeof(pc)); pt_log(kLog_info, "Initializing pcap.\n"); pc.pcap_err_buf = malloc(PCAP_ERRBUF_SIZE); pc.pcap_data_buf = malloc(pcap_buf_size); - pc.pcap_desc = pcap_open_live(pcap_device, pcap_buf_size, 0 /* promiscous */, 50 /* ms */, pc.pcap_err_buf); + pc.pcap_desc = pcap_open_live(opts.pcap_device, pcap_buf_size, 0 /* promiscous */, 50 /* ms */, pc.pcap_err_buf); if (pc.pcap_desc) { - if (pcap_lookupnet(pcap_device, &pc.netp, &pc.netmask, pc.pcap_err_buf) == -1) { + if (pcap_lookupnet(opts.pcap_device, &pc.netp, &pc.netmask, pc.pcap_err_buf) == -1) { pt_log(kLog_error, "pcap error: %s\n", pc.pcap_err_buf); - pcap = 0; + opts.pcap = 0; } pt_log(kLog_verbose, "Network: %s\n", inet_ntoa(*(struct in_addr*)&pc.netp)); pt_log(kLog_verbose, "Netmask: %s\n", inet_ntoa(*(struct in_addr*)&pc.netmask)); if (pcap_compile(pc.pcap_desc, &pc.fp, pcap_filter_program, 0, pc.netp) == -1) { pt_log(kLog_error, "Failed to compile pcap filter program.\n"); pcap_close(pc.pcap_desc); - pcap = 0; + opts.pcap = 0; } else if (pcap_setfilter(pc.pcap_desc, &pc.fp) == -1) { pt_log(kLog_error, "Failed to set pcap filter program.\n"); pcap_close(pc.pcap_desc); - pcap = 0; + opts.pcap = 0; } } else { pt_log(kLog_error, "pcap error: %s\n", pc.pcap_err_buf); - pcap = 0; + opts.pcap = 0; } pc.pkt_q.head = 0; pc.pkt_q.tail = 0; pc.pkt_q.elems = 0; // Check if we have succeeded, and free stuff if not - if (!pcap) { + if (!opts.pcap) { pt_log(kLog_error, "There were errors enabling pcap - pcap has been disabled.\n"); free(pc.pcap_err_buf); free(pc.pcap_data_buf); @@ -487,7 +477,7 @@ void* pt_proxy(void *args) { #ifndef WIN32 #ifdef HAVE_SELINUX - if (opts.uid || opts.gid || selinux_context) + if (opts.uid || opts.gid || opts.selinux_context) #else if (opts.uid || opts.gid) #endif @@ -497,8 +487,8 @@ void* pt_proxy(void *args) { if (opts.uid && -1 == setuid(opts.uid)) pt_log(kLog_error, "setuid(%d): %s\n", opts.uid, strerror(errno)); #ifdef HAVE_SELINUX - if (NULL != selinux_context && -1 == setcon(selinux_context)) - pt_log(kLog_error, "setcon(%s) failed: %s\n", selinux_context, strerror(errno)); + if (NULL != opts.selinux_context && -1 == setcon(opts.selinux_context)) + pt_log(kLog_error, "setcon(%s) failed: %s\n", opts.selinux_context, strerror(errno)); #endif #endif @@ -607,7 +597,7 @@ void* pt_proxy(void *args) { } } pthread_mutex_unlock(&chain_lock); - if (pcap) { + if (opts.pcap) { if (pcap_dispatch(pc.pcap_desc, 32, pcap_packet_handler, (u_char*)&pc.pkt_q) > 0) { pqueue_elem_t *cur; //pt_log(kLog_verbose, "pcap captured %d packets - handling them..\n", pc.pkt_q.elems); @@ -640,7 +630,7 @@ void* pt_proxy(void *args) { xfer.icmp_resent += cur->xfer.icmp_resent; } pthread_mutex_unlock(&chain_lock); - print_statistics(&xfer, (opts.log_level > kLog_verbose ? 0 : 1)); + print_statistics(&xfer, (opts.log_level >= kLog_verbose ? 0 : 1)); last_status_update = now; } } |