Skip to content

Commit

Permalink
netfilter template support
Browse files Browse the repository at this point in the history
  • Loading branch information
netblue30 committed Nov 18, 2017
1 parent eb4b505 commit ead4ec3
Show file tree
Hide file tree
Showing 20 changed files with 633 additions and 33 deletions.
7 changes: 5 additions & 2 deletions Makefile.in
Original file line number Diff line number Diff line change
Expand Up @@ -276,10 +276,13 @@ test-fs:
test-fcopy:
cd test/fcopy; ./fcopy.sh | grep TESTING

test: test-profiles test-private-lib test-fcopy test-fs test-utils test-sysutils test-environment test-apps test-apps-x11 test-apps-x11-xorg test-filters test-arguments
test-fnetfilter:
cd test/fnetfilter; ./fnetfilter.sh | grep TESTING

test: test-profiles test-private-lib test-fcopy test-fnetfilter test-fs test-utils test-sysutils test-environment test-apps test-apps-x11 test-apps-x11-xorg test-filters test-arguments
echo "TEST COMPLETE"

test-travis: test-profiles test-fcopy test-fs test-utils test-sysutils test-environment test-filters test-arguments
test-travis: test-profiles test-fcopy test-fnetfilter test-fs test-utils test-sysutils test-environment test-filters test-arguments
echo "TEST COMPLETE"

##########################################
Expand Down
13 changes: 11 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -210,18 +210,27 @@ $
--debug-private-lib
Debug messages for --private-lib option.
--netfilter=filename,arg1,arg2,arg3 ...
This is the template version of the previous command. $ARG1,
$ARG2, $ARG3 ... in the firewall script are replaced with arg1,
arg2, arg3 ... passed on the command line. Up to 16 arguments
are supported. Example:
$ firejail --net=eth0 --ip=192.168.1.105 \
--netfilter=/etc/firejail/tcpserver.net,5001 server-program
--netfilter.print=name|pid
Print the firewall installed in the sandbox specified by name
or PID. Example:
$ firejail --net=browser --net=eth0 --netfilter firefox &
$ firejail --name=browser --net=eth0 --netfilter firefox &
$ firejail --netfilter.print=browser
--netfilter6.print=name|pid
Print the IPv6 firewall installed in the sandbox specified by
name or PID. Example:
$ firejail --net=browser --net=eth0 --netfilter firefox &
$ firejail --name=browser --net=eth0 --netfilter firefox &
$ firejail --netfilter6.print=browser
`````
Expand Down
1 change: 1 addition & 0 deletions RELNOTES
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ firejail (0.9.51) baseline; urgency=low
* feature: profile build tool (--build)
* feature: --netfilter.print
* feature: --netfilter6.print
* feature: netfilter template support
* new profiles: upstreamed many profiles from the following sources:
https://github.com/chiraag-nataraj/firejail-profiles,
https://github.com/nyancat18/fe,
Expand Down
27 changes: 27 additions & 0 deletions etc/tcpserver.net
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
*filter
:INPUT DROP [0:0]
:FORWARD DROP [0:0]
:OUTPUT DROP [0:0]

###################################################################
# Simple tcp filter template. $ARG1 is the port number.
#
# Usage: $ARG1 in this template is replaced by 5001 from command line below
#
# firejail --net=eth0 --ip=192.168.1.105 --netfilter=/etc/firejail/tcpserver.net,5001 server-program
#
###################################################################

# allow server traffic
-A INPUT -p tcp --dport $ARG1 -m state --state NEW,ESTABLISHED -j ACCEPT
-A OUTPUT -p tcp --sport $ARG1 -m state --state ESTABLISHED -j ACCEPT

# allow incoming ping
-A INPUT -p icmp --icmp-type echo-request -j ACCEPT
-A OUTPUT -p icmp --icmp-type echo-reply -j ACCEPT

# allow outgoing DNS
-A OUTPUT -p udp --dport 53 -j ACCEPT
-A INPUT -p udp --sport 53 -j ACCEPT

COMMIT
13 changes: 10 additions & 3 deletions src/firejail/netfilter.c
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@
#include <sys/wait.h>
#include <fcntl.h>


void check_netfilter_file(const char *fname) {
EUID_ASSERT();

Expand All @@ -44,7 +43,6 @@ void check_netfilter_file(const char *fname) {
free(tmp);
}


void netfilter(const char *fname) {
// find iptables command
struct stat s;
Expand Down Expand Up @@ -150,6 +148,16 @@ void netfilter_print(pid_t pid, int ipv6) {
}
free(comm);

// check privileges for non-root users
uid_t uid = getuid();
if (uid != 0) {
uid_t sandbox_uid = pid_get_uid(pid);
if (uid != sandbox_uid) {
fprintf(stderr, "Error: permission is denied to join a sandbox created by a different user.\n");
exit(1);
}
}

// check network namespace
char *name;
if (asprintf(&name, "/run/firejail/network/%d-netmap", pid) == -1)
Expand Down Expand Up @@ -196,4 +204,3 @@ void netfilter_print(pid_t pid, int ipv6) {

sbox_run(SBOX_ROOT | SBOX_CAPS_NETWORK | SBOX_SECCOMP, 2, iptables, "-vL");
}

2 changes: 1 addition & 1 deletion src/firejail/usage.c
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,7 @@ void usage(void) {
printf(" --net=ethernet_interface - enable network namespaces and connect to this\n");
printf("\tEthernet interface.\n");
printf(" --net=none - enable a new, unconnected network namespace.\n");
printf(" --netfilter[=filename] - enable firewall.\n");
printf(" --netfilter[=filename,arg1,arg2,arg3 ...] - enable firewall.\n");
printf(" --netfilter.print=name|pid - print the firewall.\n");
printf(" --netfilter6=filename - enable IPv6 firewall.\n");
printf(" --netfilter6.print=name|pid - print the IPv6 firewall.\n");
Expand Down
144 changes: 121 additions & 23 deletions src/fnetfilter/main.c
Original file line number Diff line number Diff line change
Expand Up @@ -20,16 +20,20 @@
#include "../include/common.h"

#define MAXBUF 4098
#define MAXARGS 16
static char *args[MAXARGS] = {0};
static int argcnt = 0;
int arg_quiet = 0;


static char *default_filter =
"*filter\n"
":INPUT DROP [0:0]\n"
":FORWARD DROP [0:0]\n"
":OUTPUT ACCEPT [0:0]\n"
"-A INPUT -i lo -j ACCEPT\n"
"-A INPUT -m state --state RELATED,ESTABLISHED -j ACCEPT\n"
"# echo replay is handled by -m state RELATED/ESTABLISHED below\n"
"# echo replay is handled by -m state RELATED/ESTABLISHED above\n"
"#-A INPUT -p icmp --icmp-type echo-reply -j ACCEPT\n"
"-A INPUT -p icmp --icmp-type destination-unreachable -j ACCEPT\n"
"-A INPUT -p icmp --icmp-type time-exceeded -j ACCEPT\n"
Expand All @@ -46,6 +50,111 @@ static void usage(void) {
printf("\tfnetfilter netfilter-command destination-file\n");
}


static void copy(const char *src, const char *dest) {
FILE *fp1 = fopen(src, "r");
if (!fp1) {
fprintf(stderr, "Error fnetfilter: cannot open %s\n", src);
exit(1);
}

FILE *fp2 = fopen(dest, "w");
if (!fp2) {
fprintf(stderr, "Error fnetfilter: cannot open %s\n", dest);
exit(1);
}

char buf[MAXBUF];
while (fgets(buf, MAXBUF, fp1))
fprintf(fp2, "%s", buf);

fclose(fp1);
fclose(fp2);
}

static void process_template(char *src, const char *dest) {
char *arg_start = strchr(src, ',');
assert(arg_start);
*arg_start = '\0';
arg_start++;
if (*arg_start == '\0') {
fprintf(stderr, "Error fnetfilter: you need to provide at least on argument\n");
exit(1);
}

// extract the arguments from command line
char *token = strtok(arg_start, ",");
while (token) {
// look for abnormal things
int len = strlen(token);
if (strcspn(token, "\\&!?\"'<>%^(){};,*[]") != (size_t)len) {
fprintf(stderr, "Error fnetfilter: invalid argument in netfilter command\n");
exit(1);
}
args[argcnt] = token;
argcnt++;
token = strtok(NULL, ",");
}
#if 0
{
printf("argcnt %d\n", argcnt);
int i;
for (i = 0; i < argcnt; i++)
printf("%s\n", args[i]);
}
#endif

// open the files
FILE *fp1 = fopen(src, "r");
if (!fp1) {
fprintf(stderr, "Error fnetfilter: cannot open %s\n", src);
exit(1);
}

FILE *fp2 = fopen(dest, "w");
if (!fp2) {
fprintf(stderr, "Error fnetfilter: cannot open %s\n", dest);
exit(1);
}

int line = 0;
char buf[MAXBUF];
while (fgets(buf, MAXBUF, fp1)) {
line++;
char *ptr = buf;
while (*ptr != '\0') {
if (*ptr != '$')
fputc(*ptr, fp2);
else {
// parsing
int index = 0;
int rv = sscanf(ptr, "$ARG%u", &index) ;
if (rv != 1) {
fprintf(stderr, "Error fnetfilter: invalid template argument on line %d\n", line);
exit(1);
}

// print argument
if (index < 1 || index > argcnt) {
fprintf(stderr, "Error fnetfilter: $ARG%d on line %d was not defined\n", index, line);
exit(1);
}
fprintf(fp2, "%s", args[index - 1]);

// march to the end of argument
ptr += 4;
while (isdigit(*ptr))
ptr++;
ptr--;
}
ptr++;
}
}

fclose(fp1);
fclose(fp2);
}

int main(int argc, char **argv) {
#if 0
{
Expand All @@ -61,7 +170,7 @@ printf("\n");
if (quiet && strcmp(quiet, "yes") == 0)
arg_quiet = 1;

if (strcmp(argv[1], "-h") == 0 || strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") ==0) {
if (argc > 1 && (strcmp(argv[1], "-h") == 0 || strcmp(argv[1], "--help") == 0 || strcmp(argv[1], "-?") ==0)) {
usage();
return 0;
}
Expand All @@ -75,6 +184,12 @@ printf("\n");
char *command = (argc == 3)? argv[1]: NULL;
//printf("command %s\n", command);
//printf("destfile %s\n", destfile);
// destfile is a real filename
int len = strlen(destfile);
if (strcspn(destfile, "\\&!?\"'<>%^(){};,*[]") != (size_t)len) {
fprintf(stderr, "Error fnetfilter: invalid destination file in netfilter command\n");
exit(1);
}

// handle default config (command = NULL, destfile)
if (command == NULL) {
Expand All @@ -88,28 +203,11 @@ printf("\n");
fclose(fp);
}
else {
// copy the file
FILE *fp1 = fopen(command, "r");
if (!fp1) {
fprintf(stderr, "Error fnetfilter: cannot open %s\n", command);
exit(1);
}

FILE *fp2 = fopen(destfile, "w");
if (!fp2) {
fprintf(stderr, "Error fnetfilter: cannot open %s\n", destfile);
exit(1);
}

char buf[MAXBUF];
while (fgets(buf, MAXBUF, fp1))
fprintf(fp2, "%s", buf);

fclose(fp1);
fclose(fp2);
if (strrchr(command, ','))
process_template(command, destfile);
else
copy(command, destfile);
}


printf("fnetfilter running\n");
return 0;
}
23 changes: 21 additions & 2 deletions src/man/firejail.txt
Original file line number Diff line number Diff line change
Expand Up @@ -937,13 +937,32 @@ is a desktop client firewall that disable access to local network. Example:
$ firejail --netfilter=/etc/firejail/nolocal.net \\
.br
--net=eth0 firefox




.TP
\fB\-\-netfilter=filename,arg1,arg2,arg3 ...
This is the template version of the previous command. $ARG1, $ARG2, $ARG3 ... in the firewall script
are replaced with arg1, arg2, arg3 ... passed on the command line. Up to 16 arguments are supported.
Example:
.br

.br
$ firejail --net=eth0 --ip=192.168.1.105 \\
.br
--netfilter=/etc/firejail/tcpserver.net,5001 server-program
.br



.TP
\fB\-\-netfilter.print=name|pid
Print the firewall installed in the sandbox specified by name or PID. Example:
.br

.br
$ firejail --net=browser --net=eth0 --netfilter firefox &
$ firejail --name=browser --net=eth0 --netfilter firefox &
.br
$ firejail --netfilter.print=browser

Expand All @@ -959,7 +978,7 @@ Print the IPv6 firewall installed in the sandbox specified by name or PID. Examp
.br

.br
$ firejail --net=browser --net=eth0 --netfilter firefox &
$ firejail --name=browser --net=eth0 --netfilter firefox &
.br
$ firejail --netfilter6.print=browser

Expand Down
37 changes: 37 additions & 0 deletions test/fnetfilter/cmdline.exp
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
#!/usr/bin/expect -f
# This file is part of Firejail project
# Copyright (C) 2014-2017 Firejail Authors
# License GPL v2

set timeout 10
spawn $env(SHELL)
match_max 100000

send -- "fnetfilter\r"
expect {
timeout {puts "TESTING ERROR 1\n";exit}
"Usage:"
}
after 100

send -- "fnetfilter -h\r"
expect {
timeout {puts "TESTING ERROR 2\n";exit}
"Usage:"
}
after 100

send -- "fnetfilter -h a b c d\r"
expect {
timeout {puts "TESTING ERROR 2\n";exit}
"Usage:"
}
after 100

send -- "fnetfilter a b c d\r"
expect {
timeout {puts "TESTING ERROR 2\n";exit}
"Usage:"
}
after 100
puts "\nall done\n"
Loading

0 comments on commit ead4ec3

Please sign in to comment.