From 56bf7c836e8cbc74b36ded08429b2112096f6d8e Mon Sep 17 00:00:00 2001 From: netblue30 Date: Tue, 22 Sep 2015 08:18:56 -0400 Subject: added --interface option --- src/firejail/firejail.h | 24 +++++++++- src/firejail/main.c | 116 ++++++++++++++++++++++++++++++++++++------------ src/firejail/sandbox.c | 32 +++++++++++-- src/firejail/usage.c | 4 ++ src/firejail/veth.c | 38 ++++++++++++++++ src/man/firejail.txt | 12 +++++ 6 files changed, 193 insertions(+), 33 deletions(-) (limited to 'src') diff --git a/src/firejail/firejail.h b/src/firejail/firejail.h index aa8144a40..cef4b673c 100644 --- a/src/firejail/firejail.h +++ b/src/firejail/firejail.h @@ -55,6 +55,16 @@ typedef struct bridge_t { uint8_t scan; // set by --scan } Bridge; +typedef struct interface_t { + char *dev; + uint32_t ip; + uint32_t mask; + uint8_t mac[6]; + // todo: add mtu + + uint8_t configured; +} Interface; + typedef struct profile_entry_t { struct profile_entry_t *next; char *data; @@ -81,6 +91,10 @@ typedef struct config_t { Bridge bridge1; Bridge bridge2; Bridge bridge3; + Interface interface0; + Interface interface1; + Interface interface2; + Interface interface3; uint32_t dns1; // up to 3 IP addresses for dns servers uint32_t dns2; uint32_t dns3; @@ -107,7 +121,14 @@ typedef struct config_t { extern Config cfg; static inline int any_bridge_configured(void) { - if (cfg.bridge3.configured || cfg.bridge2.configured || cfg.bridge1.configured || cfg.bridge0.configured) + if (cfg.bridge0.configured || cfg.bridge1.configured || cfg.bridge2.configured || cfg.bridge3.configured) + return 1; + else + return 0; +} + +static inline int any_interface_configured(void) { + if (cfg.interface0.configured || cfg.interface1.configured || cfg.interface2.configured || cfg.interface3.configured) return 1; else return 0; @@ -243,6 +264,7 @@ void arp_scan(const char *dev, uint32_t srcaddr, uint32_t srcmask); // veth.c int net_create_veth(const char *dev, const char *nsdev, unsigned pid); int net_create_macvlan(const char *dev, const char *parent, unsigned pid); +int net_move_interface(const char *dev, unsigned pid); // util.c void drop_privs(int nogroups); diff --git a/src/firejail/main.c b/src/firejail/main.c index 82d17264a..9d94630ef 100644 --- a/src/firejail/main.c +++ b/src/firejail/main.c @@ -754,6 +754,45 @@ int main(int argc, char **argv) { //************************************* // network //************************************* + else if (strncmp(argv[i], "--interface=", 12) == 0) { + // checks + if (arg_nonetwork) { + fprintf(stderr, "Error: --network=none and --interface are incompatible\n"); + exit(1); + } + if (strcmp(argv[i] + 12, "lo") == 0) { + fprintf(stderr, "Error: cannot use lo device in --interface command\n"); + exit(1); + } + int ifindex = if_nametoindex(argv[i] + 12); + if (ifindex <= 0) { + fprintf(stderr, "Error: cannot find interface %s\n", argv[i] + 12); + exit(1); + } + + Interface *intf; + if (cfg.interface0.configured == 0) + intf = &cfg.interface0; + else if (cfg.interface1.configured == 0) + intf = &cfg.interface1; + else if (cfg.interface2.configured == 0) + intf = &cfg.interface2; + else if (cfg.interface3.configured == 0) + intf = &cfg.interface3; + else { + fprintf(stderr, "Error: maximum 4 interfaces are allowed\n"); + return 1; + } + + intf->dev = strdup(argv[i] + 12); + if (!intf->dev) + errExit("strdup"); + + if (net_get_if_addr(intf->dev, &intf->ip, &intf->mask, intf->mac)) { + fprintf(stderr, "Warning: interface %s is not configured\n", intf->dev); + } + intf->configured = 1; + } else if (strncmp(argv[i], "--net=", 6) == 0) { if (strcmp(argv[i] + 6, "none") == 0) { arg_nonetwork = 1; @@ -761,6 +800,10 @@ int main(int argc, char **argv) { cfg.bridge1.configured = 0; cfg.bridge2.configured = 0; cfg.bridge3.configured = 0; + cfg.interface0.configured = 0; + cfg.interface1.configured = 0; + cfg.interface2.configured = 0; + cfg.interface3.configured = 0; continue; } if (strcmp(argv[i] + 6, "lo") == 0) { @@ -778,7 +821,7 @@ int main(int argc, char **argv) { else if (cfg.bridge3.configured == 0) br = &cfg.bridge3; else { - fprintf(stderr, "Error: maximum 4 network devices allowed\n"); + fprintf(stderr, "Error: maximum 4 network devices are allowed\n"); return 1; } net_configure_bridge(br, argv[i] + 6); @@ -1130,7 +1173,7 @@ int main(int argc, char **argv) { if (getuid() == 0 || arg_ipc) flags |= CLONE_NEWIPC; - if (any_bridge_configured() || arg_nonetwork) { + if (any_bridge_configured() || any_interface_configured() || arg_nonetwork) { flags |= CLONE_NEWNET; } else if (arg_debug) @@ -1151,34 +1194,49 @@ int main(int argc, char **argv) { } - - // create veth pair or macvlan device - if (cfg.bridge0.configured && !arg_nonetwork) { - if (cfg.bridge0.macvlan == 0) - net_configure_veth_pair(&cfg.bridge0, "eth0", child); - else - net_create_macvlan(cfg.bridge0.devsandbox, cfg.bridge0.dev, child); - } - - if (cfg.bridge1.configured && !arg_nonetwork) { - if (cfg.bridge1.macvlan == 0) - net_configure_veth_pair(&cfg.bridge1, "eth1", child); - else - net_create_macvlan(cfg.bridge1.devsandbox, cfg.bridge1.dev, child); - } - - if (cfg.bridge2.configured && !arg_nonetwork) { - if (cfg.bridge2.macvlan == 0) - net_configure_veth_pair(&cfg.bridge2, "eth2", child); - else - net_create_macvlan(cfg.bridge2.devsandbox, cfg.bridge2.dev, child); - } + if (!arg_nonetwork) { + // create veth pair or macvlan device + if (cfg.bridge0.configured) { + if (cfg.bridge0.macvlan == 0) + net_configure_veth_pair(&cfg.bridge0, "eth0", child); + else + net_create_macvlan(cfg.bridge0.devsandbox, cfg.bridge0.dev, child); + } + + if (cfg.bridge1.configured) { + if (cfg.bridge1.macvlan == 0) + net_configure_veth_pair(&cfg.bridge1, "eth1", child); + else + net_create_macvlan(cfg.bridge1.devsandbox, cfg.bridge1.dev, child); + } + + if (cfg.bridge2.configured) { + if (cfg.bridge2.macvlan == 0) + net_configure_veth_pair(&cfg.bridge2, "eth2", child); + else + net_create_macvlan(cfg.bridge2.devsandbox, cfg.bridge2.dev, child); + } + + if (cfg.bridge3.configured) { + if (cfg.bridge3.macvlan == 0) + net_configure_veth_pair(&cfg.bridge3, "eth3", child); + else + net_create_macvlan(cfg.bridge3.devsandbox, cfg.bridge3.dev, child); + } - if (cfg.bridge3.configured && !arg_nonetwork) { - if (cfg.bridge3.macvlan == 0) - net_configure_veth_pair(&cfg.bridge3, "eth3", child); - else - net_create_macvlan(cfg.bridge3.devsandbox, cfg.bridge3.dev, child); + // move interfaces in sandbox + if (cfg.interface0.configured) { + net_move_interface(cfg.interface0.dev, child); + } + if (cfg.interface1.configured) { + net_move_interface(cfg.interface1.dev, child); + } + if (cfg.interface2.configured) { + net_move_interface(cfg.interface2.dev, child); + } + if (cfg.interface3.configured) { + net_move_interface(cfg.interface3.dev, child); + } } // close each end of the unused pipes diff --git a/src/firejail/sandbox.c b/src/firejail/sandbox.c index 41fc20084..c1a6d92ec 100644 --- a/src/firejail/sandbox.c +++ b/src/firejail/sandbox.c @@ -277,7 +277,7 @@ int sandbox(void* sandbox_arg) { if (arg_debug) printf("Network namespace enabled, only loopback interface available\n"); } - else if (any_bridge_configured()) { + else if (any_bridge_configured() || any_interface_configured()) { // configure lo and eth0...eth3 net_if_up("lo"); @@ -303,6 +303,32 @@ int sandbox(void* sandbox_arg) { if (net_add_route(0, 0, cfg.defaultgw)) fprintf(stderr, "Warning: cannot configure default route\n"); } + + // enable interfaces + if (cfg.interface0.configured && cfg.interface0.ip) { + if (arg_debug) + printf("Configuring %d.%d.%d.%d address on interface %s\n", PRINT_IP(cfg.interface0.ip), cfg.interface0.dev); + net_if_ip(cfg.interface0.dev, cfg.interface0.ip, cfg.interface0.mask); + net_if_up(cfg.interface0.dev); + } + if (cfg.interface1.configured && cfg.interface1.ip) { + if (arg_debug) + printf("Configuring %d.%d.%d.%d address on interface %s\n", PRINT_IP(cfg.interface1.ip), cfg.interface1.dev); + net_if_ip(cfg.interface1.dev, cfg.interface1.ip, cfg.interface1.mask); + net_if_up(cfg.interface1.dev); + } + if (cfg.interface2.configured && cfg.interface2.ip) { + if (arg_debug) + printf("Configuring %d.%d.%d.%d address on interface %s\n", PRINT_IP(cfg.interface2.ip), cfg.interface2.dev); + net_if_ip(cfg.interface2.dev, cfg.interface2.ip, cfg.interface2.mask); + net_if_up(cfg.interface2.dev); + } + if (cfg.interface3.configured && cfg.interface3.ip) { + if (arg_debug) + printf("Configuring %d.%d.%d.%d address on interface %s\n", PRINT_IP(cfg.interface3.ip), cfg.interface3.dev); + net_if_ip(cfg.interface3.dev, cfg.interface3.ip, cfg.interface3.mask); + net_if_up(cfg.interface3.dev); + } if (arg_debug) printf("Network namespace enabled\n"); @@ -312,9 +338,9 @@ int sandbox(void* sandbox_arg) { fs_resolvconf(); // print network configuration - if (any_bridge_configured() || cfg.defaultgw || cfg.dns1) { + if (any_bridge_configured() || any_interface_configured() || cfg.defaultgw || cfg.dns1) { printf("\n"); - if (any_bridge_configured()) + if (any_bridge_configured() || any_interface_configured()) net_ifprint(); if (cfg.defaultgw != 0) printf("Default gateway %d.%d.%d.%d\n", PRINT_IP(cfg.defaultgw)); diff --git a/src/firejail/usage.c b/src/firejail/usage.c index 1f611001d..13e3d87e2 100644 --- a/src/firejail/usage.c +++ b/src/firejail/usage.c @@ -82,6 +82,10 @@ void usage(void) { printf("\t--env=name=value - set environment variable in the new sandbox\n"); printf("\t--help, -? - this help screen.\n\n"); + + printf("\t--interface=name - move interface in a new network namespace. Up to\n"); + printf("\t\tfour --interface options can be sepcified.\n\n"); + printf("\t--ip=address - set interface IP address.\n\n"); printf("\t--ip=none - no IP address and no default gateway address are configured\n"); printf("\t\tin the new network namespace. Use this option in case you intend\n"); diff --git a/src/firejail/veth.c b/src/firejail/veth.c index 0db55709b..45bf815aa 100644 --- a/src/firejail/veth.c +++ b/src/firejail/veth.c @@ -179,6 +179,44 @@ int net_create_macvlan(const char *dev, const char *parent, unsigned pid) { return 0; } +// move the interface dev in namespace of program pid +// when the interface is moved, netlink does not preserve interface configuration +int net_move_interface(const char *dev, unsigned pid) { + int len; + struct iplink_req req; + if (arg_debug) + printf("move device %s inside the namespace\n", dev); + assert(dev); + + if (rtnl_open(&rth, 0) < 0) { + fprintf(stderr, "cannot open netlink\n"); + exit(1); + } + + memset(&req, 0, sizeof(req)); + + req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg)); + req.n.nlmsg_flags = NLM_F_REQUEST; + req.n.nlmsg_type = RTM_NEWLINK; + req.i.ifi_family = 0; + + // find ifindex + int ifindex = if_nametoindex(dev); + if (ifindex <= 0) { + fprintf(stderr, "Error: cannot find interface %s\n", dev); + exit(1); + } + req.i.ifi_index = ifindex; + + // place the interface in child namespace + addattr_l (&req.n, sizeof(req), IFLA_NET_NS_PID, &pid, 4); + + // send message + if (rtnl_talk(&rth, &req.n, 0, 0, NULL) < 0) + exit(2); + + return 0; +} /* int main(int argc, char **argv) { diff --git a/src/man/firejail.txt b/src/man/firejail.txt index 4bf537c95..3f4fba00e 100644 --- a/src/man/firejail.txt +++ b/src/man/firejail.txt @@ -320,6 +320,18 @@ $ firejail \-\-env=LD_LIBRARY_PATH=/opt/test/lib .TP \fB\-?\fR, \fB\-\-help\fR Print options end exit. + + +.TP +\fB\-\-interface=interface +Move interface in a new network namespace. Up to four --interface options can be sepcified. +.br + +.br +Example: +.br +$ firejail \-\-interface=eth1 \-\-interface=eth0.vlan100 + .TP \fB\-\-ip=address Assign IP addresses to the last network interface defined by a \-\-net option. A -- cgit v1.2.3-54-g00ecf