From f898290fd79e0e64d13ceef56fc5960da879d179 Mon Sep 17 00:00:00 2001 From: netblue30 Date: Sun, 30 Oct 2016 15:54:05 -0400 Subject: major cleanup --- .gitignore | 1 + README | 2 + src/firejail/appimage.c | 5 - src/firejail/arp.c | 186 +------------------------- src/firejail/firejail.h | 31 +++-- src/firejail/fs.c | 198 ++++----------------------- src/firejail/fs_bin.c | 1 - src/firejail/fs_dev.c | 2 - src/firejail/fs_etc.c | 1 - src/firejail/fs_home.c | 6 - src/firejail/fs_hostname.c | 2 - src/firejail/fs_trace.c | 3 - src/firejail/fs_var.c | 3 - src/firejail/fs_whitelist.c | 4 - src/firejail/main.c | 50 ++----- src/firejail/network.c | 303 +++++------------------------------------- src/firejail/network_main.c | 27 ++-- src/firejail/preproc.c | 110 +++++++++++++++ src/firejail/protocol.c | 2 - src/firejail/pulseaudio.c | 1 - src/firejail/restrict_users.c | 3 - src/firejail/sandbox.c | 285 ++++++++++++++++++++------------------- src/firejail/sbox.c | 23 ++-- src/firejail/seccomp.c | 1 - src/firejail/util.c | 34 +++++ src/fnet/arp.c | 208 +++++++++++++++++++++++++++++ src/fnet/fnet.h | 9 ++ src/fnet/interface.c | 212 +++++++++++++++++++++++++++++ src/fnet/main.c | 39 +++++- src/fseccomp/main.c | 6 +- 30 files changed, 869 insertions(+), 889 deletions(-) create mode 100644 src/firejail/preproc.c create mode 100644 src/fnet/arp.c diff --git a/.gitignore b/.gitignore index 6acb6775c..db523da59 100644 --- a/.gitignore +++ b/.gitignore @@ -19,4 +19,5 @@ src/ftee/ftee src/tags src/faudit/faudit src/fnet/fnet +src/fseccomp/fseccomp uids.h diff --git a/README b/README index cbd15f02a..f0e2ea3e8 100644 --- a/README +++ b/README @@ -86,6 +86,8 @@ valoq (https://github.com/valoq) - added support for /srv in --whitelist feature - Eye of GNOME and Evolution profiles - blacklist suid binaries in disable-common.inc +Vadim A. Misbakh-Soloviov (https://github.com/msva) + - profile fixes Rafael Cavalcanti (https://github.com/rccavalcanti) - chromium profile fixes for Arch Linux Deelvesh Bunjun (https://github.com/DeelveshBunjun) diff --git a/src/firejail/appimage.c b/src/firejail/appimage.c index 09b242964..322798ee5 100644 --- a/src/firejail/appimage.c +++ b/src/firejail/appimage.c @@ -60,11 +60,6 @@ void appimage_set(const char *appimage_path) { exit(1); } - // populate /run/firejail directory - EUID_ROOT(); - fs_build_firejail_dir(); - EUID_USER(); - // find or allocate a free loop device to use EUID_ROOT(); int cfd = open("/dev/loop-control", O_RDWR); diff --git a/src/firejail/arp.c b/src/firejail/arp.c index fb5e426b0..ddb75905f 100644 --- a/src/firejail/arp.c +++ b/src/firejail/arp.c @@ -40,6 +40,7 @@ typedef struct arp_hdr_t { uint8_t target_ip[4]; } ArpHdr; + // returns 0 if the address is not in use, -1 otherwise int arp_check(const char *dev, uint32_t destaddr, uint32_t srcaddr) { if (strlen(dev) > IFNAMSIZ) { @@ -286,189 +287,4 @@ uint32_t arp_assign(const char *dev, Bridge *br) { return ip; } -// scan interface (--scan option) -void arp_scan(const char *dev, uint32_t ifip, uint32_t ifmask) { - assert(dev); - assert(ifip); - -// printf("Scanning interface %s (%d.%d.%d.%d/%d)\n", -// dev, PRINT_IP(ifip & ifmask), mask2bits(ifmask)); - - if (strlen(dev) > IFNAMSIZ) { - fprintf(stderr, "Error: invalid network device name %s\n", dev); - exit(1); - } - - // find interface mac address - int sock; - if ((sock = socket(AF_INET, SOCK_RAW, IPPROTO_RAW)) < 0) - errExit("socket"); - struct ifreq ifr; - memset(&ifr, 0, sizeof (ifr)); - strncpy(ifr.ifr_name, dev, IFNAMSIZ); - if (ioctl(sock, SIOCGIFHWADDR, &ifr) < 0) - errExit("ioctl"); - close(sock); - uint8_t mac[6]; - memcpy (mac, ifr.ifr_hwaddr.sa_data, 6); - - // open layer2 socket - if ((sock = socket(PF_PACKET, SOCK_RAW, htons (ETH_P_ALL))) < 0) - errExit("socket"); - - // try all possible ip addresses in ascending order - uint32_t range = ~ifmask + 1; // the number of potential addresses - // this software is not supported for /31 networks - if (range < 4) { - fprintf(stderr, "Warning: this option is not supported for /31 networks\n"); - close(sock); - return; - } - - uint32_t dest = (ifip & ifmask) + 1; - uint32_t last = dest + range - 1; - uint32_t src = htonl(ifip); - - // wait not more than one second for an answer - int header_printed = 0; - uint32_t last_ip = 0; - struct timeval ts; - ts.tv_sec = 2; // 2 seconds receive timeout - ts.tv_usec = 0; - - while (1) { - fd_set rfds; - FD_ZERO(&rfds); - FD_SET(sock, &rfds); - fd_set wfds; - FD_ZERO(&wfds); - FD_SET(sock, &wfds); - int maxfd = sock; - - uint8_t frame[ETH_FRAME_LEN]; // includes eht header, vlan, and crc - memset(frame, 0, ETH_FRAME_LEN); - - int nready; - if (dest < last) - nready = select(maxfd + 1, &rfds, &wfds, (fd_set *) 0, NULL); - else - nready = select(maxfd + 1, &rfds, (fd_set *) 0, (fd_set *) 0, &ts); - - if (nready < 0) - errExit("select"); - - if (nready == 0) { // timeout - break; - } - - if (FD_ISSET(sock, &wfds) && dest < last) { - // configure layer2 socket address information - struct sockaddr_ll addr; - memset(&addr, 0, sizeof(addr)); - if ((addr.sll_ifindex = if_nametoindex(dev)) == 0) - errExit("if_nametoindex"); - addr.sll_family = AF_PACKET; - memcpy (addr.sll_addr, mac, 6); - addr.sll_halen = htons(6); - - // build the arp packet header - ArpHdr hdr; - memset(&hdr, 0, sizeof(hdr)); - hdr.htype = htons(1); - hdr.ptype = htons(ETH_P_IP); - hdr.hlen = 6; - hdr.plen = 4; - hdr.opcode = htons(1); //ARPOP_REQUEST - memcpy(hdr.sender_mac, mac, 6); - memcpy(hdr.sender_ip, (uint8_t *)&src, 4); - uint32_t dst = htonl(dest); - memcpy(hdr.target_ip, (uint8_t *)&dst, 4); - - // build ethernet frame - uint8_t frame[ETH_FRAME_LEN]; // includes eht header, vlan, and crc - memset(frame, 0, sizeof(frame)); - frame[0] = frame[1] = frame[2] = frame[3] = frame[4] = frame[5] = 0xff; - memcpy(frame + 6, mac, 6); - frame[12] = ETH_P_ARP / 256; - frame[13] = ETH_P_ARP % 256; - memcpy (frame + 14, &hdr, sizeof(hdr)); - - // send packet - int len; - if ((len = sendto (sock, frame, 14 + sizeof(ArpHdr), 0, (struct sockaddr *) &addr, sizeof (addr))) <= 0) - errExit("send"); -//printf("send %d bytes to %d.%d.%d.%d\n", len, PRINT_IP(dest)); - fflush(0); - dest++; - } - - if (FD_ISSET(sock, &rfds)) { - // read the incoming packet - int len = recvfrom(sock, frame, ETH_FRAME_LEN, 0, NULL, NULL); - if (len < 0) { - perror("recvfrom"); - } - - // parse the incoming packet - if ((unsigned int) len < 14 + sizeof(ArpHdr)) - continue; - - // look only at ARP packets - if (frame[12] != (ETH_P_ARP / 256) || frame[13] != (ETH_P_ARP % 256)) - continue; - - ArpHdr hdr; - memcpy(&hdr, frame + 14, sizeof(ArpHdr)); - - if (hdr.opcode == htons(2)) { - // check my mac and my address - if (memcmp(mac, hdr.target_mac, 6) != 0) - continue; - uint32_t ip; - memcpy(&ip, hdr.target_ip, 4); - if (ip != src) - continue; - memcpy(&ip, hdr.sender_ip, 4); - ip = ntohl(ip); - - if (ip == last_ip) // filter duplicates - continue; - last_ip = ip; - - // printing - if (header_printed == 0) { - printf(" Network scan:\n"); - - // print parent interface - if (cfg.bridge0.configured && cfg.bridge0.ip && cfg.bridge0.macvlan && - (cfg.bridge0.ip & cfg.bridge0.mask) == (ifip & cfg.bridge0.mask)) - printf(" %02x:%02x:%02x:%02x:%02x:%02x\t%d.%d.%d.%d\n", - PRINT_MAC(cfg.bridge0.mac), PRINT_IP(cfg.bridge0.ip)); - - if (cfg.bridge1.configured && cfg.bridge1.ip && cfg.bridge1.macvlan && - (cfg.bridge1.ip & cfg.bridge1.mask) == (ifip & cfg.bridge1.mask)) - printf(" %02x:%02x:%02x:%02x:%02x:%02x\t%d.%d.%d.%d\n", - PRINT_MAC(cfg.bridge1.mac), PRINT_IP(cfg.bridge1.ip)); - - if (cfg.bridge2.configured && cfg.bridge2.ip && cfg.bridge2.macvlan && - (cfg.bridge2.ip & cfg.bridge2.mask) == (ifip & cfg.bridge2.mask)) - printf(" %02x:%02x:%02x:%02x:%02x:%02x\t%d.%d.%d.%d\n", - PRINT_MAC(cfg.bridge2.mac), PRINT_IP(cfg.bridge2.ip)); - - if (cfg.bridge3.configured && cfg.bridge3.ip && cfg.bridge3.macvlan && - (cfg.bridge3.ip & cfg.bridge3.mask) == (ifip & cfg.bridge3.mask)) - printf(" %02x:%02x:%02x:%02x:%02x:%02x\t%d.%d.%d.%d\n", - PRINT_MAC(cfg.bridge3.mac), PRINT_IP(cfg.bridge3.ip)); - - header_printed = 1; - } - printf(" %02x:%02x:%02x:%02x:%02x:%02x\t%d.%d.%d.%d\n", - PRINT_MAC(hdr.sender_mac), PRINT_IP(ip)); - } - } - } - - close(sock); -} - diff --git a/src/firejail/firejail.h b/src/firejail/firejail.h index 749656f8b..dcec160fb 100644 --- a/src/firejail/firejail.h +++ b/src/firejail/firejail.h @@ -363,20 +363,19 @@ void net_if_ip(const char *ifname, uint32_t ip, uint32_t mask, int mtu); void net_if_ip6(const char *ifname, const char *addr6); int net_get_if_addr(const char *bridge, uint32_t *ip, uint32_t *mask, uint8_t mac[6], int *mtu); int net_add_route(uint32_t dest, uint32_t mask, uint32_t gw); -void net_ifprint(void); uint32_t network_get_defaultgw(void); int net_config_mac(const char *ifname, const unsigned char mac[6]); int net_get_mac(const char *ifname, unsigned char mac[6]); +void net_config_interface(const char *dev, uint32_t ip, uint32_t mask, int mtu); + +// preproc.c +void preproc_build_firejail_dir(void); +void preproc_mount_mnt_dir(void); +void preproc_build_cp_command(void); +void preproc_delete_cp_command(void) ; +void preproc_remount_mnt_dir(void); // fs.c -// build /run/firejail directory -void fs_build_firejail_dir(void); -// build /run/firejail/mnt directory -void fs_build_mnt_dir(void); -// grab a copy of cp command -void fs_build_cp_command(void); -// delete the temporary cp command -void fs_delete_cp_command(void) ; // blacklist files or directoies by mounting empty files on top of them void fs_blacklist(void); // remount a directory read-only @@ -393,7 +392,6 @@ void fs_overlayfs(void); // chroot into an existing directory; mount exiting /dev and update /etc/resolv.conf void fs_chroot(const char *rootdir); int fs_check_chroot_dir(const char *rootdir); -void fs_private_tmp(void); // profile.c // find and read the profile specified by name from dir directory @@ -430,8 +428,6 @@ int restricted_shell(const char *user); int arp_check(const char *dev, uint32_t destaddr, uint32_t srcaddr); // assign an IP address using arp scanning uint32_t arp_assign(const char *dev, Bridge *br); -// scan interface (--scan option) -void arp_scan(const char *dev, uint32_t srcaddr, uint32_t srcmask); // util.c void drop_privs(int nogroups); @@ -459,6 +455,8 @@ void invalid_filename(const char *fname); uid_t get_group_id(const char *group); int remove_directory(const char *path); void flush_stdin(void); +void create_empty_dir_as_root(const char *dir, mode_t mode); +void create_empty_file_as_root(const char *dir, mode_t mode); // fs_var.c void fs_var_log(void); // mounting /var/log @@ -687,10 +685,11 @@ void build_cmdline(char **command_line, char **window_title, int argc, char **ar #define PATH_FIREMON (PREFIX "/bin/firemon") #define PATH_FSECCOMP (LIBDIR "/firejail/fseccomp") // bitmapped filters for sbox_run -#define SBOX_ROOT 1 -#define SBOX_USER 2 -#define SBOX_CAPS 4 -#define SBOX_SECCOMP 8 +#define SBOX_ROOT (1 << 0) +#define SBOX_USER (1 << 1) +#define SBOX_SECCOMP (1 << 2) +#define SBOX_CAPS_NONE (1 << 3) // drop all capabilities +#define SBOX_CAPS_NETWORK (1 << 4) // caps filter for programs running network programs // run sbox int sbox_run(unsigned filter, int num, ...); diff --git a/src/firejail/fs.c b/src/firejail/fs.c index 572b08205..f7fc44c71 100644 --- a/src/firejail/fs.c +++ b/src/firejail/fs.c @@ -29,155 +29,8 @@ static void fs_rdwr(const char *dir); -static void create_dir_as_root(const char *dir, mode_t mode) { - assert(dir); - if (arg_debug) - printf("Creating %s directory\n", dir); - - if (mkdir(dir, mode) == -1) - errExit("mkdir"); - if (chmod(dir, mode) == -1) - errExit("chmod"); - - ASSERT_PERMS(dir, 0, 0, mode); -} - -static void create_empty_dir(void) { - struct stat s; - - if (stat(RUN_RO_DIR, &s)) { - /* coverity[toctou] */ - if (mkdir(RUN_RO_DIR, S_IRUSR | S_IXUSR) == -1) - errExit("mkdir"); - if (chmod(RUN_RO_DIR, S_IRUSR | S_IXUSR) == -1) - errExit("chmod"); - ASSERT_PERMS(RUN_RO_DIR, 0, 0, S_IRUSR | S_IXUSR); - } -} - -static void create_empty_file(void) { - struct stat s; - - if (stat(RUN_RO_FILE, &s)) { - /* coverity[toctou] */ - FILE *fp = fopen(RUN_RO_FILE, "w"); - if (!fp) - errExit("fopen"); - - SET_PERMS_STREAM(fp, 0, 0, S_IRUSR); - fclose(fp); - } -} - -// build /run/firejail directory -void fs_build_firejail_dir(void) { - struct stat s; - - // CentOS 6 doesn't have /run directory - if (stat(RUN_FIREJAIL_BASEDIR, &s)) { - create_dir_as_root(RUN_FIREJAIL_BASEDIR, 0755); - } - - // check /run/firejail directory belongs to root end exit if doesn't! - if (stat(RUN_FIREJAIL_DIR, &s) == 0) { - if (s.st_uid != 0 || s.st_gid != 0) { - fprintf(stderr, "Error: non-root %s directory, exiting...\n", RUN_FIREJAIL_DIR); - exit(1); - } - } - else { - create_dir_as_root(RUN_FIREJAIL_DIR, 0755); - } - - if (stat(RUN_FIREJAIL_NETWORK_DIR, &s)) { - create_dir_as_root(RUN_FIREJAIL_NETWORK_DIR, 0755); - } - - if (stat(RUN_FIREJAIL_BANDWIDTH_DIR, &s)) { - create_dir_as_root(RUN_FIREJAIL_BANDWIDTH_DIR, 0755); - } - - if (stat(RUN_FIREJAIL_NAME_DIR, &s)) { - create_dir_as_root(RUN_FIREJAIL_NAME_DIR, 0755); - } - - if (stat(RUN_FIREJAIL_X11_DIR, &s)) { - create_dir_as_root(RUN_FIREJAIL_X11_DIR, 0755); - } - - if (stat(RUN_FIREJAIL_APPIMAGE_DIR, &s)) { - create_dir_as_root(RUN_FIREJAIL_APPIMAGE_DIR, 0755); - } - - create_empty_dir(); - create_empty_file(); -} -// build /run/firejail/mnt directory -static int tmpfs_mounted = 0; -#ifdef HAVE_CHROOT -static void fs_build_remount_mnt_dir(void) { - tmpfs_mounted = 0; - fs_build_mnt_dir(); -} -#endif - -void fs_build_mnt_dir(void) { - struct stat s; - fs_build_firejail_dir(); - - // create /run/firejail/mnt directory - if (stat(RUN_MNT_DIR, &s)) { - create_dir_as_root(RUN_MNT_DIR, 0755); - } - - // ... and mount tmpfs on top of it - if (!tmpfs_mounted) { - // mount tmpfs on top of /run/firejail/mnt - if (arg_debug) - printf("Mounting tmpfs on %s directory\n", RUN_MNT_DIR); - if (mount("tmpfs", RUN_MNT_DIR, "tmpfs", MS_NOSUID | MS_STRICTATIME | MS_REC, "mode=755,gid=0") < 0) - errExit("mounting /run/firejail/mnt"); - tmpfs_mounted = 1; - fs_logger2("tmpfs", RUN_MNT_DIR); - } -} - -// grab a copy of cp command -void fs_build_cp_command(void) { - struct stat s; - fs_build_mnt_dir(); - if (stat(RUN_CP_COMMAND, &s)) { - char* fname = realpath("/bin/cp", NULL); - if (fname == NULL) { - fprintf(stderr, "Error: /bin/cp not found\n"); - exit(1); - } - if (stat(fname, &s)) { - fprintf(stderr, "Error: /bin/cp not found\n"); - exit(1); - } - if (is_link(fname)) { - fprintf(stderr, "Error: invalid /bin/cp file\n"); - exit(1); - } - int rv = copy_file(fname, RUN_CP_COMMAND, 0, 0, 0755); - if (rv) { - fprintf(stderr, "Error: cannot access /bin/cp\n"); - exit(1); - } - ASSERT_PERMS(RUN_CP_COMMAND, 0, 0, 0755); - - free(fname); - } -} - -// delete the temporary cp command -void fs_delete_cp_command(void) { - unlink(RUN_CP_COMMAND); -} - //*********************************************** // process profile file //*********************************************** @@ -202,9 +55,6 @@ static void disable_file(OPERATION op, const char *filename) { assert(op next; } - // create mount points - fs_build_mnt_dir(); - - // /home/user if (home_dir) { // keep a copy of real home dir in RUN_WHITELIST_HOME_USER_DIR diff --git a/src/firejail/main.c b/src/firejail/main.c index e210ceb31..fc86f9651 100644 --- a/src/firejail/main.c +++ b/src/firejail/main.c @@ -404,7 +404,7 @@ static void run_cmd_and_exit(int i, int argc, char **argv) { #ifdef HAVE_SECCOMP else if (strcmp(argv[i], "--debug-syscalls") == 0) { if (checkcfg(CFG_SECCOMP)) { - int rv = sbox_run(SBOX_USER | SBOX_CAPS | SBOX_SECCOMP, 2, PATH_FSECCOMP, "debug-syscalls"); + int rv = sbox_run(SBOX_USER | SBOX_CAPS_NONE | SBOX_SECCOMP, 2, PATH_FSECCOMP, "debug-syscalls"); exit(rv); } else { @@ -414,7 +414,7 @@ static void run_cmd_and_exit(int i, int argc, char **argv) { } else if (strcmp(argv[i], "--debug-errnos") == 0) { if (checkcfg(CFG_SECCOMP)) { - int rv = sbox_run(SBOX_USER | SBOX_CAPS | SBOX_SECCOMP, 2, PATH_FSECCOMP, "debug-errnos"); + int rv = sbox_run(SBOX_USER | SBOX_CAPS_NONE | SBOX_SECCOMP, 2, PATH_FSECCOMP, "debug-errnos"); exit(rv); } else { @@ -439,7 +439,7 @@ static void run_cmd_and_exit(int i, int argc, char **argv) { exit(0); } else if (strcmp(argv[i], "--debug-protocols") == 0) { - int rv = sbox_run(SBOX_USER | SBOX_CAPS | SBOX_SECCOMP, 2, PATH_FSECCOMP, "debug-protocols"); + int rv = sbox_run(SBOX_USER | SBOX_CAPS_NONE | SBOX_SECCOMP, 2, PATH_FSECCOMP, "debug-protocols"); exit(rv); } else if (strncmp(argv[i], "--protocol.print=", 17) == 0) { @@ -499,15 +499,15 @@ static void run_cmd_and_exit(int i, int argc, char **argv) { exit(0); } else if (strcmp(argv[i], "--list") == 0) { - int rv = sbox_run(SBOX_USER | SBOX_CAPS | SBOX_SECCOMP, 2, PATH_FIREMON, "--list"); + int rv = sbox_run(SBOX_USER | SBOX_CAPS_NONE | SBOX_SECCOMP, 2, PATH_FIREMON, "--list"); exit(rv); } else if (strcmp(argv[i], "--tree") == 0) { - int rv = sbox_run(SBOX_USER | SBOX_CAPS | SBOX_SECCOMP, 2, PATH_FIREMON, "--tree"); + int rv = sbox_run(SBOX_USER | SBOX_CAPS_NONE | SBOX_SECCOMP, 2, PATH_FIREMON, "--tree"); exit(rv); } else if (strcmp(argv[i], "--top") == 0) { - int rv = sbox_run(SBOX_USER | SBOX_CAPS | SBOX_SECCOMP, 2, PATH_FIREMON, "--top"); + int rv = sbox_run(SBOX_USER | SBOX_CAPS_NONE | SBOX_SECCOMP, 2, PATH_FIREMON, "--top"); exit(rv); } #ifdef HAVE_NETWORK @@ -516,9 +516,9 @@ static void run_cmd_and_exit(int i, int argc, char **argv) { struct stat s; int rv; if (stat("/proc/sys/kernel/grsecurity", &s) == 0) - rv = sbox_run(SBOX_ROOT | SBOX_CAPS | SBOX_SECCOMP, 2, PATH_FIREMON, "--netstats"); + rv = sbox_run(SBOX_ROOT | SBOX_CAPS_NONE | SBOX_SECCOMP, 2, PATH_FIREMON, "--netstats"); else - rv = sbox_run(SBOX_USER | SBOX_CAPS | SBOX_SECCOMP, 2, PATH_FIREMON, "--netstats"); + rv = sbox_run(SBOX_USER | SBOX_CAPS_NONE | SBOX_SECCOMP, 2, PATH_FIREMON, "--netstats"); exit(rv); } else { @@ -855,6 +855,9 @@ int main(int argc, char **argv) { int highest_errno = errno_highest_nr(); #endif + // build /run/firejail directory structure + preproc_build_firejail_dir(); + detect_quiet(argc, argv); detect_allow_debuggers(argc, argv); @@ -957,10 +960,8 @@ int main(int argc, char **argv) { // initialize globals init_cfg(argc, argv); - // check firejail directories EUID_ROOT(); - fs_build_firejail_dir(); bandwidth_del_run_file(sandbox_pid); network_del_run_file(sandbox_pid); delete_name_file(sandbox_pid); @@ -1462,35 +1463,6 @@ int main(int argc, char **argv) { } } -#if 0 // disabled for now, it could be used to overwrite system directories - else if (strncmp(argv[i], "--overlay-path=", 15) == 0) { - if (checkcfg(CFG_OVERLAYFS)) { - if (cfg.chrootdir) { - fprintf(stderr, "Error: --overlay and --chroot options are mutually exclusive\n"); - exit(1); - } - struct stat s; - if (stat("/proc/sys/kernel/grsecurity", &s) == 0) { - fprintf(stderr, "Error: --overlay option is not available on Grsecurity systems\n"); - exit(1); - } - arg_overlay = 1; - arg_overlay_keep = 1; - arg_overlay_reuse = 1; - - char *dirname = argv[i] + 15; - if (dirname == '\0') { - fprintf(stderr, "Error: invalid overlay option\n"); - exit(1); - } - cfg.overlay_dir = expand_home(dirname, cfg.homedir); - } - else { - fprintf(stderr, "Error: overlayfs feature is disabled in Firejail configuration file\n"); - exit(1); - } - } -#endif else if (strcmp(argv[i], "--overlay-tmpfs") == 0) { if (checkcfg(CFG_OVERLAYFS)) { if (cfg.chrootdir) { diff --git a/src/firejail/network.c b/src/firejail/network.c index ac0d86559..6d09d770f 100644 --- a/src/firejail/network.c +++ b/src/firejail/network.c @@ -28,70 +28,6 @@ #include #include -// scan interfaces in current namespace and print IP address/mask for each interface -void net_ifprint(void) { - uint32_t ip; - uint32_t mask; - struct ifaddrs *ifaddr, *ifa; - - if (getifaddrs(&ifaddr) == -1) - errExit("getifaddrs"); - - printf("%-17.17s%-19.19s%-17.17s%-17.17s%-6.6s\n", - "Interface", "MAC", "IP", "Mask", "Status"); - // walk through the linked list - for (ifa = ifaddr; ifa != NULL; ifa = ifa->ifa_next) { - if (ifa->ifa_addr == NULL) - continue; - - if (ifa->ifa_addr->sa_family == AF_INET) { - struct sockaddr_in *si = (struct sockaddr_in *) ifa->ifa_netmask; - mask = ntohl(si->sin_addr.s_addr); - si = (struct sockaddr_in *) ifa->ifa_addr; - ip = ntohl(si->sin_addr.s_addr); - - // interface status - char *status; - if (ifa->ifa_flags & IFF_RUNNING && ifa->ifa_flags & IFF_UP) - status = "UP"; - else - status = "DOWN"; - - // ip address and mask - char ipstr[30]; - sprintf(ipstr, "%d.%d.%d.%d", PRINT_IP(ip)); - char maskstr[30]; - sprintf(maskstr, "%d.%d.%d.%d", PRINT_IP(mask)); - - // mac address - unsigned char mac[6]; - net_get_mac(ifa->ifa_name, mac); - char macstr[30]; - if (strcmp(ifa->ifa_name, "lo") == 0) - macstr[0] = '\0'; - else - sprintf(macstr, "%02x:%02x:%02x:%02x:%02x:%02x", PRINT_MAC(mac)); - - // print - printf("%-17.17s%-19.19s%-17.17s%-17.17s%-6.6s\n", - ifa->ifa_name, macstr, ipstr, maskstr, status); - - // network scanning - if (!arg_scan) // scanning disabled - continue; - if (strcmp(ifa->ifa_name, "lo") == 0) // no loopbabck scanning - continue; - if (mask2bits(mask) < 16) // not scanning large networks - continue; - if (!ip) // if not configured - continue; - // only if the interface is up and running - if (ifa->ifa_flags & IFF_RUNNING && ifa->ifa_flags & IFF_UP) - arp_scan(ifa->ifa_name, ip, mask); - } - } - freeifaddrs(ifaddr); -} int net_get_mtu(const char *ifname) { int mtu = 0; @@ -190,101 +126,11 @@ void net_if_up(const char *ifname) { fprintf(stderr, "Error: invalid network device name %s\n", ifname); exit(1); } - - int sock = socket(AF_INET,SOCK_DGRAM,0); - if (sock < 0) - errExit("socket"); + sbox_run(SBOX_ROOT | SBOX_CAPS_NETWORK | SBOX_SECCOMP, 3, + PATH_FNET, "ifup", ifname); +} - // get the existing interface flags - struct ifreq ifr; - memset(&ifr, 0, sizeof(ifr)); - strncpy(ifr.ifr_name, ifname, IFNAMSIZ); - ifr.ifr_addr.sa_family = AF_INET; - // read the existing flags - if (ioctl(sock, SIOCGIFFLAGS, &ifr ) < 0) { - close(sock); - printf("Error: cannot bring up interface %s\n", ifname); - errExit("ioctl"); - } - - ifr.ifr_flags |= IFF_UP; - - // set the new flags - if (ioctl( sock, SIOCSIFFLAGS, &ifr ) < 0) { - close(sock); - printf("Error: cannot bring up interface %s\n", ifname); - errExit("ioctl"); - } - - // checking - // read the existing flags - if (ioctl(sock, SIOCGIFFLAGS, &ifr ) < 0) { - close(sock); - printf("Error: cannot bring up interface %s\n", ifname); - errExit("ioctl"); - } - - // wait not more than 500ms for the interface to come up - int cnt = 0; - while (cnt < 50) { - usleep(10000); // sleep 10ms - - // read the existing flags - if (ioctl(sock, SIOCGIFFLAGS, &ifr ) < 0) { - close(sock); - printf("Error: cannot bring up interface %s\n", ifname); - errExit("ioctl"); - } - if (ifr.ifr_flags & IFF_RUNNING) - break; - cnt++; - } - - close(sock); -} - -// bring interface up -void net_if_down(const char *ifname) { - if (strlen(ifname) > IFNAMSIZ) { - fprintf(stderr, "Error: invalid network device name %s\n", ifname); - exit(1); - } - - int sock = socket(AF_INET,SOCK_DGRAM,0); - if (sock < 0) - errExit("socket"); - - // get the existing interface flags - struct ifreq ifr; - memset(&ifr, 0, sizeof(ifr)); - strncpy(ifr.ifr_name, ifname, IFNAMSIZ); - ifr.ifr_addr.sa_family = AF_INET; - - // read the existing flags - if (ioctl(sock, SIOCGIFFLAGS, &ifr ) < 0) { - close(sock); - printf("Error: cannot shut down interface %s\n", ifname); - errExit("ioctl"); - } - - ifr.ifr_flags &= ~IFF_UP; - - // set the new flags - if (ioctl( sock, SIOCSIFFLAGS, &ifr ) < 0) { - close(sock); - printf("Error: cannot shut down interface %s\n", ifname); - errExit("ioctl"); - } - - close(sock); -} - -struct ifreq6 { - struct in6_addr ifr6_addr; - uint32_t ifr6_prefixlen; - unsigned int ifr6_ifindex; -}; // configure interface ipv6 address // ex: firejail --net=eth0 --ip6=2001:0db8:0:f101::1/64 void net_if_ip6(const char *ifname, const char *addr6) { @@ -293,107 +139,11 @@ void net_if_ip6(const char *ifname, const char *addr6) { exit(1); } - // extract prefix - unsigned long prefix; - char *ptr; - if ((ptr = strchr(addr6, '/'))) { - prefix = atol(ptr + 1); - if (prefix > 128) { - fprintf(stderr, "Error: invalid prefix for IPv6 address %s\n", addr6); - exit(1); - } - *ptr = '\0'; // mark the end of the address - } - else - prefix = 128; - - // extract address - struct sockaddr_in6 sin6; - memset(&sin6, 0, sizeof(sin6)); - sin6.sin6_family = AF_INET6; - int rv = inet_pton(AF_INET6, addr6, sin6.sin6_addr.s6_addr); - if (rv <= 0) { - fprintf(stderr, "Error: invalid IPv6 address %s\n", addr6); - exit(1); - } - - // open socket - int sock = socket(PF_INET6, SOCK_DGRAM, IPPROTO_IP); - if (sock < 0) { - fprintf(stderr, "Error: IPv6 is not supported on this system\n"); - exit(1); - } - - // find interface index - struct ifreq ifr; - memset(&ifr, 0, sizeof(ifr)); - strncpy(ifr.ifr_name, ifname, IFNAMSIZ); - ifr.ifr_addr.sa_family = AF_INET; - if (ioctl(sock, SIOGIFINDEX, &ifr) < 0) { - perror("ioctl SIOGIFINDEX"); - exit(1); - } - - // configure address - struct ifreq6 ifr6; - memset(&ifr6, 0, sizeof(ifr6)); - ifr6.ifr6_prefixlen = prefix; - ifr6.ifr6_ifindex = ifr.ifr_ifindex; - memcpy((char *) &ifr6.ifr6_addr, (char *) &sin6.sin6_addr, sizeof(struct in6_addr)); - if (ioctl(sock, SIOCSIFADDR, &ifr6) < 0) { - perror("ioctl SIOCSIFADDR"); - exit(1); - } - - close(sock); -} - -// configure interface ipv4 address -void net_if_ip(const char *ifname, uint32_t ip, uint32_t mask, int mtu) { - if (strlen(ifname) > IFNAMSIZ) { - fprintf(stderr, "Error: invalid network device name %s\n", ifname); - exit(1); - } - if (arg_debug) - printf("configure interface %s\n", ifname); - - int sock = socket(AF_INET,SOCK_DGRAM,0); - if (sock < 0) - errExit("socket"); - - struct ifreq ifr; - memset(&ifr, 0, sizeof(ifr)); - strncpy(ifr.ifr_name, ifname, IFNAMSIZ); - ifr.ifr_addr.sa_family = AF_INET; - - ((struct sockaddr_in *)&ifr.ifr_addr)->sin_addr.s_addr = htonl(ip); - if (ioctl( sock, SIOCSIFADDR, &ifr ) < 0) { - close(sock); - errExit("ioctl"); - } - - if (ip != 0) { - ((struct sockaddr_in *)&ifr.ifr_addr)->sin_addr.s_addr = htonl(mask); - if (ioctl( sock, SIOCSIFNETMASK, &ifr ) < 0) { - close(sock); - errExit("ioctl"); - } - } - - // configure mtu - if (mtu > 0) { - ifr.ifr_mtu = mtu; - if (ioctl( sock, SIOCSIFMTU, &ifr ) < 0) { - close(sock); - errExit("ioctl"); - } - } + sbox_run(SBOX_ROOT | SBOX_CAPS_NETWORK | SBOX_SECCOMP, 5, + PATH_FNET, "config", "ipv6", ifname, addr6); - close(sock); - usleep(10000); // sleep 10ms } - // add an IP route, return -1 if error, 0 if the route was added int net_add_route(uint32_t ip, uint32_t mask, uint32_t gw) { int sock; @@ -464,20 +214,15 @@ uint32_t network_get_defaultgw(void) { } int net_config_mac(const char *ifname, const unsigned char mac[6]) { - struct ifreq ifr; - int sock; + char *macstr; + if (asprintf(&macstr, "%02x:%02x:%02x:%02x:%02x:%02x", + mac[0], mac[1], mac[2], mac[3], mac[4], mac[5]) == -1) + errExit("asprintf"); - if ((sock = socket(AF_INET, SOCK_STREAM, 0)) < 0) - errExit("socket"); - - memset(&ifr, 0, sizeof(ifr)); - strncpy(ifr.ifr_name, ifname, IFNAMSIZ); - ifr.ifr_hwaddr.sa_family = ARPHRD_ETHER; - memcpy(ifr.ifr_hwaddr.sa_data, mac, 6); + sbox_run(SBOX_ROOT | SBOX_CAPS_NETWORK | SBOX_SECCOMP, 5, + PATH_FNET, "config", "mac", ifname, macstr); - if (ioctl(sock, SIOCSIFHWADDR, &ifr) == -1) - errExit("ioctl"); - close(sock); + free(macstr); return 0; } @@ -500,3 +245,27 @@ int net_get_mac(const char *ifname, unsigned char mac[6]) { close(sock); return 0; } + +void net_config_interface(const char *dev, uint32_t ip, uint32_t mask, int mtu) { + assert(dev); + + char *ipstr; + if (asprintf(&ipstr, "%llu", (long long unsigned) ip) == -1) + errExit("asprintf"); + + char *maskstr; + if (asprintf(&maskstr, "%llu", (long long unsigned) mask) == -1) + errExit("asprintf"); + + char *mtustr; + if (asprintf(&mtustr, "%d", mtu) == -1) + errExit("asprintf"); + + sbox_run(SBOX_ROOT | SBOX_CAPS_NETWORK | SBOX_SECCOMP, 7, + PATH_FNET, "config", "interface", dev, ipstr, maskstr, mtustr); + + free(ipstr); + free(maskstr); + free(mtustr); +} + diff --git a/src/firejail/network_main.c b/src/firejail/network_main.c index 35d6dd214..8a9c47f0e 100644 --- a/src/firejail/network_main.c +++ b/src/firejail/network_main.c @@ -128,11 +128,10 @@ void net_configure_veth_pair(Bridge *br, const char *ifname, pid_t child) { else dev = br->veth_name; -// net_create_veth(dev, ifname, child); char *cstr; if (asprintf(&cstr, "%d", child) == -1) errExit("asprintf"); - sbox_run(SBOX_ROOT | SBOX_SECCOMP, 7, PATH_FNET, "create", "veth", dev, ifname, br->dev, cstr); + sbox_run(SBOX_ROOT | SBOX_CAPS_NETWORK | SBOX_SECCOMP, 7, PATH_FNET, "create", "veth", dev, ifname, br->dev, cstr); free(cstr); char *msg; @@ -300,50 +299,42 @@ void network_main(pid_t child) { net_configure_veth_pair(&cfg.bridge0, "eth0", child); } else -// net_create_macvlan(cfg.bridge0.devsandbox, cfg.bridge0.dev, child); - sbox_run(SBOX_ROOT | SBOX_SECCOMP, 6, PATH_FNET, "create", "macvlan", cfg.bridge0.devsandbox, cfg.bridge0.dev, cstr); + sbox_run(SBOX_ROOT | SBOX_CAPS_NETWORK | SBOX_SECCOMP, 6, PATH_FNET, "create", "macvlan", cfg.bridge0.devsandbox, cfg.bridge0.dev, cstr); } 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); - sbox_run(SBOX_ROOT | SBOX_SECCOMP, 6, PATH_FNET, "create", "macvlan", cfg.bridge1.devsandbox, cfg.bridge1.dev, cstr); + sbox_run(SBOX_ROOT | SBOX_CAPS_NETWORK | SBOX_SECCOMP, 6, PATH_FNET, "create", "macvlan", cfg.bridge1.devsandbox, cfg.bridge1.dev, cstr); } 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); - sbox_run(SBOX_ROOT | SBOX_SECCOMP, 6, PATH_FNET, "create", "macvlan", cfg.bridge2.devsandbox, cfg.bridge2.dev, cstr); + sbox_run(SBOX_ROOT | SBOX_CAPS_NETWORK | SBOX_SECCOMP, 6, PATH_FNET, "create", "macvlan", cfg.bridge2.devsandbox, cfg.bridge2.dev, cstr); } 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); - sbox_run(SBOX_ROOT | SBOX_SECCOMP, 6, PATH_FNET, "create", "macvlan", cfg.bridge3.devsandbox, cfg.bridge3.dev, cstr); + sbox_run(SBOX_ROOT | SBOX_CAPS_NETWORK | SBOX_SECCOMP, 6, PATH_FNET, "create", "macvlan", cfg.bridge3.devsandbox, cfg.bridge3.dev, cstr); } // move interfaces in sandbox if (cfg.interface0.configured) { -// net_move_interface(cfg.interface0.dev, child); - sbox_run(SBOX_ROOT | SBOX_SECCOMP, 4, PATH_FNET, "moveif", cfg.interface0.dev, cstr); + sbox_run(SBOX_ROOT | SBOX_CAPS_NETWORK | SBOX_SECCOMP, 4, PATH_FNET, "moveif", cfg.interface0.dev, cstr); } if (cfg.interface1.configured) { -// net_move_interface(cfg.interface1.dev, child); - sbox_run(SBOX_ROOT | SBOX_SECCOMP, 4, PATH_FNET, "moveif", cfg.interface1.dev, cstr); + sbox_run(SBOX_ROOT | SBOX_CAPS_NETWORK | SBOX_SECCOMP, 4, PATH_FNET, "moveif", cfg.interface1.dev, cstr); } if (cfg.interface2.configured) { -// net_move_interface(cfg.interface2.dev, child); - sbox_run(SBOX_ROOT | SBOX_SECCOMP, 4, PATH_FNET, "moveif", cfg.interface3.dev, cstr); + sbox_run(SBOX_ROOT | SBOX_CAPS_NETWORK | SBOX_SECCOMP, 4, PATH_FNET, "moveif", cfg.interface3.dev, cstr); } if (cfg.interface3.configured) { -// net_move_interface(cfg.interface3.dev, child); - sbox_run(SBOX_ROOT | SBOX_SECCOMP, 4, PATH_FNET, "moveif", cfg.interface3.dev, cstr); + sbox_run(SBOX_ROOT | SBOX_CAPS_NETWORK | SBOX_SECCOMP, 4, PATH_FNET, "moveif", cfg.interface3.dev, cstr); } free(cstr); diff --git a/src/firejail/preproc.c b/src/firejail/preproc.c new file mode 100644 index 000000000..6784ff5ac --- /dev/null +++ b/src/firejail/preproc.c @@ -0,0 +1,110 @@ +/* + * Copyright (C) 2014-2016 Firejail Authors + * + * This file is part of firejail project + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ +#include "firejail.h" +#include +#include + +static int tmpfs_mounted = 0; + +// build /run/firejail directory +void preproc_build_firejail_dir(void) { + struct stat s; + + // CentOS 6 doesn't have /run directory + if (stat(RUN_FIREJAIL_BASEDIR, &s)) { + create_empty_dir_as_root(RUN_FIREJAIL_BASEDIR, 0755); + } + + if (stat(RUN_FIREJAIL_DIR, &s)) { + create_empty_dir_as_root(RUN_FIREJAIL_DIR, 0755); + } + + if (stat(RUN_FIREJAIL_NETWORK_DIR, &s)) { + create_empty_dir_as_root(RUN_FIREJAIL_NETWORK_DIR, 0755); + } + + if (stat(RUN_FIREJAIL_BANDWIDTH_DIR, &s)) { + create_empty_dir_as_root(RUN_FIREJAIL_BANDWIDTH_DIR, 0755); + } + + if (stat(RUN_FIREJAIL_NAME_DIR, &s)) { + create_empty_dir_as_root(RUN_FIREJAIL_NAME_DIR, 0755); + } + + if (stat(RUN_FIREJAIL_X11_DIR, &s)) { + create_empty_dir_as_root(RUN_FIREJAIL_X11_DIR, 0755); + } + + if (stat(RUN_FIREJAIL_APPIMAGE_DIR, &s)) { + create_empty_dir_as_root(RUN_FIREJAIL_APPIMAGE_DIR, 0755); + } + + create_empty_file_as_root(RUN_RO_FILE, S_IRUSR); + create_empty_dir_as_root(RUN_RO_DIR, S_IRUSR); +} + +// build /run/firejail/mnt directory +void preproc_mount_mnt_dir(void) { + struct stat s; + + // mount tmpfs on top of /run/firejail/mnt + if (!tmpfs_mounted) { + if (arg_debug) + printf("Mounting tmpfs on %s directory\n", RUN_MNT_DIR); + if (mount("tmpfs", RUN_MNT_DIR, "tmpfs", MS_NOSUID | MS_STRICTATIME | MS_REC, "mode=755,gid=0") < 0) + errExit("mounting /run/firejail/mnt"); + tmpfs_mounted = 1; + fs_logger2("tmpfs", RUN_MNT_DIR); + } +} + +// grab a copy of cp command +void preproc_build_cp_command(void) { + struct stat s; + preproc_mount_mnt_dir(); + if (stat(RUN_CP_COMMAND, &s)) { + char* fname = realpath("/bin/cp", NULL); + if (fname == NULL) { + fprintf(stderr, "Error: /bin/cp not found\n"); + exit(1); + } + if (stat(fname, &s)) { + fprintf(stderr, "Error: /bin/cp not found\n"); + exit(1); + } + if (is_link(fname)) { + fprintf(stderr, "Error: invalid /bin/cp file\n"); + exit(1); + } + int rv = copy_file(fname, RUN_CP_COMMAND, 0, 0, 0755); + if (rv) { + fprintf(stderr, "Error: cannot access /bin/cp\n"); + exit(1); + } + ASSERT_PERMS(RUN_CP_COMMAND, 0, 0, 0755); + + free(fname); + } +} + +// delete the temporary cp command +void preproc_delete_cp_command(void) { + unlink(RUN_CP_COMMAND); +} diff --git a/src/firejail/protocol.c b/src/firejail/protocol.c index 43f30e30a..db6c6cad9 100644 --- a/src/firejail/protocol.c +++ b/src/firejail/protocol.c @@ -70,8 +70,6 @@ void protocol_filter(const char *fname) { void protocol_filter_save(void) { // save protocol filter configuration in PROTOCOL_CFG - fs_build_mnt_dir(); - FILE *fp = fopen(RUN_PROTOCOL_CFG, "w"); if (!fp) errExit("fopen"); diff --git a/src/firejail/pulseaudio.c b/src/firejail/pulseaudio.c index 90997f934..e1a58c1c8 100644 --- a/src/firejail/pulseaudio.c +++ b/src/firejail/pulseaudio.c @@ -104,7 +104,6 @@ void pulseaudio_init(void) { return; // create the new user pulseaudio directory - fs_build_mnt_dir(); int rv = mkdir(RUN_PULSE_DIR, 0700); (void) rv; // in --chroot mode the directory can already be there if (chown(RUN_PULSE_DIR, getuid(), getgid()) < 0) diff --git a/src/firejail/restrict_users.c b/src/firejail/restrict_users.c index 5ef9524d7..57e84e5cc 100644 --- a/src/firejail/restrict_users.c +++ b/src/firejail/restrict_users.c @@ -73,7 +73,6 @@ static void sanitize_home(void) { return; } - fs_build_mnt_dir(); if (mkdir(RUN_WHITELIST_HOME_DIR, 0755) == -1) errExit("mkdir"); @@ -127,7 +126,6 @@ static void sanitize_passwd(void) { FILE *fpin = NULL; FILE *fpout = NULL; - fs_build_mnt_dir(); // open files /* coverity[toctou] */ @@ -261,7 +259,6 @@ static void sanitize_group(void) { FILE *fpin = NULL; FILE *fpout = NULL; - fs_build_mnt_dir(); // open files /* coverity[toctou] */ diff --git a/src/firejail/sandbox.c b/src/firejail/sandbox.c index 7a63461ef..a15003d03 100644 --- a/src/firejail/sandbox.c +++ b/src/firejail/sandbox.c @@ -122,7 +122,7 @@ static void sandbox_if_up(Bridge *br) { assert(br); if (!br->configured) return; - + char *dev = br->devsandbox; net_if_up(dev); @@ -137,8 +137,7 @@ static void sandbox_if_up(Bridge *br) { assert(br->ipsandbox); if (arg_debug) printf("Configuring %d.%d.%d.%d address on interface %s\n", PRINT_IP(br->ipsandbox), dev); - net_if_ip(dev, br->ipsandbox, br->mask, br->mtu); - net_if_up(dev); + net_config_interface(dev, br->ipsandbox, br->mask, br->mtu); } else if (br->arg_ip_none == 0 && br->macvlan == 1) { // reassign the macvlan address @@ -160,8 +159,7 @@ static void sandbox_if_up(Bridge *br) { if (arg_debug) printf("Configuring %d.%d.%d.%d address on interface %s\n", PRINT_IP(br->ipsandbox), dev); - net_if_ip(dev, br->ipsandbox, br->mask, br->mtu); - net_if_up(dev); + net_config_interface(dev, br->ipsandbox, br->mask, br->mtu); } if (br->ip6sandbox) @@ -256,32 +254,6 @@ static int monitor_application(pid_t app_pid) { // return the latest exit status. return status; - -#if 0 -// todo: find a way to shut down interfaces before closing the namespace -// the problem is we don't have enough privileges to shutdown interfaces in this moment - // shut down bridge/macvlan interfaces - if (any_bridge_configured()) { - - if (cfg.bridge0.configured) { - printf("Shutting down %s\n", cfg.bridge0.devsandbox); - net_if_down( cfg.bridge0.devsandbox); - } - if (cfg.bridge1.configured) { - printf("Shutting down %s\n", cfg.bridge1.devsandbox); - net_if_down( cfg.bridge1.devsandbox); - } - if (cfg.bridge2.configured) { - printf("Shutting down %s\n", cfg.bridge2.devsandbox); - net_if_down( cfg.bridge2.devsandbox); - } - if (cfg.bridge3.configured) { - printf("Shutting down %s\n", cfg.bridge3.devsandbox); - net_if_down( cfg.bridge3.devsandbox); - } - usleep(20000); // 20 ms sleep - } -#endif } void start_audit(void) { @@ -442,7 +414,8 @@ int sandbox(void* sandbox_arg) { if (mount(NULL, "/", NULL, MS_SLAVE | MS_REC, NULL) < 0) { chk_chroot(); } - + // ... and mount a tmpfs on top of /run/firejail/mnt directory + preproc_mount_mnt_dir(); //**************************** // log sandbox data @@ -459,7 +432,7 @@ int sandbox(void* sandbox_arg) { fs_logger("install mount namespace"); //**************************** - // netfilter etc. + // netfilter //**************************** if (arg_netfilter && any_bridge_configured()) { // assuming by default the client filter netfilter(arg_netfilter_file); @@ -468,6 +441,105 @@ int sandbox(void* sandbox_arg) { netfilter6(arg_netfilter6_file); } + //**************************** + // networking + //**************************** + int gw_cfg_failed = 0; // default gw configuration flag + if (arg_nonetwork) { + net_if_up("lo"); + if (arg_debug) + printf("Network namespace enabled, only loopback interface available\n"); + } + else if (any_bridge_configured() || any_interface_configured()) { + // configure lo and eth0...eth3 + net_if_up("lo"); + + if (mac_not_zero(cfg.bridge0.macsandbox)) + net_config_mac(cfg.bridge0.devsandbox, cfg.bridge0.macsandbox); + sandbox_if_up(&cfg.bridge0); + + if (mac_not_zero(cfg.bridge1.macsandbox)) + net_config_mac(cfg.bridge1.devsandbox, cfg.bridge1.macsandbox); + sandbox_if_up(&cfg.bridge1); + + if (mac_not_zero(cfg.bridge2.macsandbox)) + net_config_mac(cfg.bridge2.devsandbox, cfg.bridge2.macsandbox); + sandbox_if_up(&cfg.bridge2); + + if (mac_not_zero(cfg.bridge3.macsandbox)) + net_config_mac(cfg.bridge3.devsandbox, cfg.bridge3.macsandbox); + sandbox_if_up(&cfg.bridge3); + + +// todo: this code seems to be dead!!! + // enable interfaces + if (cfg.interface0.configured && cfg.interface0.ip) { +assert(0); + if (arg_debug) + printf("Configuring %d.%d.%d.%d address on interface %s\n", PRINT_IP(cfg.interface0.ip), cfg.interface0.dev); + net_config_interface(cfg.interface0.dev, cfg.interface0.ip, cfg.interface0.mask, cfg.interface0.mtu); + } + if (cfg.interface1.configured && cfg.interface1.ip) { +assert(0); + if (arg_debug) + printf("Configuring %d.%d.%d.%d address on interface %s\n", PRINT_IP(cfg.interface1.ip), cfg.interface1.dev); + net_config_interface(cfg.interface1.dev, cfg.interface1.ip, cfg.interface1.mask, cfg.interface1.mtu); + } + if (cfg.interface2.configured && cfg.interface2.ip) { +assert(0); + if (arg_debug) + printf("Configuring %d.%d.%d.%d address on interface %s\n", PRINT_IP(cfg.interface2.ip), cfg.interface2.dev); + net_config_interface(cfg.interface2.dev, cfg.interface2.ip, cfg.interface2.mask, cfg.interface2.mtu); + } + if (cfg.interface3.configured && cfg.interface3.ip) { +assert(0); + if (arg_debug) + printf("Configuring %d.%d.%d.%d address on interface %s\n", PRINT_IP(cfg.interface3.ip), cfg.interface3.dev); + net_config_interface(cfg.interface3.dev, cfg.interface3.ip, cfg.interface3.mask, cfg.interface3.mtu); + } + + // add a default route + if (cfg.defaultgw) { + // set the default route + if (net_add_route(0, 0, cfg.defaultgw)) { + fprintf(stderr, "Warning: cannot configure default route\n"); + gw_cfg_failed = 1; + } + } + + if (arg_debug) + printf("Network namespace enabled\n"); + } + + + // print network configuration + if (!arg_quiet) { + if (any_bridge_configured() || any_interface_configured() || cfg.defaultgw || cfg.dns1) { + printf("\n"); + if (any_bridge_configured() || any_interface_configured()) { +// net_ifprint(); + if (arg_scan) + sbox_run(SBOX_ROOT | SBOX_CAPS_NETWORK | SBOX_SECCOMP, 3, PATH_FNET, "printif", "scan"); + else + sbox_run(SBOX_ROOT | SBOX_CAPS_NETWORK | SBOX_SECCOMP, 2, PATH_FNET, "printif", "scan"); + + } + if (cfg.defaultgw != 0) { + if (gw_cfg_failed) + printf("Default gateway configuration failed\n"); + else + printf("Default gateway %d.%d.%d.%d\n", PRINT_IP(cfg.defaultgw)); + } + if (cfg.dns1 != 0) + printf("DNS server %d.%d.%d.%d\n", PRINT_IP(cfg.dns1)); + if (cfg.dns2 != 0) + printf("DNS server %d.%d.%d.%d\n", PRINT_IP(cfg.dns2)); + if (cfg.dns3 != 0) + printf("DNS server %d.%d.%d.%d\n", PRINT_IP(cfg.dns3)); + printf("\n"); + } + } + // load IBUS env variables if (arg_nonetwork || any_bridge_configured() || any_interface_configured()) { // do nothing - there are problems with ibus version 1.5.11 @@ -475,9 +547,34 @@ int sandbox(void* sandbox_arg) { else env_ibus_load(); - // grab a copy of cp command - fs_build_cp_command(); - + //**************************** + // fs pre-processing: + // - copy some commands under /run + // - build seccomp filters + // - create an empty /etc/ld.so.preload + //**************************** + preproc_build_cp_command(); + +#ifdef HAVE_SECCOMP + if (cfg.protocol) { + if (arg_debug) + printf("Build protocol filter: %s\n", cfg.protocol); + // as root, create RUN_SECCOMP_PROTOCOL file + // this is where fseccomp program will store the protocol filter + create_empty_file_as_root(RUN_SECCOMP_PROTOCOL, 0644); + if (chown(RUN_SECCOMP_PROTOCOL, getuid(), getgid()) == -1) + errExit("chown"); + if (chmod(RUN_SECCOMP_PROTOCOL, 0644) == -1) + errExit("chmod"); + + // build the seccomp filter as a regular user + int rv = sbox_run(SBOX_USER | SBOX_CAPS_NONE | SBOX_SECCOMP, 5, + PATH_FSECCOMP, "protocol", "build", cfg.protocol, RUN_SECCOMP_PROTOCOL); + if (rv) + exit(rv); + } +#endif + // trace pre-install if (arg_trace || arg_tracelog || mask_x11_abstract_socket) fs_trace_preload(); @@ -494,7 +591,7 @@ int sandbox(void* sandbox_arg) { enforce_seccomp = 1; #endif } - + #ifdef HAVE_CHROOT if (cfg.chrootdir) { fs_chroot(cfg.chrootdir); @@ -617,7 +714,6 @@ int sandbox(void* sandbox_arg) { EUID_USER(); profile_add("whitelist /tmp/.X11-unix"); EUID_ROOT(); -// fs_private_tmp(); } } @@ -664,102 +760,17 @@ int sandbox(void* sandbox_arg) { fs_dev_disable_3d(); //**************************** - // networking + // set dns //**************************** - int gw_cfg_failed = 0; // default gw configuration flag - if (arg_nonetwork) { - net_if_up("lo"); - if (arg_debug) - printf("Network namespace enabled, only loopback interface available\n"); - } - else if (any_bridge_configured() || any_interface_configured()) { - // configure lo and eth0...eth3 - net_if_up("lo"); - - if (mac_not_zero(cfg.bridge0.macsandbox)) - net_config_mac(cfg.bridge0.devsandbox, cfg.bridge0.macsandbox); - sandbox_if_up(&cfg.bridge0); - - if (mac_not_zero(cfg.bridge1.macsandbox)) - net_config_mac(cfg.bridge1.devsandbox, cfg.bridge1.macsandbox); - sandbox_if_up(&cfg.bridge1); - - if (mac_not_zero(cfg.bridge2.macsandbox)) - net_config_mac(cfg.bridge2.devsandbox, cfg.bridge2.macsandbox); - sandbox_if_up(&cfg.bridge2); - - if (mac_not_zero(cfg.bridge3.macsandbox)) - net_config_mac(cfg.bridge3.devsandbox, cfg.bridge3.macsandbox); - sandbox_if_up(&cfg.bridge3); - - // 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, cfg.interface0.mtu); - 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, cfg.interface1.mtu); - 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, cfg.interface2.mtu); - 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, cfg.interface3.mtu); - net_if_up(cfg.interface3.dev); - } - - // add a default route - if (cfg.defaultgw) { - // set the default route - if (net_add_route(0, 0, cfg.defaultgw)) { - fprintf(stderr, "Warning: cannot configure default route\n"); - gw_cfg_failed = 1; - } - } - - if (arg_debug) - printf("Network namespace enabled\n"); - } - - // if any dns server is configured, it is time to set it now fs_resolvconf(); + + //**************************** + // fs post-processing + //**************************** + preproc_delete_cp_command(); fs_logger_print(); fs_logger_change_owner(); - // print network configuration - if (!arg_quiet) { - if (any_bridge_configured() || any_interface_configured() || cfg.defaultgw || cfg.dns1) { - printf("\n"); - if (any_bridge_configured() || any_interface_configured()) - net_ifprint(); - if (cfg.defaultgw != 0) { - if (gw_cfg_failed) - printf("Default gateway configuration failed\n"); - else - printf("Default gateway %d.%d.%d.%d\n", PRINT_IP(cfg.defaultgw)); - } - if (cfg.dns1 != 0) - printf("DNS server %d.%d.%d.%d\n", PRINT_IP(cfg.dns1)); - if (cfg.dns2 != 0) - printf("DNS server %d.%d.%d.%d\n", PRINT_IP(cfg.dns2)); - if (cfg.dns3 != 0) - printf("DNS server %d.%d.%d.%d\n", PRINT_IP(cfg.dns3)); - printf("\n"); - } - } - - fs_delete_cp_command(); - //**************************** // set application environment //**************************** @@ -820,21 +831,7 @@ int sandbox(void* sandbox_arg) { // install protocol filter if (cfg.protocol) { if (arg_debug) - printf("Set protocol filter: %s\n", cfg.protocol); - // as root, create RUN_SECCOMP_PROTOCOL file - // this is where fseccomp program will store the protocol filter - int dst = open(RUN_SECCOMP_PROTOCOL, O_CREAT|O_WRONLY|O_TRUNC, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); - if (dst == -1) - errExit("open"); - close(dst); - if (chown(RUN_SECCOMP_PROTOCOL, getuid(), getgid()) == -1) - errExit("chown"); - - // build the seccomp filter as a regular user - int rv = sbox_run(SBOX_USER | SBOX_CAPS | SBOX_SECCOMP, 5, - PATH_FSECCOMP, "protocol", "build", cfg.protocol, RUN_SECCOMP_PROTOCOL); - if (rv) - exit(rv); + printf("Install protocol filter: %s\n", cfg.protocol); protocol_filter(RUN_SECCOMP_PROTOCOL); // install filter protocol_filter_save(); // save filter in RUN_PROTOCOL_CFG } diff --git a/src/firejail/sbox.c b/src/firejail/sbox.c index d1225c3bc..6499b7005 100644 --- a/src/firejail/sbox.c +++ b/src/firejail/sbox.c @@ -125,21 +125,26 @@ int sbox_run(unsigned filter, int num, ...) { arg[i] = NULL; va_end(valist); -//#if 0 -{ -int i; -for (i = 0; i <= num; i++) - printf("#%s# ", arg[i]); -printf("\n"); -} -//#endif + if (arg_debug) { + printf("sbox run: "); + for (i = 0; i <= num; i++) + printf("%s ", arg[i]); + printf("\n"); + } + pid_t child = fork(); if (child < 0) errExit("fork"); if (child == 0) { // apply filters - if (filter & SBOX_CAPS) + if (filter & SBOX_CAPS_NONE) { caps_drop_all(); + } + else if (filter & SBOX_CAPS_NETWORK) { + uint64_t set = ((uint64_t) 1) << CAP_NET_ADMIN; + set |= ((uint64_t) 1) << CAP_NET_RAW; + caps_set(set); + } if (filter & SBOX_SECCOMP) { if (prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0)) { diff --git a/src/firejail/seccomp.c b/src/firejail/seccomp.c index 09862ec20..69be04a03 100644 --- a/src/firejail/seccomp.c +++ b/src/firejail/seccomp.c @@ -275,7 +275,6 @@ static void filter_end_whitelist(void) { // save seccomp filter in /run/firejail/mnt/seccomp static void write_seccomp_file(void) { - fs_build_mnt_dir(); assert(sfilter); int fd = open(RUN_SECCOMP_CFG, O_CREAT | O_WRONLY, S_IRUSR | S_IWUSR); diff --git a/src/firejail/util.c b/src/firejail/util.c index 4b2e09953..4384c2b0b 100644 --- a/src/firejail/util.c +++ b/src/firejail/util.c @@ -696,3 +696,37 @@ void flush_stdin(void) { } } +void create_empty_dir_as_root(const char *dir, mode_t mode) { + assert(dir); + + struct stat s; + if (stat(dir, &s)) { + if (arg_debug) + printf("Creating empty %s directory\n", dir); + if (mkdir(dir, mode) == -1) + errExit("mkdir"); + if (chmod(dir, mode) == -1) + errExit("chmod"); + ASSERT_PERMS(dir, 0, 0, mode); + } + +} + +void create_empty_file_as_root(const char *fname, mode_t mode) { + assert(fname); + struct stat s; + + if (stat(fname, &s)) { + if (arg_debug) + printf("Creating empty %s file\n", fname); + + /* coverity[toctou] */ + FILE *fp = fopen(fname, "w"); + if (!fp) + errExit("fopen"); + + SET_PERMS_STREAM(fp, 0, 0, S_IRUSR); + fclose(fp); + } +} + diff --git a/src/fnet/arp.c b/src/fnet/arp.c new file mode 100644 index 000000000..96684fdf9 --- /dev/null +++ b/src/fnet/arp.c @@ -0,0 +1,208 @@ +/* + * Copyright (C) 2014-2016 Firejail Authors + * + * This file is part of firejail project + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License along + * with this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. +*/ +#include "fnet.h" +#include +#include +#include //TCP/IP Protocol Suite for Linux +#include +#include +#include +#include +#include +#include + +typedef struct arp_hdr_t { + uint16_t htype; + uint16_t ptype; + uint8_t hlen; + uint8_t plen; + uint16_t opcode; + uint8_t sender_mac[6]; + uint8_t sender_ip[4]; + uint8_t target_mac[6]; + uint8_t target_ip[4]; +} ArpHdr; + + +// scan interface (--scan option) +void arp_scan(const char *dev, uint32_t ifip, uint32_t ifmask) { + assert(dev); + assert(ifip); + +// printf("Scanning interface %s (%d.%d.%d.%d/%d)\n", +// dev, PRINT_IP(ifip & ifmask), mask2bits(ifmask)); + + if (strlen(dev) > IFNAMSIZ) { + fprintf(stderr, "Error: invalid network device name %s\n", dev); + exit(1); + } + + // find interface mac address + int sock; + if ((sock = socket(AF_INET, SOCK_RAW, IPPROTO_RAW)) < 0) + errExit("socket"); + struct ifreq ifr; + memset(&ifr, 0, sizeof (ifr)); + strncpy(ifr.ifr_name, dev, IFNAMSIZ); + if (ioctl(sock, SIOCGIFHWADDR, &ifr) < 0) + errExit("ioctl"); + close(sock); + uint8_t mac[6]; + memcpy (mac, ifr.ifr_hwaddr.sa_data, 6); + + // open layer2 socket + if ((sock = socket(PF_PACKET, SOCK_RAW, htons (ETH_P_ALL))) < 0) + errExit("socket"); + + // try all possible ip addresses in ascending order + uint32_t range = ~ifmask + 1; // the number of potential addresses + // this software is not supported for /31 networks + if (range < 4) { + fprintf(stderr, "Warning: this option is not supported for /31 networks\n"); + close(sock); + return; + } + + uint32_t dest = (ifip & ifmask) + 1; + uint32_t last = dest + range - 1; + uint32_t src = htonl(ifip); + + // wait not more than one second for an answer + int header_printed = 0; + uint32_t last_ip = 0; + struct timeval ts; + ts.tv_sec = 2; // 2 seconds receive timeout + ts.tv_usec = 0; + + while (1) { + fd_set rfds; + FD_ZERO(&rfds); + FD_SET(sock, &rfds); + fd_set wfds; + FD_ZERO(&wfds); + FD_SET(sock, &wfds); + int maxfd = sock; + + uint8_t frame[ETH_FRAME_LEN]; // includes eht header, vlan, and crc + memset(frame, 0, ETH_FRAME_LEN); + + int nready; + if (dest < last) + nready = select(maxfd + 1, &rfds, &wfds, (fd_set *) 0, NULL); + else + nready = select(maxfd + 1, &rfds, (fd_set *) 0, (fd_set *) 0, &ts); + + if (nready < 0) + errExit("select"); + + if (nready == 0) { // timeout + break; + } + + if (FD_ISSET(sock, &wfds) && dest < last) { + // configure layer2 socket address information + struct sockaddr_ll addr; + memset(&addr, 0, sizeof(addr)); + if ((addr.sll_ifindex = if_nametoindex(dev)) == 0) + errExit("if_nametoindex"); + addr.sll_family = AF_PACKET; + memcpy (addr.sll_addr, mac, 6); + addr.sll_halen = htons(6); + + // build the arp packet header + ArpHdr hdr; + memset(&hdr, 0, sizeof(hdr)); + hdr.htype = htons(1); + hdr.ptype = htons(ETH_P_IP); + hdr.hlen = 6; + hdr.plen = 4; + hdr.opcode = htons(1); //ARPOP_REQUEST + memcpy(hdr.sender_mac, mac, 6); + memcpy(hdr.sender_ip, (uint8_t *)&src, 4); + uint32_t dst = htonl(dest); + memcpy(hdr.target_ip, (uint8_t *)&dst, 4); + + // build ethernet frame + uint8_t frame[ETH_FRAME_LEN]; // includes eht header, vlan, and crc + memset(frame, 0, sizeof(frame)); + frame[0] = frame[1] = frame[2] = frame[3] = frame[4] = frame[5] = 0xff; + memcpy(frame + 6, mac, 6); + frame[12] = ETH_P_ARP / 256; + frame[13] = ETH_P_ARP % 256; + memcpy (frame + 14, &hdr, sizeof(hdr)); + + // send packet + int len; + if ((len = sendto (sock, frame, 14 + sizeof(ArpHdr), 0, (struct sockaddr *) &addr, sizeof (addr))) <= 0) + errExit("send"); +//printf("send %d bytes to %d.%d.%d.%d\n", len, PRINT_IP(dest)); + fflush(0); + dest++; + } + + if (FD_ISSET(sock, &rfds)) { + // read the incoming packet + int len = recvfrom(sock, frame, ETH_FRAME_LEN, 0, NULL, NULL); + if (len < 0) { + perror("recvfrom"); + } + + // parse the incoming packet + if ((unsigned int) len < 14 + sizeof(ArpHdr)) + continue; + + // look only at ARP packets + if (frame[12] != (ETH_P_ARP / 256) || frame[13] != (ETH_P_ARP % 256)) + continue; + + ArpHdr hdr; + memcpy(&hdr, frame + 14, sizeof(ArpHdr)); + + if (hdr.opcode == htons(2)) { + // check my mac and my address + if (memcmp(mac, hdr.target_mac, 6) != 0) + continue; + uint32_t ip; + memcpy(&ip, hdr.target_ip, 4); + if (ip != src) + continue; + memcpy(&ip, hdr.sender_ip, 4); + ip = ntohl(ip); + + if (ip == last_ip) // filter duplicates + continue; + last_ip = ip; + + // printing + if (header_printed == 0) { + printf(" Network scan:\n"); + header_printed = 1; + } + printf(" %02x:%02x:%02x:%02x:%02x:%02x\t%d.%d.%d.%d\n", + PRINT_MAC(hdr.sender_mac), PRINT_IP(ip)); + } + } + } + + close(sock); +} + + + diff --git a/src/fnet/fnet.h b/src/fnet/fnet.h index 58efbbed5..0c5e5baef 100644 --- a/src/fnet/fnet.h +++ b/src/fnet/fnet.h @@ -36,5 +36,14 @@ void net_bridge_add_interface(const char *bridge, const char *dev); void net_if_up(const char *ifname); int net_get_mtu(const char *ifname); void net_set_mtu(const char *ifname, int mtu); +void net_ifprint(int scan); +int net_get_mac(const char *ifname, unsigned char mac[6]); +void net_if_ip(const char *ifname, uint32_t ip, uint32_t mask, int mtu); +int net_if_mac(const char *ifname, const unsigned char mac[6]); +void net_if_ip6(const char *ifname, const char *addr6); + + +// arp.c +void arp_scan(const char *dev, uint32_t ifip, uint32_t ifmask); #endif diff --git a/src/fnet/interface.c b/src/fnet/interface.c index b1903dd46..67af062bf 100644 --- a/src/fnet/interface.c +++ b/src/fnet/interface.c @@ -180,4 +180,216 @@ void net_set_mtu(const char *ifname, int mtu) { close(s); } +// scan interfaces in current namespace and print IP address/mask for each interface +void net_ifprint(int scan) { + uint32_t ip; + uint32_t mask; + struct ifaddrs *ifaddr, *ifa; + if (getifaddrs(&ifaddr) == -1) + errExit("getifaddrs"); + + printf("%-17.17s%-19.19s%-17.17s%-17.17s%-6.6s\n", + "Interface", "MAC", "IP", "Mask", "Status"); + // walk through the linked list + for (ifa = ifaddr; ifa != NULL; ifa = ifa->ifa_next) { + if (ifa->ifa_addr == NULL) + continue; + + if (ifa->ifa_addr->sa_family == AF_INET) { + struct sockaddr_in *si = (struct sockaddr_in *) ifa->ifa_netmask; + mask = ntohl(si->sin_addr.s_addr); + si = (struct sockaddr_in *) ifa->ifa_addr; + ip = ntohl(si->sin_addr.s_addr); + + // interface status + char *status; + if (ifa->ifa_flags & IFF_RUNNING && ifa->ifa_flags & IFF_UP) + status = "UP"; + else + status = "DOWN"; + + // ip address and mask + char ipstr[30]; + sprintf(ipstr, "%d.%d.%d.%d", PRINT_IP(ip)); + char maskstr[30]; + sprintf(maskstr, "%d.%d.%d.%d", PRINT_IP(mask)); + + // mac address + unsigned char mac[6]; + net_get_mac(ifa->ifa_name, mac); + char macstr[30]; + if (strcmp(ifa->ifa_name, "lo") == 0) + macstr[0] = '\0'; + else + sprintf(macstr, "%02x:%02x:%02x:%02x:%02x:%02x", PRINT_MAC(mac)); + + // print + printf("%-17.17s%-19.19s%-17.17s%-17.17s%-6.6s\n", + ifa->ifa_name, macstr, ipstr, maskstr, status); + + // network scanning + if (!scan) // scanning disabled + continue; + if (strcmp(ifa->ifa_name, "lo") == 0) // no loopbabck scanning + continue; + if (mask2bits(mask) < 16) // not scanning large networks + continue; + if (!ip) // if not configured + continue; + // only if the interface is up and running + if (ifa->ifa_flags & IFF_RUNNING && ifa->ifa_flags & IFF_UP) + arp_scan(ifa->ifa_name, ip, mask); + } + } + freeifaddrs(ifaddr); +} + +int net_get_mac(const char *ifname, unsigned char mac[6]) { + + struct ifreq ifr; + int sock; + + if ((sock = socket(AF_INET, SOCK_STREAM, 0)) < 0) + errExit("socket"); + + memset(&ifr, 0, sizeof(ifr)); + strncpy(ifr.ifr_name, ifname, IFNAMSIZ); + ifr.ifr_hwaddr.sa_family = ARPHRD_ETHER; + + if (ioctl(sock, SIOCGIFHWADDR, &ifr) == -1) + errExit("ioctl"); + memcpy(mac, ifr.ifr_hwaddr.sa_data, 6); + + close(sock); + return 0; +} + +// configure interface ipv4 address +void net_if_ip(const char *ifname, uint32_t ip, uint32_t mask, int mtu) { + if (strlen(ifname) > IFNAMSIZ) { + fprintf(stderr, "Error: invalid network device name %s\n", ifname); + exit(1); + } + + int sock = socket(AF_INET,SOCK_DGRAM,0); + if (sock < 0) + errExit("socket"); + + struct ifreq ifr; + memset(&ifr, 0, sizeof(ifr)); + strncpy(ifr.ifr_name, ifname, IFNAMSIZ); + ifr.ifr_addr.sa_family = AF_INET; + + ((struct sockaddr_in *)&ifr.ifr_addr)->sin_addr.s_addr = htonl(ip); + if (ioctl( sock, SIOCSIFADDR, &ifr ) < 0) { + close(sock); + errExit("ioctl"); + } + + if (ip != 0) { + ((struct sockaddr_in *)&ifr.ifr_addr)->sin_addr.s_addr = htonl(mask); + if (ioctl( sock, SIOCSIFNETMASK, &ifr ) < 0) { + close(sock); + errExit("ioctl"); + } + } + + // configure mtu + if (mtu > 0) { + ifr.ifr_mtu = mtu; + if (ioctl( sock, SIOCSIFMTU, &ifr ) < 0) { + close(sock); + errExit("ioctl"); + } + } + + close(sock); + usleep(10000); // sleep 10ms +} + +int net_if_mac(const char *ifname, const unsigned char mac[6]) { + struct ifreq ifr; + int sock; + + if ((sock = socket(AF_INET, SOCK_STREAM, 0)) < 0) + errExit("socket"); + + memset(&ifr, 0, sizeof(ifr)); + strncpy(ifr.ifr_name, ifname, IFNAMSIZ); + ifr.ifr_hwaddr.sa_family = ARPHRD_ETHER; + memcpy(ifr.ifr_hwaddr.sa_data, mac, 6); + + if (ioctl(sock, SIOCSIFHWADDR, &ifr) == -1) + errExit("ioctl"); + close(sock); + return 0; +} + +// configure interface ipv6 address +// ex: firejail --net=eth0 --ip6=2001:0db8:0:f101::1/64 +struct ifreq6 { + struct in6_addr ifr6_addr; + uint32_t ifr6_prefixlen; + unsigned int ifr6_ifindex; +}; +void net_if_ip6(const char *ifname, const char *addr6) { + if (strchr(addr6, ':') == NULL) { + fprintf(stderr, "Error fnet: invalid IPv6 address %s\n", addr6); + exit(1); + } + + // extract prefix + unsigned long prefix; + char *ptr; + if ((ptr = strchr(addr6, '/'))) { + prefix = atol(ptr + 1); + if (prefix > 128) { + fprintf(stderr, "Error fnet: invalid prefix for IPv6 address %s\n", addr6); + exit(1); + } + *ptr = '\0'; // mark the end of the address + } + else + prefix = 128; + + // extract address + struct sockaddr_in6 sin6; + memset(&sin6, 0, sizeof(sin6)); + sin6.sin6_family = AF_INET6; + int rv = inet_pton(AF_INET6, addr6, sin6.sin6_addr.s6_addr); + if (rv <= 0) { + fprintf(stderr, "Error fnet: invalid IPv6 address %s\n", addr6); + exit(1); + } + + // open socket + int sock = socket(PF_INET6, SOCK_DGRAM, IPPROTO_IP); + if (sock < 0) { + fprintf(stderr, "Error fnet: IPv6 is not supported on this system\n"); + exit(1); + } + + // find interface index + struct ifreq ifr; + memset(&ifr, 0, sizeof(ifr)); + strncpy(ifr.ifr_name, ifname, IFNAMSIZ); + ifr.ifr_addr.sa_family = AF_INET; + if (ioctl(sock, SIOGIFINDEX, &ifr) < 0) { + perror("ioctl SIOGIFINDEX"); + exit(1); + } + + // configure address + struct ifreq6 ifr6; + memset(&ifr6, 0, sizeof(ifr6)); + ifr6.ifr6_prefixlen = prefix; + ifr6.ifr6_ifindex = ifr.ifr_ifindex; + memcpy((char *) &ifr6.ifr6_addr, (char *) &sin6.sin6_addr, sizeof(struct in6_addr)); + if (ioctl(sock, SIOCSIFADDR, &ifr6) < 0) { + perror("ioctl SIOCSIFADDR"); + exit(1); + } + + close(sock); +} diff --git a/src/fnet/main.c b/src/fnet/main.c index f17287cb9..4ae9eb6e3 100644 --- a/src/fnet/main.c +++ b/src/fnet/main.c @@ -24,12 +24,18 @@ static void usage(void) { printf("\tfnet create veth dev1 dev2 bridge child\n"); printf("\tfnet create macvlan dev parent child\n"); printf("\tfnet moveif dev proc\n"); + printf("\tfnet printif\n"); + printf("\tfnet printif scan\n"); + printf("\tfnet config interface dev ip mask mtu\n"); + printf("\tfnet config mac addr\n"); + printf("\tfnet config ipv6 dev ipn"); + printf("\tfmet ifup dev\n"); } int main(int argc, char **argv) { #if 0 { -system("cat /proc/self/status"); +//system("cat /proc/self/status"); int i; for (i = 0; i < argc; i++) printf("*%s* ", argv[i]); @@ -45,22 +51,49 @@ printf("\n"); usage(); return 0; } + else if (argc == 3 && strcmp(argv[1], "ifup") == 0) { + net_if_up(argv[2]); + } + else if (argc == 2 && strcmp(argv[1], "printif") == 0) { + net_ifprint(0); + } + else if (argc == 3 && strcmp(argv[1], "printif") == 0 && strcmp(argv[2], "scan") == 0) { + net_ifprint(1); + } else if (argc == 7 && strcmp(argv[1], "create") == 0 && strcmp(argv[2], "veth") == 0) { // create veth pair and move one end in the the namespace net_create_veth(argv[3], argv[4], atoi(argv[6])); - // connect the ohter veth end to the bridge ... net_bridge_add_interface(argv[5], argv[3]); - // ... and bring it up net_if_up(argv[3]); } else if (argc == 6 && strcmp(argv[1], "create") == 0 && strcmp(argv[2], "macvlan") == 0) { net_create_macvlan(argv[3], argv[4], atoi(argv[5])); } + else if (argc == 7 && strcmp(argv[1], "config") == 0 && strcmp(argv[2], "interface") == 0) { + char *dev = argv[3]; + uint32_t ip = (uint32_t) atoll(argv[4]); + uint32_t mask = (uint32_t) atoll(argv[5]); + int mtu = atoi(argv[6]); + // configure interface + net_if_ip(dev, ip, mask, mtu); + // ... and bring it up + net_if_up(dev); + } + else if (argc == 5 && strcmp(argv[1], "config") == 0 && strcmp(argv[2], "mac") == 0) { + unsigned char mac[6]; + if (atomac(argv[4], mac)) { + fprintf(stderr, "Error fnet: invalid mac address %s\n", argv[4]); + } + net_if_mac(argv[3], mac); + } else if (argc == 4 && strcmp(argv[1], "moveif") == 0) { net_move_interface(argv[2], atoi(argv[3])); } + else if (argc == 5 && strcmp(argv[1], "config") == 0 && strcmp(argv[2], "ipv6") == 0) { + net_if_ip6(argv[3], argv[4]); + } else { fprintf(stderr, "Error fnet: invalid arguments\n"); return 1; diff --git a/src/fseccomp/main.c b/src/fseccomp/main.c index 59d426a78..f53e2ef8b 100644 --- a/src/fseccomp/main.c +++ b/src/fseccomp/main.c @@ -9,15 +9,15 @@ static void usage(void) { } int main(int argc, char **argv) { -//#if 0 +#if 0 { -//system("cat /proc/self/status"); +system("cat /proc/self/status"); int i; for (i = 0; i < argc; i++) printf("*%s* ", argv[i]); printf("\n"); } -//#endif +#endif if (argc < 2) return 1; -- cgit v1.2.3-54-g00ecf