diff --git a/.travis.yml b/.travis.yml index 6b18ccc..0f196eb 100644 --- a/.travis.yml +++ b/.travis.yml @@ -10,8 +10,8 @@ compiler: script: - make - sudo make install - - sudo udpxd -l 127.0.0.1:53 -d 8.8.8.8:53 - - sudo udpxd -l [::1]:53 -d [2001:4860:4860::8888]:53 + - sudo udpxd -l 127.0.0.1:53 -d 8.8.8.8:53 -d + - sudo udpxd -l [::1]:53 -t [2001:4860:4860::8888]:53 -d - dig +nocmd +noall +answer google.de a @127.0.0.1 - dig +nocmd +noall +answer google.de a @::1 - sudo killall udpxd diff --git a/appveyor.yml b/appveyor.yml index 75cdf0c..36ec48a 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -25,8 +25,8 @@ install: build_script: - '%CYG_BASH% -lc "cd $APPVEYOR_BUILD_FOLDER; exec 0pw_gid) != 0) { + perror("could not set egid"); + return 1; + } + + if(setgid(pw->pw_gid) != 0) { + perror("could not set gid"); + return 1; + } + + if(setuid(pw->pw_uid) != 0) { + perror("could not set uid"); + return 1; + } + + if(seteuid(pw->pw_uid) != 0) { + perror("could not set euid"); + return 1; + } + + if (setuid(0) != -1) { + fprintf(stderr, "error, managed to regain root privileges after dropping them!\n"); + return 1; + } } + return 0; +} + +int start_listener (char *inip, char *inpt, char *srcip, char *dstip, + char *dstpt, char *pidfile, char *chrootdir, char *user) { + host_t *listen_h, *dst_h, *bind_h; + + int dm = daemonize(pidfile); + switch(dm) { + case -1: + return 1; /* parent, fork error */ + break; + case 0: + break; /* parent, not forking */ + case 1: + return 0; /* parent, fork ok, leave */ + break; + case 2: + break; /* child, fork ok, continue */ + } + listen_h = get_host(inip, atoi(inpt), NULL, NULL); dst_h = get_host(dstip, atoi(dstpt), NULL, NULL); bind_h = NULL; @@ -156,7 +227,18 @@ int start_listener (char *inip, char *inpt, char *srcip, char *dstip, char *dstp else verbose("\n"); } + + if(drop_privileges(user, chrootdir) != 0) { + host_clean(bind_h); + host_clean(listen_h); + host_clean(dst_h); + return 1; + } + close(STDIN_FILENO); + close(STDOUT_FILENO); + close(STDERR_FILENO); + main_loop(listen, listen_h, bind_h, dst_h); host_clean(bind_h); diff --git a/net.h b/net.h index 022ff30..e575d50 100644 --- a/net.h +++ b/net.h @@ -38,6 +38,7 @@ #include #include #include +#include #include "client.h" @@ -54,7 +55,10 @@ void handle_inside(int inside, host_t *listen_h, host_t *bind_h, host_t *dst_h); void handle_outside(int inside, int outside, host_t *outside_h); int main_loop(int listensocket, host_t *listen_h, host_t *bind_h, host_t *dst_h); -int start_listener (char *inip, char *inpt, char *srcip, char *dstip, char *dstpt, char *pidfile); +int start_listener (char *inip, char *inpt, char *srcip, char *dstip, + char *dstpt, char *pidfile, char *chrootdir, char *user); +int daemonize(char *pidfile); +int drop_privileges(char *user, char *chrootdir); int fill_set(fd_set *fds); int get_sender(fd_set *fds); diff --git a/udpxd.1 b/udpxd.1 index 7e2e07e..4c3dc4f 100644 --- a/udpxd.1 +++ b/udpxd.1 @@ -124,7 +124,7 @@ .\" ======================================================================== .\" .IX Title "UDPXD 1" -.TH UDPXD 1 "2015-04-26" "perl v5.14.2" "User Contributed Perl Documentation" +.TH UDPXD 1 "2015-04-27" "perl v5.14.2" "User Contributed Perl Documentation" .\" For nroff, turn off justification. Always turn off hyphenation; it makes .\" way too many mistakes in technical documents. .if n .ad l @@ -139,12 +139,14 @@ udpxd \- A general purpose UDP relay/port forwarder/proxy \& Options: \& \-\-listen \-l listen for incoming requests \& \-\-bind \-b bind ip used for outgoing requests -\& \-\-dest \-d destination to forward requests to -\& \-\-foreground \-f don\*(Aqt fork into background +\& \-\-to \-t destination to forward requests to +\& \-\-daemon \-d daemon mode, fork into background \& \-\-pidfile \-p pidfile, default: /var/run/udpxd.pid +\& \-\-user \-u run as user (only in daemon mode) +\& \-\-chroot \-c chroot to (only in daemon mode) \& \-\-help \-h \-? print help message -\& \-\-version \-v print program version -\& \-\-verbose \-V enable verbose logging +\& \-\-version \-V print program version +\& \-\-verbose \-v enable verbose logging .Ve .SH "DESCRIPTION" .IX Header "DESCRIPTION" @@ -155,7 +157,7 @@ for outgoing packets. .PP It listens on the ip address and port specified with \fB\-l\fR and waits for incoming udp packets. If one arrives, it sends -it to the destination specified with \fB\-d\fR. Responses will +it to the destination specified with \fB\-t\fR. Responses will be sent back accordingly. .PP If \fB\-b\fR has not been specified, udpxd uses the operating @@ -165,23 +167,25 @@ binds to the given ip address and uses this as the source address. .PP In any case, udpxd behaves like a proxy. The receiving end -(\fB\-d\fR) only sees the source ip address of the outgoing +(\fB\-t\fR) only sees the source ip address of the outgoing interface of the system running udpxd or the address specified with \fB\-b\fR. .PP -The options \fB\-l\fR and \fB\-d\fR are mandatory. +The options \fB\-l\fR and \fB\-t\fR are mandatory. .PP -If the option \fB\-f\fR has not been specified, udpxd forks into +If the option \fB\-d\fR has been specified, udpxd forks into the background and becomes a daemon. It writes it pidfile to \&\f(CW\*(C`/var/run/udpxd.pid\*(C'\fR, which can be changed with the \fB\-p\fR -option. +option. If started as root, it also drops privileges to the +user \f(CW\*(C`nobody\*(C'\fR or the user specified with \fB\-u\fR and chroots +to \f(CW\*(C`/var/empty\*(C'\fR or the directory specified with \fB\-c\fR. .PP -\&\fBCaution: udpxd does not drop its privileges. If started as -root, it will continue to run as root. This may change in the -future.\fR +\&\fBCaution: if not running in daemon mode, udpxd does not drop +its privileges and will continue to run as root (if started as +root).\fR .PP Udpxd supports ip version 4 and 6, it doesn't support hostnames, -\&\-l, \-d and \-b must be ip addresses. In order to specify an ipv6 +\&\fB\-l\fR, \fB\-t\fR and \fB\-b\fR must be ip addresses. In order to specify an ipv6 address and a port, use: .PP .Vb 1 @@ -216,7 +220,7 @@ an ntp server in network 192.168.1.0/24; and you dont operate a firewall, nat or routing on 'foo'. Run udpxd like this: .PP .Vb 1 -\& udpxd \-l 10.0.0.1:123 \-d 192.168.1.199:123 +\& udpxd \-l 10.0.0.1:123 \-t 192.168.1.199:123 .Ve .PP Now, if a client with the source ip address 10.0.0.110 sends @@ -244,7 +248,7 @@ In order to use 192.168.1.45 as the source ip address, use the \&\fB\-b\fR parameter: .PP .Vb 1 -\& udpxd \-l 10.0.0.1:123 \-d 192.168.1.199:123 \-b 192.168.1.45 +\& udpxd \-l 10.0.0.1:123 \-t 192.168.1.199:123 \-b 192.168.1.45 .Ve .PP In this case for the client everything looks as before, but the @@ -255,19 +259,18 @@ Here we listen on the ip v6 loopback address and forward traffic to another ip v6 destination address: .PP .Vb 1 -\& udpxd \-l [::1]:53 \-d [2001:4860:4860::8888]:53 +\& udpxd \-l [::1]:53 \-t [2001:4860:4860::8888]:53 .Ve .PP Or, we could listen on an ip v4 address and forward to an ip v6 address: .PP .Vb 1 -\& udpxd \-l 192.168.1.1:53 \-d [2001:4860:4860::8888]:53 +\& udpxd \-l 192.168.1.1:53 \-t [2001:4860:4860::8888]:53 .Ve .SH "FILES" .IX Header "FILES" -\&\fB/var/run/udpxd.pid\fR: created if running in daemon mode (\-f not -specified). +\&\fB/var/run/udpxd.pid\fR: created if running in daemon mode (\fB\-d\fR). .SH "BUGS" .IX Header "BUGS" In order to report a bug, unexpected behavior, feature requests diff --git a/udpxd.c b/udpxd.c index d23f7fb..68cbbf9 100644 --- a/udpxd.c +++ b/udpxd.c @@ -26,7 +26,7 @@ /* global client list */ client_t *clients = NULL; int VERBOSE = 0; -int FORKED = 1; +int FORKED = 0; /* parse ip:port */ int parse_ip(char *src, char *ip, char *pt) { @@ -87,23 +87,47 @@ int parse_ip(char *src, char *ip, char *pt) { +void usage() { + fprintf(stderr, + "Usage: udpxd [-lbdfpvhV]\n\n" + "Options:\n" + "--listen -l listen for incoming requests\n" + "--bind -b bind ip used for outgoing requests\n" + "--to -t destination to forward requests to\n" + "--daemon -d daemon mode, fork into background\n" + "--pidfile -p pidfile, default: /var/run/udpxd.pid\n" + "--user -u run as user (only in daemon mode)\n" + "--chroot -c chroot to (only in daemon mode)\n" + "--help -h -? print help message\n" + "--version -V print program version\n" + "--verbose -v enable verbose logging\n\n" + "Options -l and -t are mandatory.\n\n" + "This is udpxd version %s.\n", UDPXD_VERSION + ); +} + + int main ( int argc, char* argv[] ) { int opt, err; char *inip, *inpt, *srcip, *dstip, *dstpt; char pidfile[MAX_BUFFER_SIZE]; + char user[128]; + char chroot[MAX_BUFFER_SIZE]; err = 0; static struct option longopts[] = { { "listen", required_argument, NULL, 'l' }, { "bind", required_argument, NULL, 'b' }, - { "dest", required_argument, NULL, 'd' }, - { "version", no_argument, NULL, 'v' }, + { "to", required_argument, NULL, 't' }, + { "version", no_argument, NULL, 'V' }, { "help", no_argument, NULL, 'h' }, - { "verbose", no_argument, NULL, 'V' }, - { "foreground",no_argument, NULL, 'f' }, + { "verbose", no_argument, NULL, 'v' }, + { "daemon", no_argument, NULL, 'd' }, { "pidfile", required_argument, NULL, 'p' }, + { "user", required_argument, NULL, 'u' }, + { "chroot", required_argument, NULL, 'c' }, }; if( argc < 2 ) { @@ -112,23 +136,27 @@ int main ( int argc, char* argv[] ) { } srcip = dstip = inip = dstpt = inpt = NULL; + + /* set defaults */ strncpy(pidfile, "/var/run/udpxd.pid", 19); + strncpy(user, "nobody", 7); + strncpy(chroot, "/var/empty", 11); - while ((opt = getopt_long(argc, argv, "l:b:d:vfVh?", longopts, NULL)) != -1) { + while ((opt = getopt_long(argc, argv, "l:b:t:u:c:vdVh?", longopts, NULL)) != -1) { switch (opt) { - case 'v': + case 'V': fprintf(stderr, "This is %s version %s\n", argv[0], UDPXD_VERSION); return 1; break; - case 'f': - FORKED = 0; + case 'd': + FORKED = 1; break; case 'h': case '?': usage(); return 1; break; - case 'V': + case 'v': VERBOSE = 1; break; case 'l': @@ -139,7 +167,7 @@ int main ( int argc, char* argv[] ) { err = 1; } break; - case 'd': + case 't': dstip = malloc(INET6_ADDRSTRLEN+1); dstpt = malloc(6); if (parse_ip(optarg, dstip, dstpt) != 0) { @@ -158,6 +186,12 @@ int main ( int argc, char* argv[] ) { case 'p': strncpy(pidfile, optarg, strlen(optarg)); break; + case 'u': + strncpy(user, optarg, strlen(optarg)); + break; + case 'c': + strncpy(chroot, optarg, strlen(optarg)); + break; default: usage(); return 1; @@ -172,7 +206,7 @@ int main ( int argc, char* argv[] ) { } if(dstip == NULL) { - fprintf(stderr, "-d parameter is required!\n"); + fprintf(stderr, "-t parameter is required!\n"); usage(); err = 1; } @@ -185,7 +219,7 @@ int main ( int argc, char* argv[] ) { } if(! err) { - err = start_listener (inip, inpt, srcip, dstip, dstpt, pidfile); + err = start_listener (inip, inpt, srcip, dstip, dstpt, pidfile, chroot, user); } if(srcip != NULL) @@ -201,20 +235,3 @@ int main ( int argc, char* argv[] ) { return err; } - -void usage() { - fprintf(stderr, - "Usage: udpxd [-lbdfpvhV]\n\n" - "Options:\n" - "--listen -l listen for incoming requests\n" - "--bind -b bind ip used for outgoing requests\n" - "--dest -d destination to forward requests to\n" - "--foreground -f don't fork into background\n" - "--pidfile -p pidfile, default: /var/run/udpxd.pid\n" - "--help -h -? print help message\n" - "--version -v print program version\n" - "--verbose -V enable verbose logging\n\n" - "Options -l and -d are mandatory.\n\n" - "This is udpxd version %s.\n", UDPXD_VERSION - ); -} diff --git a/udpxd.h b/udpxd.h index 1523409..344bf8f 100644 --- a/udpxd.h +++ b/udpxd.h @@ -36,7 +36,7 @@ #include #include -#define UDPXD_VERSION "0.0.2" +#define UDPXD_VERSION "0.0.3" void usage(); diff --git a/udpxd.pod b/udpxd.pod index 812fef7..71936a4 100644 --- a/udpxd.pod +++ b/udpxd.pod @@ -9,12 +9,14 @@ udpxd - A general purpose UDP relay/port forwarder/proxy Options: --listen -l listen for incoming requests --bind -b bind ip used for outgoing requests - --dest -d destination to forward requests to - --foreground -f don't fork into background + --to -t destination to forward requests to + --daemon -d daemon mode, fork into background --pidfile -p pidfile, default: /var/run/udpxd.pid + --user -u run as user (only in daemon mode) + --chroot -c chroot to (only in daemon mode) --help -h -? print help message - --version -v print program version - --verbose -V enable verbose logging + --version -V print program version + --verbose -v enable verbose logging =head1 DESCRIPTION @@ -25,7 +27,7 @@ for outgoing packets. It listens on the ip address and port specified with B<-l> and waits for incoming udp packets. If one arrives, it sends -it to the destination specified with B<-d>. Responses will +it to the destination specified with B<-t>. Responses will be sent back accordingly. If B<-b> has not been specified, udpxd uses the operating @@ -35,23 +37,27 @@ binds to the given ip address and uses this as the source address. In any case, udpxd behaves like a proxy. The receiving end -(B<-d>) only sees the source ip address of the outgoing +(B<-t>) only sees the source ip address of the outgoing interface of the system running udpxd or the address specified with B<-b>. -The options B<-l> and B<-d> are mandatory. +The options B<-l> and B<-t> are mandatory. -If the option B<-f> has not been specified, udpxd forks into +If the option B<-d> has been specified, udpxd forks into the background and becomes a daemon. It writes it pidfile to C, which can be changed with the B<-p> -option. +option. If started as root, it also drops privileges to the +user C or the user specified with B<-u> and chroots +to C or the directory specified with B<-c>. udpxd +will log to syslog facility user.info if B<-v> is specified and +if running in daemon mode. -B +B Udpxd supports ip version 4 and 6, it doesn't support hostnames, --l, -d and -b must be ip addresses. In order to specify an ipv6 +B<-l>, B<-t> and B<-b> must be ip addresses. In order to specify an ipv6 address and a port, use: -l [::1]:53 @@ -80,7 +86,7 @@ And let's say, you have a client in network 10.0.0.0/24 who whiches to reach an ntp server in network 192.168.1.0/24; and you dont operate a firewall, nat or routing on 'foo'. Run udpxd like this: - udpxd -l 10.0.0.1:123 -d 192.168.1.199:123 + udpxd -l 10.0.0.1:123 -t 192.168.1.199:123 Now, if a client with the source ip address 10.0.0.110 sends a ntp request to 10.0.0.1:123, udpxd will forward that @@ -104,7 +110,7 @@ of the interface for outgoing packets. In order to use 192.168.1.45 as the source ip address, use the B<-b> parameter: - udpxd -l 10.0.0.1:123 -d 192.168.1.199:123 -b 192.168.1.45 + udpxd -l 10.0.0.1:123 -t 192.168.1.199:123 -b 192.168.1.45 In this case for the client everything looks as before, but the ntp server on the other end will see ntp requests coming from @@ -113,17 +119,16 @@ ntp server on the other end will see ntp requests coming from Here we listen on the ip v6 loopback address and forward traffic to another ip v6 destination address: - udpxd -l [::1]:53 -d [2001:4860:4860::8888]:53 + udpxd -l [::1]:53 -t [2001:4860:4860::8888]:53 Or, we could listen on an ip v4 address and forward to an ip v6 address: - udpxd -l 192.168.1.1:53 -d [2001:4860:4860::8888]:53 + udpxd -l 192.168.1.1:53 -t [2001:4860:4860::8888]:53 =head1 FILES -B: created if running in daemon mode (-f not -specified). +B: created if running in daemon mode (B<-d>). =head1 BUGS