From a37ffc3374e23b8e6318965d84e408acf7a46b73 Mon Sep 17 00:00:00 2001 From: Harald Kubota Date: Sat, 2 Jan 2021 15:32:15 +0900 Subject: [PATCH 01/10] Add first version of zsh completion Don't have duplicate descriptions and put = signs where they belong to zsh completion function now dynamically adjusts for options (e.g. no --apparmor option without AppArmor configured) No EXTRA_CFLAGS for cpp Found main.c which does the argument processing. Moved some arguments into the correct #ifdef blocks Profile selection now much better Not more cpp. Using preproc.awk instead. Updated bash firejail command completion to add profiles ignore bash and zsh dynamically created completion scripts Moved bash/zsh completions out of ALL_ITEMS to fix make install Cleanup --- .gitignore | 2 + Makefile.in | 7 +- configure | 2 +- configure.ac | 2 +- src/bash_completion/Makefile.in | 14 + ...completion => firejail.bash_completion.in} | 13 +- src/zsh_completion/Makefile.in | 14 + src/zsh_completion/_firejail.in | 246 ++++++++++++++++++ 8 files changed, 296 insertions(+), 4 deletions(-) create mode 100644 src/bash_completion/Makefile.in rename src/bash_completion/{firejail.bash_completion => firejail.bash_completion.in} (88%) create mode 100644 src/zsh_completion/Makefile.in create mode 100644 src/zsh_completion/_firejail.in diff --git a/.gitignore b/.gitignore index 76ce6c7ecd8..0c803b1358c 100644 --- a/.gitignore +++ b/.gitignore @@ -38,6 +38,8 @@ src/fcopy/fcopy src/fldd/fldd src/fbuilder/fbuilder src/profstats/profstats +src/bash_completion/firejail.bash_completion +src/zsh_completion/_firejail uids.h seccomp seccomp.debug diff --git a/Makefile.in b/Makefile.in index 623c8bd39aa..8d4dbc4305b 100644 --- a/Makefile.in +++ b/Makefile.in @@ -21,12 +21,14 @@ MAN_TARGET = man MAN_SRC = src/man endif +COMPLETIONDIRS = src/zsh_completion src/bash_completion all: all_items mydirs $(MAN_TARGET) filters APPS = src/firecfg/firecfg src/firejail/firejail src/firemon/firemon src/profstats/profstats SBOX_APPS = src/faudit/faudit src/fbuilder/fbuilder src/ftee/ftee SBOX_APPS_NON_DUMPABLE = src/fcopy/fcopy src/fldd/fldd src/fnet/fnet src/fnetfilter/fnetfilter -MYDIRS = src/lib $(MAN_SRC) +MYDIRS = src/lib $(MAN_SRC) $(COMPLETIONDIRS) MYLIBS = src/libpostexecseccomp/libpostexecseccomp.so src/libtrace/libtrace.so src/libtracelog/libtracelog.so +COMPLETIONS = src/zsh_completion/_firejail src/bash_completion/firejail.bash_completion MANPAGES = firejail.1 firemon.1 firecfg.1 firejail-profile.5 firejail-login.5 firejail-users.5 SBOX_APPS_NON_DUMPABLE += src/fsec-optimize/fsec-optimize src/fsec-print/fsec-print src/fseccomp/fseccomp SECCOMP_FILTERS = seccomp seccomp.debug seccomp.32 seccomp.block_secondary seccomp.mdwx seccomp.mdwx.32 @@ -158,6 +160,9 @@ endif install -m 0644 src/bash_completion/firejail.bash_completion $(DESTDIR)$(datarootdir)/bash-completion/completions/firejail install -m 0644 src/bash_completion/firemon.bash_completion $(DESTDIR)$(datarootdir)/bash-completion/completions/firemon install -m 0644 src/bash_completion/firecfg.bash_completion $(DESTDIR)$(datarootdir)/bash-completion/completions/firecfg + # zsh completion + install -m 0755 -d $(DESTDIR)$(datarootdir)/zsh/site-functions + install -m 0644 src/zsh_completion/_firejail $(DESTDIR)$(datarootdir)/zsh/site-functions/ install: all $(MAKE) realinstall diff --git a/configure b/configure index 75c2499a9fd..2cd474b3cd9 100755 --- a/configure +++ b/configure @@ -4273,7 +4273,7 @@ fi ac_config_files="$ac_config_files mkdeb.sh" -ac_config_files="$ac_config_files Makefile src/common.mk src/lib/Makefile src/fcopy/Makefile src/fnet/Makefile src/firejail/Makefile src/fnetfilter/Makefile src/firemon/Makefile src/libtrace/Makefile src/libtracelog/Makefile src/firecfg/Makefile src/fbuilder/Makefile src/fsec-print/Makefile src/ftee/Makefile src/faudit/Makefile src/fseccomp/Makefile src/fldd/Makefile src/libpostexecseccomp/Makefile src/fsec-optimize/Makefile src/profstats/Makefile src/man/Makefile test/Makefile" +ac_config_files="$ac_config_files Makefile src/common.mk src/lib/Makefile src/fcopy/Makefile src/fnet/Makefile src/firejail/Makefile src/fnetfilter/Makefile src/firemon/Makefile src/libtrace/Makefile src/libtracelog/Makefile src/firecfg/Makefile src/fbuilder/Makefile src/fsec-print/Makefile src/ftee/Makefile src/faudit/Makefile src/fseccomp/Makefile src/fldd/Makefile src/libpostexecseccomp/Makefile src/fsec-optimize/Makefile src/profstats/Makefile src/man/Makefile src/zsh_completion/Makefile src/bash_completion/Makefile test/Makefile" cat >confcache <<\_ACEOF # This file is a shell script that caches the results of configure diff --git a/configure.ac b/configure.ac index e21e4a01fe0..5c2456a6aee 100644 --- a/configure.ac +++ b/configure.ac @@ -231,7 +231,7 @@ AC_CONFIG_FILES([mkdeb.sh], [chmod +x mkdeb.sh]) AC_OUTPUT(Makefile src/common.mk src/lib/Makefile src/fcopy/Makefile src/fnet/Makefile src/firejail/Makefile src/fnetfilter/Makefile \ src/firemon/Makefile src/libtrace/Makefile src/libtracelog/Makefile src/firecfg/Makefile src/fbuilder/Makefile src/fsec-print/Makefile \ src/ftee/Makefile src/faudit/Makefile src/fseccomp/Makefile src/fldd/Makefile src/libpostexecseccomp/Makefile src/fsec-optimize/Makefile \ -src/profstats/Makefile src/man/Makefile test/Makefile) +src/profstats/Makefile src/man/Makefile src/zsh_completion/Makefile src/bash_completion/Makefile test/Makefile) echo echo "Configuration options:" diff --git a/src/bash_completion/Makefile.in b/src/bash_completion/Makefile.in new file mode 100644 index 00000000000..d8a393aa43e --- /dev/null +++ b/src/bash_completion/Makefile.in @@ -0,0 +1,14 @@ +all: firejail.bash_completion + +include ../common.mk + +firejail.bash_completion: firejail.bash_completion.in + gawk -f ../man/preproc.awk -- $(MANFLAGS) < $< > $@.tmp + sed "s|_SYSCONFDIR_|$(sysconfdir)|" < $@.tmp > $@ + rm $@.tmp + +clean: + rm -fr firejail.bash_completion + +distclean: clean + rm -fr Makefile diff --git a/src/bash_completion/firejail.bash_completion b/src/bash_completion/firejail.bash_completion.in similarity index 88% rename from src/bash_completion/firejail.bash_completion rename to src/bash_completion/firejail.bash_completion.in index 0a1b34d7d71..00f04c3106a 100644 --- a/src/bash_completion/firejail.bash_completion +++ b/src/bash_completion/firejail.bash_completion.in @@ -9,6 +9,17 @@ __interfaces(){ cut -f 1 -d ':' /proc/net/dev | tail -n +3 | grep -v lo | xargs } +_profiles() { + if [[ -d "$1" ]] ; then + ls -1 $1/*.profile 2>/dev/null | sed -E 's;^.*\/;;g' + fi +} +_all_profiles() { + local sys_profiles=$(_profiles _SYSCONFDIR_/firejail) + local user_profiles=$(_profiles $HOME/.config/firejail) + COMPREPLY=($(compgen -W "${sys_profiles} ${user_profiles}" -- "$cur")) +} + _firejail() { @@ -20,7 +31,7 @@ _firejail() return 0 ;; --profile) - _filedir + _all_profiles return 0 ;; --hosts-file) diff --git a/src/zsh_completion/Makefile.in b/src/zsh_completion/Makefile.in new file mode 100644 index 00000000000..3f756aa5fb6 --- /dev/null +++ b/src/zsh_completion/Makefile.in @@ -0,0 +1,14 @@ +all: _firejail + +include ../common.mk + +_firejail: _firejail.in + gawk -f ../man/preproc.awk -- $(MANFLAGS) < $< > $@.tmp + sed "s|_SYSCONFDIR_|$(sysconfdir)|" < $@.tmp > $@ + rm $@.tmp + +clean: + rm -fr _firejail + +distclean: clean + rm -fr Makefile diff --git a/src/zsh_completion/_firejail.in b/src/zsh_completion/_firejail.in new file mode 100644 index 00000000000..7e8df138e19 --- /dev/null +++ b/src/zsh_completion/_firejail.in @@ -0,0 +1,246 @@ +#compdef firejail + +_all_firejails() { + local -a _all_firejails_list + for jail in ${(f)"$(_call_program modules_tag "firejail --list 2> /dev/null | cut -d: -f1")"}; do + _all_firejails_list+=${jail%% *} + done + _describe 'firejails list' _all_firejails_list +} + +_all_cpus() { + _cpu_count=$(getconf _NPROCESSORS_ONLN) + for i in {0..$((_cpu_count-1))} ; do + print $i + done +} + +_profiles() { + print $1/*.profile | sed -E "s;^$1/;;g;s;\.profile$;;g;" +} +_profiles_with_ext() { + print $1/*.profile +} + +_all_profiles() { + _values 'profiles' $(_profiles _SYSCONFDIR_/firejail) $(_profiles $HOME/.config/firejail) $(_profiles_with_ext .) +} + +_firejail_args=( + '*::arguments:_normal' + '(--profile)'{--profile=,--profile=}'[use a custom profile]: :_all_profiles' + '--caps[enable default Linux capabilities filter]' + '(--caps.drop)'{--caps.drop=,--caps.drop=}'[drop capabilities: all|cap1,cap2,...]: :->caps_drop' + '(--caps.keep)'{--caps.keep=,--caps.keep=}'[keep capabilities: cap1,cap2,...]: :->caps_keep' + '(--caps.print)'{--caps.print=,--caps.print=}'[print the caps filter name|pid]:firejail:_all_firejails' + '--allow-debuggers[allow tools such as strace and gdb inside the sandbox]' + '(--debug)'{--debug,--debug}'[print sandbox debug messages]' + '--debug-blacklists[debug blacklisting]' + '--debug-caps[print all recognized capabilities]' + '--debug-errnos[print all recognized error numbers]' + '--debug-private-lib[debug for --private-lib option]' + '--debug-protocols[print all recognized protocols]' + '--debug-syscalls[print all recognized system calls]' + '--debug-syscalls32[print all recognized 32 bit system calls]' + '--debug-whitelists[debug whitelisting]' + # Ignore that you can do -? too as it's the only short option + '(--help)'{--help,--help}'[this help screen]' + '--allusers[all user home directories are visible inside the sandbox]' + '--appimage[sandbox an AppImage application]' + '--private[temporary home directory]' + '(--private)'{--private=,--private=}'[use directory as user home]: : _files -/' + '--seccomp[enable seccomp filter and apply the default blacklist]' + '(--seccomp=)'{--seccomp=,--seccomp=}'[enable seccomp filter, blacklist the default syscall list and the syscalls specified by the command]:' + '(--seccomp.print)'{--seccomp.print=,--seccomp.print=}'[print the seccomp filter for the sandbox identified by name|pid]: : _all_firejails' + '--seccomp.block-secondary[build only the native architecture filters]' + '(--seccomp.drop)'{--seccomp.drop=,--seccomp.drop=}'[enable seccomp filter, and blacklist the syscalls specified by the command]: :' + '(--seccomp.keep)'{--seccomp.keep=,--seccomp.keep=}'[enable seccomp filter, and whitelist the syscalls specified by the command]: :' + '(--seccomp.32.drop)'{--seccomp.32.drop=,--seccomp.32.drop=}'[enable seccomp filter, and blacklist the 32 bit syscalls specified by the command]: :' + '(--seccomp.32.keep)'{--seccomp.32.keep=,--seccomp.32.keep=}'[enable seccomp filter, and whitelist the 32 bit syscalls specified by the command]: :' + '(--seccomp-error-action)'{--seccomp-error-action=,--seccomp-error-action=}'[change error code, kill process or log the attempt]: :(ERRNO kill log)' + '--memory-deny-write-execute[seccomp filter to block attempts to create memory mappings that are both writable and executable]' + '*'{--blacklist=,--blacklist=}'[blacklist directory or file]: : _files' + '--writable-etc[/etc directory is mounted read-write]' + '--writable-run-user[allow access to /run/user/$UID/systemd and /run/user/$UID/gnupg]' + '--writable-var[/var directory is mounted read-write]' + '--writable-var-log[use the real /var/log directory, not a clone]' + '--build[build a whitelisted profile for the application and print it on stdout]' + '(--build)'{--build=,--build=}'[build a whitelisted profile for the application and save it]: : _files' + '(--fs.print)'{--fs.print=,--fs.print=}'[print the filesystem log name|pid]: : _all_firejails' + '(--join)'{--join=,--join=}'[join the sandbox name|pid]: : _all_firejails' + '(--join-filesystem)'{--join-filesystem=,--join-filesystem=}'[join the mount namespace name|pid]: : _all_firejails' + '(--profile.print)'{--profile.print=,--profile.print=}'[print the name of profile file name|pid]: : _all_firejails' + '(--protocol.print)'{--protocol.print=,--protocol.print=}'[print the protocol filter name|pid]: : _all_firejails' + '(--shutdown)'{--shutdown=,--shutdown=}'[shutdown the sandbox identified by name|pid]: : _all_firejails' + '(--cat)'{--cat=,--cat=}'[print content of file from sandbox container name|pid]: : _all_firejails' + '(--cpu.print)'{--cpu.print=,--cpu.print=}'[print the cpus in use name|pid]: : _all_firejails' + '--list[list all sandboxes]' + '(--dns)'{--dns=,--dns=}'[set DNS server]: :' + '(--protocol)'{--protocol=,--protocol=}'[enable protocol filter]: :' + '(--join-or-start)'{--join-or-start=,--join-or-start=}'[join the sandbox or start a new one name|pid]: : _all_firejails' + '(--hosts-file)'{--hosts-file=,--hosts-file=}'[use file as /etc/hosts]: : _files' + '--shell=none[run the program directly without a user shell]' + '(--shell)'{--shell=,--shell=}'[set default user shell]: : _files -g "*(*)"' + '(--output)'{--output=,--output=}'[stdout logging and log rotation]: : _files' + '(--output-stderr)'{--output-stderr=,--output-stderr=}'[stdout and stderr logging and log rotation]: : _files' + '--no3d[disable 3D hardware acceleration]' + '--nodvd[disable DVD and audio CD devices]' + '--nogroups[disable supplementary groups]' + '--nonewprivs[sets the NO_NEW_PRIVS prctl]' + '--noprofile[do not use a security profile]' + '(--noexec)'{--noexec=,--noexec=}'[remount the file or directory noexec nosuid and nodev]: : _files' + '--ipc-namespace[enable a new IPC namespace]' + '--keep-dev-shm[/dev/shm directory is untouched (even with --private-dev)]' + '--keep-var-tmp[/var/tmp directory is untouched]' + '--top[monitor the most CPU-intensive sandboxes]' + '--trace[trace open, access and connect system calls]' + '--tracelog[add a syslog message for every access to files or directories blacklisted by the security profile]' + '--tree[print a tree of all sandboxed processes]' + '(--cpu)'{--cpu=,--cpu=}'[set cpu affinity]: :->cpus' + '--private-dev[create a new /dev directory with a small number of common device files]' + '--private-tmp[mount a tmpfs on top of /tmp directory]' + '--private-cwd[do not inherit working directory inside jail]' + '(--private-cwd)'{--private-cwd=,--private-cwd=}'[set working directory inside jail]: : _files -/' + '*'{--read-only=,--read-only=}'[set directory or file read-only]: : _files' + '*'{--read-write=,--read-write=}'[set directory or file read-write]: : _files' + '(--tmpfs)'{--tmpfs=,--tmpfs=}'[mount a tmpfs filesystem on directory dirname]: : _files -/' + '(--private-etc)'{--private-etc=,--private-etc=}'[build a new /etc in a temporary filesystem, and copy the files and directories in the list]: : _files' + "--deterministic-exit-code[always exit with first child's status code]" + '--machine-id[preserve /etc/machine-id]' + # Sample values as I don't think + # many would enjoy getting a list from -20..20 + '(--nice)'{--nice=,--nice=}'[set nice value]: :(1 10 15 20)' + # Should be _files, a comma and files or files -/ + '*'{--bind=,--bind=}'[mount-bind dirname1/filename1 on top of dirname2/filename2]: :(file1,file2 dir1,dir2)' + '--audit[audit the sandbox]' + '(--audit)'{--audit=,--audit=}'[audit the sandbox with a test-program]: :' + '(--cgroup)'{--cgroup=,--cgroup=}'[place the sandbox in the specified control group]: :' + '*'{--env=,--env=}'[set environment variable]: :' + '(--hostname)'{--hostname=,--hostname=}'[set sandbox hostname]: :' + '(--ignore)'{--ignore=,--ignore=}'[ignore command in profile files]: :' + '(--name)'{--name=,--name=}'[set sandbox name]: :' + '(--rlimit-as)'{--rlimit-as=,--rlimit-as=}"[set the maximum size of the process's virtual memory (address space) in bytes]: :" + '(--rlimit-cpu)'{--rlimit-cpu=,--rlimit-cpu=}'[set the maximum CPU time in seconds]: :' + '(--rlimit-fsize)'{--rlimit-fsize=,--rlimit-fsize=}'[set the maximum file size that can be created by a process]: :' + '(--rlimit-nofile)'{--rlimit-nofile=,--rlimit-nofile=}'[set the maximum number of files that can be opened by a process]: :' + '(--rlimit-nproc)'{--rlimit-nproc=,--rlimit-nproc=}'[set the maximum number of processes that can be created for the real user ID of the calling process]: :' + '(--rlimit-sigpending)'{--rlimit-sigpending=,--rlimit-sigpending=}'[set the maximum number of pending signals for a process]: :' + '*'{--rmenv=,--rmenv=}'[remove environment variable in the new sandbox]: :' + '(--timeout)'{--timeout=,--timeout=}'[kill the sandbox automatically after the time has elapsed]: :(hh\:mm\:ss)' + "--quiet[turn off Firejail's output.]" + '--version[print program version and exit]' +#ifdef HAVE_APPARMOR + '--apparmor[enable AppArmor confinement]' + '(--apparmor.print=)'{--apparmor.print=,--apparmor.print=}'[print apparmor status name|pid]:firejail:_all_firejails' +#endif +#ifdef HAVE_CHROOT + '(--chroot)'{--chroot=,--chroot=}'[chroot into directory]: : _files -/' +#endif +#ifdef HAVE_FILE_TRANSFER + '(--get)'{--get=,--get=}'[get a file from sandbox container name|pid]: : _all_firejails' + # --put=name|pid src-filename dest-filename - put a file in sandbox container. + '(--put)'{--put=,--put=}'[put a file in sandbox container]: :' + '(--ls)'{--ls=,--ls=}'[list files in sandbox container name|pid]: : _all_firejails' +#endif +#ifdef HAVE_NETWORK + # '--net=none[enable a new, unconnected network namespace]' + '(--net)'{--net=,--net=}'[enable network namespaces and connect to this bridge or Ethernet interface (or none to disable)]: :->net_or_none' + '(--net.print)'{--net.print=,--net.print=}'[print network interface configuration name|pid]: : _all_firejails' + '(--netfilter.print)'{--netfilter.print=,--netfilter.print=}'[print the firewall name|pid]: : _all_firejails' + '(--netfilter6.print)'{--netfilter6.print=,--netfilter6.print=}'[print the IPv6 firewall name|pid]: : _all_firejails' + '--netstats[monitor network statistics]' + '(--netmask)'{--netmask=,--netmask=}'[define a network mask when dealing with unconfigured parrent interfaces]: :' + '(--netns)'{--netns=,--netns=}'[Run the program in a named, persistent network namespace]: :' + '(--netfilter)'{--netfilter=,--netfilter=}'[enable firewall]: :' + '(--netfilter6)'{--netfilter6=,--netfilter6=}'[enable IPv6 firewall]: :' + '(--veth-name)'{--veth-name=,--veth-name=}'[use this name for the interface connected to the bridge]: :' + '(--join-network)'{--join-network=,--join-network=}'[join the network namespace name|pid]: : _all_firejails' + '(--defaultgw)'{--defaultgw=,--defaultgw=}'[configure default gateway]: :' + '(--ip)'{--ip=,--ip=}'[set interface IP address none|dhcp|ADDRESS]: :(none dhcp)' + '(--dns.print)'{--dns.print=,--dns.print=}'[print DNS configuration name|pid]: : _all_firejails' + '(--interface)'{--interface=,--interface=}'[move interface in sandbox]: :' + '(--ip6)'{--ip6=,--ip6=}'[set interface IPv6 address or use dhcp via dhclient]: :(dhcp)' + '(--iprange)'{--iprange=,--iprange=}'[configure an IP address in this range]: :' + '(--mac)'{--mac=,--mac=}'[set interface MAC address]: :(xx\:xx\:xx\:xx\:xx\:xx)' + '(--mtu)'{--mtu=,--mtu=}'[set interface MTU]: :' + '--scan[ARP-scan all the networks from inside a network namespace]' + '(--bandwidth)'{--bandwidth=,--bandwidth=}'[set bandwidth limits name|pid]: : _all_firejails' +#endif +#ifdef HAVE_X11 + '--x11[enable X11 sandboxing. The software checks first if Xpra is installed, then it checks if Xephyr is installed. If all fails, it will attempt to use X11 security extension]' + '(--x11)'{--x11=,--x11=}'[disable or enable specific X11 server]: :(none xephyr xorg xpra xvfb)' + '(--xephyr-screen)'{--xephyr-screen=,--xephyr-screen=}'[set screen size for --x11=xephyr]: :(WIDTHxHEIGHT)' +#endif +#ifdef HAVE_USERNS + '--noroot[install a user namespace with only the current user]' +#endif + '--nosound[disable sound system]' + '--noautopulse[disable automatic ~/.config/pulse init]' + '--novideo[disable video devices]' + '--nou2f[disable U2F devices]' +#ifdef HAVE_OVERLAYFS + '--overlay[mount a filesystem overlay on top of the current filesystem]' + '(--overlay-named)'{--overlay-named=,--overlay-named=}'[mount a filesystem overlay on top of the current filesystem, and store it in name directory]: : _files -/' + '--overlay-tmpfs[mount a temporary filesystem overlay on top of the current filesystem]' + '--overlay-clean[clean all overlays stored in $HOME/.firejail directory]' +#endif +#ifdef HAVE_WHITELIST + '(--nowhitelist)'{--nowhitelist=,--nowhitelist=}'[disable whitelist for file or directory]: : _files' + '*'{--whitelist=,--whitelist=}'[whitelist directory or file]: : _files' +#endif + '(--noblacklist)'{--noblacklist=,--noblacklist=}'[disable blacklist for file or directory]: : _files' +#ifdef HAVE_DBUSPROXY + '(--dbus-system)'{--dbus-system=,--dbus-system=}'[set system DBus access policy or none]: :' + '(--dbus-system.broadcast)'{--dbus-system.broadcast=,--dbus-system.broadcast=}'[allow signals on the system DBus according to rule]: :' + '(--dbus-system.call)'{--dbus-system.call=,--dbus-system.call=}'[allow calls on the system DBus according to rule]: :' + '(--dbus-system.own)'{--dbus-system.own=,--dbus-system.own=}'[allow ownership of name on the system DBus]: :' + '(--dbus-system.see)'{--dbus-system.see=,--dbus-system.see=}'[allow seeing name on the system DBus]: :' + '(--dbus-system.talk)'{--dbus-system.talk=,--dbus-system.talk=}'[allow talking to name on the system DBus]: :' + '(--dbus-user)'{--dbus-user=,--dbus-user=}'[set session DBus access policy or none]: :' + '(--dbus-user.broadcast)'{--dbus-user.broadcast=,--dbus-user.broadcast=}'[allow signals on the session DBus according to rule]: :' + '(--dbus-user.call)'{--dbus-user.call=,--dbus-user.call=}'[allow calls on the session DBus according to rule]: :' + '(--dbus-user.see)'{--dbus-user.see=,--dbus-user.see=}'[allow seeing name on the session DBus]: :' + '(--dbus-user.talk)'{--dbus-user.talk=,--dbus-user.talk=}'[allow talking to name on the session DBus]: :' + '(--dbus-log)'{--dbus-log=,--dbus-log=}'[set DBus log file location]: : _files' + '(--dbus-system)'{--dbus-system=,--dbus-system=}'[set system DBus access policy]: :(filter none)' + '--dbus-user.log[turn on logging for the user DBus]' + '(--dbus-user.own)'{--dbus-user.own=,--dbus-user.own=}'[allow ownership of name on the session DBus]: :' + '--dbus-system.log[turn on logging for the system DBus]' + '--nodbus[disable D-Bus access]' +#endif +#ifdef HAVE_PRIVATE_HOME + '(--private-home)'{--private-home=,--private-home=}'[build a new user home in a temporary filesystem, and copy the files and directories in the list in the new home]: :' +#endif + '(--private-bin)'{--private-bin=,--private-bin=}'[build a new /bin in a temporary filesystem, and copy the programs in the list]: :' + '(--private-opt)'{--private-opt=,--private-opt=}'[build a new /opt in a temporary filesystem]: :' + '(--private-srv)'{--private-srv=,--private-srv=}'[build a new /srv in a temporary filesystem]: :' +#ifdef HAVE_USERTMPFS + '--private-cache[temporary ~/.cache directory]' +#endif +#ifdef HAVE_FIRETUNNEL + '(--tunnel)'{--tunnel=,--tunnel=}'[connect the sandbox to a tunnel created by firetunnel utility]: :' +#endif + ) + + +_firejail() { + _arguments -S $_firejail_args + case "$state" in + caps_drop) + local caps_and_all=(all $(firejail --debug-caps | awk '/[0-9]+\s*- /{print $3}')) + _values -s "," 'caps_drop' $caps_and_all + ;; + caps_keep) + local caps=($(firejail --debug-caps | awk '/[0-9]+\s*- /{print $3}')) + _values -s "," 'caps_keep' $caps + ;; + cpus) + _values -s "," 'cpus' $(_all_cpus) + ;; + net_or_none) + local netdevs=($(ip link | awk '{print $2}' | grep '^.*:$' | tr -d ':')) + local net_and_none=(none $netdevs) + _values 'net' $net_and_none + ;; + esac +} From 6fd17bd2d331d35ba113430f8b522b11f9e2512b Mon Sep 17 00:00:00 2001 From: smitsohu Date: Fri, 12 Feb 2021 15:59:33 +0100 Subject: [PATCH 02/10] chroot hardening the check was introduced some time ago in fs_x11(), but fs_chroot() does the same thing and needs it as well --- src/firejail/chroot.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/firejail/chroot.c b/src/firejail/chroot.c index 6de4b819cb1..9253490cae8 100644 --- a/src/firejail/chroot.c +++ b/src/firejail/chroot.c @@ -176,6 +176,18 @@ void fs_chroot(const char *rootdir) { if (env_get("FIREJAIL_X11") || env_get("FIREJAIL_CHROOT_X11")) { if (arg_debug) printf("Mounting /tmp/.X11-unix on chroot /tmp/.X11-unix\n"); + struct stat s1, s2; + if (stat("/tmp", &s1) || lstat("/tmp/.X11-unix", &s2)) + errExit("mounting /tmp/.X11-unix"); + if ((s1.st_mode & S_ISVTX) != S_ISVTX) { + fprintf(stderr, "Error: sticky bit not set on /tmp directory\n"); + exit(1); + } + if (s2.st_uid != 0) { + fprintf(stderr, "Error: /tmp/.X11-unix not owned by root user\n"); + exit(1); + } + check_subdir(parentfd, "tmp/.X11-unix", 0); fd = openat(parentfd, "tmp/.X11-unix", O_PATH|O_DIRECTORY|O_NOFOLLOW|O_CLOEXEC); if (fd == -1) From 105946495fb744558a4830591b857cdb6fc0111d Mon Sep 17 00:00:00 2001 From: smitsohu Date: Fri, 12 Feb 2021 16:20:43 +0100 Subject: [PATCH 03/10] remount hardening --- src/firejail/fs.c | 48 +++++++++++++++++++++++++---------------------- 1 file changed, 26 insertions(+), 22 deletions(-) diff --git a/src/firejail/fs.c b/src/firejail/fs.c index b1c509b3001..972ee8def61 100644 --- a/src/firejail/fs.c +++ b/src/firejail/fs.c @@ -487,27 +487,26 @@ void fs_tmpfs(const char *dir, unsigned check_owner) { close(fd); } -// remount path, but preserve existing mount flags; requires a resolved path +// remount path, preserving other mount flags; requires a resolved path static void fs_remount_simple(const char *path, OPERATION op) { + struct stat s1, s2; assert(path); // open path without following symbolic links - int fd = safe_fd(path, O_PATH|O_NOFOLLOW|O_CLOEXEC); - if (fd == -1) + int fd1 = safe_fd(path, O_PATH|O_NOFOLLOW|O_CLOEXEC); + if (fd1 == -1) goto out; - // identify file owner - struct stat s; - if (fstat(fd, &s) == -1) { + if (fstat(fd1, &s1) == -1) { // fstat can fail with EACCES if path is a FUSE mount, // mounted without 'allow_root' or 'allow_other' if (errno != EACCES) errExit("fstat"); - close(fd); + close(fd1); goto out; } // get mount flags struct statvfs buf; - if (fstatvfs(fd, &buf) == -1) + if (fstatvfs(fd1, &buf) == -1) errExit("fstatvfs"); unsigned long flags = buf.f_flag; @@ -515,13 +514,13 @@ static void fs_remount_simple(const char *path, OPERATION op) { if (op == MOUNT_RDWR || op == MOUNT_RDWR_NOCHECK) { // nothing to do if there is no read-only flag if ((flags & MS_RDONLY) == 0) { - close(fd); + close(fd1); return; } // allow only user owned directories, except the user is root - if (op == MOUNT_RDWR && getuid() != 0 && s.st_uid != getuid()) { + if (op != MOUNT_RDWR_NOCHECK && getuid() != 0 && s1.st_uid != getuid()) { fwarning("you are not allowed to change %s to read-write\n", path); - close(fd); + close(fd1); return; } flags &= ~MS_RDONLY; @@ -530,7 +529,7 @@ static void fs_remount_simple(const char *path, OPERATION op) { else if (op == MOUNT_NOEXEC) { // nothing to do if path is mounted noexec already if ((flags & (MS_NOEXEC|MS_NODEV|MS_NOSUID)) == (MS_NOEXEC|MS_NODEV|MS_NOSUID)) { - close(fd); + close(fd1); return; } flags |= MS_NOEXEC|MS_NODEV|MS_NOSUID; @@ -539,7 +538,7 @@ static void fs_remount_simple(const char *path, OPERATION op) { else if (op == MOUNT_READONLY) { // nothing to do if path is mounted read-only already if ((flags & MS_RDONLY) == MS_RDONLY) { - close(fd); + close(fd1); return; } flags |= MS_RDONLY; @@ -549,21 +548,25 @@ static void fs_remount_simple(const char *path, OPERATION op) { if (arg_debug) printf("Mounting %s %s\n", opstr[op], path); - // mount --bind /bin /bin + // mount --bind path path char *proc; - if (asprintf(&proc, "/proc/self/fd/%d", fd) == -1) + if (asprintf(&proc, "/proc/self/fd/%d", fd1) == -1) errExit("asprintf"); if (mount(proc, proc, NULL, MS_BIND|MS_REC, NULL) < 0) errExit("mount"); free(proc); - close(fd); - // mount --bind -o remount,ro /bin - // we need to open path again - fd = safe_fd(path, O_PATH|O_NOFOLLOW|O_CLOEXEC); - if (fd == -1) + // mount --bind -o remount,ro path + // need to open path again without following symbolic links + int fd2 = safe_fd(path, O_PATH|O_NOFOLLOW|O_CLOEXEC); + if (fd2 == -1) errExit("open"); - if (asprintf(&proc, "/proc/self/fd/%d", fd) == -1) + if (fstat(fd2, &s2) == -1) + errExit("fstat"); + // device and inode number should be the same + if (s1.st_dev != s2.st_dev || s1.st_ino != s2.st_ino) + errLogExit("invalid %s mount", opstr[op]); + if (asprintf(&proc, "/proc/self/fd/%d", fd2) == -1) errExit("asprintf"); if (mount(NULL, proc, NULL, flags|MS_BIND|MS_REMOUNT, NULL) < 0) errExit("mount"); @@ -579,7 +582,8 @@ static void fs_remount_simple(const char *path, OPERATION op) { errLogExit("invalid %s mount", opstr[op]); fs_logger2(opstr[op], path); free(proc); - close(fd); + close(fd1); + close(fd2); return; out: From f25b12cf483c4e531d4160aba44505ec056f152b Mon Sep 17 00:00:00 2001 From: smitsohu Date: Fri, 12 Feb 2021 18:54:10 +0100 Subject: [PATCH 04/10] tweak readability/making it more obvious buffers are properly initialized --- src/firejail/fs.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/firejail/fs.c b/src/firejail/fs.c index 972ee8def61..ef1f87f0c5c 100644 --- a/src/firejail/fs.c +++ b/src/firejail/fs.c @@ -489,13 +489,13 @@ void fs_tmpfs(const char *dir, unsigned check_owner) { // remount path, preserving other mount flags; requires a resolved path static void fs_remount_simple(const char *path, OPERATION op) { - struct stat s1, s2; assert(path); // open path without following symbolic links int fd1 = safe_fd(path, O_PATH|O_NOFOLLOW|O_CLOEXEC); if (fd1 == -1) goto out; + struct stat s1; if (fstat(fd1, &s1) == -1) { // fstat can fail with EACCES if path is a FUSE mount, // mounted without 'allow_root' or 'allow_other' @@ -561,6 +561,7 @@ static void fs_remount_simple(const char *path, OPERATION op) { int fd2 = safe_fd(path, O_PATH|O_NOFOLLOW|O_CLOEXEC); if (fd2 == -1) errExit("open"); + struct stat s2; if (fstat(fd2, &s2) == -1) errExit("fstat"); // device and inode number should be the same From 4b2914eb554635ce11e0b721c59cbb0ec428af44 Mon Sep 17 00:00:00 2001 From: "Kelvin M. Klann" Date: Fri, 12 Feb 2021 04:50:50 -0300 Subject: [PATCH 05/10] mkasc.sh: fix typo of Calculating Added on commit 64505c744 ("fix SHA1 issue when signing the realease"). --- mkasc.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mkasc.sh b/mkasc.sh index 872127ddae1..32f874bd6f0 100755 --- a/mkasc.sh +++ b/mkasc.sh @@ -3,7 +3,7 @@ # Copyright (C) 2014-2020 Firejail Authors # License GPL v2 -echo "Calculationg SHA256 for all files in /transfer - firejail version $1" +echo "Calculating SHA256 for all files in /transfer - firejail version $1" cd /transfer sha256sum * > firejail-$1-unsigned From 53bcbfa2312900daa67fa0d6f010893d6bbbe0f8 Mon Sep 17 00:00:00 2001 From: rusty-snake <41237666+rusty-snake@users.noreply.github.com> Date: Sat, 13 Feb 2021 20:49:26 +0100 Subject: [PATCH 06/10] sort.py: Better error message --- contrib/sort.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/contrib/sort.py b/contrib/sort.py index 54b2cbaa6f6..92134316261 100755 --- a/contrib/sort.py +++ b/contrib/sort.py @@ -120,8 +120,8 @@ def main(args): except PermissionError: print(f"[ Error ] Can't read/write `{filename}'") exit_code = 1 - except: - print(f"[ Error ] An error occurred while processing `{filename}'") + except Exception as err: + print(f"[ Error ] An error occurred while processing `{filename}': {err}") exit_code = 1 return exit_code From 455f75a7f45bf431b3f99493b4cb460f21407f74 Mon Sep 17 00:00:00 2001 From: rusty-snake <41237666+rusty-snake@users.noreply.github.com> Date: Sat, 13 Feb 2021 20:52:37 +0100 Subject: [PATCH 07/10] sort.py: Print the fixed line when running in a CI --- contrib/sort.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/contrib/sort.py b/contrib/sort.py index 92134316261..ad5e063acc2 100755 --- a/contrib/sort.py +++ b/contrib/sort.py @@ -24,6 +24,7 @@ # Requirements: # python >= 3.6 +from os import getenv from sys import argv @@ -80,7 +81,7 @@ def fix_profile(filename): lines = profile.read().split("\n") was_fixed = False fixed_profile = [] - for line in lines: + for lineno, line in enumerate(lines): if line[:12] in ("private-bin ", "private-etc ", "private-lib "): fixed_line = f"{line[:12]}{sort_alphabetical(line[12:])}" elif line[:13] in ("seccomp.drop ", "seccomp.keep "): @@ -95,6 +96,8 @@ def fix_profile(filename): fixed_line = line if fixed_line != line: was_fixed = True + if getenv("CI"): + print(f"{filename}:{lineno + 1}:{fixed_line}") fixed_profile.append(fixed_line) if was_fixed: profile.seek(0) From 3ff0eb2e77aac57388e23961696fde8dc863e51b Mon Sep 17 00:00:00 2001 From: rusty-snake <41237666+rusty-snake@users.noreply.github.com> Date: Sat, 13 Feb 2021 20:54:01 +0100 Subject: [PATCH 08/10] workflows/sort.yml: Run also if sort.py is changed --- .github/workflows/sort.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/sort.yml b/.github/workflows/sort.yml index 3e717f16226..f3ded0f22e9 100644 --- a/.github/workflows/sort.yml +++ b/.github/workflows/sort.yml @@ -5,10 +5,12 @@ on: branches: [ master ] paths: - 'etc/**' + - 'contrib/sort.py' pull_request: branches: [ master ] paths: - 'etc/**' + - 'contrib/sort.py' jobs: profile-sort: From a203caa710151374ffe5a33ba6b850d592b9ccc7 Mon Sep 17 00:00:00 2001 From: rusty-snake <41237666+rusty-snake@users.noreply.github.com> Date: Sun, 14 Feb 2021 08:56:45 +0100 Subject: [PATCH 09/10] sort.py: Print how many profiles are checked --- contrib/sort.py | 1 + 1 file changed, 1 insertion(+) diff --git a/contrib/sort.py b/contrib/sort.py index ad5e063acc2..b1f8d350c48 100755 --- a/contrib/sort.py +++ b/contrib/sort.py @@ -111,6 +111,7 @@ def fix_profile(filename): def main(args): exit_code = 0 + print(f"sort.py: checking {len(args)} {'profiles' if len(args) != 1 else 'profile'} ...") for filename in args: try: if exit_code not in (1, 101): From e9ec31ac922f07ae4ced23fc11c4097509959889 Mon Sep 17 00:00:00 2001 From: rusty-snake <41237666+rusty-snake@users.noreply.github.com> Date: Sun, 14 Feb 2021 09:02:06 +0100 Subject: [PATCH 10/10] sort.py: Always show the fix in a diff like format --- contrib/sort.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/contrib/sort.py b/contrib/sort.py index b1f8d350c48..babc0ba55ec 100755 --- a/contrib/sort.py +++ b/contrib/sort.py @@ -24,7 +24,6 @@ # Requirements: # python >= 3.6 -from os import getenv from sys import argv @@ -96,8 +95,10 @@ def fix_profile(filename): fixed_line = line if fixed_line != line: was_fixed = True - if getenv("CI"): - print(f"{filename}:{lineno + 1}:{fixed_line}") + print( + f"{filename}:{lineno + 1}:-{line}\n" + f"{filename}:{lineno + 1}:+{fixed_line}" + ) fixed_profile.append(fixed_line) if was_fixed: profile.seek(0)