Makefile.Linux | 4 +- Makefile.unix | 4 +- src/3proxy.c | 123 +++++++++++++++++++++++++++++++++++++++++++++++++++----- src/proxy.h | 2 + src/proxymain.c | 92 ++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 210 insertions(+), 15 deletions(-) diff --git a/Makefile.Linux b/Makefile.Linux index 17513f2..ed787ce 100644 --- a/Makefile.Linux +++ b/Makefile.Linux @@ -11,7 +11,7 @@ BUILDDIR = CC = gcc -CFLAGS = -Wall -g -O2 -c -pthread -DGETHOSTBYNAME_R -D_THREAD_SAFE -D_REENTRANT -DNOODBC -DWITH_STD_MALLOC -DFD_SETSIZE=4096 -DWITH_POLL +CFLAGS = -std=gnu99 -Wall -g -O2 -c -pthread -DGETHOSTBYNAME_R -D_THREAD_SAFE -D_REENTRANT -DNOODBC -DWITH_STD_MALLOC -DFD_SETSIZE=4096 -DWITH_POLL COUT = -o LN = gcc DCFLAGS = -fpic @@ -19,7 +19,7 @@ LDFLAGS = -Wall -O2 -pthread DLFLAGS = -shared DLSUFFICS = .ld.so # -lpthreads may be reuqired on some platforms instead of -pthreads -LIBS = -ldl +LIBS = -ldl -lcap LNOUT = -o EXESUFFICS = OBJSUFFICS = .o diff --git a/Makefile.unix b/Makefile.unix index 1be4eba..06ef321 100644 --- a/Makefile.unix +++ b/Makefile.unix @@ -12,7 +12,7 @@ BUILDDIR = CC = gcc # you may need -L/usr/pkg/lib for older NetBSD versions -CFLAGS = -Wall -g -O2 -c -pthread -D_THREAD_SAFE -D_REENTRANT -DNOODBC -DWITH_STD_MALLOC -DFD_SETSIZE=4096 -DWITH_POLL +CFLAGS = -std=gnu99 -Wall -g -O2 -c -pthread -D_THREAD_SAFE -D_REENTRANT -DNOODBC -DWITH_STD_MALLOC -DFD_SETSIZE=4096 -DWITH_POLL COUT = -o LN = gcc LDFLAGS = -Wall -O2 -pthread @@ -21,7 +21,7 @@ LDFLAGS = -Wall -O2 -pthread DCFLAGS = -fpic DLFLAGS = -shared DLSUFFICS = .ld.so -LIBS = +LIBS = -lcap LNOUT = -o EXESUFFICS = OBJSUFFICS = .o diff --git a/src/3proxy.c b/src/3proxy.c index 193b5ed..0e480e0 100644 --- a/src/3proxy.c +++ b/src/3proxy.c @@ -18,6 +18,11 @@ #define DEFAULTCONFIG conf.stringtable[25] #endif +char *opt_user = NULL; +char *opt_pidfile = NULL; +char *opt_conffile = NULL; +int opt_daemon = 0; + typedef int (*MAINFUNC)(int, char**); pthread_mutex_t bandlim_mutex; @@ -623,6 +628,14 @@ int start_proxy_thread(struct child * chp){ return 0; } +static int h_alt(int argc, unsigned char ** argv) + { + char *cmd = (char *)argv[0]; + fprintf(stderr, "ALTLinux 3proxy does not recognize '%s' command.\n", cmd); + fprintf(stderr, "Use cmdline instead. See README.ALT for details.\n"); + return 0; + } + static int h_proxy(int argc, unsigned char ** argv){ struct child ch; @@ -774,9 +787,8 @@ static int h_service(int argc, unsigned char **argv){ } static int h_daemon(int argc, unsigned char **argv){ - if(!conf.demon)daemonize(); - conf.demon = 1; - return 0; + opt_daemon = 1; + return 0; } static int h_config(int argc, unsigned char **argv){ @@ -1652,9 +1664,9 @@ static int h_chroot(int argc, unsigned char **argv){ struct commands specificcommands[]={ #ifndef _WIN32 - {specificcommands+1, "setuid", h_setuid, 2, 2}, - {specificcommands+2, "setgid", h_setgid, 2, 2}, - {specificcommands+3, "chroot", h_chroot, 2, 2}, + {specificcommands+1, "setuid", h_alt, 2, 2}, + {specificcommands+2, "setgid", h_alt, 2, 2}, + {specificcommands+3, "chroot", h_alt, 2, 2}, #endif {NULL, "", h_noop, 1, 0} }; @@ -1691,7 +1703,7 @@ struct commands commandhandlers[]={ {commandhandlers+29, "nsrecord", h_nsrecord, 3, 3}, {commandhandlers+30, "dialer", h_dialer, 2, 2}, {commandhandlers+31, "system", h_system, 2, 2}, - {commandhandlers+32, "pidfile", h_pidfile, 2, 2}, + {commandhandlers+32, "pidfile", h_alt, 2, 2}, {commandhandlers+33, "monitor", h_monitor, 2, 2}, {commandhandlers+34, "parent", h_parent, 5, 0}, {commandhandlers+35, "allow", h_ace, 1, 0}, @@ -1776,6 +1788,48 @@ int readconfig(FILE * fp){ } +void usage() + { + fprintf(stderr, "Usage: 3proxy [-c conffile] [-u username/uid] [-d] [-p pidfile]\n"); + fprintf(stderr, "\n%s %s\n%s\n", conf.stringtable[2], conf.stringtable[3], copyright); + } + +static int parse_cmdline(int argc, char *argv[]) + { + int opt = 0; + while(1) + { + opt = getopt(argc, argv, "du:c:p:"); + switch(opt) + { + case 'd': // daemonize + opt_daemon = 1; + break; + + case 'u': // username + opt_user = strdup(optarg); + break; + + case 'c': // config file + opt_conffile = strdup(optarg); + break; + + case 'p': // pidfile + opt_pidfile = strdup(optarg); + break; + + case ':': // option value not found + case '?': // option or value not found + usage(); + return -1; + + } + + if(opt == -1) + return 0; + } + } + int main(int argc, char * argv[]) { @@ -1892,27 +1946,61 @@ int main(int argc, char * argv[]) { service = 1; argc = 2; } -#endif conf.conffile = mystrdup((argc==2)?argv[1]:(char*)DEFAULTCONFIG); +#else + parse_cmdline(argc, argv); + conf.conffile = opt_conffile ? opt_conffile : DEFAULTCONFIG; + + FILE *pidfile = NULL; + if(opt_pidfile) + { + pidfile = fopen(opt_pidfile, "w"); + if(!pidfile) + { + fprintf(stderr, "open pidfile %s failed", opt_pidfile); + exit(1); + } + } + + /* chroot */ + if(chrootp){ + char *p; + if(chroot(chrootp)) { + fprintf(stderr, "Unable to chroot %s", chrootp); + return(1); + } + + /* strip slashes */ + p = chrootp + strlen((char *)chrootp) ; + while (p > chrootp && p[-1] == '/'){ + p--; + *p = 0; + } + } + + /* setuid with cap_net_bind */ + if(opt_user) + drop_root(opt_user); + +#endif if(conf.conffile && *conf.conffile != '-') { fp = confopen(); #ifndef _WIN32 if(!fp) fp = stdin; #endif } +#ifdef _WIN32 if(argc > 2 || !(fp)) { fprintf(stderr, "Usage: %s [conffile]\n", argv[0]); -#ifdef _WIN32 fprintf(stderr, "\n\t%s --install [conffile]\n\tto install as service\n" "\n\t%s --remove\n\tto remove service\n", argv[0], argv[0]); -#else fprintf(stderr, "\n if conffile is missing, configuration is expected from stdin\n"); -#endif fprintf(stderr, "\n%s %s\n%s\n", conf.stringtable[2], conf.stringtable[3], copyright); return 1; } +#endif pthread_mutex_init(&bandlim_mutex, NULL); pthread_mutex_init(&hash_mutex, NULL); @@ -1943,8 +2031,21 @@ int main(int argc, char * argv[]) { #else + if(opt_daemon) + { + daemonize(); + conf.demon = 1; + } + + if(opt_pidfile) + { + fprintf(pidfile, "%d\n", getpid()); + fclose(pidfile); + } + signal(SIGCONT, mysigpause); signal(SIGTERM, mysigterm); + signal(SIGINT, mysigterm); signal(SIGUSR1, mysigusr1); signal(SIGPIPE, SIG_IGN); cyclestep(); diff --git a/src/proxy.h b/src/proxy.h index d26b16b..15ec48d 100644 --- a/src/proxy.h +++ b/src/proxy.h @@ -305,5 +305,7 @@ extern struct commands commandhandlers[]; #define WEBBANNERS 35 +void drop_root(const char *username); + #endif diff --git a/src/proxymain.c b/src/proxymain.c index ab5b543..548c381 100644 --- a/src/proxymain.c +++ b/src/proxymain.c @@ -16,6 +16,12 @@ extern int haveerror; #endif +#include +#include +#include +#include +#include +#include int MODULEMAINFUNC (int argc, char** argv){ @@ -38,6 +44,7 @@ int MODULEMAINFUNC (int argc, char** argv){ #ifndef _WIN32 " -I inetd mode (requires real socket, doesn't work with TTY)\n" " -l@IDENT log to syslog IDENT\n" + " -Uuser user name for changing to\n"; #endif " -d go to background (daemon)\n" #else @@ -99,6 +106,13 @@ int MODULEMAINFUNC (int argc, char** argv){ for (i=1; ipw_uid; + gid = pw->pw_gid; + } + + if(uid == 0) // setuid to root, nonsense but allowed + return; + +// tzset(); + + if(initgroups(username, gid)) + { + fprintf(stderr, "Failed to lower privileges: initgroups: %s/%u", username, (unsigned) gid); + exit(1); + } + + endpwent(); + + if (setgid(gid)) + { + fprintf(stderr, "Failed to lower privileges: setgid: %s/%u", username, (unsigned) gid); + exit(1); + } + + if (prctl(PR_SET_KEEPCAPS, 1)) + { + fprintf(stderr, "Failed to lower privileges: prctl"); + exit(1); + } + + if (setreuid(uid, uid)) + { + fprintf(stderr, "Failed to lower privileges: setreuid: %s/%u", + username, (unsigned) uid); + exit(1); + } + + cap_t caps = cap_from_text("cap_net_bind_service=ep"); + + if (!caps) + { + fprintf(stderr, "Failed to lower privileges: cap_from_text"); + exit(1); + } + + if (cap_set_proc(caps) < 0) + { + fprintf(stderr, "Failed to lower privileges: cap_set_proc"); + exit(1); + } + + cap_free(caps); + } + void srvinit(struct srvparam * srv, struct clientparam *param){ memset(srv, 0, sizeof(struct srvparam));