diff --git a/docs/compile/openwrt/package/zapret/nfqws/Makefile b/docs/compile/openwrt/package/zapret/nfqws/Makefile index 5e9bcc1..a2ff05d 100644 --- a/docs/compile/openwrt/package/zapret/nfqws/Makefile +++ b/docs/compile/openwrt/package/zapret/nfqws/Makefile @@ -12,7 +12,7 @@ define Package/nfqws CATEGORY:=Network TITLE:=nfqws SUBMENU:=Zapret - DEPENDS:=+libnetfilter-queue + DEPENDS:=+libnetfilter-queue +libcap endef define Build/Prepare diff --git a/docs/readme.eng.txt b/docs/readme.eng.txt index 88570ea..8b28c02 100644 --- a/docs/readme.eng.txt +++ b/docs/readme.eng.txt @@ -136,6 +136,8 @@ It takes the following parameters: --hostnospace ; remove space after Host: and add it to User-Agent: to preserve packet size --daemon ; daemonize --pidfile= ; write pid to file + --user= ; drop root privs + --uid=uid[:gid] ; drop root privs The manipulation parameters can be combined in any way. @@ -173,7 +175,7 @@ tpws is transparent proxy. --daemon ; daemonize --pidfile= ; write pid to file --user= ; drop root privs - --uid=uid[:gid] ; менять uid процесса + --uid=uid[:gid] ; drop root privs The manipulation parameters can be combined in any way. There are exceptions: split-pos replaces split-http-req. hostdot and hosttab are mutually exclusive. diff --git a/docs/readme.txt b/docs/readme.txt index 823cc98..17208de 100644 --- a/docs/readme.txt +++ b/docs/readme.txt @@ -137,6 +137,8 @@ nfqws Она берет следующие параметры : --daemon ; демонизировать прогу --pidfile= ; сохранить PID в файл + --user= ; менять uid процесса + --uid=uid[:gid] ; менять uid процесса --qnum=200 ; номер очереди --wsize=4 ; менять tcp window size на указанный размер --hostcase ; менять регистр заголовка "Host:" по умолчанию на "host:". diff --git a/init.d/openwrt/zapret b/init.d/openwrt/zapret index 4e2221e..af63d28 100755 --- a/init.d/openwrt/zapret +++ b/init.d/openwrt/zapret @@ -14,8 +14,9 @@ ZAPRET_BASE=/opt/zapret PIDDIR=/var/run QNUM=200 +NFQWS_USER=daemon NFQWS=$ZAPRET_BASE/nfq/nfqws -NFQWS_OPT_BASE="--qnum=$QNUM" +NFQWS_OPT_BASE="--qnum=$QNUM --user=$NFQWS_USER" TPWS_USER=daemon TPPORT_HTTP=1188 diff --git a/init.d/sysv/functions b/init.d/sysv/functions index 6c406f9..585f768 100644 --- a/init.d/sysv/functions +++ b/init.d/sysv/functions @@ -8,20 +8,21 @@ PIDDIR=/var/run IPSET_CR=$ZAPRET_BASE/ipset/create_ipset.sh +WS_USER=tpws + QNUM=200 NFQWS=$ZAPRET_BASE/nfq/nfqws -NFQWS_OPT_BASE="--qnum=$QNUM" +NFQWS_OPT_BASE="--qnum=$QNUM --user=$WS_USER" -TPWS_USER=tpws TPPORT_HTTP=1188 TPPORT_HTTPS=1189 TPWS=$ZAPRET_BASE/tpws/tpws TPWS_HOSTLIST=$ZAPRET_BASE/ipset/zapret-hosts.txt.gz [ -f "$TPWS_HOSTLIST" ] || TPWS_HOSTLIST=$ZAPRET_BASE/ipset/zapret-hosts-user.txt -TPWS_OPT_BASE="--user=$TPWS_USER --bind-addr=127.0.0.1" -TPWS_OPT_BASE6="--user=$TPWS_USER --bind-addr=::1" +TPWS_OPT_BASE="--user=$WS_USER --bind-addr=127.0.0.1" +TPWS_OPT_BASE6="--user=$WS_USER --bind-addr=::1" # first wait for lan to ifup, then wait for bind-wait-ip-linklocal seconds for link local address and bind-wait-ip for any ipv6 as the worst case -TPWS_OPT_BASE6_PRE="--user=$TPWS_USER --bind-linklocal=prefer --bind-wait-ifup=30 --bind-wait-ip=30 --bind-wait-ip-linklocal=3" +TPWS_OPT_BASE6_PRE="--user=$WS_USER --bind-linklocal=prefer --bind-wait-ifup=30 --bind-wait-ip=30 --bind-wait-ip-linklocal=3" TPWS_OPT_BASE_HTTP="--port=$TPPORT_HTTP" TPWS_OPT_BASE_HTTPS="--port=$TPPORT_HTTPS" @@ -169,7 +170,7 @@ fw_tpws() [ -n "$IFACE_LAN" ] && { ipt_add_del $1 PREROUTING -t nat $IPT_ILAN -p tcp $2 -j DNAT --to 127.0.0.1:$4 } - ipt_add_del $1 OUTPUT -t nat $IPT_OWAN -m owner ! --uid-owner $TPWS_USER -p tcp $2 -j DNAT --to 127.0.0.1:$4 + ipt_add_del $1 OUTPUT -t nat $IPT_OWAN -m owner ! --uid-owner $WS_USER -p tcp $2 -j DNAT --to 127.0.0.1:$4 } [ "$DISABLE_IPV6" = "1" ] || { print_op $1 "$3" "tpws" 6 @@ -177,7 +178,7 @@ fw_tpws() dnat6_target [ "$DNAT6_TARGET" != "-" ] && ipt6_add_del $1 PREROUTING -t nat $IPT_ILAN -p tcp $3 -j DNAT --to [$DNAT6_TARGET]:$4 } - ipt6_add_del $1 OUTPUT -t nat $IPT_OWAN -m owner ! --uid-owner $TPWS_USER -p tcp $3 -j DNAT --to [::1]:$4 + ipt6_add_del $1 OUTPUT -t nat $IPT_OWAN -m owner ! --uid-owner $WS_USER -p tcp $3 -j DNAT --to [::1]:$4 } } fw_nfqws_pre() @@ -266,16 +267,25 @@ do_daemon() } -prepare_tpws() +prepare_user() { - # $TPWS_USER is required to prevent redirection of the traffic originating from TPWS itself + # $WS_USER is required to prevent redirection of the traffic originating from TPWS itself # otherwise infinite loop will occur # also its good idea not to run tpws as root - id -u $TPWS_USER >/dev/null 2>/dev/null || useradd --no-create-home --system --shell /bin/false $TPWS_USER + id -u $WS_USER >/dev/null 2>/dev/null || useradd --no-create-home --system --shell /bin/false $WS_USER +} +prepare_tpws() +{ + prepare_user + # otherwise linux kernel will treat 127.0.0.1 as "martian" ip and refuse routing to it # NOTE : kernels <3.6 do not have this feature. consider upgrading or change DNAT to REDIRECT and do not bind to 127.0.0.1 [ -n "$IFACE_LAN" ] && sysctl -qw net.ipv4.conf.$IFACE_LAN.route_localnet=1 } +prepare_nfqws() +{ + prepare_user +} do_tpws() { # $1 : 1 - run, 0 - stop @@ -289,6 +299,15 @@ do_tpws() [ -n "$IFACE_LAN" ] && do_daemon $1 $((660+$2)) $TPWS "$TPWS_OPT_BASE6_PRE --bind-iface6=$IFACE_LAN $3" } } +do_nfqws() +{ + # $1 : 1 - run, 0 - stop + # $2 : daemon number + # $3 : daemon args + + [ "$1" = "1" ] && prepare_nfqws + do_daemon $1 $2 $NFQWS "$NFQWS_OPT_BASE $3" +} create_ipset() @@ -385,7 +404,7 @@ zapret_do_daemons() do_tpws $1 1 "$TPWS_OPT_BASE_HTTP $TPWS_OPT_HTTP" ;; nfqws_ipset|nfqws_ipset_https|nfqws_all|nfqws_all_https) - do_daemon $1 1 $NFQWS "$NFQWS_OPT_BASE $NFQWS_OPT" + do_nfqws $1 1 "$NFQWS_OPT" ;; custom) # PLACEHOLDER diff --git a/nfq/Makefile b/nfq/Makefile index 0176df4..9af1acc 100644 --- a/nfq/Makefile +++ b/nfq/Makefile @@ -1,6 +1,6 @@ CC ?= gcc -CFLAGS ?= -s -O3 -LIBS = -lnetfilter_queue -lnfnetlink +CFLAGS += -s -O3 +LIBS = -lnetfilter_queue -lnfnetlink -lcap SRC_FILES = *.c all: nfqws diff --git a/nfq/nfqws.c b/nfq/nfqws.c index 229379a..2304aee 100644 --- a/nfq/nfqws.c +++ b/nfq/nfqws.c @@ -13,6 +13,8 @@ #include #include #include +#include +#include bool proto_check_ipv4(unsigned char *data,int len) { @@ -363,22 +365,52 @@ static int cb(struct nfq_q_handle *qh, struct nfgenmsg *nfmsg, return nfq_set_verdict(qh, id, NF_ACCEPT, 0, NULL); } +bool dropcaps() +{ + cap_value_t cap_values[] = {CAP_NET_ADMIN}; + cap_t capabilities; + if (!(capabilities = cap_init())) + { + perror("cap_init"); + return false; + } + if (cap_set_flag(capabilities, CAP_PERMITTED, sizeof(cap_values)/sizeof(*cap_values), cap_values, CAP_SET) || + cap_set_flag(capabilities, CAP_EFFECTIVE, sizeof(cap_values)/sizeof(*cap_values), cap_values, CAP_SET)) + { + perror("cap_set_flag"); + cap_free(capabilities); + return false; + } + if (cap_set_proc(capabilities)) + { + perror("cap_set_proc"); + cap_free(capabilities); + return false; + } + cap_free(capabilities); + return true; +} bool droproot(uid_t uid, gid_t gid) { - if (uid) - { - if (setgid(gid)) - { - perror("setgid: "); - return false; - } - if (setuid(uid)) - { - perror("setuid: "); - return false; - } - } - return true; + if (uid || gid) + { + if (prctl(PR_SET_KEEPCAPS, 1L)) + { + perror("prctl(PR_SET_KEEPCAPS): "); + return false; + } + if (setgid(gid)) + { + perror("setgid: "); + return false; + } + if (setuid(uid)) + { + perror("setuid: "); + return false; + } + } + return dropcaps(); } void daemonize() @@ -424,14 +456,16 @@ bool writepid(const char *filename) void exithelp() { printf( - " --qnum=\n" - " --wsize=\t; set window size. 0 = do not modify\n" - " --hostcase\t\t; change Host: => host:\n" - " --hostspell\t\t; exact spelling of \"Host\" header. must be 4 chars. default is \"host\"\n" - " --hostnospace\t\t; remove space after Host: and add it to User-Agent: to preserve packet size\n" - " --daemon\t\t; daemonize\n" - " --pidfile=\t; write pid to file\n" - ); + " --qnum=\n" + " --wsize=\t; set window size. 0 = do not modify\n" + " --hostcase\t\t; change Host: => host:\n" + " --hostspell\t\t; exact spelling of \"Host\" header. must be 4 chars. default is \"host\"\n" + " --hostnospace\t\t; remove space after Host: and add it to User-Agent: to preserve packet size\n" + " --daemon\t\t; daemonize\n" + " --pidfile=\t; write pid to file\n" + " --user=\t; drop root privs\n" + " --uid=uid[:gid]\t; drop root privs\n" + ); exit(1); } @@ -447,7 +481,7 @@ int main(int argc, char **argv) int v; bool daemon=false; uid_t uid=0; - gid_t gid; + gid_t gid=0; char pidfile[256]; memset(&cbdata,0,sizeof(cbdata)); @@ -455,15 +489,16 @@ int main(int argc, char **argv) *pidfile = 0; const struct option long_options[] = { - {"qnum",required_argument,0,0}, // optidx=0 - {"daemon",no_argument,0,0}, // optidx=1 - {"wsize",required_argument,0,0}, // optidx=2 - {"hostcase",no_argument,0,0}, // optidx=3 - {"hostspell",required_argument,0,0}, // optidx=4 - {"hostnospace",no_argument,0,0}, // optidx=5 - {"user",required_argument,0,0}, // optidx=6 - {"pidfile",required_argument,0,0}, // optidx=7 - {NULL,0,NULL,0} + {"qnum",required_argument,0,0}, // optidx=0 + {"daemon",no_argument,0,0}, // optidx=1 + {"wsize",required_argument,0,0}, // optidx=2 + {"hostcase",no_argument,0,0}, // optidx=3 + {"hostspell",required_argument,0,0}, // optidx=4 + {"hostnospace",no_argument,0,0}, // optidx=5 + {"pidfile",required_argument,0,0}, // optidx=6 + {"user",required_argument,0,0 },// optidx=7 + {"uid",required_argument,0,0 },// optidx=8 + {NULL,0,NULL,0} }; if (argc<2) exithelp(); while ((v=getopt_long_only(argc,argv,"",long_options,&option_index))!=-1) @@ -505,7 +540,11 @@ int main(int argc, char **argv) case 5: /* hostnospace */ cbdata.hostnospace = true; break; - case 6: /* user */ + case 6: /* pidfile */ + strncpy(pidfile,optarg,sizeof(pidfile)); + pidfile[sizeof(pidfile)-1]='\0'; + break; + case 7: /* user */ { struct passwd *pwd = getpwnam(optarg); if (!pwd) @@ -517,10 +556,14 @@ int main(int argc, char **argv) gid = pwd->pw_gid; break; } - case 7: /* pidfile */ - strncpy(pidfile,optarg,sizeof(pidfile)); - pidfile[sizeof(pidfile)-1]='\0'; - break; + case 8: /* uid */ + gid=0x7FFFFFFF; // default git. drop gid=0 + if (!sscanf(optarg,"%u:%u",&uid,&gid)) + { + fprintf(stderr, "--uid should be : uid[:gid]\n"); + exit(1); + } + break; } } @@ -566,16 +609,15 @@ int main(int argc, char **argv) fprintf(stderr, "can't set packet_copy mode\n"); goto exiterr; } - + + if (!droproot(uid,gid)) goto exiterr; + fprintf(stderr,"Running as UID=%u GID=%u\n",getuid(),getgid()); + fd = nfq_fd(h); - - if (droproot(uid,gid)) + while ((rv = recv(fd, buf, sizeof(buf), 0)) && rv >= 0) { - while ((rv = recv(fd, buf, sizeof(buf), 0)) && rv >= 0) - { - int r=nfq_handle_packet(h, buf, rv); - if (r) fprintf(stderr,"nfq_handle_packet error %d\n",r); - } + int r=nfq_handle_packet(h, buf, rv); + if (r) fprintf(stderr,"nfq_handle_packet error %d\n",r); } printf("unbinding from queue 0\n");