aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Makefile.in6
-rw-r--r--README6
-rw-r--r--README.md2
-rw-r--r--RELNOTES21
-rwxr-xr-xcontrib/fix_private-bin.py157
-rw-r--r--contrib/fix_private-bin_for_symlinked_sh.py68
-rwxr-xr-xcontrib/fjclip.py35
-rwxr-xr-xcontrib/fjdisplay.py43
-rwxr-xr-xcontrib/fjresize.py25
-rw-r--r--etc/FossaMail.profile2
-rw-r--r--etc/fossamail.profile15
-rw-r--r--etc/gpa.profile2
-rw-r--r--etc/gpg-agent.profile4
-rw-r--r--etc/gpg.profile5
-rw-r--r--platform/debian/conffiles2
-rw-r--r--src/fcopy/main.c2
-rw-r--r--src/firecfg/firecfg.config2
-rw-r--r--src/firejail/bandwidth.c9
-rw-r--r--src/firejail/firejail.h4
-rw-r--r--src/firejail/fs.c80
-rw-r--r--src/firejail/fs_home.c112
-rw-r--r--src/firejail/ls.c8
-rw-r--r--src/firejail/main.c29
-rw-r--r--src/firejail/preproc.c8
-rw-r--r--src/firejail/pulseaudio.c63
-rw-r--r--src/firejail/sandbox.c11
-rw-r--r--src/firejail/util.c50
-rw-r--r--src/man/firejail.txt4
28 files changed, 572 insertions, 203 deletions
diff --git a/Makefile.in b/Makefile.in
index 8251f9882..fb6460dfd 100644
--- a/Makefile.in
+++ b/Makefile.in
@@ -91,6 +91,10 @@ realinstall:
91 install -c -m 0644 seccomp.debug $(DESTDIR)/$(libdir)/firejail/. 91 install -c -m 0644 seccomp.debug $(DESTDIR)/$(libdir)/firejail/.
92 install -c -m 0644 seccomp.i386 $(DESTDIR)/$(libdir)/firejail/. 92 install -c -m 0644 seccomp.i386 $(DESTDIR)/$(libdir)/firejail/.
93 install -c -m 0644 seccomp.amd64 $(DESTDIR)/$(libdir)/firejail/. 93 install -c -m 0644 seccomp.amd64 $(DESTDIR)/$(libdir)/firejail/.
94 install -c -m 0755 contrib/fix_private-bin.py $(DESTDIR)/$(libdir)/firejail/.
95 install -c -m 0755 contrib/fjclip.py $(DESTDIR)/$(libdir)/firejail/.
96 install -c -m 0755 contrib/fjdisplay.py $(DESTDIR)/$(libdir)/firejail/.
97 install -c -m 0755 contrib/fjresize.py $(DESTDIR)/$(libdir)/firejail/.
94 # documents 98 # documents
95 install -m 0755 -d $(DESTDIR)/$(DOCDIR) 99 install -m 0755 -d $(DESTDIR)/$(DOCDIR)
96 install -c -m 0644 COPYING $(DESTDIR)/$(DOCDIR)/. 100 install -c -m 0644 COPYING $(DESTDIR)/$(DOCDIR)/.
@@ -158,7 +162,7 @@ uninstall:
158 rm -f $(DESTDIR)/$(datarootdir)/bash-completion/completions/firemon 162 rm -f $(DESTDIR)/$(datarootdir)/bash-completion/completions/firemon
159 rm -f $(DESTDIR)/$(datarootdir)/bash-completion/completions/firecfg 163 rm -f $(DESTDIR)/$(datarootdir)/bash-completion/completions/firecfg
160 164
161DISTFILES = "src etc platform configure configure.ac Makefile.in install.sh mkman.sh mketc.sh mkdeb.sh mkuid.sh COPYING README RELNOTES" 165DISTFILES = "src etc platform contrib configure configure.ac Makefile.in install.sh mkman.sh mketc.sh mkdeb.sh mkuid.sh COPYING README RELNOTES"
162DISTFILES_TEST = "test/apps test/apps-x11 test/apps-x11-xorg test/root test/fcopy test/environment test/profiles test/utils test/compile test/filters test/network test/arguments test/fs test/sysutils" 166DISTFILES_TEST = "test/apps test/apps-x11 test/apps-x11-xorg test/root test/fcopy test/environment test/profiles test/utils test/compile test/filters test/network test/arguments test/fs test/sysutils"
163 167
164dist: 168dist:
diff --git a/README b/README
index 751480868..c94560026 100644
--- a/README
+++ b/README
@@ -97,6 +97,10 @@ valoq (https://github.com/valoq)
97 - added skanlite, ssh-agent, transmission-cli, tracker, transmission-show, w3m, xfburn, xpra profiles 97 - added skanlite, ssh-agent, transmission-cli, tracker, transmission-show, w3m, xfburn, xpra profiles
98 - added wget profile 98 - added wget profile
99 - disable gnupg and systemd directories under /run/user 99 - disable gnupg and systemd directories under /run/user
100Pixel Fairy (https://github.com/xahare)
101 - added fjclip.py, fjdisplay.py and fjresize.py in contrib section
102pshpsh (https://github.com/pshpsh)
103 - added FossaMail profile
100eventyrer (https://github.com/eventyrer) 104eventyrer (https://github.com/eventyrer)
101 - update gnome-mplayer.profile 105 - update gnome-mplayer.profile
102thewisenerd (https://github.com/thewisenerd) 106thewisenerd (https://github.com/thewisenerd)
@@ -107,7 +111,7 @@ SYN-cook (https://github.com/SYN-cook)
107thewisenerd (https://github.com/thewisenerd) 111thewisenerd (https://github.com/thewisenerd)
108 - appimage: pass commandline arguments 112 - appimage: pass commandline arguments
109KOLANICH (https://github.com/KOLANICH) 113KOLANICH (https://github.com/KOLANICH)
110 - added symlink fixer 114 - added symlink fixer fix_private-bin.py in contrib section
111Jesse Smith (https://github.com/slicer69) 115Jesse Smith (https://github.com/slicer69)
112 - added QupZilla profile 116 - added QupZilla profile
113Lari Rauno (https://github.com/tuutti) 117Lari Rauno (https://github.com/tuutti)
diff --git a/README.md b/README.md
index 9057a9a88..f4fa7282f 100644
--- a/README.md
+++ b/README.md
@@ -98,5 +98,5 @@ gjs, gnome-books, gnome-clocks, gnome-documents, gnome-maps, gnome-music, gnome-
98goobox, gpa, gpg, gpg-agent, highlight, img2txt, k3b, kate, lynx, mediainfo, nautilus, odt2txt, pdftotext, 98goobox, gpa, gpg, gpg-agent, highlight, img2txt, k3b, kate, lynx, mediainfo, nautilus, odt2txt, pdftotext,
99simple-scan, skanlite, ssh-agent, tracker, transmission-cli, transmission-show, w3m, xfburn, xpra, wget, 99simple-scan, skanlite, ssh-agent, tracker, transmission-cli, transmission-show, w3m, xfburn, xpra, wget,
100xed, pluma, Cryptocat, Bless, Gnome 2048, Gnome Calculator, Gnome Contacts, JD-GUI, Lollypop, MultiMC5, 100xed, pluma, Cryptocat, Bless, Gnome 2048, Gnome Calculator, Gnome Contacts, JD-GUI, Lollypop, MultiMC5,
101PDFSam, Pithos, Xonotic, wireshark, keepassx2, QupZilla 101PDFSam, Pithos, Xonotic, wireshark, keepassx2, QupZilla, FossaMail
102 102
diff --git a/RELNOTES b/RELNOTES
index 2d57b1a88..a14200a0f 100644
--- a/RELNOTES
+++ b/RELNOTES
@@ -1,11 +1,17 @@
1firejail (0.9.45) baseline; urgency=low 1firejail (0.9.45) baseline; urgency=low
2 * development version, work in progress 2 * development version, work in progress
3 * security: overwrite /etc/resolv.conf found by Martin Carpenter 3 * security: --bandwidth root shell found by Martin Carpenter (CVE-2017-5207)
4 * security: disabled --allow-debuggers when running on kernel
5 versions prior to 4.8; a kernel bug in ptrace system call
6 allows a full bypass of seccomp filter; problem reported by Lizzie Dixon
7 (CVE-2017-5206)
8 * security: overwrite /etc/resolv.conf found by Martin Carpenter (CVE-2016-10118)
4 * secuirty: TOCTOU exploit for --get and --put found by Daniel Hodson 9 * secuirty: TOCTOU exploit for --get and --put found by Daniel Hodson
5 * security: invalid environment exploit found by Martin Carpenter 10 * security: invalid environment exploit found by Martin Carpenter (CVE-2016-10122)
6 * security: split most of networking code in a separate executable 11 * security: split most of networking code in a separate executable
7 * security: split seccomp filter code configuration in a separate executable 12 * security: split seccomp filter code configuration in a separate executable
8 * security: split file copying in private option in a separate executable 13 * security: split file copying in private option in a separate executable
14 * security: root exploit found by Sebastian Krahmer (CVE-2017-5180)
9 * feature: disable gnupg and systemd directories under /run/user 15 * feature: disable gnupg and systemd directories under /run/user
10 * feature: allow root user access to /dev/shm (--noblacklist=/dev/shm) 16 * feature: allow root user access to /dev/shm (--noblacklist=/dev/shm)
11 * feature: AppImage type 2 support 17 * feature: AppImage type 2 support
@@ -16,16 +22,18 @@ firejail (0.9.45) baseline; urgency=low
16 * feature: config support for firejail prompt in terminals 22 * feature: config support for firejail prompt in terminals
17 * feature: pass command line arguments to appimages 23 * feature: pass command line arguments to appimages
18 * feature: --allow-private-blacklist option 24 * feature: --allow-private-blacklist option
25 * feature: allow non-seccomp setup for OverlayFS sandboxes
26 * feature: added a number o Python scripts for handling sandboxes
19 * new profiles: xiphos, Tor Browser Bundle, display (imagemagik), Wire, 27 * new profiles: xiphos, Tor Browser Bundle, display (imagemagik), Wire,
20 * new profiles: mumble, zoom, Guayadeque, qemu, keypass2, xed, pluma, 28 * new profiles: mumble, zoom, Guayadeque, qemu, keypass2, xed, pluma,
21 * new profiles: Cryptocat, Bless, Gnome 2048, Gnome Calculator, 29 * new profiles: Cryptocat, Bless, Gnome 2048, Gnome Calculator,
22 * new profiles: Gnome Contacts, JD-GUI, Lollypop, MultiMC5, PDFSam, Pithos, 30 * new profiles: Gnome Contacts, JD-GUI, Lollypop, MultiMC5, PDFSam, Pithos,
23 * new profies: Xonotic, wireshark, keepassx2, QupZilla 31 * new profies: Xonotic, wireshark, keepassx2, QupZilla, FossaMail
24 * bugfixes 32 * bugfixes
25 -- netblue30 <netblue30@yahoo.com> Sun, 23 Oct 2016 08:00:00 -0500 33 -- netblue30 <netblue30@yahoo.com> Sun, 23 Oct 2016 08:00:00 -0500
26 34
27firejail (0.9.44) baseline; urgency=low 35firejail (0.9.44) baseline; urgency=low
28 * CVE-2016-7545 submitted by Aleksey Manevich 36 * CVE-2016-9016 submitted by Aleksey Manevich
29 * modifs: removed man firejail-config 37 * modifs: removed man firejail-config
30 * modifs: --private-tmp whitelists /tmp/.X11-unix directory 38 * modifs: --private-tmp whitelists /tmp/.X11-unix directory
31 * modifs: Nvidia drivers added to --private-dev 39 * modifs: Nvidia drivers added to --private-dev
@@ -142,11 +150,12 @@ firejail (0.9.38) baseline; urgency=low
142 * added KMail, Seamonkey, Telegram, Mathematica, uGet, 150 * added KMail, Seamonkey, Telegram, Mathematica, uGet,
143 * and mupen64plus profiles 151 * and mupen64plus profiles
144 * --chroot in user mode allowed only if seccomp support is available 152 * --chroot in user mode allowed only if seccomp support is available
145 * in current Linux kernel 153 * in current Linux kernel (CVE-2016-10123)
146 * deprecated --private-home feature 154 * deprecated --private-home feature
147 * the first protocol list installed takes precedence 155 * the first protocol list installed takes precedence
148 * --tmpfs option allowed only running as root 156 * --tmpfs option allowed only running as root (CVE-2016-10117)
149 * added --private-tmp option 157 * added --private-tmp option
158 * weak permissions (CVE-2016-10119, CVE-2016-10120, CVE-2016-10121)
150 * bugfixes 159 * bugfixes
151 -- netblue30 <netblue30@yahoo.com> Tue, 2 Feb 2016 10:00:00 -0500 160 -- netblue30 <netblue30@yahoo.com> Tue, 2 Feb 2016 10:00:00 -0500
152 161
diff --git a/contrib/fix_private-bin.py b/contrib/fix_private-bin.py
new file mode 100755
index 000000000..270c758a2
--- /dev/null
+++ b/contrib/fix_private-bin.py
@@ -0,0 +1,157 @@
1#!/usr/bin/python3
2
3__author__ = "KOLANICH"
4__copyright__ = """This is free and unencumbered software released into the public domain.
5
6Anyone is free to copy, modify, publish, use, compile, sell, or
7distribute this software, either in source code form or as a compiled
8binary, for any purpose, commercial or non-commercial, and by any
9means.
10
11In jurisdictions that recognize copyright laws, the author or authors
12of this software dedicate any and all copyright interest in the
13software to the public domain. We make this dedication for the benefit
14of the public at large and to the detriment of our heirs and
15successors. We intend this dedication to be an overt act of
16relinquishment in perpetuity of all present and future rights to this
17software under copyright law.
18
19THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
20EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
21MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
22IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
23OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
24ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
25OTHER DEALINGS IN THE SOFTWARE.
26
27For more information, please refer to <http://unlicense.org/>"""
28__license__ = "Unlicense"
29
30import sys, os, glob, re
31
32privRx=re.compile("^(?:#\s*)?private-bin")
33
34def fixSymlinkedBins(files, replMap):
35 """
36 Used to add filenames to private-bin directives of files if the ones present are mentioned in replMap
37 replMap is a dict where key is the marker filename and value is the filename to add
38 """
39
40 rxs=dict()
41 for (old,new) in replMap.items():
42 rxs[old]=re.compile("\\b"+old+"\\b")
43 rxs[new]=re.compile("\\b"+new+"\\b")
44 #print(rxs)
45
46 for filename in files:
47 lines=None
48 with open(filename,"r") as file:
49 lines=file.readlines()
50
51 shouldUpdate=False
52 for (i,line) in enumerate(lines):
53 if privRx.search(line):
54 for (old,new) in replMap.items():
55 if rxs[old].search(line) and not rxs[new].search(line):
56 lines[i]=rxs[old].sub(old+","+new, line)
57 shouldUpdate=True
58 print(lines[i])
59
60 if shouldUpdate:
61 with open(filename,"w") as file:
62 file.writelines(lines)
63 pass
64
65def createSetOfBinaries(files):
66 """
67 Creates a set of binaries mentioned in private-bin directives of files.
68 """
69 s=set()
70 for filename in files:
71 lines=None
72 with open(filename,"r") as file:
73 for line in file:
74 if privRx.search(line):
75 bins=line.split(",")
76 bins[0]=bins[0].split(" ")[-1]
77 bins = [n.strip() for n in bins]
78 s=s|set(bins)
79 return s
80
81def createSymlinkTable(binDirs, binariesSet):
82 """
83 creates a dict of symlinked binaries in the system where a key is a symlink name and value is a symlinked binary.
84 binDirs are folders to look into for binaries symlinks
85 binariesSet is a set of binaries to be checked if they are actually a symlinks
86 """
87 m=dict()
88 toProcess=binariesSet
89 while len(toProcess)!=0:
90 additional=set()
91 for sh in toProcess:
92 for bD in binDirs:
93 p=bD+os.path.sep+sh
94 if os.path.exists(p):
95 if os.path.islink(p):
96 m[sh]=os.readlink(p)
97 additional.add(m[sh].split(" ")[0])
98 else:
99 pass
100 break
101 toProcess=additional
102 return m
103
104def doTheFixes(profilesPath, binDirs):
105 """
106 Fixes private-bin in .profiles for firejail. The pipeline is as follows:
107 discover files -> discover mentioned binaries ->
108 discover the ones which are symlinks ->
109 make a look-up table for fix ->
110 filter the ones can be fixed (we cannot fix the ones which are not in directories for binaries) ->
111 apply fix
112 """
113 files=glob.glob(profilesPath+os.path.sep+"*.profile")
114 bins=createSetOfBinaries(files)
115 #print("The binaries used are:")
116 #print(bins)
117 stbl=createSymlinkTable(binDirs,bins)
118 print("The replacement table is:")
119 print(stbl)
120 stbl={a[0]:a[1] for a in stbl.items() if a[0].find(os.path.sep) < 0 and a[1].find(os.path.sep)<0}
121 print("Filtered replacement table is:")
122 print(stbl)
123 fixSymlinkedBins(files,stbl)
124
125def printHelp():
126 print("python3 "+os.path.basename(__file__)+" <dir with .profile files>\nThe default dir is "+defaultProfilesPath+"\n"+doTheFixes.__doc__)
127
128def main():
129 """The main function. Parses the commandline args, shows messages and calles the function actually doing the work."""
130 print(repr(sys.argv))
131 defaultProfilesPath="../etc"
132 if len(sys.argv)>2 or (len(sys.argv)==2 and (sys.argv[1] == '-h' or sys.argv[1] == '--help') ):
133 printHelp()
134 exit(1)
135
136 profilesPath=None
137 if len(sys.argv)==2:
138 if os.path.isdir(sys.argv[1]):
139 profilesPath=os.path.abspath(sys.argv[1])
140 else:
141 if os.path.exists(sys.argv[1]):
142 print(sys.argv[1]+" is not a dir")
143 else:
144 print(sys.argv[1]+" does not exist")
145 printHelp()
146 exit(1)
147 else:
148 print("Using default profiles dir: " + defaultProfilesPath)
149 profilesPath=defaultProfilesPath
150
151 binDirs=["/bin","/usr/bin","/usr/sbin","/usr/local/bin","/usr/local/sbin"]
152 print("Binaries dirs are:")
153 print(binDirs)
154 doTheFixes(profilesPath, binDirs)
155
156if __name__ == "__main__":
157 main()
diff --git a/contrib/fix_private-bin_for_symlinked_sh.py b/contrib/fix_private-bin_for_symlinked_sh.py
deleted file mode 100644
index 705e46e46..000000000
--- a/contrib/fix_private-bin_for_symlinked_sh.py
+++ /dev/null
@@ -1,68 +0,0 @@
1#!/usr/bin/python3
2
3import sys, os, glob, re
4
5privRx=re.compile("^(?:#\s*)?private-bin")
6
7def fixSymlinkedBins(files, replMap):
8 rxs=dict()
9 for (old,new) in replMap.items():
10 rxs[old]=re.compile("\\b"+old+"\\b")
11 rxs[new]=re.compile("\\b"+new+"\\b")
12 print(rxs)
13
14 for filename in files:
15 lines=None
16 with open(filename,"r") as file:
17 lines=file.readlines()
18
19 shouldUpdate=False
20 for (i,line) in enumerate(lines):
21 if privRx.search(line):
22 for (old,new) in replMap.items():
23 if rxs[old].search(line) and not rxs[new].search(line):
24 lines[i]=rxs[old].sub(old+","+new, line)
25 shouldUpdate=True
26 print(lines[i])
27
28 if shouldUpdate:
29 with open(filename,"w") as file:
30 file.writelines(lines)
31 pass
32
33def createListOfBinaries(files):
34 s=set()
35 for filename in files:
36 lines=None
37 with open(filename,"r") as file:
38 for line in file:
39 if privRx.search(line):
40 bins=line.split(",")
41 bins[0]=bins[0].split(" ")[-1]
42 bins = [n.strip() for n in bins]
43 s=s|set(bins)
44 return s
45
46def createSymlinkTable(binDirs, binariesSet):
47 m=dict()
48 for sh in binariesSet:
49 for bD in binDirs:
50 p=bD+os.path.sep+sh
51 if os.path.exists(p):
52 if os.path.islink(p):
53 m[sh]=os.readlink(p)
54 else:
55 pass
56 break
57 return m
58
59
60sh="sh"
61binDirs=["/bin","/usr/bin","/usr/sbin","/usr/local/bin","/usr/local/sbin"]
62profilesPath="."
63files=glob.glob(profilesPath+os.path.sep+"*.profile")
64
65bins=createListOfBinaries(files)
66stbl=createSymlinkTable(binDirs,bins)
67print(stbl)
68fixSymlinkedBins(files,{a[0]:a[1] for a in stbl.items() if a[0].find("/") < 0 and a[1].find("/")<0})
diff --git a/contrib/fjclip.py b/contrib/fjclip.py
new file mode 100755
index 000000000..cd12cd289
--- /dev/null
+++ b/contrib/fjclip.py
@@ -0,0 +1,35 @@
1#!/usr/bin/env python
2
3import re
4import sys
5import subprocess
6import fjdisplay
7
8usage = """fjclip.py src dest. src or dest can be named firejails or - for stdin or stdout.
9firemon --x11 to see available running x11 firejails. firejail names can be shortened
10to least ambiguous. for example 'work-libreoffice' can be shortened to 'work' if no
11other firejails name starts with 'work'.
12warning: browsers are dangerous. clipboards from browsers are dangerous. see
13https://github.com/dxa4481/Pastejacking
14fjclip.py strips whitespace from both
15ends, but does nothing else to protect you. use a simple gui text editor like
16gedit if you want to see what your pasting."""
17
18if len(sys.argv) != 3 or sys.argv == '-h' or sys.argv == '--help':
19 print(usage)
20 exit(1)
21
22if sys.argv[1] == '-':
23 clipin_raw = sys.stdin.read()
24else:
25 display = fjdisplay.getdisplay(sys.argv[1])
26 clipin_raw = subprocess.check_output(['xsel','-b','--display',display])
27
28clipin = clipin_raw.strip()
29
30if sys.argv[2] == '-':
31 print(clipin)
32else:
33 display = fjdisplay.getdisplay(sys.argv[2])
34 clipout = subprocess.Popen(['xsel','-b','-i','--display',display],stdin=subprocess.PIPE)
35 clipout.communicate(clipin) \ No newline at end of file
diff --git a/contrib/fjdisplay.py b/contrib/fjdisplay.py
new file mode 100755
index 000000000..0e0ef01ec
--- /dev/null
+++ b/contrib/fjdisplay.py
@@ -0,0 +1,43 @@
1#!/usr/bin/env python
2
3import re
4import sys
5import subprocess
6
7usage = """fjdisplay.py name-of-firejail
8returns the display in the form of ':NNN'
9"""
10
11def getfirejails():
12 output = subprocess.check_output(['firemon','--x11'])
13 firejails = {}
14 name = ''
15 for line in output.split('\n'):
16 namematch = re.search('--name=(\w+\S*)',line)
17 if namematch:
18 name = namematch.group(1)
19 displaymatch = re.search('DISPLAY (:\d+)',line)
20 if displaymatch:
21 firejails[name] = displaymatch.group(1)
22 return firejails
23
24def getdisplay(name):
25 firejails = getfirejails()
26 fjlist = '\n'.join(firejails.keys())
27 namere = re.compile('^'+name+'.*', re.MULTILINE)
28 matchingjails = namere.findall(fjlist)
29 if len(matchingjails) == 1:
30 return firejails[matchingjails[0]]
31 if len(matchingjails) == 0:
32 raise NameError("firejail {} does not exist".format(name))
33 else:
34 raise NameError("ambiguous firejail name")
35
36if __name__ == '__main__':
37 if '-h' in sys.argv or '--help' in sys.argv or len(sys.argv) > 2:
38 print(usage)
39 exit()
40 if len(sys.argv) == 1:
41 print(getfirejails())
42 if len(sys.argv) == 2:
43 print (getdisplay(sys.argv[1])) \ No newline at end of file
diff --git a/contrib/fjresize.py b/contrib/fjresize.py
new file mode 100755
index 000000000..52b289159
--- /dev/null
+++ b/contrib/fjresize.py
@@ -0,0 +1,25 @@
1#!/usr/bin/env python
2
3import sys
4import fjdisplay
5import subprocess
6
7usage = """usage: fjresize.py firejail-name displaysize
8resize firejail xephyr windows.
9fjdisplay.py with no other arguments will list running named firejails with displays.
10fjresize.py with only a firejail name will list valid resolutions.
11names can be shortend as long its unambiguous.
12note: you may need to move the xephyr window for the resize to take effect
13example:
14 fjresize.py browser 1280x800
15"""
16
17
18if len(sys.argv) == 2:
19 out = subprocess.check_output(['xrandr','--display',fjdisplay.getdisplay(sys.argv[1])])
20 print(out)
21elif len(sys.argv) == 3:
22 out = subprocess.check_output(['xrandr','--display',fjdisplay.getdisplay(sys.argv[1]),'--output','default','--mode',sys.argv[2]])
23 print(out)
24else:
25 print(usage) \ No newline at end of file
diff --git a/etc/FossaMail.profile b/etc/FossaMail.profile
new file mode 100644
index 000000000..0da235467
--- /dev/null
+++ b/etc/FossaMail.profile
@@ -0,0 +1,2 @@
1# Firejail profile for FossaMail
2include /etc/firejail/fossamail.profile
diff --git a/etc/fossamail.profile b/etc/fossamail.profile
new file mode 100644
index 000000000..a0dc8ae59
--- /dev/null
+++ b/etc/fossamail.profile
@@ -0,0 +1,15 @@
1# Firejail profile for FossaMail
2
3noblacklist ~/.gnupg
4mkdir ~/.gnupg
5whitelist ~/.gnupg
6
7noblacklist ~/.fossamail
8mkdir ~/.fossamail
9whitelist ~/.fossamail
10
11noblacklist ~/.cache/fossamail
12mkdir ~/.cache/fossamail
13whitelist ~/.cache/fossamail
14
15include /etc/firejail/firefox.profile
diff --git a/etc/gpa.profile b/etc/gpa.profile
index 7d7277190..9da750f9e 100644
--- a/etc/gpa.profile
+++ b/etc/gpa.profile
@@ -18,6 +18,4 @@ shell none
18tracelog 18tracelog
19 19
20# private-bin gpa,gpg 20# private-bin gpa,gpg
21private-tmp
22private-dev 21private-dev
23# private-etc none
diff --git a/etc/gpg-agent.profile b/etc/gpg-agent.profile
index 59c7383d7..f587f0d53 100644
--- a/etc/gpg-agent.profile
+++ b/etc/gpg-agent.profile
@@ -11,7 +11,7 @@ nogroups
11nonewprivs 11nonewprivs
12noroot 12noroot
13nosound 13nosound
14protocol unix 14protocol unix,inet,inet6
15seccomp 15seccomp
16netfilter 16netfilter
17no3d 17no3d
@@ -21,6 +21,4 @@ tracelog
21blacklist /tmp/.X11-unix 21blacklist /tmp/.X11-unix
22 22
23# private-bin gpg-agent,gpg 23# private-bin gpg-agent,gpg
24private-tmp
25private-dev 24private-dev
26# private-etc none
diff --git a/etc/gpg.profile b/etc/gpg.profile
index d711c6f3e..963ff5ed7 100644
--- a/etc/gpg.profile
+++ b/etc/gpg.profile
@@ -11,10 +11,9 @@ nogroups
11nonewprivs 11nonewprivs
12noroot 12noroot
13nosound 13nosound
14protocol unix 14protocol unix,inet,inet6
15seccomp 15seccomp
16netfilter 16netfilter
17net none
18no3d 17no3d
19shell none 18shell none
20tracelog 19tracelog
@@ -22,6 +21,4 @@ tracelog
22blacklist /tmp/.X11-unix 21blacklist /tmp/.X11-unix
23 22
24# private-bin gpg,gpg-agent 23# private-bin gpg,gpg-agent
25private-tmp
26private-dev 24private-dev
27# private-etc none
diff --git a/platform/debian/conffiles b/platform/debian/conffiles
index 9afe42be8..56a5c8e7e 100644
--- a/platform/debian/conffiles
+++ b/platform/debian/conffiles
@@ -240,3 +240,5 @@
240/etc/firejail/xonotic.profile 240/etc/firejail/xonotic.profile
241/etc/firejail/VirtualBox.profile 241/etc/firejail/VirtualBox.profile
242/etc/firejail/qupzilla.profile 242/etc/firejail/qupzilla.profile
243/etc/firejail/FossaMail.profile
244/etc/firejail/fossamail.profile
diff --git a/src/fcopy/main.c b/src/fcopy/main.c
index b1e2813db..a4f5ace11 100644
--- a/src/fcopy/main.c
+++ b/src/fcopy/main.c
@@ -41,7 +41,7 @@ static void copy_file(const char *srcname, const char *destname, mode_t mode, ui
41 // open source 41 // open source
42 int src = open(srcname, O_RDONLY); 42 int src = open(srcname, O_RDONLY);
43 if (src < 0) { 43 if (src < 0) {
44 fprintf(stderr, "Warning: cannot open %s, file not copied\n", srcname); 44 fprintf(stderr, "Warning fcopy: cannot open %s, file not copied\n", srcname);
45 return; 45 return;
46 } 46 }
47 47
diff --git a/src/firecfg/firecfg.config b/src/firecfg/firecfg.config
index fe65a5077..4e4e5488a 100644
--- a/src/firecfg/firecfg.config
+++ b/src/firecfg/firecfg.config
@@ -184,8 +184,6 @@ eog
184# other 184# other
185atom 185atom
186atom-beta 186atom-beta
187gpa
188gpg
189ranger 187ranger
190keepass 188keepass
191keepass2 189keepass2
diff --git a/src/firejail/bandwidth.c b/src/firejail/bandwidth.c
index 5e9002f22..84c9dc53a 100644
--- a/src/firejail/bandwidth.c
+++ b/src/firejail/bandwidth.c
@@ -435,15 +435,8 @@ void bandwidth_pid(pid_t pid, const char *command, const char *dev, int down, in
435 if (setregid(0, 0)) 435 if (setregid(0, 0))
436 errExit("setregid"); 436 errExit("setregid");
437 437
438 if (!cfg.shell)
439 cfg.shell = guess_shell();
440 if (!cfg.shell) {
441 fprintf(stderr, "Error: no POSIX shell found, please use --shell command line option\n");
442 exit(1);
443 }
444
445 char *arg[4]; 438 char *arg[4];
446 arg[0] = cfg.shell; 439 arg[0] = "/bin/sh";
447 arg[1] = "-c"; 440 arg[1] = "-c";
448 arg[2] = cmd; 441 arg[2] = cmd;
449 arg[3] = NULL; 442 arg[3] = NULL;
diff --git a/src/firejail/firejail.h b/src/firejail/firejail.h
index 36cf47435..586cfd65e 100644
--- a/src/firejail/firejail.h
+++ b/src/firejail/firejail.h
@@ -403,7 +403,7 @@ char *fs_check_overlay_dir(const char *subdirname, int allow_reuse);
403void fs_overlayfs(void); 403void fs_overlayfs(void);
404// chroot into an existing directory; mount exiting /dev and update /etc/resolv.conf 404// chroot into an existing directory; mount exiting /dev and update /etc/resolv.conf
405void fs_chroot(const char *rootdir); 405void fs_chroot(const char *rootdir);
406int fs_check_chroot_dir(const char *rootdir); 406void fs_check_chroot_dir(const char *rootdir);
407 407
408// profile.c 408// profile.c
409// find and read the profile specified by name from dir directory 409// find and read the profile specified by name from dir directory
@@ -450,6 +450,8 @@ void logmsg(const char *msg);
450void logargs(int argc, char **argv) ; 450void logargs(int argc, char **argv) ;
451void logerr(const char *msg); 451void logerr(const char *msg);
452int copy_file(const char *srcname, const char *destname, uid_t uid, gid_t gid, mode_t mode); 452int copy_file(const char *srcname, const char *destname, uid_t uid, gid_t gid, mode_t mode);
453void copy_file_as_user(const char *srcname, const char *destname, uid_t uid, gid_t gid, mode_t mode);
454void touch_file_as_user(const char *fname, uid_t uid, gid_t gid, mode_t mode);
453int is_dir(const char *fname); 455int is_dir(const char *fname);
454int is_link(const char *fname); 456int is_link(const char *fname);
455char *line_remove_spaces(const char *buf); 457char *line_remove_spaces(const char *buf);
diff --git a/src/firejail/fs.c b/src/firejail/fs.c
index e2fc09533..d7764accd 100644
--- a/src/firejail/fs.c
+++ b/src/firejail/fs.c
@@ -994,20 +994,25 @@ void fs_overlayfs(void) {
994 994
995#ifdef HAVE_CHROOT 995#ifdef HAVE_CHROOT
996// return 1 if error 996// return 1 if error
997int fs_check_chroot_dir(const char *rootdir) { 997void fs_check_chroot_dir(const char *rootdir) {
998 EUID_ASSERT(); 998 EUID_ASSERT();
999 assert(rootdir); 999 assert(rootdir);
1000 struct stat s; 1000 struct stat s;
1001 char *name; 1001 char *name;
1002 1002
1003 if (strcmp(rootdir, "/tmp") == 0 || strcmp(rootdir, "/var/tmp") == 0) {
1004 fprintf(stderr, "Error: invalid chroot directory\n");
1005 exit(1);
1006 }
1007
1003 // rootdir has to be owned by root 1008 // rootdir has to be owned by root
1004 if (stat(rootdir, &s) != 0) { 1009 if (stat(rootdir, &s) != 0) {
1005 fprintf(stderr, "Error: cannot find chroot directory\n"); 1010 fprintf(stderr, "Error: cannot find chroot directory\n");
1006 return 1; 1011 exit(1);
1007 } 1012 }
1008 if (s.st_uid != 0) { 1013 if (s.st_uid != 0) {
1009 fprintf(stderr, "Error: chroot directory should be owned by root\n"); 1014 fprintf(stderr, "Error: chroot directory should be owned by root\n");
1010 return 1; 1015 exit(1);
1011 } 1016 }
1012 1017
1013 // check /dev 1018 // check /dev
@@ -1015,7 +1020,11 @@ int fs_check_chroot_dir(const char *rootdir) {
1015 errExit("asprintf"); 1020 errExit("asprintf");
1016 if (stat(name, &s) == -1) { 1021 if (stat(name, &s) == -1) {
1017 fprintf(stderr, "Error: cannot find /dev in chroot directory\n"); 1022 fprintf(stderr, "Error: cannot find /dev in chroot directory\n");
1018 return 1; 1023 exit(1);
1024 }
1025 if (s.st_uid != 0) {
1026 fprintf(stderr, "Error: chroot /dev directory should be owned by root\n");
1027 exit(1);
1019 } 1028 }
1020 free(name); 1029 free(name);
1021 1030
@@ -1024,7 +1033,11 @@ int fs_check_chroot_dir(const char *rootdir) {
1024 errExit("asprintf"); 1033 errExit("asprintf");
1025 if (stat(name, &s) == -1) { 1034 if (stat(name, &s) == -1) {
1026 fprintf(stderr, "Error: cannot find /var/tmp in chroot directory\n"); 1035 fprintf(stderr, "Error: cannot find /var/tmp in chroot directory\n");
1027 return 1; 1036 exit(1);
1037 }
1038 if (s.st_uid != 0) {
1039 fprintf(stderr, "Error: chroot /var/tmp directory should be owned by root\n");
1040 exit(1);
1028 } 1041 }
1029 free(name); 1042 free(name);
1030 1043
@@ -1033,7 +1046,11 @@ int fs_check_chroot_dir(const char *rootdir) {
1033 errExit("asprintf"); 1046 errExit("asprintf");
1034 if (stat(name, &s) == -1) { 1047 if (stat(name, &s) == -1) {
1035 fprintf(stderr, "Error: cannot find /proc in chroot directory\n"); 1048 fprintf(stderr, "Error: cannot find /proc in chroot directory\n");
1036 return 1; 1049 exit(1);
1050 }
1051 if (s.st_uid != 0) {
1052 fprintf(stderr, "Error: chroot /proc directory should be owned by root\n");
1053 exit(1);
1037 } 1054 }
1038 free(name); 1055 free(name);
1039 1056
@@ -1042,18 +1059,41 @@ int fs_check_chroot_dir(const char *rootdir) {
1042 errExit("asprintf"); 1059 errExit("asprintf");
1043 if (stat(name, &s) == -1) { 1060 if (stat(name, &s) == -1) {
1044 fprintf(stderr, "Error: cannot find /tmp in chroot directory\n"); 1061 fprintf(stderr, "Error: cannot find /tmp in chroot directory\n");
1045 return 1; 1062 exit(1);
1063 }
1064 if (s.st_uid != 0) {
1065 fprintf(stderr, "Error: chroot /tmp directory should be owned by root\n");
1066 exit(1);
1046 } 1067 }
1047 free(name); 1068 free(name);
1048 1069
1049 // check /bin/bash 1070 // check /etc
1050// if (asprintf(&name, "%s/bin/bash", rootdir) == -1) 1071 if (asprintf(&name, "%s/etc", rootdir) == -1)
1051// errExit("asprintf"); 1072 errExit("asprintf");
1052// if (stat(name, &s) == -1) { 1073 if (stat(name, &s) == -1) {
1053// fprintf(stderr, "Error: cannot find /bin/bash in chroot directory\n"); 1074 fprintf(stderr, "Error: cannot find /etc in chroot directory\n");
1054// return 1; 1075 exit(1);
1055// } 1076 }
1056// free(name); 1077 if (s.st_uid != 0) {
1078 fprintf(stderr, "Error: chroot /etc directory should be owned by root\n");
1079 exit(1);
1080 }
1081 free(name);
1082
1083 // check /etc/resolv.conf
1084 if (asprintf(&name, "%s/etc/resolv.conf", rootdir) == -1)
1085 errExit("asprintf");
1086 if (stat(name, &s) == 0) {
1087 if (s.st_uid != 0) {
1088 fprintf(stderr, "Error: chroot /etc/resolv.conf should be owned by root\n");
1089 exit(1);
1090 }
1091 }
1092 if (is_link(name)) {
1093 fprintf(stderr, "Error: invalid %s file\n", name);
1094 exit(1);
1095 }
1096 free(name);
1057 1097
1058 // check x11 socket directory 1098 // check x11 socket directory
1059 if (getenv("FIREJAIL_X11")) { 1099 if (getenv("FIREJAIL_X11")) {
@@ -1063,12 +1103,14 @@ int fs_check_chroot_dir(const char *rootdir) {
1063 errExit("asprintf"); 1103 errExit("asprintf");
1064 if (stat(name, &s) == -1) { 1104 if (stat(name, &s) == -1) {
1065 fprintf(stderr, "Error: cannot find /tmp/.X11-unix in chroot directory\n"); 1105 fprintf(stderr, "Error: cannot find /tmp/.X11-unix in chroot directory\n");
1066 return 1; 1106 exit(1);
1107 }
1108 if (s.st_uid != 0) {
1109 fprintf(stderr, "Error: chroot /tmp/.X11-unix directory should be owned by root\n");
1110 exit(1);
1067 } 1111 }
1068 free(name); 1112 free(name);
1069 } 1113 }
1070
1071 return 0;
1072} 1114}
1073 1115
1074// chroot into an existing directory; mount exiting /dev and update /etc/resolv.conf 1116// chroot into an existing directory; mount exiting /dev and update /etc/resolv.conf
@@ -1129,7 +1171,7 @@ void fs_chroot(const char *rootdir) {
1129 fprintf(stderr, "Error: invalid %s file\n", fname); 1171 fprintf(stderr, "Error: invalid %s file\n", fname);
1130 exit(1); 1172 exit(1);
1131 } 1173 }
1132 if (copy_file("/etc/resolv.conf", fname, 0, 0, 0644) == -1) 1174 if (copy_file("/etc/resolv.conf", fname, 0, 0, 0644) == -1) // root needed
1133 fprintf(stderr, "Warning: /etc/resolv.conf not initialized\n"); 1175 fprintf(stderr, "Warning: /etc/resolv.conf not initialized\n");
1134 } 1176 }
1135 1177
diff --git a/src/firejail/fs_home.c b/src/firejail/fs_home.c
index 0872bf0d0..8a52314ed 100644
--- a/src/firejail/fs_home.c
+++ b/src/firejail/fs_home.c
@@ -42,19 +42,17 @@ static void skel(const char *homedir, uid_t u, gid_t g) {
42 // don't copy it if we already have the file 42 // don't copy it if we already have the file
43 if (stat(fname, &s) == 0) 43 if (stat(fname, &s) == 0)
44 return; 44 return;
45 if (is_link(fname)) { // stat on dangling symlinks fails, try again using lstat
46 fprintf(stderr, "Error: invalid %s file\n", fname);
47 exit(1);
48 }
45 if (stat("/etc/skel/.zshrc", &s) == 0) { 49 if (stat("/etc/skel/.zshrc", &s) == 0) {
46 if (copy_file("/etc/skel/.zshrc", fname, u, g, 0644) == 0) { 50 copy_file_as_user("/etc/skel/.zshrc", fname, u, g, 0644); // regular user
47 fs_logger("clone /etc/skel/.zshrc"); 51 fs_logger("clone /etc/skel/.zshrc");
48 }
49 } 52 }
50 else { // 53 else {
51 FILE *fp = fopen(fname, "w"); 54 touch_file_as_user(fname, u, g, 0644);
52 if (fp) { 55 fs_logger2("touch", fname);
53 fprintf(fp, "\n");
54 SET_PERMS_STREAM(fp, u, g, S_IRUSR | S_IWUSR);
55 fclose(fp);
56 fs_logger2("touch", fname);
57 }
58 } 56 }
59 free(fname); 57 free(fname);
60 } 58 }
@@ -64,23 +62,21 @@ static void skel(const char *homedir, uid_t u, gid_t g) {
64 if (asprintf(&fname, "%s/.cshrc", homedir) == -1) 62 if (asprintf(&fname, "%s/.cshrc", homedir) == -1)
65 errExit("asprintf"); 63 errExit("asprintf");
66 struct stat s; 64 struct stat s;
65
67 // don't copy it if we already have the file 66 // don't copy it if we already have the file
68 if (stat(fname, &s) == 0) 67 if (stat(fname, &s) == 0)
69 return; 68 return;
69 if (is_link(fname)) { // stat on dangling symlinks fails, try again using lstat
70 fprintf(stderr, "Error: invalid %s file\n", fname);
71 exit(1);
72 }
70 if (stat("/etc/skel/.cshrc", &s) == 0) { 73 if (stat("/etc/skel/.cshrc", &s) == 0) {
71 if (copy_file("/etc/skel/.cshrc", fname, u, g, 0644) == 0) { 74 copy_file_as_user("/etc/skel/.cshrc", fname, u, g, 0644); // regular user
72 fs_logger("clone /etc/skel/.cshrc"); 75 fs_logger("clone /etc/skel/.cshrc");
73 }
74 } 76 }
75 else { // 77 else {
76 /* coverity[toctou] */ 78 touch_file_as_user(fname, u, g, 0644);
77 FILE *fp = fopen(fname, "w"); 79 fs_logger2("touch", fname);
78 if (fp) {
79 fprintf(fp, "\n");
80 SET_PERMS_STREAM(fp, u, g, S_IRUSR | S_IWUSR);
81 fclose(fp);
82 fs_logger2("touch", fname);
83 }
84 } 80 }
85 free(fname); 81 free(fname);
86 } 82 }
@@ -93,10 +89,13 @@ static void skel(const char *homedir, uid_t u, gid_t g) {
93 // don't copy it if we already have the file 89 // don't copy it if we already have the file
94 if (stat(fname, &s) == 0) 90 if (stat(fname, &s) == 0)
95 return; 91 return;
92 if (is_link(fname)) { // stat on dangling symlinks fails, try again using lstat
93 fprintf(stderr, "Error: invalid %s file\n", fname);
94 exit(1);
95 }
96 if (stat("/etc/skel/.bashrc", &s) == 0) { 96 if (stat("/etc/skel/.bashrc", &s) == 0) {
97 if (copy_file("/etc/skel/.bashrc", fname, u, g, 0644) == 0) { 97 copy_file_as_user("/etc/skel/.bashrc", fname, u, g, 0644); // regular user
98 fs_logger("clone /etc/skel/.bashrc"); 98 fs_logger("clone /etc/skel/.bashrc");
99 }
100 } 99 }
101 free(fname); 100 free(fname);
102 } 101 }
@@ -106,6 +105,14 @@ static int store_xauthority(void) {
106 // put a copy of .Xauthority in XAUTHORITY_FILE 105 // put a copy of .Xauthority in XAUTHORITY_FILE
107 char *src; 106 char *src;
108 char *dest = RUN_XAUTHORITY_FILE; 107 char *dest = RUN_XAUTHORITY_FILE;
108 // create an empty file as root, and change ownership to user
109 FILE *fp = fopen(dest, "w");
110 if (fp) {
111 fprintf(fp, "\n");
112 SET_PERMS_STREAM(fp, getuid(), getgid(), 0600);
113 fclose(fp);
114 }
115
109 if (asprintf(&src, "%s/.Xauthority", cfg.homedir) == -1) 116 if (asprintf(&src, "%s/.Xauthority", cfg.homedir) == -1)
110 errExit("asprintf"); 117 errExit("asprintf");
111 118
@@ -115,12 +122,9 @@ static int store_xauthority(void) {
115 fprintf(stderr, "Warning: invalid .Xauthority file\n"); 122 fprintf(stderr, "Warning: invalid .Xauthority file\n");
116 return 0; 123 return 0;
117 } 124 }
118 125
119 int rv = copy_file(src, dest, -1, -1, 0600); 126 copy_file_as_user(src, dest, getuid(), getgid(), 0600); // regular user
120 if (rv) { 127 fs_logger2("clone", dest);
121 fprintf(stderr, "Warning: cannot transfer .Xauthority in private home directory\n");
122 return 0;
123 }
124 return 1; // file copied 128 return 1; // file copied
125 } 129 }
126 130
@@ -128,8 +132,17 @@ static int store_xauthority(void) {
128} 132}
129 133
130static int store_asoundrc(void) { 134static int store_asoundrc(void) {
135 // put a copy of .Xauthority in XAUTHORITY_FILE
131 char *src; 136 char *src;
132 char *dest = RUN_ASOUNDRC_FILE; 137 char *dest = RUN_ASOUNDRC_FILE;
138 // create an empty file as root, and change ownership to user
139 FILE *fp = fopen(dest, "w");
140 if (fp) {
141 fprintf(fp, "\n");
142 SET_PERMS_STREAM(fp, getuid(), getgid(), 0644);
143 fclose(fp);
144 }
145
133 if (asprintf(&src, "%s/.asoundrc", cfg.homedir) == -1) 146 if (asprintf(&src, "%s/.asoundrc", cfg.homedir) == -1)
134 errExit("asprintf"); 147 errExit("asprintf");
135 148
@@ -150,11 +163,8 @@ static int store_asoundrc(void) {
150 free(rp); 163 free(rp);
151 } 164 }
152 165
153 int rv = copy_file(src, dest, -1, -1, -0644); 166 copy_file_as_user(src, dest, getuid(), getgid(), 0644); // regular user
154 if (rv) { 167 fs_logger2("clone", dest);
155 fprintf(stderr, "Warning: cannot transfer .asoundrc in private home directory\n");
156 return 0;
157 }
158 return 1; // file copied 168 return 1; // file copied
159 } 169 }
160 170
@@ -167,13 +177,15 @@ static void copy_xauthority(void) {
167 char *dest; 177 char *dest;
168 if (asprintf(&dest, "%s/.Xauthority", cfg.homedir) == -1) 178 if (asprintf(&dest, "%s/.Xauthority", cfg.homedir) == -1)
169 errExit("asprintf"); 179 errExit("asprintf");
170 // copy, set permissions and ownership 180
171 int rv = copy_file(src, dest, getuid(), getgid(), S_IRUSR | S_IWUSR); 181 // if destination is a symbolic link, exit the sandbox!!!
172 if (rv) 182 if (is_link(dest)) {
173 fprintf(stderr, "Warning: cannot transfer .Xauthority in private home directory\n"); 183 fprintf(stderr, "Error: %s is a symbolic link\n", dest);
174 else { 184 exit(1);
175 fs_logger2("clone", dest);
176 } 185 }
186
187 copy_file_as_user(src, dest, getuid(), getgid(), S_IRUSR | S_IWUSR); // regular user
188 fs_logger2("clone", dest);
177 189
178 // delete the temporary file 190 // delete the temporary file
179 unlink(src); 191 unlink(src);
@@ -185,14 +197,16 @@ static void copy_asoundrc(void) {
185 char *dest; 197 char *dest;
186 if (asprintf(&dest, "%s/.asoundrc", cfg.homedir) == -1) 198 if (asprintf(&dest, "%s/.asoundrc", cfg.homedir) == -1)
187 errExit("asprintf"); 199 errExit("asprintf");
188 // copy, set permissions and ownership 200
189 int rv = copy_file(src, dest, getuid(), getgid(), S_IRUSR | S_IWUSR); 201 // if destination is a symbolic link, exit the sandbox!!!
190 if (rv) 202 if (is_link(dest)) {
191 fprintf(stderr, "Warning: cannot transfer .asoundrc in private home directory\n"); 203 fprintf(stderr, "Error: %s is a symbolic link\n", dest);
192 else { 204 exit(1);
193 fs_logger2("clone", dest);
194 } 205 }
195 206
207 copy_file_as_user(src, dest, getuid(), getgid(), S_IRUSR | S_IWUSR); // regular user
208 fs_logger2("clone", dest);
209
196 // delete the temporary file 210 // delete the temporary file
197 unlink(src); 211 unlink(src);
198} 212}
diff --git a/src/firejail/ls.c b/src/firejail/ls.c
index 77eb35f97..1af56751a 100644
--- a/src/firejail/ls.c
+++ b/src/firejail/ls.c
@@ -336,7 +336,7 @@ void sandboxfs(int op, pid_t pid, const char *path1, const char *path2) {
336 drop_privs(0); 336 drop_privs(0);
337 337
338 // copy the file 338 // copy the file
339 if (copy_file(src_fname, tmp_fname, getuid(), getgid(), 0600)) 339 if (copy_file(src_fname, tmp_fname, getuid(), getgid(), 0600)) // already a regular user
340 _exit(1); 340 _exit(1);
341#ifdef HAVE_GCOV 341#ifdef HAVE_GCOV
342 __gcov_flush(); 342 __gcov_flush();
@@ -362,7 +362,7 @@ void sandboxfs(int op, pid_t pid, const char *path1, const char *path2) {
362 drop_privs(0); 362 drop_privs(0);
363 363
364 // copy the file 364 // copy the file
365 if (copy_file(tmp_fname, dest_fname, getuid(), getgid(), 0600)) 365 if (copy_file(tmp_fname, dest_fname, getuid(), getgid(), 0600)) // already a regular user
366 _exit(1); 366 _exit(1);
367#ifdef HAVE_GCOV 367#ifdef HAVE_GCOV
368 __gcov_flush(); 368 __gcov_flush();
@@ -411,7 +411,7 @@ void sandboxfs(int op, pid_t pid, const char *path1, const char *path2) {
411 drop_privs(0); 411 drop_privs(0);
412 412
413 // copy the file 413 // copy the file
414 if (copy_file(src_fname, tmp_fname, getuid(), getgid(), 0600)) 414 if (copy_file(src_fname, tmp_fname, getuid(), getgid(), 0600)) // already a regular user
415 _exit(1); 415 _exit(1);
416#ifdef HAVE_GCOV 416#ifdef HAVE_GCOV
417 __gcov_flush(); 417 __gcov_flush();
@@ -443,7 +443,7 @@ void sandboxfs(int op, pid_t pid, const char *path1, const char *path2) {
443 drop_privs(0); 443 drop_privs(0);
444 444
445 // copy the file 445 // copy the file
446 if (copy_file(tmp_fname, dest_fname, getuid(), getgid(), 0600)) 446 if (copy_file(tmp_fname, dest_fname, getuid(), getgid(), 0600)) // already a regular user
447 _exit(1); 447 _exit(1);
448#ifdef HAVE_GCOV 448#ifdef HAVE_GCOV
449 __gcov_flush(); 449 __gcov_flush();
diff --git a/src/firejail/main.c b/src/firejail/main.c
index e70e20eec..84bf5e8e6 100644
--- a/src/firejail/main.c
+++ b/src/firejail/main.c
@@ -35,6 +35,7 @@
35#include <signal.h> 35#include <signal.h>
36#include <time.h> 36#include <time.h>
37#include <net/if.h> 37#include <net/if.h>
38#include <sys/utsname.h>
38 39
39#if 0 40#if 0
40#include <sys/times.h> 41#include <sys/times.h>
@@ -817,8 +818,27 @@ int main(int argc, char **argv) {
817 818
818 if (check_arg(argc, argv, "--quiet")) 819 if (check_arg(argc, argv, "--quiet"))
819 arg_quiet = 1; 820 arg_quiet = 1;
820 if (check_arg(argc, argv, "--allow-debuggers")) 821 if (check_arg(argc, argv, "--allow-debuggers")) {
822 // check kernel version
823 struct utsname u;
824 int rv = uname(&u);
825 if (rv != 0)
826 errExit("uname");
827 int major;
828 int minor;
829 if (2 != sscanf(u.release, "%d.%d", &major, &minor)) {
830 fprintf(stderr, "Error: cannot extract Linux kernel version: %s\n", u.version);
831 exit(1);
832 }
833 if (major < 4 || (major == 4 && minor < 8)) {
834 fprintf(stderr, "Error: --allow-debuggers is disabled on Linux kernels prior to 4.8. "
835 "A bug in ptrace call allows a full bypass of the seccomp filter. "
836 "Your current kernel version is %d.%d.\n", major, minor);
837 exit(1);
838 }
839
821 arg_allow_debuggers = 1; 840 arg_allow_debuggers = 1;
841 }
822 842
823 // drop permissions by default and rise them when required 843 // drop permissions by default and rise them when required
824 EUID_INIT(); 844 EUID_INIT();
@@ -1448,13 +1468,10 @@ int main(int argc, char **argv) {
1448 fprintf(stderr, "Error: invalid chroot directory\n"); 1468 fprintf(stderr, "Error: invalid chroot directory\n");
1449 exit(1); 1469 exit(1);
1450 } 1470 }
1451 free(rpath); 1471 cfg.chrootdir = rpath;
1452 1472
1453 // check chroot directory structure 1473 // check chroot directory structure
1454 if (fs_check_chroot_dir(cfg.chrootdir)) { 1474 fs_check_chroot_dir(cfg.chrootdir);
1455 fprintf(stderr, "Error: invalid chroot\n");
1456 exit(1);
1457 }
1458 } 1475 }
1459 else 1476 else
1460 exit_err_feature("chroot"); 1477 exit_err_feature("chroot");
diff --git a/src/firejail/preproc.c b/src/firejail/preproc.c
index d2db7d3dd..e17f39caa 100644
--- a/src/firejail/preproc.c
+++ b/src/firejail/preproc.c
@@ -76,12 +76,12 @@ void preproc_mount_mnt_dir(void) {
76 fs_logger2("tmpfs", RUN_MNT_DIR); 76 fs_logger2("tmpfs", RUN_MNT_DIR);
77 77
78 //copy defaultl seccomp files 78 //copy defaultl seccomp files
79 copy_file(PATH_SECCOMP_I386, RUN_SECCOMP_I386, getuid(), getgid(), 0644); 79 copy_file(PATH_SECCOMP_I386, RUN_SECCOMP_I386, getuid(), getgid(), 0644); // root needed
80 copy_file(PATH_SECCOMP_AMD64, RUN_SECCOMP_AMD64, getuid(), getgid(), 0644); 80 copy_file(PATH_SECCOMP_AMD64, RUN_SECCOMP_AMD64, getuid(), getgid(), 0644); // root needed
81 if (arg_allow_debuggers) 81 if (arg_allow_debuggers)
82 copy_file(PATH_SECCOMP_DEFAULT_DEBUG, RUN_SECCOMP_CFG, getuid(), getgid(), 0644); 82 copy_file(PATH_SECCOMP_DEFAULT_DEBUG, RUN_SECCOMP_CFG, getuid(), getgid(), 0644); // root needed
83 else 83 else
84 copy_file(PATH_SECCOMP_DEFAULT, RUN_SECCOMP_CFG, getuid(), getgid(), 0644); 84 copy_file(PATH_SECCOMP_DEFAULT, RUN_SECCOMP_CFG, getuid(), getgid(), 0644); // root needed
85 85
86 // as root, create an empty RUN_SECCOMP_PROTOCOL file 86 // as root, create an empty RUN_SECCOMP_PROTOCOL file
87 create_empty_file_as_root(RUN_SECCOMP_PROTOCOL, 0644); 87 create_empty_file_as_root(RUN_SECCOMP_PROTOCOL, 0644);
diff --git a/src/firejail/pulseaudio.c b/src/firejail/pulseaudio.c
index f890dd534..4ec84ec61 100644
--- a/src/firejail/pulseaudio.c
+++ b/src/firejail/pulseaudio.c
@@ -22,6 +22,7 @@
22#include <sys/stat.h> 22#include <sys/stat.h>
23#include <sys/mount.h> 23#include <sys/mount.h>
24#include <dirent.h> 24#include <dirent.h>
25#include <sys/wait.h>
25 26
26static void disable_file(const char *path, const char *file) { 27static void disable_file(const char *path, const char *file) {
27 assert(file); 28 assert(file);
@@ -113,7 +114,7 @@ void pulseaudio_init(void) {
113 char *pulsecfg = NULL; 114 char *pulsecfg = NULL;
114 if (asprintf(&pulsecfg, "%s/client.conf", RUN_PULSE_DIR) == -1) 115 if (asprintf(&pulsecfg, "%s/client.conf", RUN_PULSE_DIR) == -1)
115 errExit("asprintf"); 116 errExit("asprintf");
116 if (copy_file("/etc/pulse/client.conf", pulsecfg, -1, -1, 0644)) 117 if (copy_file("/etc/pulse/client.conf", pulsecfg, -1, -1, 0644)) // root needed
117 errExit("copy_file"); 118 errExit("copy_file");
118 FILE *fp = fopen(pulsecfg, "a+"); 119 FILE *fp = fopen(pulsecfg, "a+");
119 if (!fp) 120 if (!fp)
@@ -127,21 +128,63 @@ void pulseaudio_init(void) {
127 if (asprintf(&dir1, "%s/.config", cfg.homedir) == -1) 128 if (asprintf(&dir1, "%s/.config", cfg.homedir) == -1)
128 errExit("asprintf"); 129 errExit("asprintf");
129 if (stat(dir1, &s) == -1) { 130 if (stat(dir1, &s) == -1) {
130 int rv = mkdir(dir1, 0755); 131 pid_t child = fork();
131 if (rv == 0) { 132 if (child < 0)
132 if (set_perms(dir1, getuid(), getgid(), 0755)) 133 errExit("fork");
133 {;} // do nothing 134 if (child == 0) {
135 // drop privileges
136 drop_privs(0);
137
138 int rv = mkdir(dir1, 0755);
139 if (rv == 0) {
140 if (set_perms(dir1, getuid(), getgid(), 0755))
141 {;} // do nothing
142 }
143#ifdef HAVE_GCOV
144 __gcov_flush();
145#endif
146 _exit(0);
147 }
148 // wait for the child to finish
149 waitpid(child, NULL, 0);
150 }
151 else {
152 // make sure the directory is owned by the user
153 if (s.st_uid != getuid()) {
154 fprintf(stderr, "Error: user .config directory is not owned by the current user\n");
155 exit(1);
134 } 156 }
135 } 157 }
136 free(dir1); 158 free(dir1);
159
137 if (asprintf(&dir1, "%s/.config/pulse", cfg.homedir) == -1) 160 if (asprintf(&dir1, "%s/.config/pulse", cfg.homedir) == -1)
138 errExit("asprintf"); 161 errExit("asprintf");
139 if (stat(dir1, &s) == -1) { 162 if (stat(dir1, &s) == -1) {
140 /* coverity[toctou] */ 163 pid_t child = fork();
141 int rv = mkdir(dir1, 0700); 164 if (child < 0)
142 if (rv == 0) { 165 errExit("fork");
143 if (set_perms(dir1, getuid(), getgid(), 0700)) 166 if (child == 0) {
144 {;} // do nothing 167 // drop privileges
168 drop_privs(0);
169
170 int rv = mkdir(dir1, 0700);
171 if (rv == 0) {
172 if (set_perms(dir1, getuid(), getgid(), 0700))
173 {;} // do nothing
174 }
175#ifdef HAVE_GCOV
176 __gcov_flush();
177#endif
178 _exit(0);
179 }
180 // wait for the child to finish
181 waitpid(child, NULL, 0);
182 }
183 else {
184 // make sure the directory is owned by the user
185 if (s.st_uid != getuid()) {
186 fprintf(stderr, "Error: user .config/pulse directory is not owned by the current user\n");
187 exit(1);
145 } 188 }
146 } 189 }
147 free(dir1); 190 free(dir1);
diff --git a/src/firejail/sandbox.c b/src/firejail/sandbox.c
index 50fcd6ed0..493877db3 100644
--- a/src/firejail/sandbox.c
+++ b/src/firejail/sandbox.c
@@ -616,19 +616,10 @@ int sandbox(void* sandbox_arg) {
616 fs_trace_preload(); 616 fs_trace_preload();
617 } 617 }
618 else 618 else
619#endif 619#endif
620#ifdef HAVE_OVERLAYFS 620#ifdef HAVE_OVERLAYFS
621 if (arg_overlay) { 621 if (arg_overlay) {
622 fs_overlayfs(); 622 fs_overlayfs();
623 // force caps and seccomp if not started as root
624 if (getuid() != 0) {
625 enforce_filters();
626#ifdef HAVE_SECCOMP
627 enforce_seccomp = 1;
628#endif
629 }
630 else
631 arg_seccomp = 1;
632 } 623 }
633 else 624 else
634#endif 625#endif
diff --git a/src/firejail/util.c b/src/firejail/util.c
index 75f2acdb9..763e6b58b 100644
--- a/src/firejail/util.c
+++ b/src/firejail/util.c
@@ -28,6 +28,7 @@
28#include <grp.h> 28#include <grp.h>
29#include <sys/ioctl.h> 29#include <sys/ioctl.h>
30#include <termios.h> 30#include <termios.h>
31#include <sys/wait.h>
31 32
32#define MAX_GROUPS 1024 33#define MAX_GROUPS 1024
33// drop privileges 34// drop privileges
@@ -177,14 +178,14 @@ int copy_file(const char *srcname, const char *destname, uid_t uid, gid_t gid, m
177 // open source 178 // open source
178 int src = open(srcname, O_RDONLY); 179 int src = open(srcname, O_RDONLY);
179 if (src < 0) { 180 if (src < 0) {
180 fprintf(stderr, "Warning: cannot open %s, file not copied\n", srcname); 181 fprintf(stderr, "Warning: cannot open source file %s, file not copied\n", srcname);
181 return -1; 182 return -1;
182 } 183 }
183 184
184 // open destination 185 // open destination
185 int dst = open(destname, O_CREAT|O_WRONLY|O_TRUNC, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); 186 int dst = open(destname, O_CREAT|O_WRONLY|O_TRUNC, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
186 if (dst < 0) { 187 if (dst < 0) {
187 fprintf(stderr, "Warning: cannot open %s, file not copied\n", destname); 188 fprintf(stderr, "Warning: cannot open destination file %s, file not copied\n", destname);
188 close(src); 189 close(src);
189 return -1; 190 return -1;
190 } 191 }
@@ -218,6 +219,51 @@ int copy_file(const char *srcname, const char *destname, uid_t uid, gid_t gid, m
218 return 0; 219 return 0;
219} 220}
220 221
222// return -1 if error, 0 if no error
223void copy_file_as_user(const char *srcname, const char *destname, uid_t uid, gid_t gid, mode_t mode) {
224 pid_t child = fork();
225 if (child < 0)
226 errExit("fork");
227 if (child == 0) {
228 // drop privileges
229 drop_privs(0);
230
231 // copy, set permissions and ownership
232 int rv = copy_file(srcname, destname, uid, gid, mode); // already a regular user
233 if (rv)
234 fprintf(stderr, "Warning: cannot transfer .Xauthority in private home directory\n");
235#ifdef HAVE_GCOV
236 __gcov_flush();
237#endif
238 _exit(0);
239 }
240 // wait for the child to finish
241 waitpid(child, NULL, 0);
242}
243
244// return -1 if error, 0 if no error
245void touch_file_as_user(const char *fname, uid_t uid, gid_t gid, mode_t mode) {
246 pid_t child = fork();
247 if (child < 0)
248 errExit("fork");
249 if (child == 0) {
250 // drop privileges
251 drop_privs(0);
252
253 FILE *fp = fopen(fname, "w");
254 if (fp) {
255 fprintf(fp, "\n");
256 SET_PERMS_STREAM(fp, uid, gid, mode);
257 fclose(fp);
258 }
259#ifdef HAVE_GCOV
260 __gcov_flush();
261#endif
262 _exit(0);
263 }
264 // wait for the child to finish
265 waitpid(child, NULL, 0);
266}
221 267
222// return 1 if the file is a directory 268// return 1 if the file is a directory
223int is_dir(const char *fname) { 269int is_dir(const char *fname) {
diff --git a/src/man/firejail.txt b/src/man/firejail.txt
index 60c21cbc1..69d28c788 100644
--- a/src/man/firejail.txt
+++ b/src/man/firejail.txt
@@ -76,7 +76,9 @@ $ firejail [OPTIONS] firefox # starting Mozilla Firefox
76Signal the end of options and disables further option processing. 76Signal the end of options and disables further option processing.
77.TP 77.TP
78\fB\-\-allow-debuggers 78\fB\-\-allow-debuggers
79Allow tools such as strace and gdb inside the sandbox. 79Allow tools such as strace and gdb inside the sandbox. This option is only available
80when running on Linux kernels 4.8 or newer - a kernel bug in ptrace system call allows a full
81bypass of the seccomp filter.
80.br 82.br
81 83
82.br 84.br