mirror of
https://github.com/netblue30/firejail.git
synced 2026-05-15 22:01:33 -06:00
Baseline firejail 0.9.28
This commit is contained in:
parent
f104ebb698
commit
1379851360
246 changed files with 33999 additions and 0 deletions
280
COPYING
Normal file
280
COPYING
Normal file
|
|
@ -0,0 +1,280 @@
|
|||
GNU GENERAL PUBLIC LICENSE
|
||||
Version 2, June 1991
|
||||
|
||||
Copyright (C) 1989, 1991 Free Software Foundation, Inc.
|
||||
51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
||||
Everyone is permitted to copy and distribute verbatim copies
|
||||
of this license document, but changing it is not allowed.
|
||||
|
||||
Preamble
|
||||
|
||||
The licenses for most software are designed to take away your
|
||||
freedom to share and change it. By contrast, the GNU General Public
|
||||
License is intended to guarantee your freedom to share and change free
|
||||
software--to make sure the software is free for all its users. This
|
||||
General Public License applies to most of the Free Software
|
||||
Foundation's software and to any other program whose authors commit to
|
||||
using it. (Some other Free Software Foundation software is covered by
|
||||
the GNU Library General Public License instead.) You can apply it to
|
||||
your programs, too.
|
||||
|
||||
When we speak of free software, we are referring to freedom, not
|
||||
price. Our General Public Licenses are designed to make sure that you
|
||||
have the freedom to distribute copies of free software (and charge for
|
||||
this service if you wish), that you receive source code or can get it
|
||||
if you want it, that you can change the software or use pieces of it
|
||||
in new free programs; and that you know you can do these things.
|
||||
|
||||
To protect your rights, we need to make restrictions that forbid
|
||||
anyone to deny you these rights or to ask you to surrender the rights.
|
||||
These restrictions translate to certain responsibilities for you if you
|
||||
distribute copies of the software, or if you modify it.
|
||||
|
||||
For example, if you distribute copies of such a program, whether
|
||||
gratis or for a fee, you must give the recipients all the rights that
|
||||
you have. You must make sure that they, too, receive or can get the
|
||||
source code. And you must show them these terms so they know their
|
||||
rights.
|
||||
|
||||
We protect your rights with two steps: (1) copyright the software, and
|
||||
(2) offer you this license which gives you legal permission to copy,
|
||||
distribute and/or modify the software.
|
||||
|
||||
Also, for each author's protection and ours, we want to make certain
|
||||
that everyone understands that there is no warranty for this free
|
||||
software. If the software is modified by someone else and passed on, we
|
||||
want its recipients to know that what they have is not the original, so
|
||||
that any problems introduced by others will not reflect on the original
|
||||
authors' reputations.
|
||||
|
||||
Finally, any free program is threatened constantly by software
|
||||
patents. We wish to avoid the danger that redistributors of a free
|
||||
program will individually obtain patent licenses, in effect making the
|
||||
program proprietary. To prevent this, we have made it clear that any
|
||||
patent must be licensed for everyone's free use or not licensed at all.
|
||||
|
||||
The precise terms and conditions for copying, distribution and
|
||||
modification follow.
|
||||
|
||||
GNU GENERAL PUBLIC LICENSE
|
||||
TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
|
||||
|
||||
0. This License applies to any program or other work which contains
|
||||
a notice placed by the copyright holder saying it may be distributed
|
||||
under the terms of this General Public License. The "Program", below,
|
||||
refers to any such program or work, and a "work based on the Program"
|
||||
means either the Program or any derivative work under copyright law:
|
||||
that is to say, a work containing the Program or a portion of it,
|
||||
either verbatim or with modifications and/or translated into another
|
||||
language. (Hereinafter, translation is included without limitation in
|
||||
the term "modification".) Each licensee is addressed as "you".
|
||||
|
||||
Activities other than copying, distribution and modification are not
|
||||
covered by this License; they are outside its scope. The act of
|
||||
running the Program is not restricted, and the output from the Program
|
||||
is covered only if its contents constitute a work based on the
|
||||
Program (independent of having been made by running the Program).
|
||||
Whether that is true depends on what the Program does.
|
||||
|
||||
1. You may copy and distribute verbatim copies of the Program's
|
||||
source code as you receive it, in any medium, provided that you
|
||||
conspicuously and appropriately publish on each copy an appropriate
|
||||
copyright notice and disclaimer of warranty; keep intact all the
|
||||
notices that refer to this License and to the absence of any warranty;
|
||||
and give any other recipients of the Program a copy of this License
|
||||
along with the Program.
|
||||
|
||||
You may charge a fee for the physical act of transferring a copy, and
|
||||
you may at your option offer warranty protection in exchange for a fee.
|
||||
|
||||
2. You may modify your copy or copies of the Program or any portion
|
||||
of it, thus forming a work based on the Program, and copy and
|
||||
distribute such modifications or work under the terms of Section 1
|
||||
above, provided that you also meet all of these conditions:
|
||||
|
||||
a) You must cause the modified files to carry prominent notices
|
||||
stating that you changed the files and the date of any change.
|
||||
|
||||
b) You must cause any work that you distribute or publish, that in
|
||||
whole or in part contains or is derived from the Program or any
|
||||
part thereof, to be licensed as a whole at no charge to all third
|
||||
parties under the terms of this License.
|
||||
|
||||
c) If the modified program normally reads commands interactively
|
||||
when run, you must cause it, when started running for such
|
||||
interactive use in the most ordinary way, to print or display an
|
||||
announcement including an appropriate copyright notice and a
|
||||
notice that there is no warranty (or else, saying that you provide
|
||||
a warranty) and that users may redistribute the program under
|
||||
these conditions, and telling the user how to view a copy of this
|
||||
License. (Exception: if the Program itself is interactive but
|
||||
does not normally print such an announcement, your work based on
|
||||
the Program is not required to print an announcement.)
|
||||
|
||||
These requirements apply to the modified work as a whole. If
|
||||
identifiable sections of that work are not derived from the Program,
|
||||
and can be reasonably considered independent and separate works in
|
||||
themselves, then this License, and its terms, do not apply to those
|
||||
sections when you distribute them as separate works. But when you
|
||||
distribute the same sections as part of a whole which is a work based
|
||||
on the Program, the distribution of the whole must be on the terms of
|
||||
this License, whose permissions for other licensees extend to the
|
||||
entire whole, and thus to each and every part regardless of who wrote it.
|
||||
|
||||
Thus, it is not the intent of this section to claim rights or contest
|
||||
your rights to work written entirely by you; rather, the intent is to
|
||||
exercise the right to control the distribution of derivative or
|
||||
collective works based on the Program.
|
||||
|
||||
In addition, mere aggregation of another work not based on the Program
|
||||
with the Program (or with a work based on the Program) on a volume of
|
||||
a storage or distribution medium does not bring the other work under
|
||||
the scope of this License.
|
||||
|
||||
3. You may copy and distribute the Program (or a work based on it,
|
||||
under Section 2) in object code or executable form under the terms of
|
||||
Sections 1 and 2 above provided that you also do one of the following:
|
||||
|
||||
a) Accompany it with the complete corresponding machine-readable
|
||||
source code, which must be distributed under the terms of Sections
|
||||
1 and 2 above on a medium customarily used for software interchange; or,
|
||||
|
||||
b) Accompany it with a written offer, valid for at least three
|
||||
years, to give any third party, for a charge no more than your
|
||||
cost of physically performing source distribution, a complete
|
||||
machine-readable copy of the corresponding source code, to be
|
||||
distributed under the terms of Sections 1 and 2 above on a medium
|
||||
customarily used for software interchange; or,
|
||||
|
||||
c) Accompany it with the information you received as to the offer
|
||||
to distribute corresponding source code. (This alternative is
|
||||
allowed only for noncommercial distribution and only if you
|
||||
received the program in object code or executable form with such
|
||||
an offer, in accord with Subsection b above.)
|
||||
|
||||
The source code for a work means the preferred form of the work for
|
||||
making modifications to it. For an executable work, complete source
|
||||
code means all the source code for all modules it contains, plus any
|
||||
associated interface definition files, plus the scripts used to
|
||||
control compilation and installation of the executable. However, as a
|
||||
special exception, the source code distributed need not include
|
||||
anything that is normally distributed (in either source or binary
|
||||
form) with the major components (compiler, kernel, and so on) of the
|
||||
operating system on which the executable runs, unless that component
|
||||
itself accompanies the executable.
|
||||
|
||||
If distribution of executable or object code is made by offering
|
||||
access to copy from a designated place, then offering equivalent
|
||||
access to copy the source code from the same place counts as
|
||||
distribution of the source code, even though third parties are not
|
||||
compelled to copy the source along with the object code.
|
||||
|
||||
4. You may not copy, modify, sublicense, or distribute the Program
|
||||
except as expressly provided under this License. Any attempt
|
||||
otherwise to copy, modify, sublicense or distribute the Program is
|
||||
void, and will automatically terminate your rights under this License.
|
||||
However, parties who have received copies, or rights, from you under
|
||||
this License will not have their licenses terminated so long as such
|
||||
parties remain in full compliance.
|
||||
|
||||
5. You are not required to accept this License, since you have not
|
||||
signed it. However, nothing else grants you permission to modify or
|
||||
distribute the Program or its derivative works. These actions are
|
||||
prohibited by law if you do not accept this License. Therefore, by
|
||||
modifying or distributing the Program (or any work based on the
|
||||
Program), you indicate your acceptance of this License to do so, and
|
||||
all its terms and conditions for copying, distributing or modifying
|
||||
the Program or works based on it.
|
||||
|
||||
6. Each time you redistribute the Program (or any work based on the
|
||||
Program), the recipient automatically receives a license from the
|
||||
original licensor to copy, distribute or modify the Program subject to
|
||||
these terms and conditions. You may not impose any further
|
||||
restrictions on the recipients' exercise of the rights granted herein.
|
||||
You are not responsible for enforcing compliance by third parties to
|
||||
this License.
|
||||
|
||||
7. If, as a consequence of a court judgment or allegation of patent
|
||||
infringement or for any other reason (not limited to patent issues),
|
||||
conditions are imposed on you (whether by court order, agreement or
|
||||
otherwise) that contradict the conditions of this License, they do not
|
||||
excuse you from the conditions of this License. If you cannot
|
||||
distribute so as to satisfy simultaneously your obligations under this
|
||||
License and any other pertinent obligations, then as a consequence you
|
||||
may not distribute the Program at all. For example, if a patent
|
||||
license would not permit royalty-free redistribution of the Program by
|
||||
all those who receive copies directly or indirectly through you, then
|
||||
the only way you could satisfy both it and this License would be to
|
||||
refrain entirely from distribution of the Program.
|
||||
|
||||
If any portion of this section is held invalid or unenforceable under
|
||||
any particular circumstance, the balance of the section is intended to
|
||||
apply and the section as a whole is intended to apply in other
|
||||
circumstances.
|
||||
|
||||
It is not the purpose of this section to induce you to infringe any
|
||||
patents or other property right claims or to contest validity of any
|
||||
such claims; this section has the sole purpose of protecting the
|
||||
integrity of the free software distribution system, which is
|
||||
implemented by public license practices. Many people have made
|
||||
generous contributions to the wide range of software distributed
|
||||
through that system in reliance on consistent application of that
|
||||
system; it is up to the author/donor to decide if he or she is willing
|
||||
to distribute software through any other system and a licensee cannot
|
||||
impose that choice.
|
||||
|
||||
This section is intended to make thoroughly clear what is believed to
|
||||
be a consequence of the rest of this License.
|
||||
|
||||
8. If the distribution and/or use of the Program is restricted in
|
||||
certain countries either by patents or by copyrighted interfaces, the
|
||||
original copyright holder who places the Program under this License
|
||||
may add an explicit geographical distribution limitation excluding
|
||||
those countries, so that distribution is permitted only in or among
|
||||
countries not thus excluded. In such case, this License incorporates
|
||||
the limitation as if written in the body of this License.
|
||||
|
||||
9. The Free Software Foundation may publish revised and/or new versions
|
||||
of the General Public License from time to time. Such new versions will
|
||||
be similar in spirit to the present version, but may differ in detail to
|
||||
address new problems or concerns.
|
||||
|
||||
Each version is given a distinguishing version number. If the Program
|
||||
specifies a version number of this License which applies to it and "any
|
||||
later version", you have the option of following the terms and conditions
|
||||
either of that version or of any later version published by the Free
|
||||
Software Foundation. If the Program does not specify a version number of
|
||||
this License, you may choose any version ever published by the Free Software
|
||||
Foundation.
|
||||
|
||||
10. If you wish to incorporate parts of the Program into other free
|
||||
programs whose distribution conditions are different, write to the author
|
||||
to ask for permission. For software which is copyrighted by the Free
|
||||
Software Foundation, write to the Free Software Foundation; we sometimes
|
||||
make exceptions for this. Our decision will be guided by the two goals
|
||||
of preserving the free status of all derivatives of our free software and
|
||||
of promoting the sharing and reuse of software generally.
|
||||
|
||||
NO WARRANTY
|
||||
|
||||
11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
|
||||
FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
|
||||
OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
|
||||
PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
|
||||
OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
|
||||
TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
|
||||
PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
|
||||
REPAIR OR CORRECTION.
|
||||
|
||||
12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
|
||||
WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
|
||||
REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
|
||||
INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
|
||||
OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
|
||||
TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
|
||||
YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
|
||||
PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
|
||||
POSSIBILITY OF SUCH DAMAGES.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
156
Makefile.in
Normal file
156
Makefile.in
Normal file
|
|
@ -0,0 +1,156 @@
|
|||
all: apps firejail.1 firemon.1 firejail-profile.5 firejail-login.5
|
||||
MYLIBS = src/lib
|
||||
APPS = src/firejail src/firemon src/libtrace src/ftee
|
||||
|
||||
datarootdir=@datarootdir@
|
||||
PREFIX=@prefix@
|
||||
prefix=@prefix@
|
||||
VERSION=@PACKAGE_VERSION@
|
||||
NAME=@PACKAGE_NAME@
|
||||
PACKAGE_TARNAME=@PACKAGE_TARNAME@
|
||||
DOCDIR=@docdir@
|
||||
|
||||
|
||||
.PHONY: mylibs $(MYLIBS)
|
||||
mylibs: $(MYLIBS)
|
||||
$(MYLIBS):
|
||||
$(MAKE) -C $@
|
||||
|
||||
.PHONY: apps $(APPS)
|
||||
apps: $(APPS)
|
||||
$(APPS): $(MYLIBS)
|
||||
$(MAKE) -C $@
|
||||
|
||||
firemon.1: src/man/firemon.txt
|
||||
./mkman.sh $(VERSION) src/man/firemon.txt firemon.1
|
||||
firejail.1: src/man/firejail.txt
|
||||
./mkman.sh $(VERSION) src/man/firejail.txt firejail.1
|
||||
firejail-profile.5: src/man/firejail-profile.txt
|
||||
./mkman.sh $(VERSION) src/man/firejail-profile.txt firejail-profile.5
|
||||
firejail-login.5: src/man/firejail-login.txt
|
||||
./mkman.sh $(VERSION) src/man/firejail-login.txt firejail-login.5
|
||||
|
||||
clean:;
|
||||
for dir in $(APPS); do \
|
||||
$(MAKE) -C $$dir clean; \
|
||||
done
|
||||
for dir in $(MYLIBS); do \
|
||||
$(MAKE) -C $$dir clean; \
|
||||
done
|
||||
rm -f firejail.1 firejail.1.gz firemon.1 firemon.1.gz firejail-profile.5 firejail-profile.5.gz firejail-login.5 firejail-login.5.gz
|
||||
|
||||
distclean: clean
|
||||
for dir in $(APPS); do \
|
||||
$(MAKE) -C $$dir distclean; \
|
||||
done
|
||||
for dir in $(MYLIBS); do \
|
||||
$(MAKE) -C $$dir distclean; \
|
||||
done
|
||||
rm -fr Makefile autom4te.cache config.log config.status config.h
|
||||
|
||||
install: all
|
||||
# firejail executable
|
||||
strip src/firejail/firejail
|
||||
mkdir -p $(DESTDIR)/$(PREFIX)/bin
|
||||
install -c -m 0755 src/firejail/firejail $(DESTDIR)/$(PREFIX)/bin/.
|
||||
chmod u+s $(DESTDIR)/$(PREFIX)/bin/firejail
|
||||
# firemon executable
|
||||
strip src/firemon/firemon
|
||||
install -c -m 0755 src/firemon/firemon $(DESTDIR)/$(PREFIX)/bin/.
|
||||
# libraries and plugins
|
||||
strip src/libtrace/libtrace.so
|
||||
mkdir -p $(DESTDIR)/$(PREFIX)/lib/firejail
|
||||
install -c -m 0644 src/libtrace/libtrace.so $(DESTDIR)/$(PREFIX)/lib/firejail/.
|
||||
strip src/ftee/ftee
|
||||
install -c -m 0755 src/ftee/ftee $(DESTDIR)/$(PREFIX)/lib/firejail/.
|
||||
install -c -m 0755 src/fshaper/fshaper.sh $(DESTDIR)/$(PREFIX)/lib/firejail/.
|
||||
# documents
|
||||
mkdir -p $(DESTDIR)/$(DOCDIR)
|
||||
install -c -m 0644 COPYING $(DESTDIR)/$(DOCDIR)/.
|
||||
install -c -m 0644 README $(DESTDIR)/$(DOCDIR)/.
|
||||
install -c -m 0644 RELNOTES $(DESTDIR)/$(DOCDIR)/.
|
||||
# etc files
|
||||
mkdir -p $(DESTDIR)/etc/firejail
|
||||
install -c -m 0644 etc/audacious.profile $(DESTDIR)/etc/firejail/.
|
||||
install -c -m 0644 etc/clementine.profile $(DESTDIR)/etc/firejail/.
|
||||
install -c -m 0644 etc/gnome-mplayer.profile $(DESTDIR)/etc/firejail/.
|
||||
install -c -m 0644 etc/rhythmbox.profile $(DESTDIR)/etc/firejail/.
|
||||
install -c -m 0644 etc/totem.profile $(DESTDIR)/etc/firejail/.
|
||||
install -c -m 0644 etc/firefox.profile $(DESTDIR)/etc/firejail/.
|
||||
install -c -m 0644 etc/icedove.profile $(DESTDIR)/etc/firejail/.
|
||||
install -c -m 0644 etc/iceweasel.profile $(DESTDIR)/etc/firejail/.
|
||||
install -c -m 0644 etc/midori.profile $(DESTDIR)/etc/firejail/.
|
||||
install -c -m 0644 etc/evince.profile $(DESTDIR)/etc/firejail/.
|
||||
install -c -m 0644 etc/chromium-browser.profile $(DESTDIR)/etc/firejail/.
|
||||
install -c -m 0644 etc/chromium.profile $(DESTDIR)/etc/firejail/.
|
||||
install -c -m 0644 etc/disable-mgmt.inc $(DESTDIR)/etc/firejail/.
|
||||
install -c -m 0644 etc/disable-secret.inc $(DESTDIR)/etc/firejail/.
|
||||
install -c -m 0644 etc/disable-common.inc $(DESTDIR)/etc/firejail/.
|
||||
install -c -m 0644 etc/dropbox.profile $(DESTDIR)/etc/firejail/.
|
||||
install -c -m 0644 etc/opera.profile $(DESTDIR)/etc/firejail/.
|
||||
install -c -m 0644 etc/thunderbird.profile $(DESTDIR)/etc/firejail/.
|
||||
install -c -m 0644 etc/transmission-gtk.profile $(DESTDIR)/etc/firejail/.
|
||||
install -c -m 0644 etc/transmission-qt.profile $(DESTDIR)/etc/firejail/.
|
||||
install -c -m 0644 etc/vlc.profile $(DESTDIR)/etc/firejail/.
|
||||
install -c -m 0644 etc/deluge.profile $(DESTDIR)/etc/firejail/.
|
||||
install -c -m 0644 etc/qbittorrent.profile $(DESTDIR)/etc/firejail/.
|
||||
install -c -m 0644 etc/generic.profile $(DESTDIR)/etc/firejail/.
|
||||
install -c -m 0644 etc/pidgin.profile $(DESTDIR)/etc/firejail/.
|
||||
install -c -m 0644 etc/xchat.profile $(DESTDIR)/etc/firejail/.
|
||||
install -c -m 0644 etc/empathy.profile $(DESTDIR)/etc/firejail/.
|
||||
install -c -m 0644 etc/server.profile $(DESTDIR)/etc/firejail/.
|
||||
install -c -m 0644 etc/icecat.profile $(DESTDIR)/etc/firejail/.
|
||||
install -c -m 0644 etc/quassel.profile $(DESTDIR)/etc/firejail/.
|
||||
install -c -m 0644 etc/deadbeef.profile $(DESTDIR)/etc/firejail/.
|
||||
install -c -m 0644 etc/filezilla.profile $(DESTDIR)/etc/firejail/.
|
||||
bash -c "if [ ! -f /etc/firejail/login.users ]; then install -c -m 0644 etc/login.users $(DESTDIR)/etc/firejail/.; fi;"
|
||||
# man pages
|
||||
rm -f firejail.1.gz
|
||||
gzip -9n firejail.1
|
||||
rm -f firemon.1.gz
|
||||
gzip -9n firemon.1
|
||||
rm -f firejail-profile.5.gz
|
||||
gzip -9n firejail-profile.5
|
||||
rm -f firejail-login.5.gz
|
||||
gzip -9n firejail-login.5
|
||||
mkdir -p $(DESTDIR)/$(PREFIX)/share/man/man1
|
||||
install -c -m 0644 firejail.1.gz $(DESTDIR)/$(PREFIX)/share/man/man1/.
|
||||
install -c -m 0644 firemon.1.gz $(DESTDIR)/$(PREFIX)/share/man/man1/.
|
||||
mkdir -p $(DESTDIR)/$(PREFIX)/share/man/man5
|
||||
install -c -m 0644 firejail-profile.5.gz $(DESTDIR)/$(PREFIX)/share/man/man5/.
|
||||
install -c -m 0644 firejail-login.5.gz $(DESTDIR)/$(PREFIX)/share/man/man5/.
|
||||
rm -f firejail.1.gz firemon.1.gz firejail-profile.5.gz firejail-login.5.gz
|
||||
# bash completion
|
||||
mkdir -p $(DESTDIR)/$(PREFIX)/share/bash-completion/completions
|
||||
install -c -m 0644 etc/firejail.bash_completion $(DESTDIR)/$(PREFIX)/share/bash-completion/completions/firejail
|
||||
install -c -m 0644 etc/firemon.bash_completion $(DESTDIR)/$(PREFIX)/share/bash-completion/completions/firemon
|
||||
|
||||
uninstall:;
|
||||
rm -f $(DESTDIR)/$(PREFIX)/bin/firejail
|
||||
rm -f $(DESTDIR)/$(PREFIX)/bin/firemon
|
||||
rm -fr $(DESTDIR)/$(PREFIX)/lib/firejail
|
||||
rm -fr $(DESTDIR)/$(PREFIX)/share/doc/firejail
|
||||
rm -f $(DESTDIR)/$(PREFIX)/share/man/man1/firejail.1*
|
||||
rm -f $(DESTDIR)/$(PREFIX)/share/man/man1/firemon.1*
|
||||
rm -f $(DESTDIR)/$(PREFIX)/share/man/man5/firejail-profile.5*
|
||||
rm -f $(DESTDIR)/$(PREFIX)/share/man/man5/firejail-login.5*
|
||||
rm -f $(DESTDIR)/$(PREFIX)/share/bash-completion/completions/firejail
|
||||
rm -f $(DESTDIR)/$(PREFIX)/share/bash-completion/completions/firemon
|
||||
|
||||
dist:
|
||||
make distclean
|
||||
rm -fr $(NAME)-$(VERSION) $(NAME)-$(VERSION).tar.bz2
|
||||
mkdir $(NAME)-$(VERSION)
|
||||
cd $(NAME)-$(VERSION); cp -a ../src .; cp -a ../etc .; cp -a ../platform .; cp -a ../test .; rm -f src/tools/rvtest; rm -fr src/art; cd ..
|
||||
cd $(NAME)-$(VERSION); cp -a ../configure .; cp -a ../configure.ac .; cp -a ../Makefile.in .; cp -a ../install.sh .; cp -a ../mkman.sh .; cp -a ../mkdeb.sh .;cd ..
|
||||
cd $(NAME)-$(VERSION); cp -a ../COPYING .; cp -a ../README .; cp -a ../RELNOTES .; cd ..
|
||||
cd $(NAME)-$(VERSION); rm -fr `find . -name .svn`; rm -fr $(NAME)-$(VERSION); cd ..
|
||||
tar -cjvf $(NAME)-$(VERSION).tar.bz2 $(NAME)-$(VERSION)
|
||||
rm -fr $(NAME)-$(VERSION)
|
||||
|
||||
deb: dist
|
||||
./mkdeb.sh $(NAME) $(VERSION)
|
||||
|
||||
extras: all
|
||||
$(MAKE) -C extras/firetools
|
||||
|
||||
28
README
Normal file
28
README
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
Firejail is a SUID sandbox program that reduces the risk of security
|
||||
breaches by restricting the running environment of untrusted applications
|
||||
using Linux namespaces and seccomp-bpf. It includes sandbox profiles for
|
||||
Iceweasel/Mozilla Firefox, Chromium, Midori, Opera, Evince, Transmission,
|
||||
VLC, Audoacious, Clementine, Rhythmbox, Totem, Deluge and qBittorrent.
|
||||
|
||||
Firejail also expands the restricted shell facility found in bash by adding
|
||||
Linux namespace support. It supports sandboxing specific users upon login.
|
||||
|
||||
Download: http://sourceforge.net/projects/firejail/files/
|
||||
Build and install: ./configure && make && sudo make install
|
||||
Documentation and support: http://firejail.sourceforge.net
|
||||
License: GPL v2
|
||||
|
||||
Firejail Authors:
|
||||
|
||||
netblue30 (netblue30@yahoo.com)
|
||||
Patrick Toomey (http://sourceforge.net/u/ptoomey/profile/)
|
||||
- user namespace implementation, ticket 10
|
||||
Reiner Herrmann - a number of build patches, man page fixes (tickets 11, 12, 13, 19)
|
||||
sshirokov (http://sourceforge.net/u/yshirokov/profile/)
|
||||
- Patch to output "Reading profile" to stderr instead of stdout (ticket 15)
|
||||
Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru>
|
||||
- src/lib/libnetlink.c extracted from iproute2 software package
|
||||
G4JC (http://sourceforge.net/u/gaming4jc/profile/)
|
||||
- ARM support (ticket 17)
|
||||
|
||||
Copyright (C) 2014, 2015 Firejail Authors
|
||||
215
RELNOTES
Normal file
215
RELNOTES
Normal file
|
|
@ -0,0 +1,215 @@
|
|||
firejail (0.9.28) baseline; urgency=low
|
||||
* network scanning, --scan option
|
||||
* interface MAC address support, --mac option
|
||||
* IP address range, --iprange option
|
||||
* traffic shaping, --bandwidth option
|
||||
* reworked printing of network status at startup
|
||||
* man pages rework
|
||||
* added firejail-login man page
|
||||
* added GNU Icecat, FileZilla, Pidgin, XChat, Empathy, DeaDBeeF default
|
||||
profiles
|
||||
* added an /etc/firejail/disable-common.inc file to hold common directory
|
||||
blacklists
|
||||
* blacklist Opera and Chrome/Chromium config directories in profile files
|
||||
* support noroot option for profile files
|
||||
* enabled noroot in default profile files
|
||||
* bugfixes
|
||||
-- netblue30 <netblue30@yahoo.com> Sat, 1 Aug 2015 08:00:00 -0500
|
||||
|
||||
firejail (0.9.26) baseline; urgency=low
|
||||
* private dev directory
|
||||
* private.keep option for whitelisting home files in a new private directory
|
||||
* user namespaces support, noroot option
|
||||
* added Deluge and qBittorent profiles
|
||||
* bugfixes
|
||||
-- netblue30 <netblue30@yahoo.com> Thu, 30 Apr 2015 08:00:00 -0500
|
||||
|
||||
|
||||
firejail (0.9.24) baseline; urgency=low
|
||||
* whitelist and blacklist seccomp filters
|
||||
* doubledash option
|
||||
* --shell=none support
|
||||
* netfilter file support in profile files
|
||||
* dns server support in profile files
|
||||
* added --dns.print option
|
||||
* added default profiles for Audacious, Clementine, Gnome-MPlayer, Rhythmbox and Totem.
|
||||
* added --caps.drop=all in default profiles
|
||||
* new syscalls in default seccomp filter: sysfs, sysctl, adjtimex, kcmp
|
||||
* clock_adjtime, lookup_dcookie, perf_event_open, fanotify_init
|
||||
* Bugfix: using /proc/sys/kernel/pid_max for the max number of pids
|
||||
* two build patches from Reiner Herman (tickets 11, 12)
|
||||
* man page patch from Reiner Herman (ticket 13)
|
||||
* output patch (ticket 15) from sshirokov
|
||||
|
||||
-- netblue30 <netblue30@yahoo.com> Sun, 5 Apr 2015 08:00:00 -0500
|
||||
|
||||
firejail (0.9.22) baseline; urgency=low
|
||||
* Replaced --noip option with --ip=none
|
||||
* Container stdout logging and log rotation
|
||||
* Added process_vm_readv, process_vm_writev and mknod to
|
||||
* default seccomp blacklist
|
||||
* Added CAP_MKNOD to default caps blacklist
|
||||
* Blacklist and whitelist custom Linux capabilities filters
|
||||
* macvlan device driver support for --net option
|
||||
* DNS server support, --dns option
|
||||
* Netfilter support
|
||||
* Monitor network statistics, --netstats option
|
||||
* Added profile for Mozilla Thunderbird/Icedove
|
||||
* - --overlay support for Linux kernels 3.18+
|
||||
* Bugfix: preserve .Xauthority file in private mode (test with ssh -X)
|
||||
* Bugfix: check uid/gid for cgroup
|
||||
|
||||
-- netblue30 <netblue30@yahoo.com> Mon, 9 Mar 2015 09:00:00 -0500
|
||||
|
||||
firejail (0.9.20) baseline; urgency=low
|
||||
* utmp, btmp and wtmp enhancements
|
||||
* create empty /var/log/wtmp and /var/log/btmp files in sandbox
|
||||
* generate a new /var/run/utmp file in sandbox
|
||||
* CPU affinity, --cpu option
|
||||
* Linux control groups support, --cgroup option
|
||||
* Opera web browser support
|
||||
* VLC support
|
||||
* Added "empty" attribute to seccomp command to remove the default
|
||||
* syscall list form seccomp blacklist
|
||||
* Added --nogroups option to disable supplementary groups for regular
|
||||
* users. root user always runs without supplementary groups.
|
||||
* firemon enhancements
|
||||
* display the command that started the sandbox
|
||||
* added --caps option to display capabilities for all sandboxes
|
||||
* added --cgroup option to display the control groups for all sandboxes
|
||||
* added --cpu option to display CPU affinity for all sandboxes
|
||||
* added --seccomp option to display seccomp setting for all sandboxes
|
||||
* New compile time options: --disable-chroot, --disable-bind
|
||||
* bugfixes
|
||||
|
||||
-- netblue30 <netblue30@yahoo.com> Mon, 02 Feb 2015 08:00:00 -0500
|
||||
|
||||
firejail (0.9.18) baseline; urgency=low
|
||||
* Support for tracing system, setuid, setgid, setfsuid, setfsgid syscalls
|
||||
* Support for tracing setreuid, setregid, setresuid, setresguid syscalls
|
||||
* Added profiles for transmission-gtk and transmission-qt
|
||||
* bugfixes
|
||||
|
||||
-- netblue30 <netblue30@yahoo.com> Fri, 25 Dec 2014 10:00:00 -0500
|
||||
|
||||
firejail (0.9.16) baseline; urgency=low
|
||||
* Configurable private home directory
|
||||
* Configurable default user shell
|
||||
* Software configuration support for --docdir and DESTDIR
|
||||
* Profile file support for include, caps, seccomp and private keywords
|
||||
* Dropbox profile file
|
||||
* Linux capabilities and seccomp filters enabled by default for Firefox,
|
||||
Midori, Evince and Dropbox
|
||||
* bugfixes
|
||||
|
||||
-- netblue30 <netblue30@yahoo.com> Tue, 4 Nov 2014 10:00:00 -0500
|
||||
|
||||
firejail (0.9.14) baseline; urgency=low
|
||||
* Linux capabilities and seccomp filters are automatically enabled in
|
||||
chroot mode (--chroot option) if the sandbox is started as regular user
|
||||
* Added support for user defined seccomp blacklists
|
||||
* Added syscall trace support
|
||||
* Added --tmpfs option
|
||||
* Added --balcklist option
|
||||
* Added --read-only option
|
||||
* Added --bind option
|
||||
* Logging enhancements
|
||||
* --overlay option was reactivated
|
||||
* Added firemon support to print the ARP table for each sandbox
|
||||
* Added firemon support to print the route table for each sandbox
|
||||
* Added firemon support to print interface information for each sandbox
|
||||
* bugfixes
|
||||
|
||||
-- netblue30 <netblue30@yahoo.com> Tue, 15 Oct 2014 10:00:00 -0500
|
||||
|
||||
firejail (0.9.12.2) baseline; urgency=low
|
||||
* Fix for pulseaudio problems
|
||||
* --overlay option was temporarily disabled in this build
|
||||
|
||||
-- netblue30 <netblue30@yahoo.com> Mon, 29 Sept 2014 07:00:00 -0500
|
||||
|
||||
firejail (0.9.12.1) baseline; urgency=low
|
||||
* Fix for pulseaudio problems
|
||||
* --overlay option was temporarily disabled in this build
|
||||
|
||||
-- netblue30 <netblue30@yahoo.com> Mon, 22 Sept 2014 09:00:00 -0500
|
||||
|
||||
firejail (0.9.12) baseline; urgency=low
|
||||
* Added capabilities support
|
||||
* Added support for CentOS 7
|
||||
* bugfixes
|
||||
|
||||
-- netblue30 <netblue30@yahoo.com> Mon, 15 Sept 2014 10:00:00 -0500
|
||||
|
||||
firejail (0.9.10) baseline; urgency=low
|
||||
* Disable /proc/kcore, /proc/kallsyms, /dev/port, /boot
|
||||
* Fixed --top option CPU utilization calculation
|
||||
* Implemented --tree option in firejail and firemon
|
||||
* Implemented --join=name option
|
||||
* Implemented --shutdown option
|
||||
* Preserve the current working directory if possible
|
||||
* Cppcheck and clang errors cleanup
|
||||
* Added a Chromium web browser profile
|
||||
|
||||
-- netblue30 <netblue30@yahoo.com> Thu, 28 Aug 2014 07:00:00 -0500
|
||||
|
||||
firejail (0.9.8.1) baseline; urgency=low
|
||||
* FIxed a number of bugs introduced in 0.9.8
|
||||
|
||||
-- netblue30 <netblue30@yahoo.com> Fri, 25 Jul 2014 07:25:00 -0500
|
||||
|
||||
firejail (0.9.8) baseline; urgency=low
|
||||
* Implemented nowrap mode for firejail --list command option
|
||||
* Added --top option in both firejail and firemon
|
||||
* seccomp filter support
|
||||
* Added pid support for firemon
|
||||
* bugfixes
|
||||
|
||||
-- netblue30 <netblue30@yahoo.com> Tue, 24 Jul 2014 08:51:00 -0500
|
||||
|
||||
firejail (0.9.6) baseline; urgency=low
|
||||
|
||||
* Mounting tmpfs on top of /var/log, required by several server programs
|
||||
* Server fixes for /var/lib and /var/cache
|
||||
* Private mode fixes
|
||||
* csh and zsh default shell support
|
||||
* Chroot mode fixes
|
||||
* Added support for lighttpd, isc-dhcp-server, apache2, nginx, snmpd,
|
||||
|
||||
-- netblue30 <netblue30@yahoo.com> Sat, 7 Jun 2014 09:00:00 -0500
|
||||
|
||||
firejail (0.9.4) baseline; urgency=low
|
||||
|
||||
* Fixed resolv.conf on Ubuntu systems using DHCP
|
||||
* Fixed resolv.conf on Debian systems using resolvconf package
|
||||
* Fixed /var/lock directory
|
||||
* Fixed /var/tmp directory
|
||||
* Fixed symbolic links in profile files
|
||||
* Added profiles for evince, midori
|
||||
|
||||
-- netblue30 <netblue30@yahoo.com> Sun, 4 May 2014 08:00:00 -0500
|
||||
|
||||
firejail (0.9.2) baseline; urgency=low
|
||||
|
||||
* Checking IP address passed with --ip option using ARP; exit if the address
|
||||
is already present
|
||||
* Using a lock file during ARP address assignment in order to removed a race
|
||||
condition.
|
||||
* Several fixes to --private option; it also mounts a tmpfs filesystem on top
|
||||
of /tmp
|
||||
* Added user access check for profile file
|
||||
* Added --defaultgw option
|
||||
* Added support of --noip option; it is necessary for DHCP setups
|
||||
* Added syslog support
|
||||
* Added support for "tmpfs" and "read-only" profile commands
|
||||
* Added an expect-based testing framework for the project
|
||||
* Added bash completion support
|
||||
* Added support for multiple networks
|
||||
|
||||
-- netblue30 <netblue30@yahoo.com> Fri, 25 Apr 2014 08:00:00 -0500
|
||||
|
||||
firejail (0.9) baseline; urgency=low
|
||||
|
||||
* First beta version
|
||||
|
||||
-- netblue30 <netblue30@yahoo.com> Sat, 12 Apr 2014 09:00:00 -0500
|
||||
52
configure.ac
Normal file
52
configure.ac
Normal file
|
|
@ -0,0 +1,52 @@
|
|||
AC_PREREQ([2.68])
|
||||
AC_INIT(firejail, 0.9.28, netblue30@yahoo.com, , http://firejail.sourceforge.net)
|
||||
AC_CONFIG_SRCDIR([src/firejail/main.c])
|
||||
#AC_CONFIG_HEADERS([config.h])
|
||||
|
||||
|
||||
AC_PROG_CC
|
||||
#AC_PROG_CXX
|
||||
AC_PROG_INSTALL
|
||||
AC_PROG_RANLIB
|
||||
|
||||
HAVE_SECCOMP=""
|
||||
AC_ARG_ENABLE([seccomp],
|
||||
AS_HELP_STRING([--disable-seccomp], [Disable seccomp]))
|
||||
AS_IF([test "x$enable_seccomp" != "xno"], [
|
||||
HAVE_SECCOMP="-DHAVE_SECCOMP"
|
||||
AC_SUBST(HAVE_SECCOMP)
|
||||
])
|
||||
|
||||
HAVE_CHROOT=""
|
||||
AC_ARG_ENABLE([chroot],
|
||||
AS_HELP_STRING([--disable-chroot], [Disable chroot]))
|
||||
AS_IF([test "x$enable_chroot" != "xno"], [
|
||||
HAVE_CHROOT="-DHAVE_CHROOT"
|
||||
AC_SUBST(HAVE_CHROOT)
|
||||
])
|
||||
|
||||
HAVE_BIND=""
|
||||
AC_ARG_ENABLE([bind],
|
||||
AS_HELP_STRING([--disable-bind], [Disable bind]))
|
||||
AS_IF([test "x$enable_bind" != "xno"], [
|
||||
HAVE_BIND="-DHAVE_BIND"
|
||||
AC_SUBST(HAVE_BIND)
|
||||
])
|
||||
|
||||
|
||||
# checking pthread library
|
||||
AC_CHECK_LIB([pthread], [main], [], AC_MSG_ERROR([*** POSIX thread support not installed ***]))
|
||||
AC_CHECK_HEADER(pthread.h,,AC_MSG_ERROR([*** POSIX thread support not installed ***]))
|
||||
AC_CHECK_HEADER([linux/seccomp.h], HAVE_SECCOMP_H="-DHAVE_SECCOMP_H", HAVE_SECCOMP_H="")
|
||||
AC_SUBST(HAVE_SECCOMP_H)
|
||||
|
||||
AC_OUTPUT(Makefile src/lib/Makefile src/firejail/Makefile src/firemon/Makefile src/libtrace/Makefile src/ftee/Makefile)
|
||||
|
||||
echo
|
||||
echo "Configuration options:"
|
||||
echo " prefix: $prefix"
|
||||
echo " seccomp: $HAVE_SECCOMP"
|
||||
echo " <linux/seccomp.h>: $HAVE_SECCOMP_H"
|
||||
echo " chroot: $HAVE_CHROOT"
|
||||
echo " bind: $HAVE_BIND"
|
||||
echo
|
||||
8
etc/audacious.profile
Normal file
8
etc/audacious.profile
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
# Audacious profile
|
||||
include /etc/firejail/disable-mgmt.inc
|
||||
include /etc/firejail/disable-secret.inc
|
||||
include /etc/firejail/disable-common.inc
|
||||
caps.drop all
|
||||
seccomp
|
||||
noroot
|
||||
|
||||
3
etc/chromium-browser.profile
Normal file
3
etc/chromium-browser.profile
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
# Chromium browser profile
|
||||
include /etc/firejail/chromium.profile
|
||||
|
||||
7
etc/chromium.profile
Normal file
7
etc/chromium.profile
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
# Chromium browser profile
|
||||
include /etc/firejail/disable-mgmt.inc
|
||||
include /etc/firejail/disable-secret.inc
|
||||
include /etc/firejail/disable-common.inc chromium
|
||||
netfilter
|
||||
|
||||
|
||||
7
etc/clementine.profile
Normal file
7
etc/clementine.profile
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
# Clementine profile
|
||||
include /etc/firejail/disable-mgmt.inc
|
||||
include /etc/firejail/disable-secret.inc
|
||||
include /etc/firejail/disable-common.inc
|
||||
caps.drop all
|
||||
seccomp
|
||||
noroot
|
||||
8
etc/deadbeef.profile
Normal file
8
etc/deadbeef.profile
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
# DeaDBeeF profile
|
||||
include /etc/firejail/disable-mgmt.inc
|
||||
include /etc/firejail/disable-secret.inc
|
||||
include /etc/firejail/disable-common.inc
|
||||
caps.drop all
|
||||
seccomp
|
||||
noroot
|
||||
|
||||
9
etc/deluge.profile
Normal file
9
etc/deluge.profile
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
# deluge profile
|
||||
include /etc/firejail/disable-mgmt.inc
|
||||
include /etc/firejail/disable-secret.inc
|
||||
include /etc/firejail/disable-common.inc
|
||||
caps.drop all
|
||||
seccomp
|
||||
netfilter
|
||||
noroot
|
||||
|
||||
10
etc/disable-common.inc
Normal file
10
etc/disable-common.inc
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
blacklist ${HOME}/.adobe
|
||||
blacklist ${HOME}/.macromedia
|
||||
blacklist ${HOME}/.mozilla
|
||||
blacklist ${HOME}/.icedove
|
||||
blacklist ${HOME}/.thunderbird
|
||||
blacklist ${HOME}/.config/midori
|
||||
blacklist ${HOME}/.config/opera
|
||||
blacklist ${HOME}/.config/chromium
|
||||
blacklist ${HOME}/.config/google-chrome
|
||||
blacklist ${HOME}/.filezilla
|
||||
12
etc/disable-mgmt.inc
Normal file
12
etc/disable-mgmt.inc
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
# system directories
|
||||
blacklist /sbin
|
||||
blacklist /usr/sbin
|
||||
|
||||
# system management
|
||||
blacklist ${PATH}/umount
|
||||
blacklist ${PATH}/mount
|
||||
blacklist ${PATH}/fusermount
|
||||
blacklist ${PATH}/su
|
||||
blacklist ${PATH}/sudo
|
||||
blacklist ${PATH}/xinput
|
||||
blacklist ${PATH}/strace
|
||||
9
etc/disable-secret.inc
Normal file
9
etc/disable-secret.inc
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
# HOME directory
|
||||
blacklist ${HOME}/.ssh
|
||||
tmpfs ${HOME}/.gnome2_private
|
||||
blacklist ${HOME}/.gnome2/keyrings
|
||||
blacklist ${HOME}/kde4/share/apps/kwallet
|
||||
blacklist ${HOME}/kde/share/apps/kwallet
|
||||
blacklist ${HOME}/.pki/nssdb
|
||||
blacklist ${HOME}/.gnupg
|
||||
blacklist ${HOME}/.local/share/recently-used.xbel
|
||||
7
etc/dropbox.profile
Normal file
7
etc/dropbox.profile
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
# dropbox profile
|
||||
include /etc/firejail/disable-mgmt.inc
|
||||
include /etc/firejail/disable-secret.inc
|
||||
include /etc/firejail/disable-common.inc
|
||||
caps
|
||||
seccomp
|
||||
noroot
|
||||
6
etc/empathy.profile
Normal file
6
etc/empathy.profile
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
# Empathy profile
|
||||
include /etc/firejail/disable-mgmt.inc
|
||||
include /etc/firejail/disable-secret.inc
|
||||
include /etc/firejail/disable-common.inc
|
||||
caps.drop all
|
||||
seccomp
|
||||
8
etc/evince.profile
Normal file
8
etc/evince.profile
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
# evince profile
|
||||
include /etc/firejail/disable-mgmt.inc
|
||||
include /etc/firejail/disable-secret.inc
|
||||
include /etc/firejail/disable-common.inc
|
||||
caps.drop all
|
||||
seccomp
|
||||
netfilter
|
||||
noroot
|
||||
10
etc/filezilla.profile
Normal file
10
etc/filezilla.profile
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
# FileZilla profile
|
||||
include /etc/firejail/disable-mgmt.inc
|
||||
include /etc/firejail/disable-secret.inc
|
||||
include /etc/firejail/disable-common.inc .filezilla
|
||||
caps.drop all
|
||||
seccomp
|
||||
noroot
|
||||
netfilter
|
||||
|
||||
|
||||
9
etc/firefox.profile
Normal file
9
etc/firefox.profile
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
# Firejail profile for Mozilla Firefox (Iceweasel in Debian)
|
||||
include /etc/firejail/disable-mgmt.inc
|
||||
include /etc/firejail/disable-secret.inc
|
||||
include /etc/firejail/disable-common.inc .mozilla
|
||||
caps.drop all
|
||||
seccomp
|
||||
netfilter
|
||||
noroot
|
||||
|
||||
86
etc/firejail.bash_completion
Normal file
86
etc/firejail.bash_completion
Normal file
|
|
@ -0,0 +1,86 @@
|
|||
# bash completion for firejail -*- shell-script -*-
|
||||
#********************************************************************
|
||||
# Script based on completions/configure script in bash-completion package in
|
||||
# Debian. The original package is release under GPL v2 license, the webpage is
|
||||
# http://bash-completion.alioth.debian.org
|
||||
#*******************************************************************
|
||||
|
||||
__interfaces(){
|
||||
cut -f 1 -d ':' /proc/net/dev | tail -n +3 | grep -v lo | xargs
|
||||
}
|
||||
|
||||
|
||||
_firejail()
|
||||
{
|
||||
local cur prev words cword split
|
||||
_init_completion -s || return
|
||||
|
||||
case $prev in
|
||||
--help|--version|-debug-caps|--debug-syscalls|--list|--tree|--top|--join|--shutdown)
|
||||
return 0
|
||||
;;
|
||||
--profile)
|
||||
_filedir
|
||||
return 0
|
||||
;;
|
||||
--chroot)
|
||||
_filedir -d
|
||||
return 0
|
||||
;;
|
||||
--cgroup)
|
||||
_filedir -d
|
||||
return 0
|
||||
;;
|
||||
--tmpfs)
|
||||
_filedir
|
||||
return 0
|
||||
;;
|
||||
--blacklist)
|
||||
_filedir
|
||||
return 0
|
||||
;;
|
||||
--read-only)
|
||||
_filedir
|
||||
return 0
|
||||
;;
|
||||
--bind)
|
||||
_filedir
|
||||
return 0
|
||||
;;
|
||||
--private)
|
||||
_filedir
|
||||
return 0
|
||||
;;
|
||||
--shell)
|
||||
_filedir
|
||||
return 0
|
||||
;;
|
||||
--net)
|
||||
comps=$(__interfaces)
|
||||
COMPREPLY=( $(compgen -W '$comps' -- "$cur") )
|
||||
return 0
|
||||
;;
|
||||
esac
|
||||
|
||||
$split && return 0
|
||||
|
||||
# if $COMP_CONFIGURE_HINTS is not null, then completions of the form
|
||||
# --option=SETTING will include 'SETTING' as a contextual hint
|
||||
[[ "$cur" != -* ]] && _filedir && return 0
|
||||
|
||||
if [[ -n $COMP_CONFIGURE_HINTS ]]; then
|
||||
COMPREPLY=( $( compgen -W "$( $1 --help 2>&1 | \
|
||||
awk '/^ --[A-Za-z]/ { print $1; \
|
||||
if ($2 ~ /--[A-Za-z]/) print $2 }' | sed -e 's/[[,].*//g' )" \
|
||||
-- "$cur" ) )
|
||||
[[ $COMPREPLY == *=* ]] && compopt -o nospace
|
||||
else
|
||||
COMPREPLY=( $( compgen -W '$( _parse_help "$1" )' -- "$cur" ) )
|
||||
[[ $COMPREPLY == *= ]] && compopt -o nospace
|
||||
fi
|
||||
|
||||
} &&
|
||||
complete -F _firejail firejail
|
||||
|
||||
|
||||
|
||||
39
etc/firemon.bash_completion
Normal file
39
etc/firemon.bash_completion
Normal file
|
|
@ -0,0 +1,39 @@
|
|||
# bash completion for firemon -*- shell-script -*-
|
||||
#********************************************************************
|
||||
# Script based on completions/configure script in bash-completion package in
|
||||
# Debian. The original package is release under GPL v2 license, the webpage is
|
||||
# http://bash-completion.alioth.debian.org
|
||||
#*******************************************************************
|
||||
|
||||
_firemon()
|
||||
{
|
||||
local cur prev words cword split
|
||||
_init_completion -s || return
|
||||
|
||||
case $prev in
|
||||
--help|--version)
|
||||
return
|
||||
;;
|
||||
esac
|
||||
|
||||
$split && return 0
|
||||
|
||||
# if $COMP_CONFIGURE_HINTS is not null, then completions of the form
|
||||
# --option=SETTING will include 'SETTING' as a contextual hint
|
||||
[[ "$cur" != -* ]] && return 0
|
||||
|
||||
if [[ -n $COMP_CONFIGURE_HINTS ]]; then
|
||||
COMPREPLY=( $( compgen -W "$( $1 --help 2>&1 | \
|
||||
awk '/^ --[A-Za-z]/ { print $1; \
|
||||
if ($2 ~ /--[A-Za-z]/) print $2 }' | sed -e 's/[[,].*//g' )" \
|
||||
-- "$cur" ) )
|
||||
[[ $COMPREPLY == *=* ]] && compopt -o nospace
|
||||
else
|
||||
COMPREPLY=( $( compgen -W '$( _parse_help "$1" )' -- "$cur" ) )
|
||||
[[ $COMPREPLY == *= ]] && compopt -o nospace
|
||||
fi
|
||||
} &&
|
||||
complete -F _firemon firemon
|
||||
|
||||
|
||||
|
||||
41
etc/generic.profile
Normal file
41
etc/generic.profile
Normal file
|
|
@ -0,0 +1,41 @@
|
|||
################################
|
||||
# Generic profile based on Firefox profile
|
||||
################################
|
||||
#include /etc/firejail/disable-mgmt.inc
|
||||
# system directories
|
||||
blacklist /sbin
|
||||
blacklist /usr/sbin
|
||||
# system management
|
||||
blacklist ${PATH}/umount
|
||||
blacklist ${PATH}/mount
|
||||
blacklist ${PATH}/fusermount
|
||||
blacklist ${PATH}/su
|
||||
blacklist ${PATH}/sudo
|
||||
blacklist ${PATH}/xinput
|
||||
blacklist ${PATH}/strace
|
||||
|
||||
#include /etc/firejail/disable-secret.inc
|
||||
# HOME directory
|
||||
blacklist ${HOME}/.ssh
|
||||
tmpfs ${HOME}/.gnome2_private
|
||||
blacklist ${HOME}/.gnome2/keyrings
|
||||
blacklist ${HOME}/kde4/share/apps/kwallet
|
||||
blacklist ${HOME}/kde/share/apps/kwallet
|
||||
blacklist ${HOME}/.pki/nssdb
|
||||
blacklist ${HOME}/.gnupg
|
||||
blacklist ${HOME}/.local/share/recently-used.xbel
|
||||
|
||||
blacklist ${HOME}/.adobe
|
||||
blacklist ${HOME}/.macromedia
|
||||
blacklist ${HOME}/.mozilla
|
||||
blacklist ${HOME}/.icedove
|
||||
blacklist ${HOME}/.thunderbird
|
||||
blacklist ${HOME}/.config/opera
|
||||
blacklist ${HOME}/.config/chromium
|
||||
blacklist ${HOME}/.config/google-chrome
|
||||
|
||||
caps.drop all
|
||||
seccomp
|
||||
netfilter
|
||||
noroot
|
||||
|
||||
7
etc/gnome-mplayer.profile
Normal file
7
etc/gnome-mplayer.profile
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
# GNOME MPlayer profile
|
||||
include /etc/firejail/disable-mgmt.inc
|
||||
include /etc/firejail/disable-secret.inc
|
||||
include /etc/firejail/disable-common.inc
|
||||
caps.drop all
|
||||
seccomp
|
||||
noroot
|
||||
2
etc/icecat.profile
Normal file
2
etc/icecat.profile
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
# Firejail profile for GNU Icecat
|
||||
include /etc/firejail/firefox.profile
|
||||
3
etc/icedove.profile
Normal file
3
etc/icedove.profile
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
# Firejail profile for Mozilla Thunderbird (Icedove in Debian)
|
||||
include /etc/firejail/thunderbird.profile
|
||||
|
||||
2
etc/iceweasel.profile
Normal file
2
etc/iceweasel.profile
Normal file
|
|
@ -0,0 +1,2 @@
|
|||
# Firejail profile for Mozilla Firefox (Iceweasel in Debian)
|
||||
include /etc/firejail/firefox.profile
|
||||
14
etc/login.users
Normal file
14
etc/login.users
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
# /etc/firejail/login.users - restricted user shell configuration
|
||||
#
|
||||
# Each user entry consists of a user name and firejail
|
||||
# program arguments:
|
||||
#
|
||||
# user name: arguments
|
||||
#
|
||||
# For example:
|
||||
#
|
||||
# netblue:--debug --net=none
|
||||
#
|
||||
# The extra arguments are inserted into program command line if firejail
|
||||
# was started as a login shell.
|
||||
|
||||
9
etc/midori.profile
Normal file
9
etc/midori.profile
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
# Midory browser profile
|
||||
include /etc/firejail/disable-mgmt.inc
|
||||
include /etc/firejail/disable-secret.inc
|
||||
include /etc/firejail/disable-common.inc midori
|
||||
caps.drop all
|
||||
seccomp
|
||||
netfilter
|
||||
noroot
|
||||
|
||||
8
etc/opera.profile
Normal file
8
etc/opera.profile
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
# Chromium browser profile
|
||||
include /etc/firejail/disable-mgmt.inc
|
||||
include /etc/firejail/disable-secret.inc
|
||||
include /etc/firejail/disable-common.inc opera
|
||||
netfilter
|
||||
noroot
|
||||
|
||||
|
||||
7
etc/pidgin.profile
Normal file
7
etc/pidgin.profile
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
# Pidgin profile
|
||||
include /etc/firejail/disable-mgmt.inc
|
||||
include /etc/firejail/disable-secret.inc
|
||||
include /etc/firejail/disable-common.inc
|
||||
caps.drop all
|
||||
seccomp
|
||||
noroot
|
||||
9
etc/qbittorrent.profile
Normal file
9
etc/qbittorrent.profile
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
# abittorrent profile
|
||||
include /etc/firejail/disable-mgmt.inc
|
||||
include /etc/firejail/disable-secret.inc
|
||||
include /etc/firejail/disable-common.inc
|
||||
caps.drop all
|
||||
seccomp
|
||||
netfilter
|
||||
noroot
|
||||
|
||||
7
etc/quassel.profile
Normal file
7
etc/quassel.profile
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
# Quassel IRC profile
|
||||
include /etc/firejail/disable-mgmt.inc
|
||||
include /etc/firejail/disable-secret.inc
|
||||
include /etc/firejail/disable-common.inc
|
||||
caps.drop all
|
||||
seccomp
|
||||
noroot
|
||||
7
etc/rhythmbox.profile
Normal file
7
etc/rhythmbox.profile
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
# Rhythmbox profile
|
||||
include /etc/firejail/disable-mgmt.inc
|
||||
include /etc/firejail/disable-secret.inc
|
||||
include /etc/firejail/disable-common.inc
|
||||
caps.drop all
|
||||
seccomp
|
||||
noroot
|
||||
6
etc/server.profile
Normal file
6
etc/server.profile
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
# generic server profile
|
||||
include /etc/firejail/disable-mgmt.inc sbin
|
||||
private
|
||||
private-dev
|
||||
seccomp
|
||||
|
||||
9
etc/thunderbird.profile
Normal file
9
etc/thunderbird.profile
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
# Firejail profile for Mozilla Thunderbird (Icedove in Debian)
|
||||
include /etc/firejail/disable-mgmt.inc
|
||||
include /etc/firejail/disable-secret.inc
|
||||
include /etc/firejail/disable-common.inc thunderbird icedove
|
||||
caps.drop all
|
||||
seccomp
|
||||
netfilter
|
||||
noroot
|
||||
|
||||
7
etc/totem.profile
Normal file
7
etc/totem.profile
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
# Totem profile
|
||||
include /etc/firejail/disable-mgmt.inc
|
||||
include /etc/firejail/disable-secret.inc
|
||||
include /etc/firejail/disable-common.inc
|
||||
caps.drop all
|
||||
seccomp
|
||||
noroot
|
||||
9
etc/transmission-gtk.profile
Normal file
9
etc/transmission-gtk.profile
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
# transmission-gtk profile
|
||||
include /etc/firejail/disable-mgmt.inc
|
||||
include /etc/firejail/disable-secret.inc
|
||||
include /etc/firejail/disable-common.inc
|
||||
caps.drop all
|
||||
seccomp
|
||||
netfilter
|
||||
noroot
|
||||
|
||||
9
etc/transmission-qt.profile
Normal file
9
etc/transmission-qt.profile
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
# transmission-qt profile
|
||||
include /etc/firejail/disable-mgmt.inc
|
||||
include /etc/firejail/disable-secret.inc
|
||||
include /etc/firejail/disable-common.inc
|
||||
caps.drop all
|
||||
seccomp
|
||||
netfilter
|
||||
noroot
|
||||
|
||||
7
etc/vlc.profile
Normal file
7
etc/vlc.profile
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
# VLC profile
|
||||
include /etc/firejail/disable-mgmt.inc
|
||||
include /etc/firejail/disable-secret.inc
|
||||
include /etc/firejail/disable-common.inc
|
||||
caps.drop all
|
||||
seccomp
|
||||
noroot
|
||||
7
etc/xchat.profile
Normal file
7
etc/xchat.profile
Normal file
|
|
@ -0,0 +1,7 @@
|
|||
# XChat profile
|
||||
include /etc/firejail/disable-mgmt.inc
|
||||
include /etc/firejail/disable-secret.inc
|
||||
include /etc/firejail/disable-common.inc
|
||||
caps.drop all
|
||||
seccomp
|
||||
noroot
|
||||
2
install.sh
Executable file
2
install.sh
Executable file
|
|
@ -0,0 +1,2 @@
|
|||
#!/bin/bash
|
||||
echo "installing..."
|
||||
96
mkdeb.sh
Executable file
96
mkdeb.sh
Executable file
|
|
@ -0,0 +1,96 @@
|
|||
#!/bin/bash
|
||||
|
||||
# a code archive should already be available
|
||||
|
||||
TOP=`pwd`
|
||||
CODE_ARCHIVE="$1-$2.tar.bz2"
|
||||
CODE_DIR="$1-$2"
|
||||
INSTALL_DIR=$TOP
|
||||
INSTALL_DIR+="/debian/usr"
|
||||
DEBIAN_CTRL_DIR=$TOP
|
||||
DEBIAN_CTRL_DIR+="/debian/DEBIAN"
|
||||
|
||||
echo "*****************************************"
|
||||
echo "code archive: $CODE_ARCHIVE"
|
||||
echo "code directory: $CODE_DIR"
|
||||
echo "install directory: $INSTALL_DIR"
|
||||
echo "debian control directory: $DEBIAN_CTRL_DIR"
|
||||
echo "*****************************************"
|
||||
tar -xjvf $CODE_ARCHIVE
|
||||
mkdir -p $INSTALL_DIR
|
||||
cd $CODE_DIR
|
||||
./configure --prefix=$INSTALL_DIR
|
||||
make && make install
|
||||
|
||||
# second compilation - the path to libtrace.so is hardcoded in firejail executable
|
||||
# pointing according to --prefix=$INSTALL_DIR. We need it to point to /usr/lib
|
||||
make distclean
|
||||
./configure --prefix=/usr
|
||||
make
|
||||
# install firejail executable in $TOP/$INSTALL_DIR
|
||||
strip src/firejail/firejail
|
||||
install -c -m 0755 src/firejail/firejail $INSTALL_DIR/bin/.
|
||||
chmod u+s $INSTALL_DIR/bin/firejail
|
||||
|
||||
|
||||
cd ..
|
||||
echo "*****************************************"
|
||||
SIZE=`du -s debian/usr`
|
||||
echo "install size $SIZE"
|
||||
echo "*****************************************"
|
||||
|
||||
mv $INSTALL_DIR/share/doc/firejail/RELNOTES $INSTALL_DIR/share/doc/firejail/changelog.Debian
|
||||
gzip -9 $INSTALL_DIR/share/doc/firejail/changelog.Debian
|
||||
rm $INSTALL_DIR/share/doc/firejail/COPYING
|
||||
cp platform/debian/copyright $INSTALL_DIR/share/doc/firejail/.
|
||||
mkdir -p $DEBIAN_CTRL_DIR
|
||||
sed "s/FIREJAILVER/$2/g" platform/debian/control > $DEBIAN_CTRL_DIR/control
|
||||
mkdir -p debian/etc/firejail
|
||||
cp etc/chromium.profile debian/etc/firejail/.
|
||||
cp etc/chromium-browser.profile debian/etc/firejail/.
|
||||
cp etc/disable-mgmt.inc debian/etc/firejail/.
|
||||
cp etc/disable-secret.inc debian/etc/firejail/.
|
||||
cp etc/dropbox.profile debian/etc/firejail/.
|
||||
cp etc/evince.profile debian/etc/firejail/.
|
||||
cp etc/firefox.profile debian/etc/firejail/.
|
||||
cp etc/iceweasel.profile debian/etc/firejail/.
|
||||
cp etc/icedove.profile debian/etc/firejail/.
|
||||
cp etc/login* debian/etc/firejail/.
|
||||
cp etc/midori.profile debian/etc/firejail/.
|
||||
cp etc/opera.profile debian/etc/firejail/.
|
||||
cp etc/thunderbird.profile debian/etc/firejail/.
|
||||
cp etc/transmission-gtk.profile debian/etc/firejail/.
|
||||
cp etc/transmission-qt.profile debian/etc/firejail/.
|
||||
cp etc/vlc.profile debian/etc/firejail/.
|
||||
cp etc/audacious.profile debian/etc/firejail/.
|
||||
cp etc/clementine.profile debian/etc/firejail/.
|
||||
cp etc/gnome-mplayer.profile debian/etc/firejail/.
|
||||
cp etc/rhythmbox.profile debian/etc/firejail/.
|
||||
cp etc/totem.profile debian/etc/firejail/.
|
||||
cp etc/deluge.profile debian/etc/firejail/.
|
||||
cp etc/qbittorrent.profile debian/etc/firejail/.
|
||||
cp etc/generic.profile debian/etc/firejail/.
|
||||
cp etc/xchat.profile debian/etc/firejail/.
|
||||
cp etc/server.profile debian/etc/firejail/.
|
||||
cp etc/quassel.profile debian/etc/firejail/.
|
||||
cp etc/pidgin.profile debian/etc/firejail/.
|
||||
cp etc/filezilla.profile debian/etc/firejail/.
|
||||
cp etc/empathy.profile debian/etc/firejail/.
|
||||
cp etc/disable-common.inc debian/etc/firejail/.
|
||||
cp etc/deadbeef.profile debian/etc/firejail/.
|
||||
cp etc/icecat.profile debian/etc/firejail/.
|
||||
cp platform/debian/conffiles $DEBIAN_CTRL_DIR/.
|
||||
find ./debian -type d | xargs chmod 755
|
||||
dpkg-deb --build debian
|
||||
lintian debian.deb
|
||||
mv debian.deb firejail_$2_1_amd64.deb
|
||||
echo "if building a 32bit package, rename the deb file manually"
|
||||
rm -fr debian
|
||||
rm -fr $CODE_DIR
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
7
mkman.sh
Executable file
7
mkman.sh
Executable file
|
|
@ -0,0 +1,7 @@
|
|||
#!/bin/bash
|
||||
|
||||
sed "s/VERSION/$1/g" $2 > $3
|
||||
MONTH=`date +%b`
|
||||
sed -i "s/MONTH/$MONTH/g" $3
|
||||
YEAR=`date +%Y`
|
||||
sed -i "s/YEAR/$YEAR/g" $3
|
||||
33
platform/debian/conffiles
Normal file
33
platform/debian/conffiles
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
/etc/firejail/evince.profile
|
||||
/etc/firejail/disable-secret.inc
|
||||
/etc/firejail/chromium.profile
|
||||
/etc/firejail/midori.profile
|
||||
/etc/firejail/icedove.profile
|
||||
/etc/firejail/iceweasel.profile
|
||||
/etc/firejail/dropbox.profile
|
||||
/etc/firejail/login.users
|
||||
/etc/firejail/chromium-browser.profile
|
||||
/etc/firejail/disable-mgmt.inc
|
||||
/etc/firejail/firefox.profile
|
||||
/etc/firejail/opera.profile
|
||||
/etc/firejail/thunderbird.profile
|
||||
/etc/firejail/transmission-gtk.profile
|
||||
/etc/firejail/transmission-qt.profile
|
||||
/etc/firejail/vlc.profile
|
||||
/etc/firejail/audacious.profile
|
||||
/etc/firejail/clementine.profile
|
||||
/etc/firejail/gnome-mplayer.profile
|
||||
/etc/firejail/rhythmbox.profile
|
||||
/etc/firejail/totem.profile
|
||||
/etc/firejail/deluge.profile
|
||||
/etc/firejail/qbittorrent.profile
|
||||
/etc/firejail/generic.profile
|
||||
/etc/firejail/xchat.profile
|
||||
/etc/firejail/server.profile
|
||||
/etc/firejail/quassel.profile
|
||||
/etc/firejail/pidgin.profile
|
||||
/etc/firejail/filezilla.profile
|
||||
/etc/firejail/empathy.profile
|
||||
/etc/firejail/disable-common.inc
|
||||
/etc/firejail/deadbeef.profile
|
||||
/etc/firejail/icecat.profile
|
||||
20
platform/debian/control
Normal file
20
platform/debian/control
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
Package: firejail
|
||||
Version: FIREJAILVER-1
|
||||
Architecture: amd64
|
||||
Maintainer: netblue30 <netblue30@yahoo.com>
|
||||
Installed-Size: 272
|
||||
Depends: libc6
|
||||
Section: admin
|
||||
Priority: extra
|
||||
Homepage: http://firejail.sourceforge.net
|
||||
Description: Linux namepaces sandbox program.
|
||||
Firejail is a SUID sandbox program that reduces the risk of security
|
||||
breaches by restricting the running environment of untrusted applications
|
||||
using Linux namespaces and seccmp-bpf. It includes sandbox profiles for
|
||||
Iceweasel/Mozilla Firefox, Chromium, Midori, Opera, Evince, Transmission
|
||||
and VLC.
|
||||
.
|
||||
Firejail also expands the restricted shell facility found in bash by
|
||||
adding Linux namespace support. It also supports sandboxing SSH users
|
||||
upon login.
|
||||
|
||||
30
platform/debian/copyright
Normal file
30
platform/debian/copyright
Normal file
|
|
@ -0,0 +1,30 @@
|
|||
|
||||
This is the Debian/Ubuntu prepackaged version of firejail.
|
||||
|
||||
Firejail is a sandbox program that reduces the risk of security breaches
|
||||
by restricting the running environment of untrusted applications using
|
||||
Linux namespaces. It currently implements hostname, filesystem, PID, IPC
|
||||
and networking stack isolation, and it runs on any recent Linux system. It
|
||||
includes a sandbox profile for Mozilla Firefox.
|
||||
|
||||
Copyright (C) 2014,2015 Firejail Authors (see README file for more details)
|
||||
|
||||
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 St, Fifth Floor, Boston, MA 02110-1301, USA.
|
||||
|
||||
The complete text of the GNU General Public License can be found
|
||||
in /usr/share/common-licenses/GPL-2.
|
||||
|
||||
Homepage: http://firejail.sourceforge.net.
|
||||
|
||||
256
platform/rpm/mkrpm.sh
Executable file
256
platform/rpm/mkrpm.sh
Executable file
|
|
@ -0,0 +1,256 @@
|
|||
#!/bin/bash
|
||||
VERSION="0.9.26"
|
||||
rm -fr ~/rpmbuild
|
||||
rm -f firejail-$VERSION-1.x86_64.rpm
|
||||
|
||||
mkdir -p ~/rpmbuild/{RPMS,SRPMS,BUILD,SOURCES,SPECS,tmp}
|
||||
cat <<EOF >~/.rpmmacros
|
||||
%_topdir %(echo $HOME)/rpmbuild
|
||||
%_tmppath %{_topdir}/tmp
|
||||
EOF
|
||||
|
||||
cd ~/rpmbuild
|
||||
echo "building directory tree"
|
||||
|
||||
mkdir -p firejail-$VERSION/usr/bin
|
||||
install -m 755 /usr/bin/firejail firejail-$VERSION/usr/bin/.
|
||||
install -m 755 /usr/bin/firemon firejail-$VERSION/usr/bin/.
|
||||
|
||||
mkdir -p firejail-$VERSION/usr/lib/firejail
|
||||
install -m 644 /usr/lib/firejail/libtrace.so firejail-$VERSION/usr/lib/firejail/.
|
||||
install -m 755 /usr/lib/firejail/ftee firejail-$VERSION/usr/lib/firejail/.
|
||||
|
||||
mkdir -p firejail-$VERSION/usr/share/man/man1
|
||||
install -m 644 /usr/share/man/man1/firejail.1.gz firejail-$VERSION/usr/share/man/man1/.
|
||||
install -m 644 /usr/share/man/man1/firemon.1.gz firejail-$VERSION/usr/share/man/man1/.
|
||||
|
||||
mkdir -p firejail-$VERSION/usr/share/man/man5
|
||||
install -m 644 /usr/share/man/man5/firejail-profile.5.gz firejail-$VERSION/usr/share/man/man5/.
|
||||
|
||||
mkdir -p firejail-$VERSION/usr/share/doc/packages/firejail
|
||||
install -m 644 /usr/share/doc/firejail/COPYING firejail-$VERSION/usr/share/doc/packages/firejail/.
|
||||
install -m 644 /usr/share/doc/firejail/README firejail-$VERSION/usr/share/doc/packages/firejail/.
|
||||
install -m 644 /usr/share/doc/firejail/RELNOTES firejail-$VERSION/usr/share/doc/packages/firejail/.
|
||||
|
||||
mkdir -p firejail-$VERSION/etc/firejail
|
||||
install -m 644 /etc/firejail/chromium-browser.profile firejail-$VERSION/etc/firejail/chromium-browser.profile
|
||||
install -m 644 /etc/firejail/chromium.profile firejail-$VERSION/etc/firejail/chromium.profile
|
||||
install -m 644 /etc/firejail/dropbox.profile firejail-$VERSION/etc/firejail/dropbox.profile
|
||||
install -m 644 /etc/firejail/disable-secret.inc firejail-$VERSION/etc/firejail/disable-secret.inc
|
||||
install -m 644 /etc/firejail/disable-mgmt.inc firejail-$VERSION/etc/firejail/disable-mgmt.inc
|
||||
install -m 644 /etc/firejail/evince.profile firejail-$VERSION/etc/firejail/evince.profile
|
||||
install -m 644 /etc/firejail/firefox.profile firejail-$VERSION/etc/firejail/firefox.profile
|
||||
install -m 644 /etc/firejail/icedove.profile firejail-$VERSION/etc/firejail/icedove.profile
|
||||
install -m 644 /etc/firejail/iceweasel.profile firejail-$VERSION/etc/firejail/iceweasel.profile
|
||||
install -m 644 /etc/firejail/midori.profile firejail-$VERSION/etc/firejail/midori.profile
|
||||
install -m 644 /etc/firejail/thunderbird.profile firejail-$VERSION/etc/firejail/thunderbird.profile
|
||||
install -m 644 /etc/firejail/opera.profile firejail-$VERSION/etc/firejail/opera.profile
|
||||
install -m 644 /etc/firejail/transmission-gtk.profile firejail-$VERSION/etc/firejail/transmission-gtk.profile
|
||||
install -m 644 /etc/firejail/transmission-qt.profile firejail-$VERSION/etc/firejail/transmission-qt.profile
|
||||
install -m 644 /etc/firejail/vlc.profile firejail-$VERSION/etc/firejail/vlc.profile
|
||||
install -m 644 /etc/firejail/audacious.profile firejail-$VERSION/etc/firejail/audacious.profile
|
||||
install -m 644 /etc/firejail/clementine.profile firejail-$VERSION/etc/firejail/clementine.profile
|
||||
install -m 644 /etc/firejail/gnome-mplayer.profile firejail-$VERSION/etc/firejail/gnome-mplayer.profile
|
||||
install -m 644 /etc/firejail/rhythmbox.profile firejail-$VERSION/etc/firejail/rhythmbox.profile
|
||||
install -m 644 /etc/firejail/totem.profile firejail-$VERSION/etc/firejail/totem.profile
|
||||
install -m 644 /etc/firejail/deluge.profile firejail-$VERSION/etc/firejail/deluge.profile
|
||||
install -m 644 /etc/firejail/qbittorrent.profile firejail-$VERSION/etc/firejail/qbittorrent.profile
|
||||
install -m 644 /etc/firejail/generic.profile firejail-$VERSION/etc/firejail/generic.profile
|
||||
install -m 644 /etc/firejail/login.users firejail-$VERSION/etc/firejail/login.users
|
||||
|
||||
mkdir -p firejail-$VERSION/usr/share/bash-completion/completions
|
||||
install -m 644 /usr/share/bash-completion/completions/firejail firejail-$VERSION/usr/share/bash-completion/completions/.
|
||||
|
||||
echo "building tar.gz archive"
|
||||
tar -czvf firejail-$VERSION.tar.gz firejail-$VERSION
|
||||
|
||||
cp firejail-$VERSION.tar.gz SOURCES/.
|
||||
|
||||
echo "building config spec"
|
||||
cat <<EOF > SPECS/firejail.spec
|
||||
%define __spec_install_post %{nil}
|
||||
%define debug_package %{nil}
|
||||
%define __os_install_post %{_dbpath}/brp-compress
|
||||
|
||||
Summary: Linux namepaces sandbox program
|
||||
Name: firejail
|
||||
Version: $VERSION
|
||||
Release: 1
|
||||
License: GPL+
|
||||
Group: Development/Tools
|
||||
SOURCE0 : %{name}-%{version}.tar.gz
|
||||
URL: http://firejail.sourceforege.net
|
||||
|
||||
BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-root
|
||||
|
||||
%description
|
||||
Firejail is a SUID sandbox program that reduces the risk of security
|
||||
breaches by restricting the running environment of untrusted applications
|
||||
using Linux namespaces. It includes a sandbox profile for Mozilla Firefox.
|
||||
|
||||
%prep
|
||||
%setup -q
|
||||
|
||||
%build
|
||||
|
||||
%install
|
||||
rm -rf %{buildroot}
|
||||
mkdir -p %{buildroot}
|
||||
|
||||
cp -a * %{buildroot}
|
||||
|
||||
|
||||
%clean
|
||||
rm -rf %{buildroot}
|
||||
|
||||
|
||||
%files
|
||||
%defattr(-,root,root,-)
|
||||
%config(noreplace) %{_sysconfdir}/%{name}/chromium-browser.profile
|
||||
%config(noreplace) %{_sysconfdir}/%{name}/chromium.profile
|
||||
%config(noreplace) %{_sysconfdir}/%{name}/disable-mgmt.inc
|
||||
%config(noreplace) %{_sysconfdir}/%{name}/disable-secret.inc
|
||||
%config(noreplace) %{_sysconfdir}/%{name}/dropbox.profile
|
||||
%config(noreplace) %{_sysconfdir}/%{name}/evince.profile
|
||||
%config(noreplace) %{_sysconfdir}/%{name}/firefox.profile
|
||||
%config(noreplace) %{_sysconfdir}/%{name}/icedove.profile
|
||||
%config(noreplace) %{_sysconfdir}/%{name}/iceweasel.profile
|
||||
%config(noreplace) %{_sysconfdir}/%{name}/login.users
|
||||
%config(noreplace) %{_sysconfdir}/%{name}/midori.profile
|
||||
%config(noreplace) %{_sysconfdir}/%{name}/opera.profile
|
||||
%config(noreplace) %{_sysconfdir}/%{name}/thunderbird.profile
|
||||
%config(noreplace) %{_sysconfdir}/%{name}/transmission-gtk.profile
|
||||
%config(noreplace) %{_sysconfdir}/%{name}/transmission-qt.profile
|
||||
%config(noreplace) %{_sysconfdir}/%{name}/vlc.profile
|
||||
%config(noreplace) %{_sysconfdir}/%{name}/audacious.profile
|
||||
%config(noreplace) %{_sysconfdir}/%{name}/clementine.profile
|
||||
%config(noreplace) %{_sysconfdir}/%{name}/gnome-mplayer.profile
|
||||
%config(noreplace) %{_sysconfdir}/%{name}/rhythmbox.profile
|
||||
%config(noreplace) %{_sysconfdir}/%{name}/totem.profile
|
||||
%config(noreplace) %{_sysconfdir}/%{name}/deluge.profile
|
||||
%config(noreplace) %{_sysconfdir}/%{name}/qbittorrent.profile
|
||||
%config(noreplace) %{_sysconfdir}/%{name}/generic.profile
|
||||
|
||||
/usr/bin/firejail
|
||||
/usr/bin/firemon
|
||||
/usr/lib/firejail/libtrace.so
|
||||
/usr/lib/firejail/ftee
|
||||
/usr/share/doc/packages/firejail/COPYING
|
||||
/usr/share/doc/packages/firejail/README
|
||||
/usr/share/doc/packages/firejail/RELNOTES
|
||||
/usr/share/man/man1/firejail.1.gz
|
||||
/usr/share/man/man1/firemon.1.gz
|
||||
/usr/share/man/man5/firejail-profile.5.gz
|
||||
/usr/share/bash-completion/completions/firejail
|
||||
|
||||
%post
|
||||
chmod u+s /usr/bin/firejail
|
||||
|
||||
%changelog
|
||||
* Thu Apr 30 2015 netblue30 <netblue30@yahoo.com> 0.9.26-1
|
||||
- private dev directory
|
||||
- private.keep option for whitelisting home files in a new private directory
|
||||
- user namespaces support, noroot option
|
||||
- added Deluge and qBittorent profiles
|
||||
- bugfixes
|
||||
|
||||
* Sun Apr 5 2015 netblue30 <netblue30@yahoo.com> 0.9.24-1
|
||||
- whitelist and blacklist seccomp filters
|
||||
- doubledash option
|
||||
- --shell=none support
|
||||
- netfilter file support in profile files
|
||||
- dns server support in profile files
|
||||
- added --dns.print option
|
||||
- added default profiles for Audoacious, Clementine, Rhythmbox and Totem.
|
||||
- added --caps.drop=all in default profiles
|
||||
- new syscalls in default seccomp filter: sysfs, sysctl, adjtimex, kcmp
|
||||
- clock_adjtime, lookup_dcookie, perf_event_open, fanotify_init
|
||||
- Bugfix: using /proc/sys/kernel/pid_max for the max number of pids
|
||||
- two build patches from Reiner Herman (tickets 11, 12)
|
||||
- man page patch from Reiner Herman (ticket 13)
|
||||
- output patch (ticket 15) from sshirokov
|
||||
|
||||
* Mon Mar 9 2015 netblue30 <netblue30@yahoo.com> 0.9.22-1
|
||||
- Replaced --noip option with --ip=none
|
||||
- Container stdout logging and log rotation
|
||||
- Added process_vm_readv, process_vm_writev and mknod to
|
||||
default seccomp blacklist
|
||||
- Added CAP_MKNOD to default caps blacklist
|
||||
- Blacklist and whitelist custom Linux capabilities filters
|
||||
- macvlan device driver support for --net option
|
||||
- DNS server support, --dns option
|
||||
- Netfilter support
|
||||
- Monitor network statistics, --netstats option
|
||||
- Added profile for Mozilla Thunderbird/Icedove
|
||||
- --overlay support for Linux kernels 3.18+
|
||||
- Bugfix: preserve .Xauthority file in private mode (test with ssh -X)
|
||||
- Bugfix: check uid/gid for cgroup
|
||||
|
||||
* Fri Feb 6 2015 netblue30 <netblue30@yahoo.com> 0.9.20-1
|
||||
- utmp, btmp and wtmp enhancements
|
||||
- create empty /var/log/wtmp and /var/log/btmp files in sandbox
|
||||
- generate a new /var/run/utmp file in sandbox
|
||||
- CPU affinity, --cpu option
|
||||
- Linux control groups support, --cgroup option
|
||||
- Opera web browser support
|
||||
- VLC support
|
||||
- Added "empty" attribute to seccomp command to remove the default
|
||||
- syscall list form seccomp blacklist
|
||||
- Added --nogroups option to disable supplementary groups for regular
|
||||
- users. root user always runs without supplementary groups.
|
||||
- firemon enhancements
|
||||
- display the command that started the sandbox
|
||||
- added --caps option to display capabilities for all sandboxes
|
||||
- added --cgroup option to display the control groups for all sandboxes
|
||||
- added --cpu option to display CPU affinity for all sandboxes
|
||||
- added --seccomp option to display seccomp setting for all sandboxes
|
||||
- New compile time options: --disable-chroot, --disable-bind
|
||||
- bugfixes
|
||||
|
||||
* Sat Dec 27 2014 netblue30 <netblue30@yahoo.com> 0.9.18-1
|
||||
- Support for tracing system, setuid, setgid, setfsuid, setfsgid syscalls
|
||||
- Support for tracing setreuid, setregid, setresuid, setresguid syscalls
|
||||
- Added profiles for transmission-gtk and transmission-qt
|
||||
- bugfixes
|
||||
|
||||
* Tue Nov 4 2014 netblue30 <netblue30@yahoo.com> 0.9.16-1
|
||||
- Configurable private home directory
|
||||
- Configurable default user shell
|
||||
- Software configuration support for --docdir and DESTDIR
|
||||
- Profile file support for include, caps, seccomp and private keywords
|
||||
- Dropbox profile file
|
||||
- Linux capabilities and seccomp filters enabled by default for Firefox,
|
||||
Midori, Evince and Dropbox
|
||||
- bugfixes
|
||||
|
||||
* Wed Oct 8 2014 netblue30 <netblue30@yahoo.com> 0.9.14-1
|
||||
- Linux capabilities and seccomp filters are automatically enabled in
|
||||
chroot mode (--chroot option) if the sandbox is started as regular
|
||||
user
|
||||
- Added support for user defined seccomp blacklists
|
||||
- Added syscall trace support
|
||||
- Added --tmpfs option
|
||||
- Added --balcklist option
|
||||
- Added --read-only option
|
||||
- Added --bind option
|
||||
- Logging enhancements
|
||||
- --overlay option was reactivated
|
||||
- Added firemon support to print the ARP table for each sandbox
|
||||
- Added firemon support to print the route table for each sandbox
|
||||
- Added firemon support to print interface information for each sandbox
|
||||
- bugfixes
|
||||
|
||||
* Tue Sep 16 2014 netblue30 <netblue30@yahoo.com> 0.9.12-1
|
||||
- Added capabilities support
|
||||
- Added support for CentOS 7
|
||||
- bugfixes
|
||||
|
||||
EOF
|
||||
|
||||
echo "building rpm"
|
||||
rpmbuild -ba SPECS/firejail.spec
|
||||
rpm -qpl RPMS/x86_64/firejail-$VERSION-1.x86_64.rpm
|
||||
cd ..
|
||||
rm -f firejail-$VERSION-1.x86_64.rpm
|
||||
cp rpmbuild/RPMS/x86_64/firejail-$VERSION-1.x86_64.rpm .
|
||||
|
||||
28
src/firejail/Makefile.in
Normal file
28
src/firejail/Makefile.in
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
all: firejail
|
||||
|
||||
PREFIX=@prefix@
|
||||
VERSION=@PACKAGE_VERSION@
|
||||
NAME=@PACKAGE_NAME@
|
||||
HAVE_SECCOMP_H=@HAVE_SECCOMP_H@
|
||||
HAVE_SECCOMP=@HAVE_SECCOMP@
|
||||
HAVE_CHROOT=@HAVE_CHROOT@
|
||||
HAVE_BIND=@HAVE_BIND@
|
||||
|
||||
H_FILE_LIST = $(wildcard *.[h])
|
||||
C_FILE_LIST = $(wildcard *.c)
|
||||
OBJS = $(C_FILE_LIST:.c=.o)
|
||||
BINOBJS = $(foreach file, $(OBJS), $file)
|
||||
CFLAGS += -ggdb -O2 -DVERSION='"$(VERSION)"' -DPREFIX='"$(PREFIX)"' $(HAVE_SECCOMP) $(HAVE_SECCOMP_H) $(HAVE_CHROOT) $(HAVE_BIND) -fstack-protector-all -D_FORTIFY_SOURCE=2 -fPIE -pie -Wformat -Wformat-security
|
||||
LDFLAGS += -pie -Wl,-z,relro -Wl,-z,now -lpthread
|
||||
|
||||
%.o : %.c $(H_FILE_LIST)
|
||||
$(CC) $(CFLAGS) $(INCLUDE) -c $< -o $@
|
||||
|
||||
firejail: $(OBJS) ../lib/libnetlink.o ../lib/common.o
|
||||
$(CC) $(LDFLAGS) -o $@ $(OBJS) ../lib/libnetlink.o ../lib/common.o $(LIBS)
|
||||
|
||||
clean:; rm -f *.o firejail firejail.1 firejail.1.gz
|
||||
|
||||
distclean: clean
|
||||
rm -fr Makefile
|
||||
|
||||
85
src/firejail/arg-checking.txt
Normal file
85
src/firejail/arg-checking.txt
Normal file
|
|
@ -0,0 +1,85 @@
|
|||
arg checking:
|
||||
|
||||
1. --output=filename
|
||||
- not supported in profiles
|
||||
- checking no "..",
|
||||
- checking no link,
|
||||
- checking no dir,
|
||||
- checking same permissions,
|
||||
- checking no hard links
|
||||
- unit test
|
||||
|
||||
2. --chroot=dirname
|
||||
- not supported in profiles
|
||||
- expand "~"
|
||||
- checking no "..",
|
||||
- checking is dir,
|
||||
- checking no link
|
||||
- checking directory structure
|
||||
- unit test
|
||||
|
||||
3. --bind=dirname1,dirname2, --bind=filename1,filenam2
|
||||
- supported in profiles
|
||||
- accepted only when running as root
|
||||
- checking string chars
|
||||
- checking no ".."
|
||||
- unit test non root
|
||||
|
||||
4. --tmpfs=dirname
|
||||
- supported in profiles
|
||||
- checking string chars
|
||||
- checking no ".."
|
||||
- unit test
|
||||
|
||||
5. --blacklist=filename, --blacklist=dirname
|
||||
- supported in profiles
|
||||
- checking string chars
|
||||
- checking no ".."
|
||||
- unit test
|
||||
|
||||
6. --read-only=filename, --read-only=dirname
|
||||
- supported in profiles
|
||||
- checking string chars
|
||||
- checking no ".."
|
||||
- unit test
|
||||
|
||||
7. --profile=filename
|
||||
- check access as real GID/UID
|
||||
- checking no dir
|
||||
- checking no link
|
||||
- checking no ".."
|
||||
- unit test
|
||||
|
||||
8. --private=dirname
|
||||
- supported in profiles
|
||||
- expand "~"
|
||||
- check is dir
|
||||
- check no link
|
||||
- checking no ".."
|
||||
- check same owner
|
||||
- unit test
|
||||
|
||||
9. --private.keep=filelist
|
||||
- supported in profiles
|
||||
- checking no ".."
|
||||
- checking file found
|
||||
- checking same owner
|
||||
- checking no link
|
||||
- unit test
|
||||
|
||||
10. --netfilter=filename
|
||||
- supported in profiles
|
||||
- check access as real GID/UID
|
||||
- checking no dir
|
||||
- checking no link
|
||||
- checking no ".."
|
||||
- unit test
|
||||
|
||||
11. --shell=filename
|
||||
- not supported in profiles
|
||||
- check access as real GID/UID
|
||||
- checking no dir
|
||||
- checking no link
|
||||
- checking no ".."
|
||||
- unit test
|
||||
|
||||
474
src/firejail/arp.c
Normal file
474
src/firejail/arp.c
Normal file
|
|
@ -0,0 +1,474 @@
|
|||
/*
|
||||
* Copyright (C) 2014, 2015 netblue30 (netblue30@yahoo.com)
|
||||
*
|
||||
* 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 <sys/socket.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <linux/if_ether.h> //TCP/IP Protocol Suite for Linux
|
||||
#include <net/if.h>
|
||||
#include <netinet/in.h>
|
||||
#include <linux/ip.h>
|
||||
#include <linux/udp.h>
|
||||
#include <linux/tcp.h>
|
||||
#include <linux/if_packet.h>
|
||||
|
||||
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;
|
||||
|
||||
// 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) {
|
||||
fprintf(stderr, "Error: invalid network device name %s\n", dev);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if (arg_debug)
|
||||
printf("Trying %d.%d.%d.%d ...\n", PRINT_IP(destaddr));
|
||||
|
||||
// find interface address
|
||||
int sock;
|
||||
if ((sock = socket(AF_INET, SOCK_RAW, IPPROTO_RAW)) < 0)
|
||||
errExit("socket");
|
||||
|
||||
srcaddr = htonl(srcaddr);
|
||||
destaddr = htonl(destaddr);
|
||||
|
||||
// Find interface MAC address
|
||||
struct ifreq ifr;
|
||||
memset(&ifr, 0, sizeof (ifr));
|
||||
strncpy(ifr.ifr_name, dev, IFNAMSIZ);
|
||||
if (ioctl(sock, SIOCGIFHWADDR, &ifr) < 0)
|
||||
errExit("ioctl");
|
||||
close(sock);
|
||||
|
||||
// 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, ifr.ifr_hwaddr.sa_data, 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, ifr.ifr_hwaddr.sa_data, 6);
|
||||
memcpy(hdr.sender_ip, (uint8_t *)&srcaddr, 4);
|
||||
memcpy(hdr.target_ip, (uint8_t *)&destaddr, 4);
|
||||
|
||||
// buiild 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, ifr.ifr_hwaddr.sa_data, 6);
|
||||
frame[12] = ETH_P_ARP / 256;
|
||||
frame[13] = ETH_P_ARP % 256;
|
||||
memcpy (frame + 14, &hdr, sizeof(hdr));
|
||||
|
||||
// open layer2 socket
|
||||
if ((sock = socket(PF_PACKET, SOCK_RAW, htons (ETH_P_ALL))) < 0)
|
||||
errExit("socket");
|
||||
|
||||
int len;
|
||||
if ((len = sendto (sock, frame, 14 + sizeof(ArpHdr), 0, (struct sockaddr *) &addr, sizeof (addr))) <= 0)
|
||||
errExit("send");
|
||||
fflush(0);
|
||||
|
||||
// wait not more than one second for an answer
|
||||
fd_set fds;
|
||||
FD_ZERO(&fds);
|
||||
FD_SET(sock, &fds);
|
||||
int maxfd = sock;
|
||||
struct timeval ts;
|
||||
ts.tv_sec = 1; // 1 second wait time
|
||||
ts.tv_usec = 0;
|
||||
while (1) {
|
||||
int nready = select(maxfd + 1, &fds, (fd_set *) 0, (fd_set *) 0, &ts);
|
||||
if (nready < 0)
|
||||
errExit("select");
|
||||
else if (nready == 0) { // timeout
|
||||
close(sock);
|
||||
return 0;
|
||||
}
|
||||
else {
|
||||
// read the incoming packet
|
||||
int len = recvfrom(sock, frame, ETH_FRAME_LEN, 0, NULL, NULL);
|
||||
if (len < 0) {
|
||||
perror("recvfrom");
|
||||
close(sock);
|
||||
return -1;
|
||||
}
|
||||
|
||||
// parse the incomming packet
|
||||
if (len < 14 + sizeof(ArpHdr))
|
||||
continue;
|
||||
if (frame[12] != (ETH_P_ARP / 256) || frame[13] != (ETH_P_ARP % 256))
|
||||
continue;
|
||||
memcpy(&hdr, frame + 14, sizeof(ArpHdr));
|
||||
if (hdr.opcode == htons(1))
|
||||
continue;
|
||||
if (hdr.opcode == htons(2)) {
|
||||
// check my mac and my address
|
||||
if (memcmp(ifr.ifr_hwaddr.sa_data, hdr.target_mac, 6) != 0)
|
||||
continue;
|
||||
uint32_t ip;
|
||||
memcpy(&ip, hdr.target_ip, 4);
|
||||
if (ip != srcaddr) {
|
||||
continue;
|
||||
}
|
||||
close(sock);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// it will never get here!
|
||||
close(sock);
|
||||
return -1;
|
||||
}
|
||||
|
||||
// assign a random IP address and check it
|
||||
// the address needs to be in the range if it --iprange was specified
|
||||
static uint32_t arp_random(const char *dev, Bridge *br) {
|
||||
assert(dev);
|
||||
assert(br);
|
||||
uint32_t ifip = br->ip;
|
||||
uint32_t ifmask = br->mask;
|
||||
assert(ifip);
|
||||
assert(ifmask);
|
||||
|
||||
if (arg_debug)
|
||||
printf("ARP-scan %s, %d.%d.%d.%d/%d\n",
|
||||
dev, PRINT_IP(ifip), mask2bits(ifmask));
|
||||
|
||||
// determine the range based on network address
|
||||
uint32_t range = ~ifmask + 1; // the number of potential addresses
|
||||
// this software is not supported for /31 networks
|
||||
if (range < 4)
|
||||
return 0; // the user will have to set the IP address manually
|
||||
range -= 2; // subtract the network address and the broadcast address
|
||||
uint32_t start = (ifip & ifmask) + 1;
|
||||
|
||||
// adjust range based on --iprange params
|
||||
if (br->iprange_start && br->iprange_end) {
|
||||
start = br->iprange_start;
|
||||
range = br->iprange_end - br->iprange_start;
|
||||
}
|
||||
|
||||
if (arg_debug)
|
||||
printf("IP address range from %d.%d.%d.%d to %d.%d.%d.%d\n",
|
||||
PRINT_IP(start), PRINT_IP(start + range));
|
||||
|
||||
// generate a random address - 10 tries
|
||||
uint32_t dest = 0;
|
||||
int i = 0;
|
||||
for (i = 0; i < 10; i++) {
|
||||
dest = start + ((uint32_t) rand()) % range;
|
||||
if (dest == ifip) // do not allow the interface address
|
||||
continue; // try again
|
||||
|
||||
// if we've made it up to here, we have a valid address
|
||||
break;
|
||||
}
|
||||
if (i == 10) // we failed 10 times
|
||||
return 0;
|
||||
|
||||
// check address
|
||||
uint32_t rv = arp_check(dev, dest, ifip);
|
||||
if (!rv)
|
||||
return dest;
|
||||
return 0;
|
||||
}
|
||||
|
||||
// go sequentially trough all IP addresses and assign the first one not in use
|
||||
static uint32_t arp_sequential(const char *dev, Bridge *br) {
|
||||
assert(dev);
|
||||
assert(br);
|
||||
uint32_t ifip = br->ip;
|
||||
uint32_t ifmask = br->mask;
|
||||
assert(ifip);
|
||||
assert(ifmask);
|
||||
|
||||
// range based on network address
|
||||
uint32_t range = ~ifmask + 1; // the number of potential addresses
|
||||
// this software is not supported for /31 networks
|
||||
if (range < 4)
|
||||
return 0; // the user will have to set the IP address manually
|
||||
range -= 2; // subtract the network address and the broadcast address
|
||||
|
||||
// try all possible ip addresses in ascending order
|
||||
// start address
|
||||
uint32_t dest = (ifip & ifmask) + 1;
|
||||
if (br->iprange_start)
|
||||
dest = br->iprange_start;
|
||||
// end address
|
||||
uint32_t last = dest + range - 1;
|
||||
if (br->iprange_end)
|
||||
last = br->iprange_end;
|
||||
|
||||
if (arg_debug)
|
||||
printf("Trying IP address range from %d.%d.%d.%d to %d.%d.%d.%d\n",
|
||||
PRINT_IP(dest), PRINT_IP(last));
|
||||
|
||||
// loop through addresses and stop as soon as you find an unused one
|
||||
while (dest <= last) {
|
||||
if (dest == ifip) {
|
||||
dest++;
|
||||
continue;
|
||||
}
|
||||
uint32_t rv = arp_check(dev, dest, ifip);
|
||||
if (!rv)
|
||||
return dest;
|
||||
dest++;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
// assign an IP address first trying some random addresses, and if this fails
|
||||
// by doing an arp scan.
|
||||
//
|
||||
// dev is the name of the device to use in scanning,
|
||||
// br is bridge structure holding the ip address and mask to use in
|
||||
// arp packets. It also holds values for for the range of addresses
|
||||
// if --iprange was set by the user
|
||||
uint32_t arp_assign(const char *dev, Bridge *br) {
|
||||
assert(br);
|
||||
uint32_t ip = 0;
|
||||
|
||||
// try two random IP addresses
|
||||
ip = arp_random(dev, br);
|
||||
if (!ip)
|
||||
ip = arp_random(dev, br);
|
||||
|
||||
// try all possible IP addresses one by one
|
||||
if (!ip)
|
||||
ip = arp_sequential(dev, br);
|
||||
|
||||
// print result
|
||||
if (!ip) {
|
||||
fprintf(stderr, "Error: cannot assign an IP address; it looks like all of them are in use.\n");
|
||||
logerr("Cannot assign an IP address; it looks like all of them are in use.");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
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;
|
||||
int 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);
|
||||
|
||||
// buiild 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 incomming packet
|
||||
if (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);
|
||||
}
|
||||
|
||||
|
||||
483
src/firejail/bandwidth.c
Normal file
483
src/firejail/bandwidth.c
Normal file
|
|
@ -0,0 +1,483 @@
|
|||
/*
|
||||
* Copyright (C) 2014, 2015 netblue30 (netblue30@yahoo.com)
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
#define _GNU_SOURCE
|
||||
#include <stdio.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
#include <net/if.h>
|
||||
#include "firejail.h"
|
||||
|
||||
//***********************************
|
||||
// interface bandwidth linked list
|
||||
//***********************************
|
||||
typedef struct ifbw_t {
|
||||
struct ifbw_t *next;
|
||||
char *txt;
|
||||
} IFBW;
|
||||
IFBW *ifbw = NULL;
|
||||
|
||||
|
||||
#if 0
|
||||
static void ifbw_print(void) {
|
||||
IFBW *ptr = ifbw;
|
||||
while (ptr) {
|
||||
printf("#%s#\n", ptr->txt);
|
||||
ptr = ptr->next;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
static void ifbw_add(IFBW *ptr) {
|
||||
assert(ptr);
|
||||
|
||||
if (ifbw != NULL)
|
||||
ptr->next = ifbw;
|
||||
ifbw = ptr;
|
||||
}
|
||||
|
||||
|
||||
IFBW *ifbw_find(const char *dev) {
|
||||
assert(dev);
|
||||
int len = strlen(dev);
|
||||
assert(len);
|
||||
|
||||
if (ifbw == NULL)
|
||||
return NULL;
|
||||
|
||||
IFBW *ptr = ifbw;
|
||||
while (ptr) {
|
||||
if (strncmp(ptr->txt, dev, len) == 0 && ptr->txt[len] == ':')
|
||||
return ptr;
|
||||
ptr = ptr->next;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
void ifbw_remove(IFBW *r) {
|
||||
if (ifbw == NULL)
|
||||
return;
|
||||
|
||||
// remove the first element
|
||||
if (ifbw == r) {
|
||||
ifbw = ifbw->next;
|
||||
return;
|
||||
}
|
||||
|
||||
// walk the list
|
||||
IFBW *ptr = ifbw->next;
|
||||
IFBW *prev = ifbw;
|
||||
while (ptr) {
|
||||
if (ptr == r) {
|
||||
prev->next = ptr->next;
|
||||
return;
|
||||
}
|
||||
|
||||
prev = ptr;
|
||||
ptr = ptr->next;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
int fibw_count(viod) {
|
||||
int rv = 0;
|
||||
IFBW *ptr = ifbw;
|
||||
|
||||
while (ptr) {
|
||||
rv++;
|
||||
ptr = ptr->next;
|
||||
}
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
|
||||
//***********************************
|
||||
// shm file handling
|
||||
//***********************************
|
||||
void shm_create_firejail_dir(void) {
|
||||
struct stat s;
|
||||
if (stat("/dev/shm/firejail", &s) == -1) {
|
||||
/* coverity[toctou] */
|
||||
if (mkdir("/dev/shm/firejail", 0777) == -1)
|
||||
errExit("mkdir");
|
||||
if (chown("/dev/shm/firejail", 0, 0) == -1)
|
||||
errExit("chown");
|
||||
}
|
||||
else { // check /dev/shm/firejail directory belongs to root end exit if doesn't!
|
||||
if (s.st_uid != 0 || s.st_gid != 0) {
|
||||
fprintf(stderr, "Error: non-root %s directory, exiting...\n", "/dev/shm/firejail");
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void shm_create_bandwidth_file(pid_t pid) {
|
||||
char *fname;
|
||||
if (asprintf(&fname, "/dev/shm/firejail/%d-bandwidth", (int) pid) == -1)
|
||||
errExit("asprintf");
|
||||
|
||||
// if the file already exists, do nothing
|
||||
struct stat s;
|
||||
if (stat(fname, &s) == 0) {
|
||||
free(fname);
|
||||
return;
|
||||
}
|
||||
|
||||
// create an empty file and set mod and ownership
|
||||
/* coverity[toctou] */
|
||||
FILE *fp = fopen(fname, "w");
|
||||
if (fp) {
|
||||
fclose(fp);
|
||||
|
||||
/* coverity[toctou] */
|
||||
if (chmod(fname, 0644) == -1)
|
||||
errExit("chmod");
|
||||
/* coverity[toctou] */
|
||||
if (chown(fname, 0, 0) == -1)
|
||||
errExit("chown");
|
||||
}
|
||||
else {
|
||||
fprintf(stderr, "Error: cannot create bandwidth file in /dev/shm/firejail directory\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
free(fname);
|
||||
}
|
||||
|
||||
// delete shm bandwidth file
|
||||
void bandwidth_shm_del_file(pid_t pid) {
|
||||
char *fname;
|
||||
if (asprintf(&fname, "/dev/shm/firejail/%d-bandwidth", (int) pid) == -1)
|
||||
errExit("asprintf");
|
||||
unlink(fname);
|
||||
free(fname);
|
||||
}
|
||||
|
||||
void network_shm_del_file(pid_t pid) {
|
||||
char *fname;
|
||||
if (asprintf(&fname, "/dev/shm/firejail/%d-netmap", (int) pid) == -1)
|
||||
errExit("asprintf");
|
||||
unlink(fname);
|
||||
free(fname);
|
||||
}
|
||||
|
||||
void network_shm_set_file(pid_t pid) {
|
||||
char *fname;
|
||||
if (asprintf(&fname, "/dev/shm/firejail/%d-netmap", (int) pid) == -1)
|
||||
errExit("asprintf");
|
||||
|
||||
// create an empty file and set mod and ownership
|
||||
FILE *fp = fopen(fname, "w");
|
||||
if (fp) {
|
||||
if (cfg.bridge0.configured)
|
||||
fprintf(fp, "%s:%s\n", cfg.bridge0.dev, cfg.bridge0.devsandbox);
|
||||
if (cfg.bridge1.configured)
|
||||
fprintf(fp, "%s:%s\n", cfg.bridge1.dev, cfg.bridge1.devsandbox);
|
||||
if (cfg.bridge2.configured)
|
||||
fprintf(fp, "%s:%s\n", cfg.bridge2.dev, cfg.bridge2.devsandbox);
|
||||
if (cfg.bridge3.configured)
|
||||
fprintf(fp, "%s:%s\n", cfg.bridge3.dev, cfg.bridge3.devsandbox);
|
||||
fclose(fp);
|
||||
|
||||
if (chmod(fname, 0644) == -1)
|
||||
errExit("chmod");
|
||||
if (chown(fname, 0, 0) == -1)
|
||||
errExit("chown");
|
||||
}
|
||||
else {
|
||||
fprintf(stderr, "Error: cannot create network map file in /dev/shm/firejail directory\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
free(fname);
|
||||
}
|
||||
|
||||
|
||||
void shm_read_bandwidth_file(pid_t pid) {
|
||||
assert(ifbw == NULL);
|
||||
|
||||
char *fname;
|
||||
if (asprintf(&fname, "/dev/shm/firejail/%d-bandwidth", (int) pid) == -1)
|
||||
errExit("asprintf");
|
||||
|
||||
FILE *fp = fopen(fname, "r");
|
||||
if (fp) {
|
||||
char buf[1024];
|
||||
while (fgets(buf, 1024,fp)) {
|
||||
// remove '\n'
|
||||
char *ptr = strchr(buf, '\n');
|
||||
if (ptr)
|
||||
*ptr = '\0';
|
||||
if (strlen(buf) == 0)
|
||||
continue;
|
||||
|
||||
// create a new IFBW entry
|
||||
IFBW *ifbw_new = malloc(sizeof(IFBW));
|
||||
if (!ifbw_new)
|
||||
errExit("malloc");
|
||||
memset(ifbw_new, 0, sizeof(IFBW));
|
||||
ifbw_new->txt = strdup(buf);
|
||||
if (!ifbw_new->txt)
|
||||
errExit("strdup");
|
||||
|
||||
// add it to the linked list
|
||||
ifbw_add(ifbw_new);
|
||||
}
|
||||
|
||||
fclose(fp);
|
||||
}
|
||||
}
|
||||
|
||||
void shm_write_bandwidth_file(pid_t pid) {
|
||||
if (ifbw == NULL)
|
||||
return; // nothing to do
|
||||
|
||||
char *fname;
|
||||
if (asprintf(&fname, "/dev/shm/firejail/%d-bandwidth", (int) pid) == -1)
|
||||
errExit("asprintf");
|
||||
|
||||
FILE *fp = fopen(fname, "w");
|
||||
if (fp) {
|
||||
IFBW *ptr = ifbw;
|
||||
while (ptr) {
|
||||
fprintf(fp, "%s\n", ptr->txt);
|
||||
ptr = ptr->next;
|
||||
}
|
||||
fclose(fp);
|
||||
}
|
||||
else {
|
||||
fprintf(stderr, "Error: cannot write bandwidht file %s\n", fname);
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
//***********************************
|
||||
// add or remove interfaces
|
||||
//***********************************
|
||||
|
||||
// remove interface from shm file
|
||||
void bandwidth_shm_remove(pid_t pid, const char *dev) {
|
||||
// create bandwidth directory & file in case they are not in the filesystem yet
|
||||
shm_create_firejail_dir();
|
||||
shm_create_bandwidth_file(pid);
|
||||
|
||||
// read bandwidth file
|
||||
shm_read_bandwidth_file(pid);
|
||||
|
||||
// find the element and remove it
|
||||
IFBW *elem = ifbw_find(dev);
|
||||
if (elem) {
|
||||
ifbw_remove(elem);
|
||||
shm_write_bandwidth_file(pid) ;
|
||||
}
|
||||
|
||||
// remove the file if there are no entries in the list
|
||||
if (ifbw == NULL) {
|
||||
bandwidth_shm_del_file(pid);
|
||||
}
|
||||
}
|
||||
|
||||
// add interface to shm file
|
||||
void bandwidth_shm_set(pid_t pid, const char *dev, int down, int up) {
|
||||
// create bandwidth directory & file in case they are not in the filesystem yet
|
||||
shm_create_firejail_dir();
|
||||
shm_create_bandwidth_file(pid);
|
||||
|
||||
// create the new text entry
|
||||
char *txt;
|
||||
if (asprintf(&txt, "%s: RX %dKB/s, TX %dKB/s", dev, down, up) == -1)
|
||||
errExit("asprintf");
|
||||
|
||||
// read bandwidth file
|
||||
shm_read_bandwidth_file(pid);
|
||||
|
||||
// look for an existing entry and replace the text
|
||||
IFBW *ptr = ifbw_find(dev);
|
||||
if (ptr) {
|
||||
assert(ptr->txt);
|
||||
free(ptr->txt);
|
||||
ptr->txt = txt;
|
||||
}
|
||||
// ... or add a new entry
|
||||
else {
|
||||
IFBW *ifbw_new = malloc(sizeof(IFBW));
|
||||
if (!ifbw_new)
|
||||
errExit("malloc");
|
||||
memset(ifbw_new, 0, sizeof(IFBW));
|
||||
ifbw_new->txt = txt;
|
||||
|
||||
// add it to the linked list
|
||||
ifbw_add(ifbw_new);
|
||||
}
|
||||
shm_write_bandwidth_file(pid) ;
|
||||
}
|
||||
|
||||
|
||||
//***********************************
|
||||
// command execution
|
||||
//***********************************
|
||||
void bandwidth_name(const char *name, const char *command, const char *dev, int down, int up) {
|
||||
if (!name || strlen(name) == 0) {
|
||||
fprintf(stderr, "Error: invalid sandbox name\n");
|
||||
exit(1);
|
||||
}
|
||||
pid_t pid;
|
||||
if (name2pid(name, &pid)) {
|
||||
fprintf(stderr, "Error: cannot find sandbox %s\n", name);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
bandwidth_pid(pid, command, dev, down, up);
|
||||
}
|
||||
|
||||
void bandwidth_pid(pid_t pid, const char *command, const char *dev, int down, int up) {
|
||||
//************************
|
||||
// verify sandbox
|
||||
//************************
|
||||
char *comm = pid_proc_comm(pid);
|
||||
if (!comm) {
|
||||
fprintf(stderr, "Error: cannot find sandbox\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
// remove \n and check for firejail sandbox
|
||||
char *ptr = strchr(comm, '\n');
|
||||
if (ptr)
|
||||
*ptr = '\0';
|
||||
if (strcmp(comm, "firejail") != 0) {
|
||||
fprintf(stderr, "Error: cannot find sandbox\n");
|
||||
exit(1);
|
||||
}
|
||||
free(comm);
|
||||
|
||||
// check network namespace
|
||||
char *cmd = pid_proc_cmdline(pid);
|
||||
if (!cmd || strstr(cmd, "--net") == NULL) {
|
||||
fprintf(stderr, "Error: the sandbox doesn't use a new network namespace\n");
|
||||
exit(1);
|
||||
}
|
||||
free(cmd);
|
||||
|
||||
|
||||
//************************
|
||||
// join the network namespace
|
||||
//************************
|
||||
pid_t child;
|
||||
if (find_child(pid, &child) == -1) {
|
||||
fprintf(stderr, "Error: cannot join the network namespace\n");
|
||||
exit(1);
|
||||
}
|
||||
if (join_namespace(child, "net")) {
|
||||
fprintf(stderr, "Error: cannot join the network namespace\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
// set shm file
|
||||
if (strcmp(command, "set") == 0)
|
||||
bandwidth_shm_set(pid, dev, down, up);
|
||||
else if (strcmp(command, "clear") == 0)
|
||||
bandwidth_shm_remove(pid, dev);
|
||||
|
||||
//************************
|
||||
// build command
|
||||
//************************
|
||||
char *devname = NULL;
|
||||
if (dev) {
|
||||
// read shm network map file
|
||||
char *fname;
|
||||
if (asprintf(&fname, "/dev/shm/firejail/%d-netmap", (int) pid) == -1)
|
||||
errExit("asprintf");
|
||||
FILE *fp = fopen(fname, "r");
|
||||
if (!fp) {
|
||||
fprintf(stderr, "Error: cannot read netowk map filel %s\n", fname);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
char buf[1024];
|
||||
int len = strlen(dev);
|
||||
while (fgets(buf, 1024, fp)) {
|
||||
// remove '\n'
|
||||
char *ptr = strchr(buf, '\n');
|
||||
if (ptr)
|
||||
*ptr = '\0';
|
||||
if (*buf == '\0')
|
||||
break;
|
||||
|
||||
if (strncmp(buf, dev, len) == 0 && buf[len] == ':') {
|
||||
devname = strdup(buf + len + 1);
|
||||
if (!devname)
|
||||
errExit("strdup");
|
||||
// check device in namespace
|
||||
if (if_nametoindex(devname) == 0) {
|
||||
fprintf(stderr, "Error: cannot find network device %s\n", devname);
|
||||
exit(1);
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
free(fname);
|
||||
fclose(fp);
|
||||
}
|
||||
|
||||
// build fshaper.sh command
|
||||
cmd = NULL;
|
||||
if (devname) {
|
||||
if (strcmp(command, "set") == 0) {
|
||||
if (asprintf(&cmd, "%s/lib/firejail/fshaper.sh --%s %s %d %d",
|
||||
PREFIX, command, devname, down, up) == -1)
|
||||
errExit("asprintf");
|
||||
}
|
||||
else {
|
||||
if (asprintf(&cmd, "%s/lib/firejail/fshaper.sh --%s %s",
|
||||
PREFIX, command, devname) == -1)
|
||||
errExit("asprintf");
|
||||
}
|
||||
}
|
||||
else {
|
||||
if (asprintf(&cmd, "%s/lib/firejail/fshaper.sh --%s", PREFIX, command) == -1)
|
||||
errExit("asprintf");
|
||||
}
|
||||
assert(cmd);
|
||||
|
||||
// wipe out environment variables
|
||||
environ = NULL;
|
||||
|
||||
//************************
|
||||
// build command
|
||||
//************************
|
||||
// elevate privileges
|
||||
if (setreuid(0, 0))
|
||||
errExit("setreuid");
|
||||
if (setregid(0, 0))
|
||||
errExit("setregid");
|
||||
|
||||
char *arg[4];
|
||||
arg[0] = "/bin/bash";
|
||||
arg[1] = "-c";
|
||||
arg[2] = cmd;
|
||||
arg[3] = NULL;
|
||||
execvp("/bin/bash", arg);
|
||||
|
||||
// it will never get here
|
||||
exit(0);
|
||||
}
|
||||
453
src/firejail/caps.c
Normal file
453
src/firejail/caps.c
Normal file
|
|
@ -0,0 +1,453 @@
|
|||
/*
|
||||
* Copyright (C) 2014, 2015 netblue30 (netblue30@yahoo.com)
|
||||
*
|
||||
* 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 <errno.h>
|
||||
#include <linux/filter.h>
|
||||
#include <stddef.h>
|
||||
#include <linux/capability.h>
|
||||
#include <linux/audit.h>
|
||||
#include <sys/prctl.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
|
||||
extern int capget(cap_user_header_t hdrp, cap_user_data_t datap);
|
||||
extern int capset(cap_user_header_t hdrp, const cap_user_data_t datap);
|
||||
|
||||
|
||||
typedef struct {
|
||||
char *name;
|
||||
int nr;
|
||||
} CapsEntry;
|
||||
|
||||
static CapsEntry capslist[] = {
|
||||
//
|
||||
// code generated using tools/extract-caps
|
||||
// updated manually based on kernel 3.18/include/linux/capability.h (added block suspend and audit_read
|
||||
//
|
||||
#ifdef CAP_CHOWN
|
||||
{"chown", CAP_CHOWN },
|
||||
#endif
|
||||
#ifdef CAP_DAC_OVERRIDE
|
||||
{"dac_override", CAP_DAC_OVERRIDE },
|
||||
#endif
|
||||
#ifdef CAP_DAC_READ_SEARCH
|
||||
{"dac_read_search", CAP_DAC_READ_SEARCH },
|
||||
#endif
|
||||
#ifdef CAP_FOWNER
|
||||
{"fowner", CAP_FOWNER },
|
||||
#endif
|
||||
#ifdef CAP_FSETID
|
||||
{"fsetid", CAP_FSETID },
|
||||
#endif
|
||||
#ifdef CAP_KILL
|
||||
{"kill", CAP_KILL },
|
||||
#endif
|
||||
#ifdef CAP_SETGID
|
||||
{"setgid", CAP_SETGID },
|
||||
#endif
|
||||
#ifdef CAP_SETUID
|
||||
{"setuid", CAP_SETUID },
|
||||
#endif
|
||||
#ifdef CAP_SETPCAP
|
||||
{"setpcap", CAP_SETPCAP },
|
||||
#endif
|
||||
#ifdef CAP_LINUX_IMMUTABLE
|
||||
{"linux_immutable", CAP_LINUX_IMMUTABLE },
|
||||
#endif
|
||||
#ifdef CAP_NET_BIND_SERVICE
|
||||
{"net_bind_service", CAP_NET_BIND_SERVICE },
|
||||
#endif
|
||||
#ifdef CAP_NET_BROADCAST
|
||||
{"net_broadcast", CAP_NET_BROADCAST },
|
||||
#endif
|
||||
#ifdef CAP_NET_ADMIN
|
||||
{"net_admin", CAP_NET_ADMIN },
|
||||
#endif
|
||||
#ifdef CAP_NET_RAW
|
||||
{"net_raw", CAP_NET_RAW },
|
||||
#endif
|
||||
#ifdef CAP_IPC_LOCK
|
||||
{"ipc_lock", CAP_IPC_LOCK },
|
||||
#endif
|
||||
#ifdef CAP_IPC_OWNER
|
||||
{"ipc_owner", CAP_IPC_OWNER },
|
||||
#endif
|
||||
#ifdef CAP_SYS_MODULE
|
||||
{"sys_module", CAP_SYS_MODULE },
|
||||
#endif
|
||||
#ifdef CAP_SYS_RAWIO
|
||||
{"sys_rawio", CAP_SYS_RAWIO },
|
||||
#endif
|
||||
#ifdef CAP_SYS_CHROOT
|
||||
{"sys_chroot", CAP_SYS_CHROOT },
|
||||
#endif
|
||||
#ifdef CAP_SYS_PTRACE
|
||||
{"sys_ptrace", CAP_SYS_PTRACE },
|
||||
#endif
|
||||
#ifdef CAP_SYS_PACCT
|
||||
{"sys_pacct", CAP_SYS_PACCT },
|
||||
#endif
|
||||
#ifdef CAP_SYS_ADMIN
|
||||
{"sys_admin", CAP_SYS_ADMIN },
|
||||
#endif
|
||||
#ifdef CAP_SYS_BOOT
|
||||
{"sys_boot", CAP_SYS_BOOT },
|
||||
#endif
|
||||
#ifdef CAP_SYS_NICE
|
||||
{"sys_nice", CAP_SYS_NICE },
|
||||
#endif
|
||||
#ifdef CAP_SYS_RESOURCE
|
||||
{"sys_resource", CAP_SYS_RESOURCE },
|
||||
#endif
|
||||
#ifdef CAP_SYS_TIME
|
||||
{"sys_time", CAP_SYS_TIME },
|
||||
#endif
|
||||
#ifdef CAP_SYS_TTY_CONFIG
|
||||
{"sys_tty_config", CAP_SYS_TTY_CONFIG },
|
||||
#endif
|
||||
#ifdef CAP_MKNOD
|
||||
{"mknod", CAP_MKNOD },
|
||||
#endif
|
||||
#ifdef CAP_LEASE
|
||||
{"lease", CAP_LEASE },
|
||||
#endif
|
||||
#ifdef CAP_AUDIT_WRITE
|
||||
{"audit_write", CAP_AUDIT_WRITE },
|
||||
#endif
|
||||
#ifdef CAP_AUDIT_CONTROL
|
||||
{"audit_control", CAP_AUDIT_CONTROL },
|
||||
#endif
|
||||
#ifdef CAP_SETFCAP
|
||||
{"setfcap", CAP_SETFCAP },
|
||||
#endif
|
||||
#ifdef CAP_MAC_OVERRIDE
|
||||
{"mac_override", CAP_MAC_OVERRIDE },
|
||||
#endif
|
||||
#ifdef CAP_MAC_ADMIN
|
||||
{"mac_admin", CAP_MAC_ADMIN },
|
||||
#endif
|
||||
#ifdef CAP_SYSLOG
|
||||
{"syslog", CAP_SYSLOG },
|
||||
#endif
|
||||
#ifdef CAP_WAKE_ALARM
|
||||
{"wake_alarm", CAP_WAKE_ALARM },
|
||||
#endif
|
||||
// not in Debian 7
|
||||
#ifdef CAP_BLOCK_SUSPEND
|
||||
{"block_suspend", CAP_BLOCK_SUSPEND },
|
||||
#else
|
||||
{"block_suspend", 36 },
|
||||
#endif
|
||||
#ifdef CAP_AUDIT_READ
|
||||
{"audit_read", CAP_AUDIT_READ },
|
||||
#else
|
||||
{"audit_read", 37 },
|
||||
#endif
|
||||
|
||||
//
|
||||
// end of generated code
|
||||
//
|
||||
}; // end of capslist
|
||||
|
||||
const char *caps_find_nr(int nr) {
|
||||
int i;
|
||||
int elems = sizeof(capslist) / sizeof(capslist[0]);
|
||||
for (i = 0; i < elems; i++) {
|
||||
if (nr == capslist[i].nr)
|
||||
return capslist[i].name;
|
||||
}
|
||||
|
||||
return "unknown";
|
||||
}
|
||||
|
||||
// return -1 if error, or syscall number
|
||||
static int caps_find_name(const char *name) {
|
||||
int i;
|
||||
int elems = sizeof(capslist) / sizeof(capslist[0]);
|
||||
for (i = 0; i < elems; i++) {
|
||||
if (strcmp(name, capslist[i].name) == 0)
|
||||
return capslist[i].nr;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
// return 1 if error, 0 if OK
|
||||
int caps_check_list(const char *clist, void (*callback)(int)) {
|
||||
// don't allow empty lists
|
||||
if (clist == NULL || *clist == '\0') {
|
||||
fprintf(stderr, "Error: empty capabilities lists are not allowed\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
// work on a copy of the string
|
||||
char *str = strdup(clist);
|
||||
if (!str)
|
||||
errExit("strdup");
|
||||
|
||||
char *ptr = str;
|
||||
char *start = str;
|
||||
while (*ptr != '\0') {
|
||||
if (islower(*ptr) || isdigit(*ptr) || *ptr == '_')
|
||||
;
|
||||
else if (*ptr == ',') {
|
||||
*ptr = '\0';
|
||||
int nr = caps_find_name(start);
|
||||
if (nr == -1) {
|
||||
fprintf(stderr, "Error: capability %s not found\n", start);
|
||||
free(str);
|
||||
return -1;
|
||||
}
|
||||
else if (callback != NULL)
|
||||
callback(nr);
|
||||
|
||||
start = ptr + 1;
|
||||
}
|
||||
ptr++;
|
||||
}
|
||||
if (*start != '\0') {
|
||||
int nr = caps_find_name(start);
|
||||
if (nr == -1) {
|
||||
fprintf(stderr, "Error: capability %s not found\n", start);
|
||||
free(str);
|
||||
return -1;
|
||||
}
|
||||
else if (callback != NULL)
|
||||
callback(nr);
|
||||
}
|
||||
|
||||
free(str);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void caps_print(void) {
|
||||
int i;
|
||||
int elems = sizeof(capslist) / sizeof(capslist[0]);
|
||||
|
||||
// check current caps supported by the kernel
|
||||
int cnt = 0;
|
||||
unsigned long cap;
|
||||
for (cap=0; cap <= 63; cap++) {
|
||||
int code = prctl(PR_CAPBSET_DROP, cap, 0, 0, 0);
|
||||
if (code == 0)
|
||||
cnt++;
|
||||
}
|
||||
printf("Your kernel supports %d capabilities.\n", cnt);
|
||||
|
||||
for (i = 0; i < elems; i++) {
|
||||
printf("%d\t- %s\n", capslist[i].nr, capslist[i].name);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
// enabled by default
|
||||
int caps_default_filter(void) {
|
||||
// drop capabilities
|
||||
if (prctl(PR_CAPBSET_DROP, CAP_SYS_MODULE, 0, 0, 0) && arg_debug)
|
||||
fprintf(stderr, "Warning: cannot drop CAP_SYS_MODULE");
|
||||
else if (arg_debug)
|
||||
printf("Drop CAP_SYS_MODULE\n");
|
||||
|
||||
if (prctl(PR_CAPBSET_DROP, CAP_SYS_RAWIO, 0, 0, 0) && arg_debug)
|
||||
fprintf(stderr, "Warning: cannot drop CAP_SYS_RAWIO");
|
||||
else if (arg_debug)
|
||||
printf("Drop CAP_SYS_RAWIO\n");
|
||||
|
||||
if (prctl(PR_CAPBSET_DROP, CAP_SYS_BOOT, 0, 0, 0) && arg_debug)
|
||||
fprintf(stderr, "Warning: cannot drop CAP_SYS_BOOT");
|
||||
else if (arg_debug)
|
||||
printf("Drop CAP_SYS_BOOT\n");
|
||||
|
||||
if (prctl(PR_CAPBSET_DROP, CAP_SYS_NICE, 0, 0, 0) && arg_debug)
|
||||
fprintf(stderr, "Warning: cannot drop CAP_SYS_NICE");
|
||||
else if (arg_debug)
|
||||
printf("Drop CAP_SYS_NICE\n");
|
||||
|
||||
if (prctl(PR_CAPBSET_DROP, CAP_SYS_TTY_CONFIG, 0, 0, 0) && arg_debug)
|
||||
fprintf(stderr, "Warning: cannot drop CAP_SYS_TTY_CONFIG");
|
||||
else if (arg_debug)
|
||||
printf("Drop CAP_SYS_TTY_CONFIG\n");
|
||||
|
||||
if (prctl(PR_CAPBSET_DROP, CAP_SYSLOG, 0, 0, 0) && arg_debug)
|
||||
fprintf(stderr, "Warning: cannot drop CAP_SYSLOG");
|
||||
else if (arg_debug)
|
||||
printf("Drop CAP_SYSLOG\n");
|
||||
|
||||
if (prctl(PR_CAPBSET_DROP, CAP_MKNOD, 0, 0, 0) && arg_debug)
|
||||
fprintf(stderr, "Warning: cannot drop CAP_MKNOD");
|
||||
else if (arg_debug)
|
||||
printf("Drop CAP_MKNOD\n");
|
||||
|
||||
if (prctl(PR_CAPBSET_DROP, CAP_SYS_ADMIN, 0, 0, 0) && arg_debug)
|
||||
fprintf(stderr, "Warning: cannot drop CAP_SYS_ADMIN");
|
||||
else if (arg_debug)
|
||||
printf("Drop CAP_SYS_ADMIN\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void caps_drop_all(void) {
|
||||
if (arg_debug)
|
||||
printf("Droping all capabilities\n");
|
||||
|
||||
unsigned long cap;
|
||||
for (cap=0; cap <= 63; cap++) {
|
||||
int code = prctl(PR_CAPBSET_DROP, cap, 0, 0, 0);
|
||||
if (code == -1 && errno != EINVAL)
|
||||
errExit("PR_CAPBSET_DROP");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void caps_set(uint64_t caps) {
|
||||
if (arg_debug)
|
||||
printf("Set caps filter %llx\n", (unsigned long long) caps);
|
||||
|
||||
unsigned long i;
|
||||
uint64_t mask = 1LLU;
|
||||
for (i = 0; i < 64; i++, mask <<= 1) {
|
||||
if ((mask & caps) == 0) {
|
||||
int code = prctl(PR_CAPBSET_DROP, i, 0, 0, 0);
|
||||
if (code == -1 && errno != EINVAL)
|
||||
errExit("PR_CAPBSET_DROP");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static uint64_t filter;
|
||||
|
||||
static void caps_set_bit(int nr) {
|
||||
uint64_t mask = 1LLU << nr;
|
||||
filter |= mask;
|
||||
}
|
||||
static void caps_reset_bit(int nr) {
|
||||
uint64_t mask = 1LLU << nr;
|
||||
filter &= ~mask;
|
||||
}
|
||||
|
||||
void caps_drop_list(const char *clist) {
|
||||
filter = 0;
|
||||
filter--;
|
||||
caps_check_list(clist, caps_reset_bit);
|
||||
caps_set(filter);
|
||||
}
|
||||
|
||||
void caps_keep_list(const char *clist) {
|
||||
filter = 0;
|
||||
caps_check_list(clist, caps_set_bit);
|
||||
caps_set(filter);
|
||||
}
|
||||
|
||||
#define MAXBUF 4098
|
||||
static uint64_t extract_caps(int pid) {
|
||||
char *file;
|
||||
if (asprintf(&file, "/proc/%d/status", pid) == -1) {
|
||||
errExit("asprintf");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
FILE *fp = fopen(file, "r");
|
||||
if (!fp) {
|
||||
printf("Error: cannot open %s\n", file);
|
||||
free(file);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
char buf[MAXBUF];
|
||||
while (fgets(buf, MAXBUF, fp)) {
|
||||
if (strncmp(buf, "CapBnd:", 7) == 0) {
|
||||
char *ptr = buf + 8;
|
||||
unsigned long long val;
|
||||
sscanf(ptr, "%llx", &val);
|
||||
free(file);
|
||||
fclose(fp);
|
||||
return val;
|
||||
}
|
||||
}
|
||||
fclose(fp);
|
||||
free(file);
|
||||
printf("Error: cannot read caps configuration\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
|
||||
void caps_print_filter_name(const char *name) {
|
||||
if (!name || strlen(name) == 0) {
|
||||
fprintf(stderr, "Error: invalid sandbox name\n");
|
||||
exit(1);
|
||||
}
|
||||
pid_t pid;
|
||||
if (name2pid(name, &pid)) {
|
||||
fprintf(stderr, "Error: cannot find sandbox %s\n", name);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
caps_print_filter(pid);
|
||||
}
|
||||
|
||||
void caps_print_filter(pid_t pid) {
|
||||
// if the pid is that of a firejail process, use the pid of the first child process
|
||||
char *comm = pid_proc_comm(pid);
|
||||
if (comm) {
|
||||
// remove \n
|
||||
char *ptr = strchr(comm, '\n');
|
||||
if (ptr)
|
||||
*ptr = '\0';
|
||||
if (strcmp(comm, "firejail") == 0) {
|
||||
pid_t child;
|
||||
if (find_child(pid, &child) == 0) {
|
||||
pid = child;
|
||||
}
|
||||
}
|
||||
free(comm);
|
||||
}
|
||||
|
||||
// check privileges for non-root users
|
||||
uid_t uid = getuid();
|
||||
if (uid != 0) {
|
||||
struct stat s;
|
||||
char *dir;
|
||||
if (asprintf(&dir, "/proc/%u/ns", pid) == -1)
|
||||
errExit("asprintf");
|
||||
if (stat(dir, &s) < 0)
|
||||
errExit("stat");
|
||||
if (s.st_uid != uid) {
|
||||
printf("Error: permission denied.\n");
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
uint64_t caps = extract_caps(pid);
|
||||
drop_privs(1);
|
||||
|
||||
int i;
|
||||
uint64_t mask;
|
||||
int elems = sizeof(capslist) / sizeof(capslist[0]);
|
||||
for (i = 0, mask = 1; i < elems; i++, mask <<= 1) {
|
||||
printf("%-18.18s - %s\n", capslist[i].name, (mask & caps)? "enabled": "disabled");
|
||||
}
|
||||
|
||||
exit(0);
|
||||
}
|
||||
118
src/firejail/cgroup.c
Normal file
118
src/firejail/cgroup.c
Normal file
|
|
@ -0,0 +1,118 @@
|
|||
/*
|
||||
* Copyright (C) 2014, 2015 netblue30 (netblue30@yahoo.com)
|
||||
*
|
||||
* 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 <sys/stat.h>
|
||||
|
||||
#define MAXBUF 4096
|
||||
|
||||
void save_cgroup(void) {
|
||||
if (cfg.cgroup == NULL)
|
||||
return;
|
||||
|
||||
char *fname;
|
||||
if (asprintf(&fname, "%s/cgroup", MNT_DIR) == -1)
|
||||
errExit(fname);
|
||||
|
||||
FILE *fp = fopen(fname, "w");
|
||||
if (fp) {
|
||||
fprintf(fp, "%s", cfg.cgroup);
|
||||
fflush(0);
|
||||
fclose(fp);
|
||||
if (chown(fname, 0, 0) < 0)
|
||||
errExit("chown");
|
||||
}
|
||||
else {
|
||||
fprintf(stderr, "Error: cannot save cgroup\n");
|
||||
free(fname);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
free(fname);
|
||||
}
|
||||
|
||||
void load_cgroup(const char *fname) {
|
||||
if (!fname)
|
||||
return;
|
||||
|
||||
FILE *fp = fopen(fname, "r");
|
||||
if (fp) {
|
||||
char buf[MAXBUF];
|
||||
if (fgets(buf, MAXBUF, fp)) {
|
||||
cfg.cgroup = strdup(buf);
|
||||
if (!cfg.cgroup)
|
||||
errExit("strdup");
|
||||
}
|
||||
else
|
||||
goto errout;
|
||||
|
||||
fclose(fp);
|
||||
return;
|
||||
}
|
||||
errout:
|
||||
fprintf(stderr, "Warrning: cannot load control group\n");
|
||||
if (fp)
|
||||
fclose(fp);
|
||||
}
|
||||
|
||||
|
||||
void set_cgroup(const char *path) {
|
||||
// path starts with /sys/fs/cgroup
|
||||
if (strncmp(path, "/sys/fs/cgroup", 14) != 0)
|
||||
goto errout;
|
||||
|
||||
// path ends in tasks
|
||||
char *ptr = strstr(path, "tasks");
|
||||
if (!ptr)
|
||||
goto errout;
|
||||
if (*(ptr + 5) != '\0')
|
||||
goto errout;
|
||||
|
||||
// no .. traversal
|
||||
ptr = strstr(path, "..");
|
||||
if (ptr)
|
||||
goto errout;
|
||||
|
||||
// tasks file exists
|
||||
struct stat s;
|
||||
if (stat(path, &s) == -1)
|
||||
goto errout;
|
||||
|
||||
// task file belongs to the user running the sandbox
|
||||
if (s.st_uid != getuid() && s.st_gid != getgid())
|
||||
goto errout2;
|
||||
|
||||
// add the task to cgroup
|
||||
/* coverity[toctou] */
|
||||
FILE *fp = fopen(path, "a");
|
||||
if (!fp)
|
||||
goto errout;
|
||||
pid_t pid = getpid();
|
||||
int rv = fprintf(fp, "%d\n", pid);
|
||||
(void) rv;
|
||||
fclose(fp);
|
||||
return;
|
||||
|
||||
errout:
|
||||
fprintf(stderr, "Error: invalid cgroup\n");
|
||||
exit(1);
|
||||
errout2:
|
||||
fprintf(stderr, "Error: you don't have permissions to use this control group\n");
|
||||
exit(1);
|
||||
}
|
||||
141
src/firejail/cpu.c
Normal file
141
src/firejail/cpu.c
Normal file
|
|
@ -0,0 +1,141 @@
|
|||
/*
|
||||
* Copyright (C) 2014, 2015 netblue30 (netblue30@yahoo.com)
|
||||
*
|
||||
* 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 <sched.h>
|
||||
|
||||
// converts a numeric cpu value in the corresponding bit mask
|
||||
static void set_cpu(const char *str) {
|
||||
if (strlen(str) == 0)
|
||||
return;
|
||||
|
||||
int val = atoi(str);
|
||||
if (val < 0 || val >= 32) {
|
||||
fprintf(stderr, "Error: invalid cpu number. Accepted values are between 0 and 31.\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
uint32_t mask = 1;
|
||||
int i;
|
||||
for (i = 0; i < val; i++, mask <<= 1);
|
||||
cfg.cpus |= mask;
|
||||
}
|
||||
|
||||
void read_cpu_list(const char *str) {
|
||||
char *tmp = strdup(str);
|
||||
if (tmp == NULL)
|
||||
errExit("strdup");
|
||||
|
||||
char *ptr = tmp;
|
||||
while (*ptr != '\0') {
|
||||
if (*ptr == ',' || isdigit(*ptr))
|
||||
;
|
||||
else {
|
||||
fprintf(stderr, "Error: invalid cpu list\n");
|
||||
exit(1);
|
||||
}
|
||||
ptr++;
|
||||
}
|
||||
|
||||
char *start = tmp;
|
||||
ptr = tmp;
|
||||
while (*ptr != '\0') {
|
||||
if (*ptr == ',') {
|
||||
*ptr = '\0';
|
||||
set_cpu(start);
|
||||
start = ptr + 1;
|
||||
}
|
||||
ptr++;
|
||||
}
|
||||
set_cpu(start);
|
||||
free(tmp);
|
||||
}
|
||||
|
||||
void save_cpu(void) {
|
||||
if (cfg.cpus == 0)
|
||||
return;
|
||||
|
||||
char *fname;
|
||||
if (asprintf(&fname, "%s/cpu", MNT_DIR) == -1)
|
||||
errExit("asprintf");
|
||||
FILE *fp = fopen(fname, "w");
|
||||
if (fp) {
|
||||
fprintf(fp, "%x\n", cfg.cpus);
|
||||
fclose(fp);
|
||||
if (chown(fname, 0, 0) < 0)
|
||||
errExit("chown");
|
||||
}
|
||||
else {
|
||||
fprintf(stderr, "Error: cannot save cpu affinity mask\n");
|
||||
free(fname);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
free(fname);
|
||||
}
|
||||
|
||||
void load_cpu(const char *fname) {
|
||||
if (!fname)
|
||||
return;
|
||||
|
||||
FILE *fp = fopen(fname, "r");
|
||||
if (fp) {
|
||||
unsigned tmp;
|
||||
int rv = fscanf(fp, "%x", &tmp);
|
||||
if (rv)
|
||||
cfg.cpus = (uint32_t) tmp;
|
||||
fclose(fp);
|
||||
}
|
||||
else
|
||||
fprintf(stderr, "Warning: cannot load cpu affinity mask\n");
|
||||
}
|
||||
|
||||
void set_cpu_affinity(void) {
|
||||
// set cpu affinity
|
||||
cpu_set_t mask;
|
||||
CPU_ZERO(&mask);
|
||||
|
||||
int i;
|
||||
uint32_t m = 1;
|
||||
for (i = 0; i < 32; i++, m <<= 1) {
|
||||
if (cfg.cpus & m)
|
||||
CPU_SET(i, &mask);
|
||||
}
|
||||
|
||||
if (sched_setaffinity(0, sizeof(mask), &mask) == -1) {
|
||||
fprintf(stderr, "Warning: cannot set cpu affinity\n");
|
||||
fprintf(stderr, " ");
|
||||
perror("sched_setaffinity");
|
||||
}
|
||||
|
||||
// verify cpu affinity
|
||||
cpu_set_t mask2;
|
||||
CPU_ZERO(&mask2);
|
||||
if (sched_getaffinity(0, sizeof(mask2), &mask2) == -1) {
|
||||
fprintf(stderr, "Warning: cannot verify cpu affinity\n");
|
||||
fprintf(stderr, " ");
|
||||
perror("sched_getaffinity");
|
||||
}
|
||||
else {
|
||||
if (CPU_EQUAL(&mask, &mask2))
|
||||
printf("CPU affinity set\n");
|
||||
else
|
||||
printf("CPU affinity not set\n");
|
||||
}
|
||||
}
|
||||
354
src/firejail/firejail.h
Normal file
354
src/firejail/firejail.h
Normal file
|
|
@ -0,0 +1,354 @@
|
|||
/*
|
||||
* Copyright (C) 2014, 2015 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.
|
||||
*/
|
||||
#ifndef FIREJAIL_H
|
||||
#define FIREJAIL_H
|
||||
#include "../include/common.h"
|
||||
|
||||
#define USELOCK
|
||||
#define FIREJAIL_DIR "/tmp/firejail"
|
||||
#define RO_DIR "/tmp/firejail/firejail.ro.dir"
|
||||
#define RO_FILE "/tmp/firejail/firejail.ro.file"
|
||||
#define MNT_DIR "/tmp/firejail/mnt"
|
||||
#define OVERLAY_DIR "/tmp/firejail/overlay"
|
||||
#define HOME_DIR "/tmp/firejail/mnt/home"
|
||||
#define MAX_INCLUDE_LEVEL 6
|
||||
|
||||
// main.c
|
||||
typedef struct bridge_t {
|
||||
// on the host
|
||||
char *dev; // interface device name: bridge or regular ethernet
|
||||
uint32_t ip; // interface device IP address
|
||||
uint32_t mask; // interface device mask
|
||||
uint8_t mac[6]; // interface mac address
|
||||
|
||||
// inside the sandbox
|
||||
char *devsandbox; // name of the device inside the sandbox
|
||||
uint32_t ipsandbox; // ip address inside the sandbox
|
||||
uint8_t macsandbox[6]; // mac address inside the sandbox
|
||||
uint32_t iprange_start;// iprange arp scan start range
|
||||
uint32_t iprange_end; // iprange arp scan end range
|
||||
|
||||
// flags
|
||||
uint8_t arg_ip_none; // --ip=none
|
||||
uint8_t macvlan; // set by --net=eth0 (or eth1, ...); reset by --net=br0 (or br1, ...)
|
||||
uint8_t configured;
|
||||
uint8_t scan; // set by --scan
|
||||
} Bridge;
|
||||
|
||||
typedef struct profile_entry_t {
|
||||
struct profile_entry_t *next;
|
||||
char *data;
|
||||
}ProfileEntry;
|
||||
|
||||
typedef struct config_t {
|
||||
// user data
|
||||
char *username;
|
||||
char *homedir;
|
||||
|
||||
// filesystem
|
||||
ProfileEntry *profile;
|
||||
char *chrootdir; // chroot directory
|
||||
char *home_private; // private home directory
|
||||
char *home_private_keep; // keep list for private home directory
|
||||
char *cwd; // current working directory
|
||||
|
||||
// networking
|
||||
char *hostname;
|
||||
uint32_t defaultgw; // default gateway
|
||||
Bridge bridge0;
|
||||
Bridge bridge1;
|
||||
Bridge bridge2;
|
||||
Bridge bridge3;
|
||||
uint32_t dns1; // up to 3 IP addresses for dns servers
|
||||
uint32_t dns2;
|
||||
uint32_t dns3;
|
||||
|
||||
// rlimits
|
||||
unsigned rlimit_nofile;
|
||||
unsigned rlimit_nproc;
|
||||
unsigned rlimit_fsize;
|
||||
unsigned rlimit_sigpending;
|
||||
|
||||
// cpu affinity and control groups
|
||||
uint32_t cpus;
|
||||
char *cgroup;
|
||||
|
||||
|
||||
// command line
|
||||
char *command_line;
|
||||
char *command_name;
|
||||
char *shell;
|
||||
char **original_argv;
|
||||
int original_argc;
|
||||
int original_program_index;
|
||||
} Config;
|
||||
extern Config cfg;
|
||||
|
||||
static inline int any_bridge_configured(void) {
|
||||
if (cfg.bridge3.configured || cfg.bridge2.configured || cfg.bridge1.configured || cfg.bridge0.configured)
|
||||
return 1;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
extern int arg_private; // mount private /home and /tmp directory
|
||||
extern int arg_debug; // print debug messages
|
||||
extern int arg_nonetwork; // --net=none
|
||||
extern int arg_command; // -c
|
||||
extern int arg_overlay; // --overlay
|
||||
extern int arg_zsh; // use zsh as default shell
|
||||
extern int arg_csh; // use csh as default shell
|
||||
|
||||
extern int arg_seccomp; // enable default seccomp filter
|
||||
extern char *arg_seccomp_list;// optional seccomp list on top of default filter
|
||||
extern char *arg_seccomp_list_drop; // seccomp drop list
|
||||
extern char *arg_seccomp_list_keep; // seccomp keep list
|
||||
|
||||
extern int arg_caps_default_filter; // enable default capabilities filter
|
||||
extern int arg_caps_drop; // drop list
|
||||
extern int arg_caps_drop_all; // drop all capabilities
|
||||
extern int arg_caps_keep; // keep list
|
||||
extern char *arg_caps_list; // optional caps list
|
||||
|
||||
extern int arg_trace; // syscall tracing support
|
||||
extern int arg_rlimit_nofile; // rlimit nofile
|
||||
extern int arg_rlimit_nproc; // rlimit nproc
|
||||
extern int arg_rlimit_fsize; // rlimit fsize
|
||||
extern int arg_rlimit_sigpending;// rlimit sigpending
|
||||
extern int arg_nox11; // kill the program if x11 unix domain socket is accessed
|
||||
extern int arg_nodbus; // kill the program if D-Bus is accessed
|
||||
extern int arg_nogroups; // disable supplementary groups
|
||||
extern int arg_noroot; // create a new user namespace and disable root user
|
||||
extern int arg_netfilter; // enable netfilter
|
||||
extern char *arg_netfilter_file; // netfilter file
|
||||
extern int arg_doubledash; // double dash
|
||||
extern int arg_shell_none; // run the program directly without a shell
|
||||
extern int arg_private_dev; // private dev directory
|
||||
extern int arg_scan; // arp-scan all interfaces
|
||||
|
||||
extern int parent_to_child_fds[2];
|
||||
extern int child_to_parent_fds[2];
|
||||
extern pid_t sandbox_pid;
|
||||
|
||||
|
||||
|
||||
#define MAX_ARGS 128 // maximum number of command arguments (argc)
|
||||
extern char *fullargv[MAX_ARGS];
|
||||
extern int fullargc;
|
||||
|
||||
// main.c
|
||||
void check_user_namespace(void);
|
||||
|
||||
// sandbox.c
|
||||
int sandbox(void* sandbox_arg);
|
||||
|
||||
// network_main.c
|
||||
void net_configure_bridge(Bridge *br, char *dev_name);
|
||||
void net_configure_sandbox_ip(Bridge *br);
|
||||
void net_configure_veth_pair(Bridge *br, const char *ifname, pid_t child);
|
||||
void net_check_cfg(void);
|
||||
void net_dns_print_name(const char *name);
|
||||
void net_dns_print(pid_t pid);
|
||||
|
||||
// network.c
|
||||
void net_if_up(const char *ifname);
|
||||
void net_if_ip(const char *ifname, uint32_t ip, uint32_t mask);
|
||||
int net_get_if_addr(const char *bridge, uint32_t *ip, uint32_t *mask, uint8_t mac[6]);
|
||||
int net_add_route(uint32_t dest, uint32_t mask, uint32_t gw);
|
||||
void net_ifprint(void);
|
||||
void net_bridge_add_interface(const char *bridge, const char *dev);
|
||||
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]);
|
||||
|
||||
// fs.c
|
||||
// build /tmp/firejail directory
|
||||
void fs_build_firejail_dir(void);
|
||||
// build /tmp/firejail/mnt directory
|
||||
void fs_build_mnt_dir(void);
|
||||
// blacklist files or directoies by mounting empty files on top of them
|
||||
void fs_blacklist(const char *homedir);
|
||||
//void fs_blacklist(char **blacklist, const char *homedir);
|
||||
// remount a directory read-only
|
||||
void fs_rdonly(const char *dir);
|
||||
// mount /proc and /sys directories
|
||||
void fs_proc_sys_dev_boot(void);
|
||||
// build a basic read-only filesystem
|
||||
void fs_basic_fs(void);
|
||||
// mount overlayfs on top of / directory
|
||||
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);
|
||||
|
||||
// profile.c
|
||||
// find and read the profile specified by name from dir directory
|
||||
int profile_find(const char *name, const char *dir);
|
||||
// read a profile file
|
||||
void profile_read(const char *fname, const char *skip1, const char *skip2);
|
||||
// check profile line; if line == 0, this was generated from a command line option
|
||||
// return 1 if the command is to be added to the linked list of profile commands
|
||||
// return 0 if the command was already executed inside the function
|
||||
int profile_check_line(char *ptr, int lineno);
|
||||
// add a profile entry in cfg.profile list; use str to populate the list
|
||||
void profile_add(char *str);
|
||||
|
||||
// list.c
|
||||
void list(void);
|
||||
void tree(void);
|
||||
void top(void);
|
||||
void netstats(void);
|
||||
|
||||
// usage.c
|
||||
void usage(void);
|
||||
|
||||
// join.c
|
||||
void join(pid_t pid, const char *homedir, int argc, char **argv, int index);
|
||||
void join_name(const char *name, const char *homedir, int argc, char **argv, int index);
|
||||
void shut(pid_t pid);
|
||||
void shut_name(const char *name);
|
||||
|
||||
// restricted_shell.c
|
||||
extern char *restricted_user;
|
||||
int restricted_shell(const char *user);
|
||||
|
||||
// arp.c
|
||||
// returns 0 if the address is not in use, -1 otherwise
|
||||
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);
|
||||
|
||||
// veth.c
|
||||
int net_create_veth(const char *dev, const char *nsdev, unsigned pid);
|
||||
int net_create_macvlan(const char *dev, const char *parent, unsigned pid);
|
||||
|
||||
// util.c
|
||||
void drop_privs(int nogroups);
|
||||
void extract_command_name(const char *str);
|
||||
void logsignal(int s);
|
||||
void logmsg(const char *msg);
|
||||
void logargs(int argc, char **argv) ;
|
||||
void logerr(const char *msg);
|
||||
int copy_file(const char *srcname, const char *destname);
|
||||
char *get_link(const char *fname);
|
||||
int is_dir(const char *fname);
|
||||
int is_link(const char *fname);
|
||||
char *line_remove_spaces(const char *buf);
|
||||
char *split_comma(char *str);
|
||||
int not_unsigned(const char *str);
|
||||
int find_child(pid_t parent, pid_t *child);
|
||||
void check_private_dir(void);
|
||||
void update_map(char *mapping, char *map_file);
|
||||
void wait_for_other(int fd);
|
||||
void notify_other(int fd);
|
||||
|
||||
// fs_var.c
|
||||
void fs_var_log(void); // mounting /var/log
|
||||
void fs_var_lib(void); // various other fixes for software in /var directory
|
||||
void fs_var_cache(void); // various other fixes for software in /var/cache directory
|
||||
void fs_var_run(void);
|
||||
void fs_var_lock(void);
|
||||
void fs_var_tmp(void);
|
||||
void fs_var_utmp(void);
|
||||
void dbg_test_dir(const char *dir);
|
||||
|
||||
// fs_dev.c
|
||||
void fs_dev_shm(void);
|
||||
void fs_private_dev(void);
|
||||
|
||||
// fs_home.c
|
||||
// private mode (--private)
|
||||
void fs_private(void);
|
||||
// private mode (--private=homedir)
|
||||
void fs_private_homedir(void);
|
||||
// private mode (--private.keep=list)
|
||||
void fs_private_home_list(void);
|
||||
// check directory linst specified by user (--private.keep option) - exit if it fails
|
||||
void fs_check_home_list(void);
|
||||
// check new private home directory (--private= option) - exit if it fails
|
||||
void fs_check_private_dir(void);
|
||||
|
||||
|
||||
// seccomp.c
|
||||
int seccomp_filter_drop(void);
|
||||
int seccomp_filter_keep(void);
|
||||
void seccomp_set(void);
|
||||
void seccomp_print_filter_name(const char *name);
|
||||
void seccomp_print_filter(pid_t pid);
|
||||
|
||||
// caps.c
|
||||
int caps_default_filter(void);
|
||||
void caps_print(void);
|
||||
void caps_drop_all(void);
|
||||
void caps_set(uint64_t caps);
|
||||
int caps_check_list(const char *clist, void (*callback)(int));
|
||||
void caps_drop_list(const char *clist);
|
||||
void caps_keep_list(const char *clist);
|
||||
void caps_print_filter(pid_t pid);
|
||||
void caps_print_filter_name(const char *name);
|
||||
|
||||
// syscall.c
|
||||
const char *syscall_find_nr(int nr);
|
||||
// return -1 if error, 0 if no error
|
||||
int syscall_check_list(const char *slist, void (*callback)(int));
|
||||
// print all available syscalls
|
||||
void syscall_print(void);
|
||||
|
||||
// fs_trace.c
|
||||
void fs_trace_preload(void);
|
||||
void fs_trace(void);
|
||||
|
||||
// fs_hostname.c
|
||||
void fs_hostname(const char *hostname);
|
||||
void fs_resolvconf(void);
|
||||
|
||||
// rlimit.c
|
||||
void set_rlimits(void);
|
||||
|
||||
// cpu.c
|
||||
void read_cpu_list(const char *str);
|
||||
void set_cpu_affinity(void);
|
||||
void load_cpu(const char *fname);
|
||||
void save_cpu(void);
|
||||
|
||||
// cgroup.c
|
||||
void save_cgroup(void);
|
||||
void load_cgroup(const char *fname);
|
||||
void set_cgroup(const char *path);
|
||||
|
||||
// output.c
|
||||
void check_output(int argc, char **argv);
|
||||
|
||||
// netfilter.c
|
||||
void check_netfilter_file(const char *fname);
|
||||
void netfilter(const char *fname);
|
||||
|
||||
// bandwidth.c
|
||||
void shm_create_firejail_dir(void);
|
||||
void bandwidth_shm_del_file(pid_t pid);
|
||||
void bandwidth_shm_set(pid_t pid, const char *dev, int down, int up);
|
||||
void bandwidth_name(const char *name, const char *command, const char *dev, int down, int up);
|
||||
void bandwidth_pid(pid_t pid, const char *command, const char *dev, int down, int up);
|
||||
void network_shm_del_file(pid_t pid);
|
||||
void network_shm_set_file(pid_t pid);
|
||||
|
||||
|
||||
#endif
|
||||
825
src/firejail/fs.c
Normal file
825
src/firejail/fs.c
Normal file
|
|
@ -0,0 +1,825 @@
|
|||
/*
|
||||
* Copyright (C) 2014, 2015 netblue30 (netblue30@yahoo.com)
|
||||
*
|
||||
* 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 <sys/mount.h>
|
||||
#include <sys/stat.h>
|
||||
#include <linux/limits.h>
|
||||
#include <glob.h>
|
||||
#include <dirent.h>
|
||||
#include <fcntl.h>
|
||||
#include <errno.h>
|
||||
|
||||
// build /tmp/firejail directory
|
||||
void fs_build_firejail_dir(void) {
|
||||
struct stat s;
|
||||
|
||||
if (stat(FIREJAIL_DIR, &s)) {
|
||||
if (arg_debug)
|
||||
printf("Creating %s directory\n", FIREJAIL_DIR);
|
||||
/* coverity[toctou] */
|
||||
int rv = mkdir(FIREJAIL_DIR, S_IRWXU | S_IRWXG | S_IRWXO);
|
||||
if (rv == -1)
|
||||
errExit("mkdir");
|
||||
if (chown(FIREJAIL_DIR, 0, 0) < 0)
|
||||
errExit("chown");
|
||||
if (chmod(FIREJAIL_DIR, S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH) < 0)
|
||||
errExit("chmod");
|
||||
}
|
||||
else { // check /tmp/firejail directory belongs to root end exit if doesn't!
|
||||
if (s.st_uid != 0 || s.st_gid != 0) {
|
||||
fprintf(stderr, "Error: non-root %s directory, exiting...\n", FIREJAIL_DIR);
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// build /tmp/firejail/mnt directory
|
||||
static int tmpfs_mounted = 0;
|
||||
void fs_build_mnt_dir(void) {
|
||||
struct stat s;
|
||||
fs_build_firejail_dir();
|
||||
|
||||
// create /tmp/firejail directory
|
||||
if (stat(MNT_DIR, &s)) {
|
||||
if (arg_debug)
|
||||
printf("Creating %s directory\n", MNT_DIR);
|
||||
/* coverity[toctou] */
|
||||
int rv = mkdir(MNT_DIR, S_IRWXU | S_IRWXG | S_IRWXO);
|
||||
if (rv == -1)
|
||||
errExit("mkdir");
|
||||
if (chown(MNT_DIR, 0, 0) < 0)
|
||||
errExit("chown");
|
||||
if (chmod(MNT_DIR, S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH) < 0)
|
||||
errExit("chmod");
|
||||
}
|
||||
|
||||
// ... and mount tmpfs on top of it
|
||||
if (!tmpfs_mounted) {
|
||||
// mount tmpfs on top of /tmp/firejail/mnt
|
||||
if (arg_debug)
|
||||
printf("Mounting tmpfs on %s directory\n", MNT_DIR);
|
||||
if (mount("tmpfs", MNT_DIR, "tmpfs", MS_NOSUID | MS_STRICTATIME | MS_REC, "mode=755,gid=0") < 0)
|
||||
errExit("mounting /tmp/firejail/mnt");
|
||||
tmpfs_mounted = 1;
|
||||
}
|
||||
}
|
||||
|
||||
// build /tmp/firejail/overlay directory
|
||||
void fs_build_overlay_dir(void) {
|
||||
struct stat s;
|
||||
fs_build_firejail_dir();
|
||||
|
||||
// create /tmp/firejail directory
|
||||
if (stat(OVERLAY_DIR, &s)) {
|
||||
if (arg_debug)
|
||||
printf("Creating %s directory\n", MNT_DIR);
|
||||
/* coverity[toctou] */
|
||||
int rv = mkdir(OVERLAY_DIR, S_IRWXU | S_IRWXG | S_IRWXO);
|
||||
if (rv == -1)
|
||||
errExit("mkdir");
|
||||
if (chown(OVERLAY_DIR, 0, 0) < 0)
|
||||
errExit("chown");
|
||||
if (chmod(OVERLAY_DIR, S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH) < 0)
|
||||
errExit("chmod");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
//***********************************************
|
||||
// process profile file
|
||||
//***********************************************
|
||||
typedef enum {
|
||||
BLACKLIST_FILE,
|
||||
MOUNT_READONLY,
|
||||
MOUNT_TMPFS,
|
||||
OPERATION_MAX
|
||||
} OPERATION;
|
||||
|
||||
|
||||
static char *create_empty_dir(void) {
|
||||
struct stat s;
|
||||
fs_build_firejail_dir();
|
||||
|
||||
if (stat(RO_DIR, &s)) {
|
||||
/* coverity[toctou] */
|
||||
int rv = mkdir(RO_DIR, S_IRUSR | S_IXUSR);
|
||||
if (rv == -1)
|
||||
errExit("mkdir");
|
||||
if (chown(RO_DIR, 0, 0) < 0)
|
||||
errExit("chown");
|
||||
}
|
||||
|
||||
return RO_DIR;
|
||||
}
|
||||
|
||||
static char *create_empty_file(void) {
|
||||
struct stat s;
|
||||
fs_build_firejail_dir();
|
||||
|
||||
if (stat(RO_FILE, &s)) {
|
||||
/* coverity[toctou] */
|
||||
FILE *fp = fopen(RO_FILE, "w");
|
||||
if (!fp)
|
||||
errExit("fopen");
|
||||
fclose(fp);
|
||||
if (chown(RO_FILE, 0, 0) < 0)
|
||||
errExit("chown");
|
||||
if (chmod(RO_FILE, S_IRUSR) < 0)
|
||||
errExit("chown");
|
||||
}
|
||||
|
||||
return RO_FILE;
|
||||
}
|
||||
|
||||
static void disable_file(OPERATION op, const char *fname, const char *emptydir, const char *emptyfile) {
|
||||
assert(fname);
|
||||
assert(emptydir);
|
||||
assert(emptyfile);
|
||||
assert(op <OPERATION_MAX);
|
||||
|
||||
// if the file is a link, follow the link
|
||||
char *lnk = NULL;
|
||||
if (is_link(fname)) {
|
||||
lnk = get_link(fname);
|
||||
if (lnk)
|
||||
fname = lnk;
|
||||
else
|
||||
fprintf(stderr, "Warning: cannot follow link %s, skipping...\n", fname);
|
||||
}
|
||||
|
||||
// if the file is not present, do nothing
|
||||
struct stat s;
|
||||
if (stat(fname, &s) == -1) {
|
||||
if (lnk)
|
||||
free(lnk);
|
||||
return;
|
||||
}
|
||||
|
||||
// modify the file
|
||||
if (op == BLACKLIST_FILE) {
|
||||
if (arg_debug)
|
||||
printf("Disable %s\n", fname);
|
||||
if (S_ISDIR(s.st_mode)) {
|
||||
if (mount(emptydir, fname, "none", MS_BIND, "mode=400,gid=0") < 0)
|
||||
errExit("disable file");
|
||||
}
|
||||
else {
|
||||
if (mount(emptyfile, fname, "none", MS_BIND, "mode=400,gid=0") < 0)
|
||||
errExit("disable file");
|
||||
}
|
||||
}
|
||||
else if (op == MOUNT_READONLY) {
|
||||
if (arg_debug)
|
||||
printf("Mounting read-only %s\n", fname);
|
||||
fs_rdonly(fname);
|
||||
}
|
||||
else if (op == MOUNT_TMPFS) {
|
||||
if (S_ISDIR(s.st_mode)) {
|
||||
if (arg_debug)
|
||||
printf("Mounting tmpfs on %s\n", fname);
|
||||
// preserve owner and mode for the directory
|
||||
if (mount("tmpfs", fname, "tmpfs", MS_NOSUID | MS_NODEV | MS_STRICTATIME | MS_REC, 0) < 0)
|
||||
errExit("mounting tmpfs");
|
||||
/* coverity[toctou] */
|
||||
if (chown(fname, s.st_uid, s.st_gid) == -1)
|
||||
errExit("mounting tmpfs chmod");
|
||||
}
|
||||
else
|
||||
printf("Warning: %s is not a directory; cannot mount a tmpfs on top of it.\n", fname);
|
||||
}
|
||||
else
|
||||
assert(0);
|
||||
|
||||
if (lnk)
|
||||
free(lnk);
|
||||
}
|
||||
|
||||
static void globbing(OPERATION op, const char *fname, const char *emptydir, const char *emptyfile) {
|
||||
assert(fname);
|
||||
assert(emptydir);
|
||||
assert(emptyfile);
|
||||
|
||||
// filename globbing: expand * macro and continue processing for every single file
|
||||
if (strchr(fname, '*')) {
|
||||
glob_t globbuf;
|
||||
globbuf.gl_offs = 0;
|
||||
glob(fname, GLOB_DOOFFS, NULL, &globbuf);
|
||||
int i;
|
||||
for (i = 0; i < globbuf.gl_pathc; i++) {
|
||||
assert(globbuf.gl_pathv[i]);
|
||||
disable_file(op, globbuf.gl_pathv[i], emptydir, emptyfile);
|
||||
}
|
||||
}
|
||||
else
|
||||
disable_file(op, fname, emptydir, emptyfile);
|
||||
}
|
||||
|
||||
static void expand_path(OPERATION op, const char *path, const char *fname, const char *emptydir, const char *emptyfile) {
|
||||
assert(path);
|
||||
assert(fname);
|
||||
assert(emptydir);
|
||||
assert(emptyfile);
|
||||
char newname[strlen(path) + strlen(fname) + 1];
|
||||
sprintf(newname, "%s%s", path, fname);
|
||||
|
||||
globbing(op, newname, emptydir, emptyfile);
|
||||
}
|
||||
|
||||
// blacklist files or directoies by mounting empty files on top of them
|
||||
void fs_blacklist(const char *homedir) {
|
||||
ProfileEntry *entry = cfg.profile;
|
||||
if (!entry)
|
||||
return;
|
||||
|
||||
char *emptydir = create_empty_dir();
|
||||
char *emptyfile = create_empty_file();
|
||||
|
||||
while (entry) {
|
||||
OPERATION op = OPERATION_MAX;
|
||||
char *ptr;
|
||||
|
||||
// process blacklist command
|
||||
if (strncmp(entry->data, "bind", 4) == 0) {
|
||||
char *dname1 = entry->data + 5;
|
||||
char *dname2 = split_comma(dname1);
|
||||
if (dname2 == NULL) {
|
||||
fprintf(stderr, "Error: second directory missing in bind command\n");
|
||||
entry = entry->next;
|
||||
continue;
|
||||
}
|
||||
struct stat s;
|
||||
if (stat(dname1, &s) == -1) {
|
||||
fprintf(stderr, "Error: cannot find directories for bind command\n");
|
||||
entry = entry->next;
|
||||
continue;
|
||||
}
|
||||
if (stat(dname2, &s) == -1) {
|
||||
fprintf(stderr, "Error: cannot find directories for bind command\n");
|
||||
entry = entry->next;
|
||||
continue;
|
||||
}
|
||||
|
||||
// mount --bind olddir newdir
|
||||
if (arg_debug)
|
||||
printf("Mount-bind %s on top of %s\n", dname1, dname2);
|
||||
// preserve dname2 mode and ownership
|
||||
if (mount(dname1, dname2, NULL, MS_BIND|MS_REC, NULL) < 0)
|
||||
errExit("mount bind");
|
||||
/* coverity[toctou] */
|
||||
if (chown(dname2, s.st_uid, s.st_gid) == -1)
|
||||
errExit("mount-bind chown");
|
||||
/* coverity[toctou] */
|
||||
if (chmod(dname2, s.st_mode) == -1)
|
||||
errExit("mount-bind chmod");
|
||||
|
||||
entry = entry->next;
|
||||
continue;
|
||||
}
|
||||
|
||||
// process blacklist command
|
||||
if (strncmp(entry->data, "blacklist", 9) == 0) {
|
||||
ptr = entry->data + 10;
|
||||
op = BLACKLIST_FILE;
|
||||
}
|
||||
else if (strncmp(entry->data, "read-only", 9) == 0) {
|
||||
ptr = entry->data + 10;
|
||||
op = MOUNT_READONLY;
|
||||
}
|
||||
else if (strncmp(entry->data, "tmpfs", 5) == 0) {
|
||||
ptr = entry->data + 6;
|
||||
op = MOUNT_TMPFS;
|
||||
}
|
||||
else {
|
||||
fprintf(stderr, "Error: invalid profile line %s\n", entry->data);
|
||||
entry = entry->next;
|
||||
continue;
|
||||
}
|
||||
|
||||
// replace home macro in blacklist array
|
||||
char *new_name = NULL;
|
||||
if (strncmp(ptr, "${HOME}", 7) == 0) {
|
||||
if (asprintf(&new_name, "%s%s", homedir, ptr + 7) == -1)
|
||||
errExit("asprintf");
|
||||
ptr = new_name;
|
||||
}
|
||||
|
||||
// expand path macro - look for the file in /bin, /usr/bin, /sbin and /usr/sbin directories
|
||||
if (strncmp(ptr, "${PATH}", 7) == 0) {
|
||||
expand_path(op, "/bin", ptr + 7, emptydir, emptyfile);
|
||||
expand_path(op, "/sbin", ptr + 7, emptydir, emptyfile);
|
||||
expand_path(op, "/usr/bin", ptr + 7, emptydir, emptyfile);
|
||||
expand_path(op, "/usr/sbin", ptr + 7, emptydir, emptyfile);
|
||||
}
|
||||
else
|
||||
globbing(op, ptr, emptydir, emptyfile);
|
||||
|
||||
if (new_name)
|
||||
free(new_name);
|
||||
entry = entry->next;
|
||||
}
|
||||
}
|
||||
|
||||
//***********************************************
|
||||
// mount namespace
|
||||
//***********************************************
|
||||
|
||||
// remount a directory read-only
|
||||
void fs_rdonly(const char *dir) {
|
||||
assert(dir);
|
||||
// check directory exists
|
||||
struct stat s;
|
||||
int rv = stat(dir, &s);
|
||||
if (rv == 0) {
|
||||
// mount --bind /bin /bin
|
||||
if (mount(dir, dir, NULL, MS_BIND|MS_REC, NULL) < 0)
|
||||
errExit("mount read-only");
|
||||
// mount --bind -o remount,ro /bin
|
||||
if (mount(NULL, dir, NULL, MS_BIND|MS_REMOUNT|MS_RDONLY|MS_REC, NULL) < 0)
|
||||
errExit("mount read-only");
|
||||
}
|
||||
}
|
||||
void fs_rdonly_noexit(const char *dir) {
|
||||
assert(dir);
|
||||
// check directory exists
|
||||
struct stat s;
|
||||
int rv = stat(dir, &s);
|
||||
if (rv == 0) {
|
||||
int merr = 0;
|
||||
// mount --bind /bin /bin
|
||||
if (mount(dir, dir, NULL, MS_BIND|MS_REC, NULL) < 0)
|
||||
merr = 1;
|
||||
// mount --bind -o remount,ro /bin
|
||||
if (mount(NULL, dir, NULL, MS_BIND|MS_REMOUNT|MS_RDONLY|MS_REC, NULL) < 0)
|
||||
merr = 1;
|
||||
if (merr)
|
||||
fprintf(stderr, "Warning: cannot mount %s read-only\n", dir);
|
||||
}
|
||||
}
|
||||
|
||||
// mount /proc and /sys directories
|
||||
void fs_proc_sys_dev_boot(void) {
|
||||
struct stat s;
|
||||
|
||||
if (arg_debug)
|
||||
printf("Remounting /proc and /proc/sys filesystems\n");
|
||||
if (mount("proc", "/proc", "proc", MS_NOSUID | MS_NOEXEC | MS_NODEV | MS_REC, NULL) < 0)
|
||||
errExit("mounting /proc");
|
||||
|
||||
// remount /proc/sys readonly
|
||||
if (mount("/proc/sys", "/proc/sys", NULL, MS_BIND | MS_REC, NULL) < 0)
|
||||
errExit("mounting /proc/sys");
|
||||
|
||||
if (mount(NULL, "/proc/sys", NULL, MS_BIND | MS_REMOUNT | MS_RDONLY | MS_REC, NULL) < 0)
|
||||
errExit("mounting /proc/sys");
|
||||
|
||||
|
||||
/* Mount a version of /sys that describes the network namespace */
|
||||
if (arg_debug)
|
||||
printf("Remounting /sys directory\n");
|
||||
if (umount2("/sys", MNT_DETACH) < 0)
|
||||
fprintf(stderr, "Warning: failed to unmount /sys\n");
|
||||
if (mount("sysfs", "/sys", "sysfs", MS_RDONLY|MS_NOSUID|MS_NOEXEC|MS_NODEV|MS_REC, NULL) < 0)
|
||||
fprintf(stderr, "Warning: failed to mount /sys\n");
|
||||
|
||||
// if (mount("sysfs", "/sys", "sysfs", MS_RDONLY|MS_NOSUID|MS_NOEXEC|MS_NODEV|MS_REC, NULL) < 0)
|
||||
// errExit("mounting /sys");
|
||||
|
||||
|
||||
// mounting firejail kernel module files
|
||||
if (stat("/proc/firejail-uptime", &s) == 0) {
|
||||
errno = 0;
|
||||
FILE *fp = fopen("/proc/firejail", "w");
|
||||
int cnt = 0;
|
||||
while (errno == EBUSY && cnt < 10) {
|
||||
if (!fp) {
|
||||
int s = random();
|
||||
s /= 200000;
|
||||
usleep(s);
|
||||
fp = fopen("/proc/firejail", "w");
|
||||
}
|
||||
else
|
||||
break;
|
||||
}
|
||||
if (!fp) {
|
||||
fprintf(stderr, "Error: cannot register sandbox with firejail-lkm\n");
|
||||
exit(1);
|
||||
}
|
||||
if (fp) {
|
||||
// registration
|
||||
fprintf(fp, "register\n");
|
||||
fflush(0);
|
||||
// filtering x11 connect calls
|
||||
if (arg_nox11) {
|
||||
fprintf(fp, "no connect unix /tmp/.X11\n");
|
||||
fflush(0);
|
||||
printf("X11 access disabled\n");
|
||||
}
|
||||
if (arg_nodbus) {
|
||||
fprintf(fp, "no connect unix /var/run/dbus/system_bus_socket\n");
|
||||
fflush(0);
|
||||
fprintf(fp, "no connect unix /tmp/dbus\n");
|
||||
fflush(0);
|
||||
printf("D-Bus access disabled\n");
|
||||
}
|
||||
fclose(fp);
|
||||
if (mount("/proc/firejail-uptime", "/proc/uptime", NULL, MS_BIND|MS_REC, NULL) < 0)
|
||||
fprintf(stderr, "Warning: cannot mount /proc/firejail-uptime\n");
|
||||
}
|
||||
}
|
||||
|
||||
// Disable SysRq
|
||||
// a linux box can be shut down easily using the following commands (as root):
|
||||
// # echo 1 > /proc/sys/kernel/sysrq
|
||||
// #echo b > /proc/sysrq-trigger
|
||||
// for more information see https://www.kernel.org/doc/Documentation/sysrq.txt
|
||||
if (arg_debug)
|
||||
printf("Disable /proc/sysrq-trigger\n");
|
||||
fs_rdonly_noexit("/proc/sysrq-trigger");
|
||||
|
||||
// disable hotplug and uevent_helper
|
||||
if (arg_debug)
|
||||
printf("Disable /proc/sys/kernel/hotplug\n");
|
||||
fs_rdonly_noexit("/proc/sys/kernel/hotplug");
|
||||
if (arg_debug)
|
||||
printf("Disable /sys/kernel/uevent_helper\n");
|
||||
fs_rdonly_noexit("/sys/kernel/uevent_helper");
|
||||
|
||||
// read-only /proc/irq and /proc/bus
|
||||
if (arg_debug)
|
||||
printf("Disable /proc/irq\n");
|
||||
fs_rdonly_noexit("/proc/irq");
|
||||
if (arg_debug)
|
||||
printf("Disable /proc/bus\n");
|
||||
fs_rdonly_noexit("/proc/bus");
|
||||
|
||||
// disable /proc/kcore
|
||||
disable_file(BLACKLIST_FILE, "/proc/kcore", "not used", "/dev/null");
|
||||
|
||||
// disable /proc/kallsyms
|
||||
disable_file(BLACKLIST_FILE, "/proc/kallsyms", "not used", "/dev/null");
|
||||
|
||||
// disable /boot
|
||||
if (stat("/boot", &s) == 0) {
|
||||
if (arg_debug)
|
||||
printf("Mounting a new /boot directory\n");
|
||||
if (mount("tmpfs", "/boot", "tmpfs", MS_NOSUID | MS_NODEV | MS_STRICTATIME | MS_REC, "mode=777,gid=0") < 0)
|
||||
errExit("mounting /boot directory");
|
||||
}
|
||||
|
||||
// disable /dev/port
|
||||
if (stat("/dev/port", &s) == 0) {
|
||||
disable_file(BLACKLIST_FILE, "/dev/port", "not used", "/dev/null");
|
||||
}
|
||||
}
|
||||
|
||||
static void sanitize_home(void) {
|
||||
// extract current /home directory data
|
||||
struct dirent *dir;
|
||||
DIR *d = opendir("/home");
|
||||
if (d == NULL)
|
||||
return;
|
||||
|
||||
char *emptydir = create_empty_dir();
|
||||
while ((dir = readdir(d))) {
|
||||
if(strcmp(dir->d_name, "." ) == 0 || strcmp(dir->d_name, ".." ) == 0)
|
||||
continue;
|
||||
|
||||
if (dir->d_type == DT_DIR ) {
|
||||
// get properties
|
||||
struct stat s;
|
||||
char *name;
|
||||
if (asprintf(&name, "/home/%s", dir->d_name) == -1)
|
||||
continue;
|
||||
if (stat(name, &s) == -1)
|
||||
continue;
|
||||
if (S_ISLNK(s.st_mode)) {
|
||||
free(name);
|
||||
continue;
|
||||
}
|
||||
|
||||
if (strcmp(name, cfg.homedir) == 0)
|
||||
continue;
|
||||
|
||||
// printf("directory %u %u:%u #%s#\n",
|
||||
// s.st_mode,
|
||||
// s.st_uid,
|
||||
// s.st_gid,
|
||||
// name);
|
||||
|
||||
// disable directory
|
||||
disable_file(BLACKLIST_FILE, name, emptydir, "not used");
|
||||
free(name);
|
||||
}
|
||||
}
|
||||
closedir(d);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
// build a basic read-only filesystem
|
||||
void fs_basic_fs(void) {
|
||||
if (arg_debug)
|
||||
printf("Mounting read-only /bin, /sbin, /lib, /lib64, /usr, /etc, /var\n");
|
||||
fs_rdonly("/bin");
|
||||
fs_rdonly("/sbin");
|
||||
fs_rdonly("/lib");
|
||||
fs_rdonly("/lib64");
|
||||
fs_rdonly("/usr");
|
||||
fs_rdonly("/etc");
|
||||
fs_rdonly("/var");
|
||||
|
||||
// update /var directory in order to support multiple sandboxes running on the same root directory
|
||||
if (!arg_private_dev)
|
||||
fs_dev_shm();
|
||||
fs_var_lock();
|
||||
fs_var_tmp();
|
||||
fs_var_log();
|
||||
fs_var_lib();
|
||||
fs_var_cache();
|
||||
fs_var_utmp();
|
||||
|
||||
// only in user mode
|
||||
if (getuid())
|
||||
sanitize_home();
|
||||
}
|
||||
|
||||
|
||||
// mount overlayfs on top of / directory
|
||||
// mounting an overlay and chrooting into it:
|
||||
//
|
||||
// Old Ubuntu kernel
|
||||
// # cd ~
|
||||
// # mkdir -p overlay/root
|
||||
// # mkdir -p overlay/diff
|
||||
// # mount -t overlayfs -o lowerdir=/,upperdir=/root/overlay/diff overlayfs /root/overlay/root
|
||||
// # chroot /root/overlay/root
|
||||
// to shutdown, first exit the chroot and then unmount the overlay
|
||||
// # exit
|
||||
// # umount /root/overlay/root
|
||||
//
|
||||
// Kernels 3.18+
|
||||
// # cd ~
|
||||
// # mkdir -p overlay/root
|
||||
// # mkdir -p overlay/diff
|
||||
// # mkdir -p overlay/work
|
||||
// # mount -t overlay -o lowerdir=/,upperdir=/root/overlay/diff,workdir=/root/overlay/work overlay /root/overlay/root
|
||||
// # cat /etc/mtab | grep overlay
|
||||
// /root/overlay /root/overlay/root overlay rw,relatime,lowerdir=/,upperdir=/root/overlay/diff,workdir=/root/overlay/work 0 0
|
||||
// # chroot /root/overlay/root
|
||||
// to shutdown, first exit the chroot and then unmount the overlay
|
||||
// # exit
|
||||
// # umount /root/overlay/root
|
||||
|
||||
|
||||
// to do: fix the code below; also, it might work without /dev; impose seccomp/caps filters when not root
|
||||
#include <sys/utsname.h>
|
||||
void fs_overlayfs(void) {
|
||||
// check kernel version
|
||||
struct utsname u;
|
||||
int rv = uname(&u);
|
||||
if (rv != 0)
|
||||
errExit("uname");
|
||||
int major;
|
||||
int minor;
|
||||
if (2 != sscanf(u.release, "%d.%d", &major, &minor)) {
|
||||
fprintf(stderr, "Error: cannot extract Linux kernel version: %s\n", u.version);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if (arg_debug)
|
||||
printf("Linux kernel version %d.%d\n", major, minor);
|
||||
int oldkernel = 0;
|
||||
if (major < 3) {
|
||||
fprintf(stderr, "Error: minimum kernel version required 3.x\n");
|
||||
exit(1);
|
||||
}
|
||||
if (major == 3 && minor < 18)
|
||||
oldkernel = 1;
|
||||
|
||||
// build overlay directories
|
||||
fs_build_mnt_dir();
|
||||
|
||||
char *oroot;
|
||||
if(asprintf(&oroot, "%s/oroot", MNT_DIR) == -1)
|
||||
errExit("asprintf");
|
||||
if (mkdir(oroot, S_IRWXU | S_IRWXG | S_IRWXO))
|
||||
errExit("mkdir");
|
||||
if (chown(oroot, 0, 0) < 0)
|
||||
errExit("chown");
|
||||
if (chmod(oroot, S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH) < 0)
|
||||
errExit("chmod");
|
||||
|
||||
char *odiff;
|
||||
if(asprintf(&odiff, "%s/odiff", MNT_DIR) == -1)
|
||||
errExit("asprintf");
|
||||
if (mkdir(odiff, S_IRWXU | S_IRWXG | S_IRWXO))
|
||||
errExit("mkdir");
|
||||
if (chown(odiff, 0, 0) < 0)
|
||||
errExit("chown");
|
||||
if (chmod(odiff, S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH) < 0)
|
||||
errExit("chmod");
|
||||
|
||||
char *owork;
|
||||
if(asprintf(&owork, "%s/owork", MNT_DIR) == -1)
|
||||
errExit("asprintf");
|
||||
if (mkdir(owork, S_IRWXU | S_IRWXG | S_IRWXO))
|
||||
errExit("mkdir");
|
||||
if (chown(owork, 0, 0) < 0)
|
||||
errExit("chown");
|
||||
if (chmod(owork, S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH) < 0)
|
||||
errExit("chmod");
|
||||
|
||||
// mount overlayfs
|
||||
if (arg_debug)
|
||||
printf("Mounting OverlayFS\n");
|
||||
char *option;
|
||||
if (oldkernel) { // old Ubuntu/OpenSUSE kernels
|
||||
if (asprintf(&option, "lowerdir=/,upperdir=%s", odiff) == -1)
|
||||
errExit("asprintf");
|
||||
if (mount("overlayfs", oroot, "overlayfs", MS_MGC_VAL, option) < 0)
|
||||
errExit("mounting overlayfs");
|
||||
}
|
||||
else { // kernel 3.18 or newer
|
||||
if (asprintf(&option, "lowerdir=/,upperdir=%s,workdir=%s", odiff, owork) == -1)
|
||||
errExit("asprintf");
|
||||
if (mount("overlay", oroot, "overlay", MS_MGC_VAL, option) < 0)
|
||||
errExit("mounting overlayfs");
|
||||
}
|
||||
|
||||
// mount-bind dev directory
|
||||
if (arg_debug)
|
||||
printf("Mounting /dev\n");
|
||||
char *dev;
|
||||
if (asprintf(&dev, "%s/dev", oroot) == -1)
|
||||
errExit("asprintf");
|
||||
if (mount("/dev", dev, NULL, MS_BIND|MS_REC, NULL) < 0)
|
||||
errExit("mounting /dev");
|
||||
|
||||
// chroot in the new filesystem
|
||||
if (chroot(oroot) == -1)
|
||||
errExit("chroot");
|
||||
// update /var directory in order to support multiple sandboxes running on the same root directory
|
||||
if (!arg_private_dev)
|
||||
fs_dev_shm();
|
||||
fs_var_lock();
|
||||
fs_var_tmp();
|
||||
fs_var_log();
|
||||
fs_var_lib();
|
||||
fs_var_cache();
|
||||
fs_var_utmp();
|
||||
|
||||
// only in user mode
|
||||
if (getuid())
|
||||
sanitize_home();
|
||||
|
||||
// cleanup and exit
|
||||
free(option);
|
||||
free(oroot);
|
||||
free(odiff);
|
||||
}
|
||||
|
||||
|
||||
|
||||
#ifdef HAVE_CHROOT
|
||||
// return 1 if error
|
||||
int fs_check_chroot_dir(const char *rootdir) {
|
||||
assert(rootdir);
|
||||
struct stat s;
|
||||
char *name;
|
||||
|
||||
// check /dev
|
||||
if (asprintf(&name, "%s/dev", rootdir) == -1)
|
||||
errExit("asprintf");
|
||||
if (stat(name, &s) == -1) {
|
||||
fprintf(stderr, "Error: cannot find /dev in chroot directory\n");
|
||||
return 1;
|
||||
}
|
||||
free(name);
|
||||
|
||||
// check /var/tmp
|
||||
if (asprintf(&name, "%s/var/tmp", rootdir) == -1)
|
||||
errExit("asprintf");
|
||||
if (stat(name, &s) == -1) {
|
||||
fprintf(stderr, "Error: cannot find /var/tmp in chroot directory\n");
|
||||
return 1;
|
||||
}
|
||||
free(name);
|
||||
|
||||
// check /proc
|
||||
if (asprintf(&name, "%s/proc", rootdir) == -1)
|
||||
errExit("asprintf");
|
||||
if (stat(name, &s) == -1) {
|
||||
fprintf(stderr, "Error: cannot find /proc in chroot directory\n");
|
||||
return 1;
|
||||
}
|
||||
free(name);
|
||||
|
||||
// check /proc
|
||||
if (asprintf(&name, "%s/tmp", rootdir) == -1)
|
||||
errExit("asprintf");
|
||||
if (stat(name, &s) == -1) {
|
||||
fprintf(stderr, "Error: cannot find /tmp in chroot directory\n");
|
||||
return 1;
|
||||
}
|
||||
free(name);
|
||||
|
||||
// check /bin/bash
|
||||
if (asprintf(&name, "%s/bin/bash", rootdir) == -1)
|
||||
errExit("asprintf");
|
||||
if (stat(name, &s) == -1) {
|
||||
fprintf(stderr, "Error: cannot find /bin/bash in chroot directory\n");
|
||||
return 1;
|
||||
}
|
||||
free(name);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
// chroot into an existing directory; mount exiting /dev and update /etc/resolv.conf
|
||||
void fs_chroot(const char *rootdir) {
|
||||
assert(rootdir);
|
||||
|
||||
//***********************************
|
||||
// mount-bind a /dev in rootdir
|
||||
//***********************************
|
||||
// mount /dev
|
||||
char *newdev;
|
||||
if (asprintf(&newdev, "%s/dev", rootdir) == -1)
|
||||
errExit("asprintf");
|
||||
if (arg_debug)
|
||||
printf("Mounting /dev on %s\n", newdev);
|
||||
if (mount("/dev", newdev, NULL, MS_BIND|MS_REC, NULL) < 0)
|
||||
errExit("mounting /dev");
|
||||
|
||||
// some older distros don't have a /run directory
|
||||
// create one by default
|
||||
// no exit on error, let the user deal with any problems
|
||||
char *rundir;
|
||||
if (asprintf(&rundir, "%s/run", rootdir) == -1)
|
||||
errExit("asprintf");
|
||||
if (!is_dir(rundir)) {
|
||||
int rv = mkdir(rundir, S_IRWXU | S_IRWXG | S_IRWXO);
|
||||
(void) rv;
|
||||
rv = chown(rundir, 0, 0);
|
||||
(void) rv;
|
||||
}
|
||||
|
||||
// copy /etc/resolv.conf in chroot directory
|
||||
// if resolv.conf in chroot is a symbolic link, this will fail
|
||||
// no exit on error, let the user deal with the problem
|
||||
char *fname;
|
||||
if (asprintf(&fname, "%s/etc/resolv.conf", rootdir) == -1)
|
||||
errExit("asprintf");
|
||||
if (arg_debug)
|
||||
printf("Updating /etc/resolv.conf in %s\n", fname);
|
||||
if (copy_file("/etc/resolv.conf", fname) == -1)
|
||||
fprintf(stderr, "Warning: /etc/resolv.conf not initialized\n");
|
||||
|
||||
// chroot into the new directory
|
||||
if (arg_debug)
|
||||
printf("Chrooting into %s\n", rootdir);
|
||||
if (chroot(rootdir) < 0)
|
||||
errExit("chroot");
|
||||
|
||||
// update /var directory in order to support multiple sandboxes running on the same root directory
|
||||
if (!arg_private_dev)
|
||||
fs_dev_shm();
|
||||
fs_var_lock();
|
||||
fs_var_tmp();
|
||||
fs_var_log();
|
||||
fs_var_lib();
|
||||
fs_var_cache();
|
||||
fs_var_utmp();
|
||||
|
||||
// only in user mode
|
||||
if (getuid())
|
||||
sanitize_home();
|
||||
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
163
src/firejail/fs_dev.c
Normal file
163
src/firejail/fs_dev.c
Normal file
|
|
@ -0,0 +1,163 @@
|
|||
/*
|
||||
* Copyright (C) 2014, 2015 netblue30 (netblue30@yahoo.com)
|
||||
*
|
||||
* 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 <sys/mount.h>
|
||||
#include <sys/stat.h>
|
||||
#include <linux/limits.h>
|
||||
#include <glob.h>
|
||||
#include <dirent.h>
|
||||
#include <fcntl.h>
|
||||
#include <pwd.h>
|
||||
#ifndef _BSD_SOURCE
|
||||
#define _BSD_SOURCE
|
||||
#endif
|
||||
#include <sys/types.h>
|
||||
|
||||
static void create_char_dev(const char *path, mode_t mode, int major, int minor) {
|
||||
dev_t dev = makedev(major, minor);
|
||||
int rv = mknod(path, S_IFCHR | mode, dev);
|
||||
if (rv == -1)
|
||||
goto errexit;
|
||||
|
||||
|
||||
if (chmod(path, mode) < 0)
|
||||
goto errexit;
|
||||
if (chown(path, 0, 0) < 0)
|
||||
goto errexit;
|
||||
|
||||
return;
|
||||
|
||||
errexit:
|
||||
fprintf(stderr, "Error: cannot create %s device\n", path);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
static void create_link(const char *oldpath, const char *newpath) {
|
||||
if (symlink(oldpath, newpath) == -1)
|
||||
goto errexit;
|
||||
if (chown(newpath, 0, 0) < 0)
|
||||
goto errexit;
|
||||
return;
|
||||
|
||||
errexit:
|
||||
fprintf(stderr, "Error: cannot create %s device\n", newpath);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
void fs_private_dev(void){
|
||||
// install a new /dev directory
|
||||
if (arg_debug)
|
||||
printf("Mounting tmpfs on /dev\n");
|
||||
if (mount("tmpfs", "/dev", "tmpfs", MS_NOSUID | MS_STRICTATIME | MS_REC, "mode=777,gid=0") < 0)
|
||||
errExit("mounting /dev");
|
||||
|
||||
// create /dev/shm
|
||||
if (arg_debug)
|
||||
printf("Create /dev/shm directory\n");
|
||||
int rv = mkdir("/dev/shm", S_IRWXU | S_IRWXG | S_IRWXO);
|
||||
if (rv == -1)
|
||||
errExit("mkdir");
|
||||
if (chown("/dev/shm", 0, 0) < 0)
|
||||
errExit("chown");
|
||||
if (chmod("/dev/shm", S_IRWXU | S_IRWXG | S_IRWXO) < 0)
|
||||
errExit("chmod");
|
||||
|
||||
// create devices
|
||||
create_char_dev("/dev/zero", 0666, 1, 5); // mknod -m 666 /dev/zero c 1 5
|
||||
create_char_dev("/dev/null", 0666, 1, 3); // mknod -m 666 /dev/null c 1 3
|
||||
create_char_dev("/dev/full", 0666, 1, 7); // mknod -m 666 /dev/full c 1 7
|
||||
create_char_dev("/dev/random", 0666, 1, 8); // Mknod -m 666 /dev/random c 1 8
|
||||
create_char_dev("/dev/urandom", 0666, 1, 9); // mknod -m 666 /dev/urandom c 1 9
|
||||
create_char_dev("/dev/tty", 0666, 5, 0); // mknod -m 666 /dev/tty c 5 0
|
||||
#if 0
|
||||
create_dev("/dev/tty0", "mknod -m 666 /dev/tty0 c 4 0");
|
||||
create_dev("/dev/console", "mknod -m 622 /dev/console c 5 1");
|
||||
#endif
|
||||
|
||||
// pseudo-terminal
|
||||
rv = mkdir("/dev/pts", 0755);
|
||||
if (rv == -1)
|
||||
errExit("mkdir");
|
||||
if (chown("/dev/pts", 0, 0) < 0)
|
||||
errExit("chown");
|
||||
if (chmod("/dev/pts", 0755) < 0)
|
||||
errExit("chmod");
|
||||
create_char_dev("/dev/pts/ptmx", 0666, 5, 2); //"mknod -m 666 /dev/pts/ptmx c 5 2");
|
||||
create_link("/dev/pts/ptmx", "/dev/ptmx");
|
||||
// mount -vt devpts -o newinstance -o ptmxmode=0666 devpts //dev/pts
|
||||
if (mount("devpts", "/dev/pts", "devpts", MS_MGC_VAL, "newinstance,ptmxmode=0666") < 0)
|
||||
errExit("mounting /dev/pts");
|
||||
|
||||
#if 0
|
||||
// stdin, stdout, stderr
|
||||
create_link("/proc/self/fd", "/dev/fd");
|
||||
create_link("/proc/self/fd/0", "/dev/stdin");
|
||||
create_link("/proc/self/fd/1", "/dev/stdout");
|
||||
create_link("/proc/self/fd/2", "/dev/stderr");
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
void fs_dev_shm(void) {
|
||||
uid_t uid = getuid(); // set a new shm only if we started as root
|
||||
if (uid)
|
||||
return;
|
||||
|
||||
if (is_dir("/dev/shm")) {
|
||||
if (arg_debug)
|
||||
printf("Mounting tmpfs on /dev/shm\n");
|
||||
if (mount("tmpfs", "/dev/shm", "tmpfs", MS_NOSUID | MS_STRICTATIME | MS_REC, "mode=777,gid=0") < 0)
|
||||
errExit("mounting /dev/shm");
|
||||
}
|
||||
else {
|
||||
char *lnk = get_link("/dev/shm");
|
||||
if (lnk) {
|
||||
// convert a link such as "../shm" into "/shm"
|
||||
char *lnk2 = lnk;
|
||||
int cnt = 0;
|
||||
while (strncmp(lnk2, "../", 3) == 0) {
|
||||
cnt++;
|
||||
lnk2 = lnk2 + 3;
|
||||
}
|
||||
if (cnt != 0)
|
||||
lnk2 = lnk + (cnt - 1) * 3 + 2;
|
||||
|
||||
if (!is_dir(lnk2)) {
|
||||
// create directory
|
||||
if (mkdir(lnk2, S_IRWXU|S_IRWXG|S_IRWXO))
|
||||
errExit("mkdir");
|
||||
if (chown(lnk2, 0, 0))
|
||||
errExit("chown");
|
||||
if (chmod(lnk2, S_IRWXU|S_IRWXG|S_IRWXO))
|
||||
errExit("chmod");
|
||||
}
|
||||
if (arg_debug)
|
||||
printf("Mounting tmpfs on %s on behalf of /dev/shm\n", lnk2);
|
||||
if (mount("tmpfs", lnk2, "tmpfs", MS_NOSUID | MS_STRICTATIME | MS_REC, "mode=777,gid=0") < 0)
|
||||
errExit("mounting /var/tmp");
|
||||
free(lnk);
|
||||
}
|
||||
else {
|
||||
fprintf(stderr, "Warning: /dev/shm not mounted\n");
|
||||
dbg_test_dir("/dev/shm");
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
494
src/firejail/fs_home.c
Normal file
494
src/firejail/fs_home.c
Normal file
|
|
@ -0,0 +1,494 @@
|
|||
/*
|
||||
* Copyright (C) 2014, 2015 netblue30 (netblue30@yahoo.com)
|
||||
*
|
||||
* 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 <sys/mount.h>
|
||||
#include <linux/limits.h>
|
||||
#include <glob.h>
|
||||
#include <dirent.h>
|
||||
#include <fcntl.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/wait.h>
|
||||
#include <unistd.h>
|
||||
#include <grp.h>
|
||||
|
||||
static void skel(const char *homedir, uid_t u, gid_t g) {
|
||||
char *fname;
|
||||
// zsh
|
||||
if (arg_zsh) {
|
||||
// copy skel files
|
||||
if (asprintf(&fname, "%s/.zshrc", homedir) == -1)
|
||||
errExit("asprintf");
|
||||
struct stat s;
|
||||
// don't copy it if we already have the file
|
||||
if (stat(fname, &s) == 0)
|
||||
return;
|
||||
if (stat("/etc/skel/.zshrc", &s) == 0) {
|
||||
if (copy_file("/etc/skel/.zshrc", fname) == 0) {
|
||||
if (chown(fname, u, g) == -1)
|
||||
errExit("chown");
|
||||
}
|
||||
}
|
||||
else { //
|
||||
FILE *fp = fopen(fname, "w");
|
||||
if (fp) {
|
||||
fprintf(fp, "\n");
|
||||
fclose(fp);
|
||||
if (chown(fname, u, g) == -1)
|
||||
errExit("chown");
|
||||
if (chmod(fname, S_IRUSR | S_IWUSR) < 0)
|
||||
errExit("chown");
|
||||
}
|
||||
}
|
||||
free(fname);
|
||||
}
|
||||
// csh
|
||||
else if (arg_csh) {
|
||||
// copy skel files
|
||||
if (asprintf(&fname, "%s/.cshrc", homedir) == -1)
|
||||
errExit("asprintf");
|
||||
struct stat s;
|
||||
// don't copy it if we already have the file
|
||||
if (stat(fname, &s) == 0)
|
||||
return;
|
||||
if (stat("/etc/skel/.cshrc", &s) == 0) {
|
||||
if (copy_file("/etc/skel/.cshrc", fname) == 0) {
|
||||
if (chown(fname, u, g) == -1)
|
||||
errExit("chown");
|
||||
}
|
||||
}
|
||||
else { //
|
||||
/* coverity[toctou] */
|
||||
FILE *fp = fopen(fname, "w");
|
||||
if (fp) {
|
||||
fprintf(fp, "\n");
|
||||
fclose(fp);
|
||||
if (chown(fname, u, g) == -1)
|
||||
errExit("chown");
|
||||
if (chmod(fname, S_IRUSR | S_IWUSR) < 0)
|
||||
errExit("chown");
|
||||
}
|
||||
}
|
||||
free(fname);
|
||||
}
|
||||
// bash etc.
|
||||
else {
|
||||
// copy skel files
|
||||
if (asprintf(&fname, "%s/.bashrc", homedir) == -1)
|
||||
errExit("asprintf");
|
||||
struct stat s;
|
||||
// don't copy it if we already have the file
|
||||
if (stat(fname, &s) == 0)
|
||||
return;
|
||||
if (stat("/etc/skel/.bashrc", &s) == 0) {
|
||||
if (copy_file("/etc/skel/.bashrc", fname) == 0) {
|
||||
/* coverity[toctou] */
|
||||
if (chown(fname, u, g) == -1)
|
||||
errExit("chown");
|
||||
}
|
||||
}
|
||||
free(fname);
|
||||
}
|
||||
}
|
||||
|
||||
static int store_xauthority(void) {
|
||||
// put a copy of .Xauthority in MNT_DIR
|
||||
fs_build_mnt_dir();
|
||||
|
||||
char *src;
|
||||
char *dest;
|
||||
if (asprintf(&src, "%s/.Xauthority", cfg.homedir) == -1)
|
||||
errExit("asprintf");
|
||||
if (asprintf(&dest, "%s/.Xauthority", MNT_DIR) == -1)
|
||||
errExit("asprintf");
|
||||
|
||||
struct stat s;
|
||||
if (stat(src, &s) == 0) {
|
||||
int rv = copy_file(src, dest);
|
||||
if (rv) {
|
||||
fprintf(stderr, "Warning: cannot transfer .Xauthority in private home directory\n");
|
||||
return 0;
|
||||
}
|
||||
return 1; // file copied
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void copy_xauthority(void) {
|
||||
// put a copy of .Xauthority in MNT_DIR
|
||||
fs_build_mnt_dir();
|
||||
|
||||
char *src;
|
||||
char *dest;
|
||||
if (asprintf(&dest, "%s/.Xauthority", cfg.homedir) == -1)
|
||||
errExit("asprintf");
|
||||
if (asprintf(&src, "%s/.Xauthority", MNT_DIR) == -1)
|
||||
errExit("asprintf");
|
||||
int rv = copy_file(src, dest);
|
||||
if (rv)
|
||||
fprintf(stderr, "Warning: cannot transfer .Xauthority in private home directory\n");
|
||||
|
||||
// set permissions and ownership
|
||||
if (chown(dest, getuid(), getgid()) < 0)
|
||||
errExit("chown");
|
||||
if (chmod(dest, S_IRUSR | S_IWUSR) < 0)
|
||||
errExit("chmod");
|
||||
|
||||
// delete the temporary file
|
||||
unlink(src);
|
||||
}
|
||||
|
||||
// private mode (--private=homedir):
|
||||
// mount homedir on top of /home/user,
|
||||
// tmpfs on top of /root in nonroot mode,
|
||||
// tmpfs on top of /tmp in root mode,
|
||||
// set skel files,
|
||||
// restore .Xauthority
|
||||
void fs_private_homedir(void) {
|
||||
char *homedir = cfg.homedir;
|
||||
char *private_homedir = cfg.home_private;
|
||||
assert(homedir);
|
||||
assert(private_homedir);
|
||||
|
||||
int xflag = store_xauthority();
|
||||
|
||||
uid_t u = getuid();
|
||||
gid_t g = getgid();
|
||||
struct stat s;
|
||||
if (stat(homedir, &s) == -1) {
|
||||
fprintf(stderr, "Error: cannot find user home directory\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
|
||||
// mount bind private_homedir on top of homedir
|
||||
if (arg_debug)
|
||||
printf("Mount-bind %s on top of %s\n", private_homedir, homedir);
|
||||
if (mount(private_homedir, homedir, NULL, MS_BIND|MS_REC, NULL) < 0)
|
||||
errExit("mount bind");
|
||||
// preserve mode and ownership
|
||||
// if (chown(homedir, s.st_uid, s.st_gid) == -1)
|
||||
// errExit("mount-bind chown");
|
||||
// if (chmod(homedir, s.st_mode) == -1)
|
||||
// errExit("mount-bind chmod");
|
||||
|
||||
if (u != 0) {
|
||||
// mask /root
|
||||
if (arg_debug)
|
||||
printf("Mounting a new /root directory\n");
|
||||
if (mount("tmpfs", "/root", "tmpfs", MS_NOSUID | MS_NODEV | MS_STRICTATIME | MS_REC, "mode=700,gid=0") < 0)
|
||||
errExit("mounting home directory");
|
||||
}
|
||||
else {
|
||||
// mask /home
|
||||
if (arg_debug)
|
||||
printf("Mounting a new /home directory\n");
|
||||
if (mount("tmpfs", "/home", "tmpfs", MS_NOSUID | MS_NODEV | MS_STRICTATIME | MS_REC, "mode=755,gid=0") < 0)
|
||||
errExit("mounting home directory");
|
||||
|
||||
// mask /tmp only in root mode; KDE keeps all kind of sockets in /tmp!
|
||||
if (arg_debug)
|
||||
printf("Mounting a new /tmp directory\n");
|
||||
if (mount("tmpfs", "/tmp", "tmpfs", MS_NOSUID | MS_NODEV | MS_STRICTATIME | MS_REC, "mode=777,gid=0") < 0)
|
||||
errExit("mounting tmp directory");
|
||||
}
|
||||
|
||||
|
||||
skel(homedir, u, g);
|
||||
if (xflag)
|
||||
copy_xauthority();
|
||||
}
|
||||
|
||||
// private mode (--private):
|
||||
// mount tmpfs over /home/user,
|
||||
// tmpfs on top of /root in nonroot mode,
|
||||
// tmpfs on top of /tmp in root mode
|
||||
// set skel files,
|
||||
// restore .Xauthority
|
||||
void fs_private(void) {
|
||||
char *homedir = cfg.homedir;
|
||||
assert(homedir);
|
||||
uid_t u = getuid();
|
||||
gid_t g = getgid();
|
||||
|
||||
int xflag = store_xauthority();
|
||||
|
||||
// mask /home
|
||||
if (arg_debug)
|
||||
printf("Mounting a new /home directory\n");
|
||||
if (mount("tmpfs", "/home", "tmpfs", MS_NOSUID | MS_NODEV | MS_STRICTATIME | MS_REC, "mode=755,gid=0") < 0)
|
||||
errExit("mounting home directory");
|
||||
|
||||
// mask /root
|
||||
if (arg_debug)
|
||||
printf("Mounting a new /root directory\n");
|
||||
if (mount("tmpfs", "/root", "tmpfs", MS_NOSUID | MS_NODEV | MS_STRICTATIME | MS_REC, "mode=700,gid=0") < 0)
|
||||
errExit("mounting home directory");
|
||||
|
||||
if (u != 0) {
|
||||
// create /home/user
|
||||
if (arg_debug)
|
||||
printf("Create a new user directory\n");
|
||||
int rv = mkdir(homedir, S_IRWXU);
|
||||
if (rv == -1)
|
||||
errExit("mkdir");
|
||||
if (chown(homedir, u, g) < 0)
|
||||
errExit("chown");
|
||||
}
|
||||
else {
|
||||
// mask tmp only in root mode; KDE keeps all kind of sockets in /tmp!
|
||||
if (arg_debug)
|
||||
printf("Mounting a new /tmp directory\n");
|
||||
if (mount("tmpfs", "/tmp", "tmpfs", MS_NOSUID | MS_NODEV | MS_STRICTATIME | MS_REC, "mode=777,gid=0") < 0)
|
||||
errExit("mounting tmp directory");
|
||||
}
|
||||
|
||||
skel(homedir, u, g);
|
||||
if (xflag)
|
||||
copy_xauthority();
|
||||
}
|
||||
|
||||
static void check_dir_or_file(const char *name) {
|
||||
assert(name);
|
||||
struct stat s;
|
||||
char *fname;
|
||||
if (asprintf(&fname, "%s/%s", cfg.homedir, name) == -1)
|
||||
errExit("asprintf");
|
||||
if (arg_debug)
|
||||
printf("***************Checking %s\n", fname);
|
||||
if (stat(fname, &s) == -1) {
|
||||
fprintf(stderr, "Error: file %s not found.\n", fname);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
// check uid
|
||||
uid_t uid = getuid();
|
||||
gid_t gid = getgid();
|
||||
if (s.st_uid != uid || s.st_gid != gid) {
|
||||
fprintf(stderr, "Error: only files or directories created by the current user are allowed.\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
// dir or regular file
|
||||
if (S_ISDIR(s.st_mode) || S_ISREG(s.st_mode)) {
|
||||
free(fname);
|
||||
return;
|
||||
}
|
||||
|
||||
if (!is_link(fname)) {
|
||||
free(fname);
|
||||
return;
|
||||
}
|
||||
|
||||
fprintf(stderr, "Error: invalid file type, %s.\n", fname);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
// check directory linst specified by user (--private.keep option) - exit if it fails
|
||||
void fs_check_home_list(void) {
|
||||
if (strstr(cfg.home_private_keep, "..")) {
|
||||
fprintf(stderr, "Error: invalid private.keep list\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
char *dlist = strdup(cfg.home_private_keep);
|
||||
if (!dlist)
|
||||
errExit("strdup");
|
||||
|
||||
char *ptr = strtok(dlist, ",");
|
||||
check_dir_or_file(ptr);
|
||||
while ((ptr = strtok(NULL, ",")) != NULL)
|
||||
check_dir_or_file(ptr);
|
||||
|
||||
free(dlist);
|
||||
}
|
||||
|
||||
// check new private home directory (--private= option) - exit if it fails
|
||||
void fs_check_private_dir(void) {
|
||||
// if the directory starts with ~, expand the home directory
|
||||
if (*cfg.home_private == '~') {
|
||||
char *tmp;
|
||||
if (asprintf(&tmp, "%s%s", cfg.homedir, cfg.home_private + 1) == -1)
|
||||
errExit("asprintf");
|
||||
cfg.home_private = tmp;
|
||||
}
|
||||
|
||||
if (!is_dir(cfg.home_private) || is_link(cfg.home_private) || strstr(cfg.home_private, "..")) {
|
||||
fprintf(stderr, "Error: invalid private directory\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
// check home directory and chroot home directory have the same owner
|
||||
struct stat s2;
|
||||
int rv = stat(cfg.home_private, &s2);
|
||||
if (rv < 0) {
|
||||
fprintf(stderr, "Error: cannot find %s directory\n", cfg.home_private);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
struct stat s1;
|
||||
rv = stat(cfg.homedir, &s1);
|
||||
if (rv < 0) {
|
||||
fprintf(stderr, "Error: cannot find %s directory, full path name required\n", cfg.homedir);
|
||||
exit(1);
|
||||
}
|
||||
if (s1.st_uid != s2.st_uid) {
|
||||
printf("Error: the two home directories must have the same owner\n");
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
#if 0
|
||||
static int mkpath(char* file_path, mode_t mode) {
|
||||
assert(file_path && *file_path);
|
||||
char* p;
|
||||
for (p=strchr(file_path+1, '/'); p; p=strchr(p+1, '/')) {
|
||||
*p='\0';
|
||||
if (mkdir(file_path, mode)==-1) {
|
||||
if (errno!=EEXIST) { *p='/'; return -1; }
|
||||
}
|
||||
*p='/';
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
static void duplicate(char *fname) {
|
||||
char *cmd;
|
||||
|
||||
// copy the file
|
||||
if (asprintf(&cmd, "cp -a --parents %s/%s %s", cfg.homedir, fname, HOME_DIR) == -1)
|
||||
errExit("asprintf");
|
||||
if (arg_debug)
|
||||
printf("%s\n", cmd);
|
||||
if (system(cmd))
|
||||
errExit("system cp -a --parents");
|
||||
free(cmd);
|
||||
}
|
||||
|
||||
|
||||
// private mode (--private.keep=list):
|
||||
// mount homedir on top of /home/user,
|
||||
// tmpfs on top of /root in nonroot mode,
|
||||
// tmpfs on top of /tmp in root mode,
|
||||
// set skel files,
|
||||
// restore .Xauthority
|
||||
void fs_private_home_list(void) {
|
||||
char *homedir = cfg.homedir;
|
||||
char *private_list = cfg.home_private_keep;
|
||||
assert(homedir);
|
||||
assert(private_list);
|
||||
|
||||
int xflag = store_xauthority();
|
||||
|
||||
uid_t u = getuid();
|
||||
gid_t g = getgid();
|
||||
struct stat s;
|
||||
if (stat(homedir, &s) == -1) {
|
||||
fprintf(stderr, "Error: cannot find user home directory\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
// create /tmp/firejail/mnt/home directory
|
||||
fs_build_mnt_dir();
|
||||
int rv = mkdir(HOME_DIR, S_IRWXU | S_IRWXG | S_IRWXO);
|
||||
if (rv == -1)
|
||||
errExit("mkdir");
|
||||
if (chown(HOME_DIR, u, g) < 0)
|
||||
errExit("chown");
|
||||
if (chmod(HOME_DIR, 0755) < 0)
|
||||
errExit("chmod");
|
||||
|
||||
// copy the list of files in the new home directory
|
||||
// using a new child process without root privileges
|
||||
pid_t child = fork();
|
||||
if (child < 0)
|
||||
errExit("fork");
|
||||
if (child == 0) {
|
||||
if (arg_debug)
|
||||
printf("Copying files in the new home:\n");
|
||||
|
||||
// drop privileges
|
||||
if (setgroups(0, NULL) < 0)
|
||||
errExit("setgroups");
|
||||
if (setgid(getgid()) < 0)
|
||||
errExit("setgid/getgid");
|
||||
if (setuid(getuid()) < 0)
|
||||
errExit("setuid/getuid");
|
||||
|
||||
// copy the list of files in the new home directory
|
||||
char *dlist = strdup(cfg.home_private_keep);
|
||||
if (!dlist)
|
||||
errExit("strdup");
|
||||
|
||||
char *ptr = strtok(dlist, ",");
|
||||
duplicate(ptr);
|
||||
|
||||
while ((ptr = strtok(NULL, ",")) != NULL)
|
||||
duplicate(ptr);
|
||||
free(dlist);
|
||||
exit(0);
|
||||
}
|
||||
// wait for the child to finish
|
||||
waitpid(child, NULL, 0);
|
||||
|
||||
// mount bind private_homedir on top of homedir
|
||||
char *newhome;
|
||||
if (asprintf(&newhome, "%s%s", HOME_DIR, cfg.homedir) == -1)
|
||||
errExit("asprintf");
|
||||
|
||||
if (arg_debug)
|
||||
printf("Mount-bind %s on top of %s\n", newhome, homedir);
|
||||
if (mount(newhome, homedir, NULL, MS_BIND|MS_REC, NULL) < 0)
|
||||
errExit("mount bind");
|
||||
// preserve mode and ownership
|
||||
// if (chown(homedir, s.st_uid, s.st_gid) == -1)
|
||||
// errExit("mount-bind chown");
|
||||
// if (chmod(homedir, s.st_mode) == -1)
|
||||
// errExit("mount-bind chmod");
|
||||
|
||||
if (u != 0) {
|
||||
// mask /root
|
||||
if (arg_debug)
|
||||
printf("Mounting a new /root directory\n");
|
||||
if (mount("tmpfs", "/root", "tmpfs", MS_NOSUID | MS_NODEV | MS_STRICTATIME | MS_REC, "mode=700,gid=0") < 0)
|
||||
errExit("mounting home directory");
|
||||
}
|
||||
else {
|
||||
// mask /home
|
||||
if (arg_debug)
|
||||
printf("Mounting a new /home directory\n");
|
||||
if (mount("tmpfs", "/home", "tmpfs", MS_NOSUID | MS_NODEV | MS_STRICTATIME | MS_REC, "mode=755,gid=0") < 0)
|
||||
errExit("mounting home directory");
|
||||
|
||||
// mask /tmp only in root mode; KDE keeps all kind of sockets in /tmp!
|
||||
if (arg_debug)
|
||||
printf("Mounting a new /tmp directory\n");
|
||||
if (mount("tmpfs", "/tmp", "tmpfs", MS_NOSUID | MS_NODEV | MS_STRICTATIME | MS_REC, "mode=777,gid=0") < 0)
|
||||
errExit("mounting tmp directory");
|
||||
}
|
||||
|
||||
skel(homedir, u, g);
|
||||
if (xflag)
|
||||
copy_xauthority();
|
||||
|
||||
}
|
||||
|
||||
157
src/firejail/fs_hostname.c
Normal file
157
src/firejail/fs_hostname.c
Normal file
|
|
@ -0,0 +1,157 @@
|
|||
/*
|
||||
* Copyright (C) 2014, 2015 netblue30 (netblue30@yahoo.com)
|
||||
*
|
||||
* 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 <sys/mount.h>
|
||||
#include <sys/stat.h>
|
||||
#include <linux/limits.h>
|
||||
#include <glob.h>
|
||||
#include <dirent.h>
|
||||
#include <fcntl.h>
|
||||
|
||||
void fs_hostname(const char *hostname) {
|
||||
struct stat s;
|
||||
fs_build_mnt_dir();
|
||||
|
||||
// create a new /etc/hostname
|
||||
if (stat("/etc/hostname", &s) == 0) {
|
||||
if (arg_debug)
|
||||
printf("Creating a new /etc/hostname file\n");
|
||||
char *fhost;
|
||||
if (asprintf(&fhost, "%s/hostname", MNT_DIR) == -1)
|
||||
errExit("asprintf");
|
||||
FILE *fp = fopen(fhost, "w");
|
||||
if (!fp) {
|
||||
fprintf(stderr, "Error: cannot create %s\n", fhost);
|
||||
free(fhost);
|
||||
exit(1);
|
||||
}
|
||||
fprintf(fp, "%s\n", hostname);
|
||||
fclose(fp);
|
||||
|
||||
// mode and owner
|
||||
if (chown(fhost, 0, 0) < 0)
|
||||
errExit("chown");
|
||||
if (chmod(fhost, S_IRUSR | S_IWRITE | S_IRGRP | S_IROTH ) < 0)
|
||||
errExit("chmod");
|
||||
|
||||
// bind-mount the file on top of /etc/hostname
|
||||
if (mount(fhost, "/etc/hostname", NULL, MS_BIND|MS_REC, NULL) < 0)
|
||||
errExit("mount bind /etc/hostname");
|
||||
free(fhost);
|
||||
}
|
||||
|
||||
// create a new /etc/hosts
|
||||
if (stat("/etc/hosts", &s) == 0) {
|
||||
if (arg_debug)
|
||||
printf("Creating a new /etc/hosts file\n");
|
||||
char *fhost;
|
||||
if (asprintf(&fhost, "%s/hosts", MNT_DIR) == -1)
|
||||
errExit("asprintf");
|
||||
// copy /etc/host into our new file, and modify it on the fly
|
||||
/* coverity[toctou] */
|
||||
FILE *fp1 = fopen("/etc/hosts", "r");
|
||||
if (!fp1) {
|
||||
fprintf(stderr, "Error: cannot open /etc/hosts\n");
|
||||
free(fhost);
|
||||
exit(1);
|
||||
}
|
||||
FILE *fp2 = fopen(fhost, "w");
|
||||
if (!fp2) {
|
||||
fprintf(stderr, "Error: cannot create %s\n", fhost);
|
||||
free(fhost);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
char buf[4096];
|
||||
while (fgets(buf, sizeof(buf), fp1)) {
|
||||
// remove '\n'
|
||||
char *ptr = strchr(buf, '\n');
|
||||
if (ptr)
|
||||
*ptr = '\0';
|
||||
|
||||
// copy line
|
||||
if (strstr(buf, "127.0.0.1"))
|
||||
fprintf(fp2, "%s %s\n", buf, hostname);
|
||||
else
|
||||
fprintf(fp2, "%s\n", buf);
|
||||
}
|
||||
fclose(fp1);
|
||||
fclose(fp2);
|
||||
|
||||
// mode and owner
|
||||
if (chown(fhost, 0, 0) < 0)
|
||||
errExit("chown");
|
||||
if (chmod(fhost, S_IRUSR | S_IWRITE | S_IRGRP | S_IROTH ) < 0)
|
||||
errExit("chmod");
|
||||
|
||||
// bind-mount the file on top of /etc/hostname
|
||||
if (mount(fhost, "/etc/hosts", NULL, MS_BIND|MS_REC, NULL) < 0)
|
||||
errExit("mount bind /etc/hosts");
|
||||
free(fhost);
|
||||
}
|
||||
}
|
||||
|
||||
void fs_resolvconf(void) {
|
||||
if (cfg.dns1 == 0)
|
||||
return;
|
||||
|
||||
struct stat s;
|
||||
fs_build_mnt_dir();
|
||||
|
||||
// create a new /etc/hostname
|
||||
if (stat("/etc/resolv.conf", &s) == 0) {
|
||||
if (arg_debug)
|
||||
printf("Creating a new /etc/resolv.conf file\n");
|
||||
char *fname;
|
||||
if (asprintf(&fname, "%s/resolv.conf", MNT_DIR) == -1)
|
||||
errExit("asprintf");
|
||||
FILE *fp = fopen(fname, "w");
|
||||
if (!fp) {
|
||||
fprintf(stderr, "Error: cannot create %s\n", fname);
|
||||
free(fname);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if (cfg.dns1)
|
||||
fprintf(fp, "nameserver %d.%d.%d.%d\n", PRINT_IP(cfg.dns1));
|
||||
if (cfg.dns2)
|
||||
fprintf(fp, "nameserver %d.%d.%d.%d\n", PRINT_IP(cfg.dns2));
|
||||
if (cfg.dns3)
|
||||
fprintf(fp, "nameserver %d.%d.%d.%d\n", PRINT_IP(cfg.dns3));
|
||||
fclose(fp);
|
||||
|
||||
// mode and owner
|
||||
if (chown(fname, 0, 0) < 0)
|
||||
errExit("chown");
|
||||
if (chmod(fname, S_IRUSR | S_IWRITE | S_IRGRP | S_IROTH ) < 0)
|
||||
errExit("chmod");
|
||||
|
||||
// bind-mount the file on top of /etc/hostname
|
||||
if (mount(fname, "/etc/resolv.conf", NULL, MS_BIND|MS_REC, NULL) < 0)
|
||||
errExit("mount bind /etc/resolv.conf");
|
||||
free(fname);
|
||||
}
|
||||
else {
|
||||
fprintf(stderr, "Error: cannot set DNS servers, /etc/resolv.conf file is missing\n");
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
76
src/firejail/fs_trace.c
Normal file
76
src/firejail/fs_trace.c
Normal file
|
|
@ -0,0 +1,76 @@
|
|||
/*
|
||||
* Copyright (C) 2014, 2015 netblue30 (netblue30@yahoo.com)
|
||||
*
|
||||
* 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 <sys/mount.h>
|
||||
#include <sys/stat.h>
|
||||
#include <linux/limits.h>
|
||||
#include <glob.h>
|
||||
#include <dirent.h>
|
||||
#include <fcntl.h>
|
||||
#include <pwd.h>
|
||||
|
||||
void fs_trace_preload(void) {
|
||||
struct stat s;
|
||||
|
||||
// create an empty /etc/ld.so.preload
|
||||
if (stat("/etc/ld.so.preload", &s)) {
|
||||
if (arg_debug)
|
||||
printf("Creating an empty /etc/ld.so.preload file\n");
|
||||
/* coverity[toctou] */
|
||||
FILE *fp = fopen("/etc/ld.so.preload", "w");
|
||||
if (!fp)
|
||||
errExit("fopen");
|
||||
fclose(fp);
|
||||
if (chown("/etc/ld.so.preload", 0, 0) < 0)
|
||||
errExit("chown");
|
||||
if (chmod("/etc/ld.so.preload", S_IRUSR | S_IWRITE | S_IRGRP | S_IROTH ) < 0)
|
||||
errExit("chmod");
|
||||
}
|
||||
}
|
||||
|
||||
void fs_trace(void) {
|
||||
// create /tmp/firejail/mnt directory
|
||||
fs_build_mnt_dir();
|
||||
|
||||
// create the new ld.so.preload file and mount-bind it
|
||||
if (arg_debug)
|
||||
printf("Create the new ld.so.preload file\n");
|
||||
char *preload;
|
||||
if (asprintf(&preload, "%s/ld.so.preload", MNT_DIR) == -1)
|
||||
errExit("asprintf");
|
||||
FILE *fp = fopen(preload, "w");
|
||||
if (!fp)
|
||||
errExit("fopen");
|
||||
fprintf(fp, "%s/lib/firejail/libtrace.so\n", PREFIX);
|
||||
fclose(fp);
|
||||
if (chown(preload, 0, 0) < 0)
|
||||
errExit("chown");
|
||||
if (chmod(preload, S_IRUSR | S_IWRITE | S_IRGRP | S_IROTH ) < 0)
|
||||
errExit("chmod");
|
||||
|
||||
// mount the new preload file
|
||||
if (arg_debug)
|
||||
printf("Mount the new ld.so.preload file\n");
|
||||
if (mount(preload, "/etc/ld.so.preload", NULL, MS_BIND|MS_REC, NULL) < 0)
|
||||
errExit("mount bind ls.so.preload");
|
||||
}
|
||||
|
||||
|
||||
|
||||
388
src/firejail/fs_var.c
Normal file
388
src/firejail/fs_var.c
Normal file
|
|
@ -0,0 +1,388 @@
|
|||
/*
|
||||
* Copyright (C) 2014, 2015 netblue30 (netblue30@yahoo.com)
|
||||
*
|
||||
* 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 <sys/mount.h>
|
||||
#include <sys/stat.h>
|
||||
#include <linux/limits.h>
|
||||
#include <glob.h>
|
||||
#include <dirent.h>
|
||||
#include <fcntl.h>
|
||||
#include <pwd.h>
|
||||
#include <utmp.h>
|
||||
#include <time.h>
|
||||
|
||||
typedef struct dirdata_t{
|
||||
struct dirdata_t *next;
|
||||
char *name;
|
||||
mode_t st_mode;
|
||||
uid_t st_uid;
|
||||
gid_t st_gid;
|
||||
} DirData;
|
||||
|
||||
static DirData *dirlist = NULL;
|
||||
|
||||
static void release_all(void) {
|
||||
DirData *ptr = dirlist;
|
||||
while (ptr) {
|
||||
DirData *next = ptr->next;
|
||||
free(ptr->name);
|
||||
free(ptr);
|
||||
ptr = next;
|
||||
}
|
||||
dirlist = NULL;
|
||||
}
|
||||
|
||||
static void build_list(const char *srcdir) {
|
||||
// extract current /var/log directory data
|
||||
struct dirent *dir;
|
||||
DIR *d = opendir(srcdir);
|
||||
if (d == NULL)
|
||||
return;
|
||||
|
||||
while ((dir = readdir(d))) {
|
||||
if(strcmp(dir->d_name, "." ) == 0 || strcmp(dir->d_name, ".." ) == 0)
|
||||
continue;
|
||||
|
||||
if (dir->d_type == DT_DIR ) {
|
||||
// get properties
|
||||
struct stat s;
|
||||
char *name;
|
||||
if (asprintf(&name, "%s/%s", srcdir, dir->d_name) == -1)
|
||||
continue;
|
||||
if (stat(name, &s) == -1)
|
||||
continue;
|
||||
if (S_ISLNK(s.st_mode)) {
|
||||
free(name);
|
||||
continue;
|
||||
}
|
||||
|
||||
// printf("directory %u %u:%u %s\n",
|
||||
// s.st_mode,
|
||||
// s.st_uid,
|
||||
// s.st_gid,
|
||||
// dir->d_name);
|
||||
|
||||
DirData *ptr = malloc(sizeof(DirData));
|
||||
if (ptr == NULL)
|
||||
errExit("malloc");
|
||||
memset(ptr, 0, sizeof(DirData));
|
||||
ptr->name = name;
|
||||
ptr->st_mode = s.st_mode;
|
||||
ptr->st_uid = s.st_uid;
|
||||
ptr->st_gid = s.st_gid;
|
||||
ptr->next = dirlist;
|
||||
dirlist = ptr;
|
||||
}
|
||||
}
|
||||
closedir(d);
|
||||
}
|
||||
|
||||
static void build_dirs(void) {
|
||||
// create directories under /var/log
|
||||
DirData *ptr = dirlist;
|
||||
while (ptr) {
|
||||
if (mkdir(ptr->name, ptr->st_mode))
|
||||
errExit("mkdir");
|
||||
if (chown(ptr->name, ptr->st_uid, ptr->st_gid))
|
||||
errExit("chown");
|
||||
ptr = ptr->next;
|
||||
}
|
||||
}
|
||||
|
||||
void fs_var_log(void) {
|
||||
build_list("/var/log");
|
||||
|
||||
// create /var/log if it does't exit
|
||||
if (is_dir("/var/log")) {
|
||||
// extract group id for /var/log/wtmp
|
||||
struct stat s;
|
||||
gid_t wtmp_group = 0;
|
||||
if (stat("/var/log/wtmp", &s) == 0)
|
||||
wtmp_group = s.st_gid;
|
||||
|
||||
// mount a tmpfs on top of /var/log
|
||||
if (arg_debug)
|
||||
printf("Mounting tmpfs on /var/log\n");
|
||||
if (mount("tmpfs", "/var/log", "tmpfs", MS_NOSUID | MS_STRICTATIME | MS_REC, "mode=755,gid=0") < 0)
|
||||
errExit("mounting /var/log");
|
||||
|
||||
build_dirs();
|
||||
release_all();
|
||||
|
||||
// create an empty /var/log/wtmp file
|
||||
/* coverity[toctou] */
|
||||
FILE *fp = fopen("/var/log/wtmp", "w");
|
||||
if (fp)
|
||||
fclose(fp);
|
||||
if (chown("/var/log/wtmp", 0, wtmp_group) < 0)
|
||||
errExit("chown");
|
||||
if (chmod("/var/log/wtmp", S_IRUSR | S_IWRITE | S_IRGRP | S_IWGRP | S_IROTH ) < 0)
|
||||
errExit("chmod");
|
||||
|
||||
// create an empty /var/log/btmp file
|
||||
fp = fopen("/var/log/btmp", "w");
|
||||
if (fp)
|
||||
fclose(fp);
|
||||
if (chown("/var/log/btmp", 0, wtmp_group) < 0)
|
||||
errExit("chown");
|
||||
if (chmod("/var/log/btmp", S_IRUSR | S_IWRITE | S_IRGRP | S_IWGRP) < 0)
|
||||
errExit("chmod");
|
||||
}
|
||||
else
|
||||
fprintf(stderr, "Warning: cannot mount tmpfs in top of /var/log\n");
|
||||
}
|
||||
|
||||
void fs_var_lib(void) {
|
||||
struct stat s;
|
||||
|
||||
// ISC DHCP multiserver
|
||||
if (stat("/var/lib/dhcp", &s) == 0) {
|
||||
if (arg_debug)
|
||||
printf("Mounting tmpfs on /var/lib/dhcp\n");
|
||||
if (mount("tmpfs", "/var/lib/dhcp", "tmpfs", MS_NOSUID | MS_STRICTATIME | MS_REC, "mode=755,gid=0") < 0)
|
||||
errExit("mounting /var/lib/dhcp");
|
||||
|
||||
// isc dhcp server requires a /var/lib/dhcp/dhcpd.leases file
|
||||
FILE *fp = fopen("/var/lib/dhcp/dhcpd.leases", "w");
|
||||
|
||||
if (fp) {
|
||||
fprintf(fp, "\n");
|
||||
fclose(fp);
|
||||
if (chown("/var/lib/dhcp/dhcpd.leases", 0, 0) == -1)
|
||||
errExit("chown");
|
||||
if (chmod("/var/lib/dhcp/dhcpd.leases", S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH))
|
||||
errExit("chmod");
|
||||
}
|
||||
}
|
||||
|
||||
// nginx multiserver
|
||||
if (stat("/var/lib/nginx", &s) == 0) {
|
||||
if (arg_debug)
|
||||
printf("Mounting tmpfs on /var/lib/nginx\n");
|
||||
if (mount("tmpfs", "/var/lib/nginx", "tmpfs", MS_NOSUID | MS_STRICTATIME | MS_REC, "mode=755,gid=0") < 0)
|
||||
errExit("mounting /var/lib/nginx");
|
||||
}
|
||||
|
||||
// net-snmp multiserver
|
||||
if (stat("/var/lib/snmp", &s) == 0) {
|
||||
if (arg_debug)
|
||||
printf("Mounting tmpfs on /var/lib/snmp\n");
|
||||
if (mount("tmpfs", "/var/lib/snmp", "tmpfs", MS_NOSUID | MS_STRICTATIME | MS_REC, "mode=755,gid=0") < 0)
|
||||
errExit("mounting /var/lib/snmp");
|
||||
}
|
||||
|
||||
// this is where sudo remembers its state
|
||||
if (stat("/var/lib/sudo", &s) == 0) {
|
||||
if (arg_debug)
|
||||
printf("Mounting tmpfs on /var/lib/sudo\n");
|
||||
if (mount("tmpfs", "/var/lib/sudo", "tmpfs", MS_NOSUID | MS_STRICTATIME | MS_REC, "mode=755,gid=0") < 0)
|
||||
errExit("mounting /var/lib/sudo");
|
||||
}
|
||||
}
|
||||
|
||||
void fs_var_cache(void) {
|
||||
struct stat s;
|
||||
|
||||
if (stat("/var/cache/apache2", &s) == 0) {
|
||||
if (arg_debug)
|
||||
printf("Mounting tmpfs on /var/cache/apache2\n");
|
||||
if (mount("tmpfs", "/var/cache/apache2", "tmpfs", MS_NOSUID | MS_STRICTATIME | MS_REC, "mode=755,gid=0") < 0)
|
||||
errExit("mounting /var/cahce/apache2");
|
||||
}
|
||||
|
||||
if (stat("/var/cache/lighttpd", &s) == 0) {
|
||||
if (arg_debug)
|
||||
printf("Mounting tmpfs on /var/cache/lighttpd\n");
|
||||
if (mount("tmpfs", "/var/cache/lighttpd", "tmpfs", MS_NOSUID | MS_STRICTATIME | MS_REC, "mode=755,gid=0") < 0)
|
||||
errExit("mounting /var/cache/lighttpd");
|
||||
|
||||
struct passwd *p = getpwnam("www-data");
|
||||
uid_t uid = 0;
|
||||
gid_t gid = 0;
|
||||
if (p) {
|
||||
uid = p->pw_uid;
|
||||
gid = p->pw_gid;
|
||||
}
|
||||
|
||||
int rv = mkdir("/var/cache/lighttpd/compress", S_IRWXU | S_IRWXG | S_IRWXO);
|
||||
if (rv == -1)
|
||||
errExit("mkdir");
|
||||
if (chown("/var/cache/lighttpd/compress", uid, gid) < 0)
|
||||
errExit("chown");
|
||||
|
||||
rv = mkdir("/var/cache/lighttpd/uploads", S_IRWXU | S_IRGRP | S_IXGRP | S_IROTH | S_IXOTH);
|
||||
if (rv == -1)
|
||||
errExit("mkdir");
|
||||
if (chown("/var/cache/lighttpd/uploads", uid, gid) < 0)
|
||||
errExit("chown");
|
||||
}
|
||||
}
|
||||
|
||||
void dbg_test_dir(const char *dir) {
|
||||
if (arg_debug) {
|
||||
if (is_dir(dir))
|
||||
printf("%s is a directory\n", dir);
|
||||
if (is_link(dir)) {
|
||||
char *lnk = get_link(dir);
|
||||
if (lnk) {
|
||||
printf("%s is a symbolic link to %s\n", dir, lnk);
|
||||
free(lnk);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void fs_var_lock(void) {
|
||||
|
||||
if (is_dir("/var/lock")) {
|
||||
if (arg_debug)
|
||||
printf("Mounting tmpfs on /var/lock\n");
|
||||
if (mount("tmpfs", "/var/lock", "tmpfs", MS_NOSUID | MS_STRICTATIME | MS_REC, "mode=777,gid=0") < 0)
|
||||
errExit("mounting /lock");
|
||||
}
|
||||
else {
|
||||
char *lnk = get_link("/var/lock");
|
||||
if (lnk) {
|
||||
// convert a link such as "../shm" into "/shm"
|
||||
char *lnk2 = lnk;
|
||||
int cnt = 0;
|
||||
while (strncmp(lnk2, "../", 3) == 0) {
|
||||
cnt++;
|
||||
lnk2 = lnk2 + 3;
|
||||
}
|
||||
if (cnt != 0)
|
||||
lnk2 = lnk + (cnt - 1) * 3 + 2;
|
||||
|
||||
if (!is_dir(lnk2)) {
|
||||
// create directory
|
||||
if (mkdir(lnk2, S_IRWXU|S_IRWXG|S_IRWXO))
|
||||
errExit("mkdir");
|
||||
if (chown(lnk2, 0, 0))
|
||||
errExit("chown");
|
||||
if (chmod(lnk2, S_IRWXU|S_IRWXG|S_IRWXO))
|
||||
errExit("chmod");
|
||||
}
|
||||
if (arg_debug)
|
||||
printf("Mounting tmpfs on %s on behalf of /var/lock\n", lnk2);
|
||||
if (mount("tmpfs", lnk2, "tmpfs", MS_NOSUID | MS_STRICTATIME | MS_REC, "mode=777,gid=0") < 0)
|
||||
errExit("mounting /var/lock");
|
||||
free(lnk);
|
||||
}
|
||||
else {
|
||||
fprintf(stderr, "Warning: /var/lock not mounted\n");
|
||||
dbg_test_dir("/var/lock");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void fs_var_tmp(void) {
|
||||
|
||||
if (!is_link("/var/tmp")) {
|
||||
if (arg_debug)
|
||||
printf("Mounting tmpfs on /var/tmp\n");
|
||||
if (mount("tmpfs", "/var/tmp", "tmpfs", MS_NOSUID | MS_STRICTATIME | MS_REC, "mode=777,gid=0") < 0)
|
||||
errExit("mounting /var/tmp");
|
||||
}
|
||||
else {
|
||||
fprintf(stderr, "Warning: /var/tmp not mounted\n");
|
||||
dbg_test_dir("/var/tmp");
|
||||
}
|
||||
}
|
||||
|
||||
void fs_var_utmp(void) {
|
||||
struct stat s;
|
||||
|
||||
// extract utmp group id
|
||||
gid_t utmp_group = 0;
|
||||
if (stat("/var/run/utmp", &s) == 0)
|
||||
utmp_group = s.st_gid;
|
||||
else {
|
||||
fprintf(stderr, "Warning: cannot find /var/run/utmp\n");
|
||||
return;
|
||||
}
|
||||
|
||||
// create /tmp/firejail/mnt directory
|
||||
fs_build_mnt_dir();
|
||||
|
||||
// create a new utmp file
|
||||
if (arg_debug)
|
||||
printf("Create the new utmp file\n");
|
||||
char *utmp;
|
||||
if (asprintf(&utmp, "%s/utmp", MNT_DIR) == -1)
|
||||
errExit("asprintf");
|
||||
FILE *fp = fopen(utmp, "w");
|
||||
if (!fp)
|
||||
errExit("fopen");
|
||||
|
||||
// read current utmp
|
||||
struct utmp *u;
|
||||
struct utmp u_boot;
|
||||
setutent();
|
||||
while ((u = getutent()) != NULL) {
|
||||
if (u->ut_type == BOOT_TIME) {
|
||||
memcpy(&u_boot, u, sizeof(u_boot));
|
||||
u_boot.ut_tv.tv_sec = (unsigned) time(NULL);
|
||||
}
|
||||
}
|
||||
endutent();
|
||||
|
||||
// save new utmp file
|
||||
fwrite(&u_boot, sizeof(u_boot), 1, fp);
|
||||
fclose(fp);
|
||||
if (chown(utmp, 0, utmp_group) < 0)
|
||||
errExit("chown");
|
||||
if (chmod(utmp, S_IRUSR | S_IWRITE | S_IRGRP | S_IWGRP | S_IROTH ) < 0)
|
||||
errExit("chmod");
|
||||
|
||||
// mount the new utmp file
|
||||
if (arg_debug)
|
||||
printf("Mount the new utmp file\n");
|
||||
if (mount(utmp, "/var/run/utmp", NULL, MS_BIND|MS_REC, NULL) < 0)
|
||||
errExit("mount bind utmp");
|
||||
}
|
||||
|
||||
|
||||
#if 0
|
||||
Testing servers:
|
||||
|
||||
brctl addbr br0
|
||||
ifconfig br0 10.10.20.1/24
|
||||
|
||||
apt-get install snmpd
|
||||
insserv -r snmpd
|
||||
sudo firejail --net=br0 --ip=10.10.20.10 "/etc/init.d/rsyslog start; /etc/init.d/ssh start; /etc/init.d/snmpd start; sleep inf"
|
||||
|
||||
apt-get install apache2
|
||||
insserv -r apache2
|
||||
sudo firejail --net=br0 --ip=10.10.20.10 "/etc/init.d/rsyslog start; /etc/init.d/ssh start; /etc/init.d/apache2 start; sleep inf"
|
||||
|
||||
apt-get install nginx
|
||||
insserv -r nginx
|
||||
sudo firejail --net=br0 --ip=10.10.20.10 "/etc/init.d/rsyslog start; /etc/init.d/ssh start; /etc/init.d/nginx start; sleep inf"
|
||||
|
||||
apt-get install lighttpd
|
||||
insserv -r lighttpd
|
||||
sudo firejail --net=br0 --ip=10.10.20.10 "/etc/init.d/rsyslog start; /etc/init.d/ssh start; /etc/init.d/lighttpd start; sleep inf"
|
||||
|
||||
apt-get install isc-dhcp-server
|
||||
insserv -r isc-dhcp-server
|
||||
sudo firejail --net=br0 --ip=10.10.20.10 "/etc/init.d/rsyslog start; /etc/init.d/ssh start; /etc/init.d/isc-dhcp-server start; sleep inf"
|
||||
#endif
|
||||
364
src/firejail/join.c
Normal file
364
src/firejail/join.c
Normal file
|
|
@ -0,0 +1,364 @@
|
|||
/*
|
||||
* Copyright (C) 2014, 2015 netblue30 (netblue30@yahoo.com)
|
||||
*
|
||||
* 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 <sys/stat.h>
|
||||
#include <sys/wait.h>
|
||||
#include <fcntl.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/prctl.h>
|
||||
|
||||
static int apply_caps = 0;
|
||||
static uint64_t caps = 0;
|
||||
static int apply_seccomp = 0;
|
||||
#define BUFLEN 4096
|
||||
|
||||
static void extract_command(int argc, char **argv, int index) {
|
||||
if (index >= argc)
|
||||
return;
|
||||
|
||||
// doubledash followed by positional parameters
|
||||
if (strcmp(argv[index], "--") == 0) {
|
||||
arg_doubledash = 1;
|
||||
index++;
|
||||
if (index >= argc)
|
||||
return;
|
||||
}
|
||||
|
||||
// first argv needs to be a valid command
|
||||
if (arg_doubledash == 0 && *argv[index] == '-') {
|
||||
fprintf(stderr, "Error: invalid option %s after --join\n", argv[index]);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
|
||||
int len = 0;
|
||||
int i;
|
||||
// calculate command length
|
||||
for (i = index; i < argc; i++) {
|
||||
len += strlen(argv[i]) + 1;
|
||||
}
|
||||
assert(len > 0);
|
||||
|
||||
// build command
|
||||
cfg.command_line = malloc(len + 1);
|
||||
*cfg.command_line = '\0';
|
||||
for (i = index; i < argc; i++) {
|
||||
strcat(cfg.command_line, argv[i]);
|
||||
strcat(cfg.command_line, " ");
|
||||
}
|
||||
if (arg_debug)
|
||||
printf("Extracted command #%s#\n", cfg.command_line);
|
||||
}
|
||||
|
||||
static void extract_nogroups(pid_t pid) {
|
||||
char *fname;
|
||||
if (asprintf(&fname, "/proc/%d/root%s/groups", pid, MNT_DIR) == -1)
|
||||
errExit("asprintf");
|
||||
|
||||
struct stat s;
|
||||
if (stat(fname, &s) == -1)
|
||||
return;
|
||||
|
||||
arg_nogroups = 1;
|
||||
free(fname);
|
||||
}
|
||||
|
||||
static void extract_cpu(pid_t pid) {
|
||||
char *fname;
|
||||
if (asprintf(&fname, "/proc/%d/root%s/cpu", pid, MNT_DIR) == -1)
|
||||
errExit("asprintf");
|
||||
|
||||
struct stat s;
|
||||
if (stat(fname, &s) == -1)
|
||||
return;
|
||||
|
||||
// there is a cpu file in MNT_DIR; load the information from the file
|
||||
load_cpu(fname);
|
||||
free(fname);
|
||||
}
|
||||
|
||||
static void extract_cgroup(pid_t pid) {
|
||||
char *fname;
|
||||
if (asprintf(&fname, "/proc/%d/root%s/cgroup", pid, MNT_DIR) == -1)
|
||||
errExit("asprintf");
|
||||
|
||||
struct stat s;
|
||||
if (stat(fname, &s) == -1)
|
||||
return;
|
||||
|
||||
// there is a cgroup file in MNT_DIR; load the information from the file
|
||||
load_cgroup(fname);
|
||||
free(fname);
|
||||
}
|
||||
|
||||
static void extract_caps_seccomp(pid_t pid) {
|
||||
// open stat file
|
||||
char *file;
|
||||
if (asprintf(&file, "/proc/%u/status", pid) == -1) {
|
||||
perror("asprintf");
|
||||
exit(1);
|
||||
}
|
||||
FILE *fp = fopen(file, "r");
|
||||
if (!fp) {
|
||||
free(file);
|
||||
fprintf(stderr, "Error: cannot open stat file for process %u\n", pid);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
char buf[BUFLEN];
|
||||
while (fgets(buf, BUFLEN - 1, fp)) {
|
||||
if (strncmp(buf, "Seccomp:", 8) == 0) {
|
||||
char *ptr = buf + 8;
|
||||
int val;
|
||||
sscanf(ptr, "%d", &val);
|
||||
if (val == 2)
|
||||
apply_seccomp = 1;
|
||||
break;
|
||||
}
|
||||
else if (strncmp(buf, "CapBnd:", 7) == 0) {
|
||||
char *ptr = buf + 8;
|
||||
unsigned long long val;
|
||||
sscanf(ptr, "%llx", &val);
|
||||
apply_caps = 1;
|
||||
caps = val;
|
||||
}
|
||||
}
|
||||
fclose(fp);
|
||||
free(file);
|
||||
}
|
||||
|
||||
void extract_user_namespace(pid_t pid) {
|
||||
// test user namespaces available in the kernel
|
||||
struct stat s1;
|
||||
struct stat s2;
|
||||
struct stat s3;
|
||||
if (stat("/proc/self/ns/user", &s1) == 0 &&
|
||||
stat("/proc/self/uid_map", &s2) == 0 &&
|
||||
stat("/proc/self/gid_map", &s3) == 0);
|
||||
else
|
||||
return;
|
||||
|
||||
// read uid map
|
||||
char *uidmap;
|
||||
if (asprintf(&uidmap, "/proc/%u/uid_map", pid) == -1)
|
||||
errExit("asprintf");
|
||||
FILE *fp = fopen(uidmap, "r");
|
||||
if (!fp) {
|
||||
free(uidmap);
|
||||
return;
|
||||
}
|
||||
|
||||
// check uid map
|
||||
int u1;
|
||||
int u2;
|
||||
if (fscanf(fp, "%d %d", &u1, &u2) == 2) {
|
||||
if (arg_debug)
|
||||
printf("User namespace detected: %s, %d, %d\n", uidmap, u1, u2);
|
||||
if (u1 != 0 || u2 != 0)
|
||||
arg_noroot = 1;
|
||||
}
|
||||
fclose(fp);
|
||||
free(uidmap);
|
||||
}
|
||||
|
||||
void join_name(const char *name, const char *homedir, int argc, char **argv, int index) {
|
||||
if (!name || strlen(name) == 0) {
|
||||
fprintf(stderr, "Error: invalid sandbox name\n");
|
||||
exit(1);
|
||||
}
|
||||
pid_t pid;
|
||||
if (name2pid(name, &pid)) {
|
||||
fprintf(stderr, "Error: cannot find sandbox %s\n", name);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
join(pid, homedir, argc, argv, index);
|
||||
}
|
||||
|
||||
void join(pid_t pid, const char *homedir, int argc, char **argv, int index) {
|
||||
extract_command(argc, argv, index);
|
||||
|
||||
// if the pid is that of a firejail process, use the pid of the first child process
|
||||
char *comm = pid_proc_comm(pid);
|
||||
if (comm) {
|
||||
// remove \n
|
||||
char *ptr = strchr(comm, '\n');
|
||||
if (ptr)
|
||||
*ptr = '\0';
|
||||
if (strcmp(comm, "firejail") == 0) {
|
||||
pid_t child;
|
||||
if (find_child(pid, &child) == 0) {
|
||||
pid = child;
|
||||
printf("Switching to pid %u, the first child process inside the sandbox\n", (unsigned) pid);
|
||||
}
|
||||
}
|
||||
free(comm);
|
||||
}
|
||||
|
||||
// check privileges for non-root users
|
||||
uid_t uid = getuid();
|
||||
if (uid != 0) {
|
||||
struct stat s;
|
||||
char *dir;
|
||||
if (asprintf(&dir, "/proc/%u/ns", pid) == -1)
|
||||
errExit("asprintf");
|
||||
if (stat(dir, &s) < 0)
|
||||
errExit("stat");
|
||||
if (s.st_uid != uid) {
|
||||
fprintf(stderr, "Error: permission is denied to join a sandbox created by a different user.\n");
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
// in user mode set caps seccomp, cpu, cgroup, etc
|
||||
if (getuid() != 0) {
|
||||
extract_caps_seccomp(pid);
|
||||
extract_cpu(pid);
|
||||
extract_cgroup(pid);
|
||||
extract_nogroups(pid);
|
||||
extract_user_namespace(pid);
|
||||
}
|
||||
|
||||
// set cgroup
|
||||
if (cfg.cgroup)
|
||||
set_cgroup(cfg.cgroup);
|
||||
|
||||
// join namespaces
|
||||
if (join_namespace(pid, "ipc"))
|
||||
exit(1);
|
||||
if (join_namespace(pid, "net"))
|
||||
exit(1);
|
||||
if (join_namespace(pid, "pid"))
|
||||
exit(1);
|
||||
if (join_namespace(pid, "uts"))
|
||||
exit(1);
|
||||
if (join_namespace(pid, "mnt"))
|
||||
exit(1);
|
||||
|
||||
pid_t child = fork();
|
||||
if (child < 0)
|
||||
errExit("fork");
|
||||
if (child == 0) {
|
||||
// chroot into /proc/PID/root directory
|
||||
char *rootdir;
|
||||
if (asprintf(&rootdir, "/proc/%d/root", pid) == -1)
|
||||
errExit("asprintf");
|
||||
|
||||
int rv = chroot(rootdir); // this will fail for processes in sandboxes not started with --chroot option
|
||||
if (rv == 0)
|
||||
printf("changing root to %s\n", rootdir);
|
||||
|
||||
prctl(PR_SET_PDEATHSIG, SIGKILL, 0, 0, 0); // kill the child in case the parent died
|
||||
if (chdir("/") < 0)
|
||||
errExit("chdir");
|
||||
if (homedir) {
|
||||
struct stat s;
|
||||
if (stat(homedir, &s) == 0) {
|
||||
/* coverity[toctou] */
|
||||
if (chdir(homedir) < 0)
|
||||
errExit("chdir");
|
||||
}
|
||||
}
|
||||
|
||||
// set cpu affinity
|
||||
if (cfg.cpus)
|
||||
set_cpu_affinity();
|
||||
|
||||
// set caps filter
|
||||
if (apply_caps == 1)
|
||||
caps_set(caps);
|
||||
#ifdef HAVE_SECCOMP
|
||||
// set seccomp filter
|
||||
if (apply_seccomp == 1)
|
||||
seccomp_set();
|
||||
#endif
|
||||
|
||||
// fix qt 4.8
|
||||
if (setenv("QT_X11_NO_MITSHM", "1", 1) < 0)
|
||||
errExit("setenv");
|
||||
if (setenv("container", "firejail", 1) < 0) // LXC sets container=lxc,
|
||||
errExit("setenv");
|
||||
|
||||
// mount user namespace or drop privileges
|
||||
if (arg_noroot) {
|
||||
if (arg_debug)
|
||||
printf("Joining user namespace\n");
|
||||
if (join_namespace(1, "user"))
|
||||
exit(1);
|
||||
}
|
||||
else
|
||||
drop_privs(arg_nogroups);
|
||||
|
||||
// set prompt color to green
|
||||
//export PS1='\[\e[1;32m\][\u@\h \W]\$\[\e[0m\] '
|
||||
if (setenv("PROMPT_COMMAND", "export PS1=\"\\[\\e[1;32m\\][\\u@\\h \\W]\\$\\[\\e[0m\\] \"", 1) < 0)
|
||||
errExit("setenv");
|
||||
|
||||
// run icmdline trough /bin/bash
|
||||
if (cfg.command_line == NULL)
|
||||
// replace the process with a regular bash session
|
||||
execlp("/bin/bash", "/bin/bash", NULL);
|
||||
else {
|
||||
// run the command supplied by the user
|
||||
int cwd = 0;
|
||||
if (cfg.cwd) {
|
||||
if (chdir(cfg.cwd) == 0)
|
||||
cwd = 1;
|
||||
}
|
||||
|
||||
if (!cwd) {
|
||||
if (chdir("/") < 0)
|
||||
errExit("chdir");
|
||||
if (cfg.homedir) {
|
||||
struct stat s;
|
||||
if (stat(cfg.homedir, &s) == 0) {
|
||||
if (chdir(cfg.homedir) < 0)
|
||||
errExit("chdir");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
char *arg[5];
|
||||
arg[0] = "/bin/bash";
|
||||
arg[1] = "-c";
|
||||
if (arg_debug)
|
||||
printf("Starting %s\n", cfg.command_line);
|
||||
if (!arg_doubledash) {
|
||||
arg[2] = cfg.command_line;
|
||||
arg[3] = NULL;
|
||||
}
|
||||
else {
|
||||
arg[2] = "--";
|
||||
arg[3] = cfg.command_line;
|
||||
arg[4] = NULL;
|
||||
}
|
||||
execvp("/bin/bash", arg);
|
||||
}
|
||||
|
||||
// it will never get here!!!
|
||||
}
|
||||
|
||||
// wait for the child to finish
|
||||
waitpid(child, NULL, 0);
|
||||
exit(0);
|
||||
}
|
||||
|
||||
|
||||
|
||||
65
src/firejail/list.c
Normal file
65
src/firejail/list.c
Normal file
|
|
@ -0,0 +1,65 @@
|
|||
/*
|
||||
* Copyright (C) 2014, 2015 netblue30 (netblue30@yahoo.com)
|
||||
*
|
||||
* 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"
|
||||
|
||||
void top(void) {
|
||||
drop_privs(1);
|
||||
|
||||
char *arg[4];
|
||||
arg[0] = "bash";
|
||||
arg[1] = "-c";
|
||||
arg[2] = "firemon --top";
|
||||
arg[3] = NULL;
|
||||
execvp("/bin/bash", arg);
|
||||
}
|
||||
|
||||
void netstats(void) {
|
||||
drop_privs(1);
|
||||
|
||||
char *arg[4];
|
||||
arg[0] = "bash";
|
||||
arg[1] = "-c";
|
||||
arg[2] = "firemon --netstats";
|
||||
arg[3] = NULL;
|
||||
execvp("/bin/bash", arg);
|
||||
}
|
||||
|
||||
void list(void) {
|
||||
drop_privs(1);
|
||||
|
||||
char *arg[4];
|
||||
arg[0] = "bash";
|
||||
arg[1] = "-c";
|
||||
arg[2] = "firemon --list";
|
||||
arg[3] = NULL;
|
||||
execvp("/bin/bash", arg);
|
||||
}
|
||||
|
||||
void tree(void) {
|
||||
drop_privs(1);
|
||||
|
||||
char *arg[4];
|
||||
arg[0] = "bash";
|
||||
arg[1] = "-c";
|
||||
arg[2] = "firemon --tree";
|
||||
arg[3] = NULL;
|
||||
execvp("/bin/bash", arg);
|
||||
}
|
||||
|
||||
1168
src/firejail/main.c
Normal file
1168
src/firejail/main.c
Normal file
File diff suppressed because it is too large
Load diff
164
src/firejail/netfilter.c
Normal file
164
src/firejail/netfilter.c
Normal file
|
|
@ -0,0 +1,164 @@
|
|||
/*
|
||||
* Copyright (C) 2014, 2015 netblue30 (netblue30@yahoo.com)
|
||||
*
|
||||
* 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 <sys/mount.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/wait.h>
|
||||
#include <fcntl.h>
|
||||
|
||||
static char *client_filter =
|
||||
"*filter\n"
|
||||
":INPUT DROP [0:0]\n"
|
||||
":FORWARD DROP [0:0]\n"
|
||||
":OUTPUT ACCEPT [0:0]\n"
|
||||
"-A INPUT -i lo -j ACCEPT\n"
|
||||
"# echo replay is handled by -m state RELEATED/ESTABLISHED below\n"
|
||||
"#-A INPUT -p icmp --icmp-type echo-reply -j ACCEPT\n"
|
||||
"-A INPUT -m state --state RELATED,ESTABLISHED -j ACCEPT\n"
|
||||
"-A INPUT -p icmp --icmp-type destination-unreachable -j ACCEPT\n"
|
||||
"-A INPUT -p icmp --icmp-type time-exceeded -j ACCEPT\n"
|
||||
"-A INPUT -p icmp --icmp-type echo-request -j ACCEPT \n"
|
||||
"COMMIT\n";
|
||||
|
||||
void check_netfilter_file(const char *fname) {
|
||||
if (is_dir(fname) || is_link(fname) || strstr(fname, "..")) {
|
||||
fprintf(stderr, "Error: invalid network filter file\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
// access call checks as real UID/GID, not as effective UID/GID
|
||||
if (access(fname, R_OK)) {
|
||||
fprintf(stderr, "Error: cannot access network filter file\n");
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void netfilter(const char *fname) {
|
||||
// default filter
|
||||
char *filter = client_filter;
|
||||
|
||||
// custom filter
|
||||
int allocated = 0;
|
||||
if (fname) {
|
||||
// buffer the filter
|
||||
struct stat s;
|
||||
if (stat(fname, &s) == -1) {
|
||||
fprintf(stderr, "Error: cannot find network filter file\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
filter = malloc(s.st_size + 1); // + '\0'
|
||||
memset(filter, 0, s.st_size + 1);
|
||||
if (!filter)
|
||||
errExit("malloc");
|
||||
|
||||
/* coverity[toctou] */
|
||||
FILE *fp = fopen(fname, "r");
|
||||
if (!fp) {
|
||||
fprintf(stderr, "Error: cannot open network filter file\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
size_t sz = fread(filter, 1, s.st_size, fp);
|
||||
if (sz != s.st_size) {
|
||||
fprintf(stderr, "Error: cannot read network filter file\n");
|
||||
exit(1);
|
||||
}
|
||||
fclose(fp);
|
||||
allocated = 1;
|
||||
}
|
||||
|
||||
// mount a tempfs on top of /tmp directory
|
||||
if (mount("tmpfs", "/tmp", "tmpfs", MS_NOSUID | MS_STRICTATIME | MS_REC, "mode=755,gid=0") < 0)
|
||||
errExit("mounting /tmp");
|
||||
|
||||
// create the filter file
|
||||
FILE *fp = fopen("/tmp/netfilter", "w");
|
||||
if (!fp) {
|
||||
fprintf(stderr, "Error: cannot open /tmp/netfilter file\n");
|
||||
exit(1);
|
||||
}
|
||||
fprintf(fp, "%s\n", filter);
|
||||
fclose(fp);
|
||||
|
||||
// find iptables command
|
||||
struct stat s;
|
||||
char *iptables = NULL;
|
||||
char *iptables_restore = NULL;
|
||||
if (stat("/sbin/iptables", &s) == 0) {
|
||||
iptables = "/sbin/iptables";
|
||||
iptables_restore = "/sbin/iptables-restore";
|
||||
}
|
||||
else if (stat("/usr/sbin/iptables", &s) == 0) {
|
||||
iptables = "/usr/sbin/iptables";
|
||||
iptables_restore = "/usr/sbin/iptables-restore";
|
||||
}
|
||||
if (iptables == NULL || iptables_restore == NULL) {
|
||||
fprintf(stderr, "Error: iptables command not found\n");
|
||||
goto doexit;
|
||||
}
|
||||
|
||||
// push filter
|
||||
pid_t child = fork();
|
||||
if (child < 0)
|
||||
errExit("fork");
|
||||
if (child == 0) {
|
||||
if (arg_debug)
|
||||
printf("Installing network filter:\n%s\n", filter);
|
||||
|
||||
int fd;
|
||||
if((fd = open("/tmp/netfilter", O_RDONLY)) == -1) {
|
||||
fprintf(stderr,"Error: cannot open /tmp/netfilter\n");
|
||||
exit(1);
|
||||
}
|
||||
dup2(fd,STDIN_FILENO);
|
||||
close(fd);
|
||||
|
||||
// wipe out environment variables
|
||||
environ = NULL;
|
||||
execl(iptables_restore, iptables_restore, NULL);
|
||||
// it will never get here!!!
|
||||
}
|
||||
// wait for the child to finish
|
||||
waitpid(child, NULL, 0);
|
||||
|
||||
// debug
|
||||
if (arg_debug) {
|
||||
child = fork();
|
||||
if (child < 0)
|
||||
errExit("fork");
|
||||
if (child == 0) {
|
||||
environ = NULL;
|
||||
execl(iptables, iptables, "-vL", NULL);
|
||||
// it will never get here!!!
|
||||
}
|
||||
// wait for the child to finish
|
||||
waitpid(child, NULL, 0);
|
||||
}
|
||||
|
||||
doexit:
|
||||
// unmount /tmp
|
||||
umount("/tmp");
|
||||
|
||||
if (allocated)
|
||||
free(filter);
|
||||
}
|
||||
362
src/firejail/network.c
Normal file
362
src/firejail/network.c
Normal file
|
|
@ -0,0 +1,362 @@
|
|||
/*
|
||||
* Copyright (C) 2014, 2015 netblue30 (netblue30@yahoo.com)
|
||||
*
|
||||
* 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 <arpa/inet.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <netdb.h>
|
||||
#include <ifaddrs.h>
|
||||
#include <net/if.h>
|
||||
#include <net/if_arp.h>
|
||||
#include <net/route.h>
|
||||
#include <linux/if_bridge.h>
|
||||
|
||||
// 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);
|
||||
}
|
||||
|
||||
|
||||
// return -1 if the interface was not found; if the interface was found retrn 0 and fill in IP address and mask
|
||||
int net_get_if_addr(const char *bridge, uint32_t *ip, uint32_t *mask, uint8_t mac[6]) {
|
||||
assert(bridge);
|
||||
assert(ip);
|
||||
assert(mask);
|
||||
int rv = -1;
|
||||
struct ifaddrs *ifaddr, *ifa;
|
||||
|
||||
if (getifaddrs(&ifaddr) == -1)
|
||||
errExit("getifaddrs");
|
||||
|
||||
// walk through the linked list; if the interface is found, extract IP address and mask
|
||||
for (ifa = ifaddr; ifa != NULL; ifa = ifa->ifa_next) {
|
||||
if (ifa->ifa_addr == NULL)
|
||||
continue;
|
||||
if (strcmp(ifa->ifa_name, bridge) != 0)
|
||||
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);
|
||||
if (strcmp(ifa->ifa_name, "lo") != 0)
|
||||
net_get_mac(ifa->ifa_name, mac);
|
||||
|
||||
rv = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
freeifaddrs(ifaddr);
|
||||
return rv;
|
||||
}
|
||||
|
||||
// bring interface up
|
||||
void net_if_up(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);
|
||||
errExit("ioctl");
|
||||
}
|
||||
|
||||
ifr.ifr_flags |= IFF_UP;
|
||||
|
||||
// set the new flags
|
||||
if (ioctl( sock, SIOCSIFFLAGS, &ifr ) < 0) {
|
||||
close(sock);
|
||||
errExit("ioctl");
|
||||
}
|
||||
|
||||
// checking
|
||||
// read the existing flags
|
||||
if (ioctl(sock, SIOCGIFFLAGS, &ifr ) < 0) {
|
||||
close(sock);
|
||||
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);
|
||||
errExit("ioctl");
|
||||
}
|
||||
if (ifr.ifr_flags & IFF_RUNNING)
|
||||
break;
|
||||
cnt++;
|
||||
}
|
||||
|
||||
close(sock);
|
||||
}
|
||||
|
||||
// configure interface
|
||||
void net_if_ip(const char *ifname, uint32_t ip, uint32_t mask) {
|
||||
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");
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
struct rtentry route;
|
||||
struct sockaddr_in *addr;
|
||||
int err = 0;
|
||||
|
||||
// create the socket
|
||||
if((sock = socket(AF_INET, SOCK_DGRAM, 0)) < 0)
|
||||
errExit("socket");
|
||||
|
||||
memset(&route, 0, sizeof(route));
|
||||
|
||||
addr = (struct sockaddr_in*) &route.rt_gateway;
|
||||
addr->sin_family = AF_INET;
|
||||
addr->sin_addr.s_addr = htonl(gw);
|
||||
|
||||
addr = (struct sockaddr_in*) &route.rt_dst;
|
||||
addr->sin_family = AF_INET;
|
||||
addr->sin_addr.s_addr = htonl(ip);
|
||||
|
||||
addr = (struct sockaddr_in*) &route.rt_genmask;
|
||||
addr->sin_family = AF_INET;
|
||||
addr->sin_addr.s_addr = htonl(mask);
|
||||
|
||||
route.rt_flags = RTF_UP | RTF_GATEWAY;
|
||||
route.rt_metric = 0;
|
||||
if ((err = ioctl(sock, SIOCADDRT, &route)) != 0) {
|
||||
close(sock);
|
||||
return -1;
|
||||
}
|
||||
|
||||
close(sock);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
// add a veth device to a bridge
|
||||
void net_bridge_add_interface(const char *bridge, const char *dev) {
|
||||
if (strlen(bridge) > IFNAMSIZ) {
|
||||
fprintf(stderr, "Error: invalid network device name %s\n", bridge);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
struct ifreq ifr;
|
||||
int err;
|
||||
int ifindex = if_nametoindex(dev);
|
||||
|
||||
if (ifindex <= 0)
|
||||
errExit("if_nametoindex");
|
||||
|
||||
int sock;
|
||||
if ((sock = socket(AF_INET, SOCK_STREAM, 0)) < 0)
|
||||
errExit("socket");
|
||||
|
||||
memset(&ifr, 0, sizeof(ifr));
|
||||
strncpy(ifr.ifr_name, bridge, IFNAMSIZ);
|
||||
#ifdef SIOCBRADDIF
|
||||
ifr.ifr_ifindex = ifindex;
|
||||
err = ioctl(sock, SIOCBRADDIF, &ifr);
|
||||
if (err < 0)
|
||||
#endif
|
||||
{
|
||||
unsigned long args[4] = { BRCTL_ADD_IF, ifindex, 0, 0 };
|
||||
|
||||
ifr.ifr_data = (char *) args;
|
||||
err = ioctl(sock, SIOCDEVPRIVATE, &ifr);
|
||||
}
|
||||
(void) err;
|
||||
close(sock);
|
||||
}
|
||||
|
||||
#define BUFSIZE 1024
|
||||
uint32_t network_get_defaultgw(void) {
|
||||
FILE *fp = fopen("/proc/self/net/route", "r");
|
||||
if (!fp)
|
||||
errExit("fopen");
|
||||
|
||||
char buf[BUFSIZE];
|
||||
uint32_t retval = 0;
|
||||
while (fgets(buf, BUFSIZE, fp)) {
|
||||
if (strncmp(buf, "Iface", 5) == 0)
|
||||
continue;
|
||||
|
||||
char *ptr = buf;
|
||||
while (*ptr != ' ' && *ptr != '\t')
|
||||
ptr++;
|
||||
while (*ptr == ' ' || *ptr == '\t')
|
||||
ptr++;
|
||||
|
||||
unsigned dest;
|
||||
unsigned gw;
|
||||
int rv = sscanf(ptr, "%x %x", &dest, &gw);
|
||||
if (rv == 2 && dest == 0) {
|
||||
retval = ntohl(gw);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
fclose(fp);
|
||||
return retval;
|
||||
}
|
||||
|
||||
int net_config_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;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
95
src/firejail/network.txt
Normal file
95
src/firejail/network.txt
Normal file
|
|
@ -0,0 +1,95 @@
|
|||
struct Bridge {
|
||||
char *dev; // bridge device name
|
||||
uint32_t ip; // bridge device IP address
|
||||
uint32_t mask; // bridge device mask
|
||||
uint32_t ipsandbox // sandbox interface IP address
|
||||
}
|
||||
|
||||
net_configure_bridge(br, device) {
|
||||
br->dev = devname;
|
||||
br->ip = extracted from kernel device - using net_get_if_addr() in network.c
|
||||
br->mask = extracted from kernel device - using net_get_if_addr() in network.c
|
||||
check available network range; /31 networks are not supported
|
||||
}
|
||||
|
||||
net_configure_sandbox_ip(br) {
|
||||
if br->ip_snadbox
|
||||
check br->ipsandbox inside the bridge network
|
||||
arp_check(br->ipsandbox) // send an arp req to check if anybody else is using this address
|
||||
else
|
||||
br->ipsandbox = arp_assign();
|
||||
}
|
||||
|
||||
net_configure_veth_pair {
|
||||
create a veth pair
|
||||
place one interface end in the bridge
|
||||
place the other end in the namespace of the child process
|
||||
}
|
||||
|
||||
net_bridge_wait_ip {
|
||||
arp_check br->ipsandbox address to come up
|
||||
wait for not more than 5 seconds
|
||||
}
|
||||
|
||||
main() {
|
||||
|
||||
foreach argv[i] {
|
||||
if --net
|
||||
br = next bridge available
|
||||
net_configure_bridge(br, device name from argv[i]);
|
||||
else if --ip
|
||||
br = last bridge configured
|
||||
br->ipsandbox = ip address extracted from argv[i]
|
||||
else if --defaultgw
|
||||
cfg.defaultgw = ip address extracted from argv[i]
|
||||
}
|
||||
|
||||
net_check_cfg(); // check the validity of network configuration so far
|
||||
|
||||
if (any bridge configured) {
|
||||
lock /var/lock/firejail.lock file
|
||||
for each bridge
|
||||
net_configure_sandbox_ip(br)
|
||||
}
|
||||
|
||||
clone (new network namespace if any bridge configured or --net=none)
|
||||
|
||||
if (any bridge configured) {
|
||||
for each bridge
|
||||
net_configure_veth_pair
|
||||
}
|
||||
|
||||
notify child init is done
|
||||
|
||||
if (any bridge configured) {
|
||||
for each bridge
|
||||
net_bridge_wait_ip
|
||||
unlock /var/lock/firejail.lock file
|
||||
}
|
||||
|
||||
wait on child
|
||||
exit
|
||||
}
|
||||
|
||||
|
||||
******************************************************
|
||||
* macvlan notes
|
||||
******************************************************
|
||||
Configure a macvlan interface
|
||||
|
||||
# ip link add virtual0 link eth0 type macvlan mode bridge
|
||||
(you can configure it with # ifconfig virtual0 192.168.1.52/24 up)
|
||||
|
||||
Create a new network namespace and move the interface in the new network namespace
|
||||
|
||||
# ip netns add dummy0
|
||||
# ip link set virtual0 netns dummy0
|
||||
|
||||
Join the namespace and configure the interfaces
|
||||
|
||||
# ip netns exec dummy0 bash
|
||||
# ifconfig lo up
|
||||
# ifconfig virtual0 192.168.1.52/24
|
||||
|
||||
Investigate ipvlan interface - added to linux kernel 3.19
|
||||
https://github.com/torvalds/linux/blob/master/Documentation/networking/ipvlan.txt
|
||||
268
src/firejail/network_main.c
Normal file
268
src/firejail/network_main.c
Normal file
|
|
@ -0,0 +1,268 @@
|
|||
/*
|
||||
* Copyright (C) 2014, 2015 netblue30 (netblue30@yahoo.com)
|
||||
*
|
||||
* 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 <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
#include <net/if.h>
|
||||
|
||||
// configure bridge structure
|
||||
// - extract ip address and mask from the bridge interface
|
||||
void net_configure_bridge(Bridge *br, char *dev_name) {
|
||||
assert(br);
|
||||
assert(dev_name);
|
||||
|
||||
br->dev = dev_name;
|
||||
|
||||
// check the bridge device exists
|
||||
char sysbridge[30 + strlen(br->dev)];
|
||||
sprintf(sysbridge, "/sys/class/net/%s/bridge", br->dev);
|
||||
struct stat s;
|
||||
int rv = stat(sysbridge, &s);
|
||||
if (rv == 0) {
|
||||
// this is a bridge device
|
||||
br->macvlan = 0;
|
||||
}
|
||||
else {
|
||||
// is this a regular Ethernet interface
|
||||
if (if_nametoindex(br->dev) > 0) {
|
||||
br->macvlan = 1;
|
||||
char *newname;
|
||||
if (asprintf(&newname, "%s-%u", br->devsandbox, getpid()) == -1)
|
||||
errExit("asprintf");
|
||||
br->devsandbox = newname;
|
||||
}
|
||||
else {
|
||||
fprintf(stderr, "Error: cannot find network device %s\n", br->dev);
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
if (net_get_if_addr(br->dev, &br->ip, &br->mask, br->mac)) {
|
||||
fprintf(stderr, "Error: interface %s is not configured\n", br->dev);
|
||||
exit(1);
|
||||
}
|
||||
if (arg_debug) {
|
||||
if (br->macvlan == 0)
|
||||
printf("Bridge device %s at %d.%d.%d.%d/%d\n",
|
||||
br->dev, PRINT_IP(br->ip), mask2bits(br->mask));
|
||||
else
|
||||
printf("macvlan parent device %s at %d.%d.%d.%d/%d\n",
|
||||
br->dev, PRINT_IP(br->ip), mask2bits(br->mask));
|
||||
}
|
||||
|
||||
uint32_t range = ~br->mask + 1; // the number of potential addresses
|
||||
// this software is not supported for /31 networks
|
||||
if (range < 4) {
|
||||
fprintf(stderr, "Error: the software is not supported for /31 networks\n");
|
||||
exit(1);
|
||||
}
|
||||
br->configured = 1;
|
||||
}
|
||||
|
||||
|
||||
void net_configure_sandbox_ip(Bridge *br) {
|
||||
assert(br);
|
||||
if (br->configured == 0)
|
||||
return;
|
||||
|
||||
if (br->arg_ip_none)
|
||||
br->ipsandbox = 0;
|
||||
else if (br->ipsandbox) {
|
||||
// check network range
|
||||
char *rv = in_netrange(br->ipsandbox, br->ip, br->mask);
|
||||
if (rv) {
|
||||
fprintf(stderr, "%s", rv);
|
||||
exit(1);
|
||||
}
|
||||
// send an ARP request and check if there is anybody on this IP address
|
||||
if (arp_check(br->dev, br->ipsandbox, br->ip)) {
|
||||
fprintf(stderr, "Error: IP address %d.%d.%d.%d is already in use\n", PRINT_IP(br->ipsandbox));
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
else
|
||||
// ip address assigned by arp-scan for a bridge device
|
||||
br->ipsandbox = arp_assign(br->dev, br); //br->ip, br->mask);
|
||||
}
|
||||
|
||||
|
||||
// create a veth pair
|
||||
// - br - bridge device
|
||||
// - ifname - interface name in sandbox namespace
|
||||
// - child - child process running the namespace
|
||||
|
||||
void net_configure_veth_pair(Bridge *br, const char *ifname, pid_t child) {
|
||||
assert(br);
|
||||
if (br->configured == 0)
|
||||
return;
|
||||
|
||||
// create a veth pair
|
||||
char *dev;
|
||||
if (asprintf(&dev, "veth%u%s", getpid(), ifname) < 0)
|
||||
errExit("asprintf");
|
||||
net_create_veth(dev, ifname, child);
|
||||
|
||||
// bring up the interface
|
||||
net_if_up(dev);
|
||||
|
||||
// add interface to the bridge
|
||||
net_bridge_add_interface(br->dev, dev);
|
||||
|
||||
char *msg;
|
||||
if (asprintf(&msg, "%d.%d.%d.%d address assigned to sandbox", PRINT_IP(br->ipsandbox)) == -1)
|
||||
errExit("asprintf");
|
||||
logmsg(msg);
|
||||
fflush(0);
|
||||
free(msg);
|
||||
}
|
||||
|
||||
// the default address should be in the range of at least on of the bridge devices
|
||||
void check_default_gw(uint32_t defaultgw) {
|
||||
assert(defaultgw);
|
||||
|
||||
if (cfg.bridge0.configured) {
|
||||
char *rv = in_netrange(defaultgw, cfg.bridge0.ip, cfg.bridge0.mask);
|
||||
if (rv == 0)
|
||||
return;
|
||||
}
|
||||
if (cfg.bridge1.configured) {
|
||||
char *rv = in_netrange(defaultgw, cfg.bridge1.ip, cfg.bridge1.mask);
|
||||
if (rv == 0)
|
||||
return;
|
||||
}
|
||||
if (cfg.bridge2.configured) {
|
||||
char *rv = in_netrange(defaultgw, cfg.bridge2.ip, cfg.bridge2.mask);
|
||||
if (rv == 0)
|
||||
return;
|
||||
}
|
||||
if (cfg.bridge3.configured) {
|
||||
char *rv = in_netrange(defaultgw, cfg.bridge3.ip, cfg.bridge3.mask);
|
||||
if (rv == 0)
|
||||
return;
|
||||
}
|
||||
|
||||
fprintf(stderr, "Error: default gateway %d.%d.%d.%d is not in the range of any network\n", PRINT_IP(defaultgw));
|
||||
exit(1);
|
||||
}
|
||||
|
||||
void net_check_cfg(void) {
|
||||
int net_configured = 0;
|
||||
if (cfg.bridge0.configured)
|
||||
net_configured++;
|
||||
if (cfg.bridge1.configured)
|
||||
net_configured++;
|
||||
if (cfg.bridge2.configured)
|
||||
net_configured++;
|
||||
if (cfg.bridge3.configured)
|
||||
net_configured++;
|
||||
|
||||
// --defaultgw requires a network
|
||||
if (cfg.defaultgw && net_configured == 0) {
|
||||
fprintf(stderr, "Error: option --defaultgw requires at least one network to be configured\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if (net_configured == 0) // nothing to check
|
||||
return;
|
||||
|
||||
// --net=none
|
||||
if (arg_nonetwork && net_configured) {
|
||||
fprintf(stderr, "Error: --net and --net=none are mutually exclusive\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
// check default gateway address or assign one
|
||||
assert(cfg.bridge0.configured);
|
||||
if (cfg.defaultgw)
|
||||
check_default_gw(cfg.defaultgw);
|
||||
else {
|
||||
// first network is a regular bridge
|
||||
if (cfg.bridge0.macvlan == 0)
|
||||
cfg.defaultgw = cfg.bridge0.ip;
|
||||
// first network is a mac device
|
||||
else {
|
||||
// get the host default gw
|
||||
uint32_t gw = network_get_defaultgw();
|
||||
// check the gateway is network range
|
||||
if (in_netrange(gw, cfg.bridge0.ip, cfg.bridge0.mask))
|
||||
gw = 0;
|
||||
cfg.defaultgw = gw;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
void net_dns_print_name(const char *name) {
|
||||
if (!name || strlen(name) == 0) {
|
||||
fprintf(stderr, "Error: invalid sandbox name\n");
|
||||
exit(1);
|
||||
}
|
||||
pid_t pid;
|
||||
if (name2pid(name, &pid)) {
|
||||
fprintf(stderr, "Error: cannot find sandbox %s\n", name);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
net_dns_print(pid);
|
||||
}
|
||||
|
||||
#define MAXBUF 4096
|
||||
void net_dns_print(pid_t pid) {
|
||||
// drop privileges - will not be able to read /etc/resolv.conf for --noroot option
|
||||
// drop_privs(1);
|
||||
|
||||
// if the pid is that of a firejail process, use the pid of the first child process
|
||||
char *comm = pid_proc_comm(pid);
|
||||
if (comm) {
|
||||
// remove \n
|
||||
char *ptr = strchr(comm, '\n');
|
||||
if (ptr)
|
||||
*ptr = '\0';
|
||||
if (strcmp(comm, "firejail") == 0) {
|
||||
pid_t child;
|
||||
if (find_child(pid, &child) == 0) {
|
||||
pid = child;
|
||||
}
|
||||
}
|
||||
free(comm);
|
||||
}
|
||||
|
||||
char *fname;
|
||||
if (asprintf(&fname, "/proc/%d/root/etc/resolv.conf", pid) == -1)
|
||||
errExit("asprintf");
|
||||
|
||||
// access /etc/resolv.conf
|
||||
FILE *fp = fopen(fname, "r");
|
||||
if (!fp) {
|
||||
fprintf(stderr, "Error: cannot access /etc/resolv.conf\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
char buf[MAXBUF];
|
||||
while (fgets(buf, MAXBUF, fp))
|
||||
printf("%s", buf);
|
||||
printf("\n");
|
||||
fclose(fp);
|
||||
free(fname);
|
||||
exit(0);
|
||||
}
|
||||
84
src/firejail/output.c
Normal file
84
src/firejail/output.c
Normal file
|
|
@ -0,0 +1,84 @@
|
|||
#include "firejail.h"
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
|
||||
void check_output(int argc, char **argv) {
|
||||
int i;
|
||||
char *outfile = NULL;
|
||||
// drop_privs(0);
|
||||
|
||||
int found = 0;
|
||||
for (i = 1; i < argc; i++) {
|
||||
if (strncmp(argv[i], "--output=", 9) == 0) {
|
||||
found = 1;
|
||||
outfile = argv[i] + 9;
|
||||
|
||||
// do not accept directories, links, and files with ".."
|
||||
if (strstr(outfile, "..") || is_link(outfile) || is_dir(outfile)) {
|
||||
fprintf(stderr, "Error: invalid output file. Links, directories and files with \"..\" are not allowed.\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
struct stat s;
|
||||
if (stat(outfile, &s) == 0) {
|
||||
// check permissions
|
||||
if (s.st_uid != getuid() || s.st_gid != getgid()) {
|
||||
fprintf(stderr, "Error: the output file needs to be owned by the current user.\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
// check hard links
|
||||
if (s.st_nlink != 1) {
|
||||
fprintf(stderr, "Error: no hard links allowed.\n");
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
// drop privileges and try to open the file for writing
|
||||
drop_privs(0);
|
||||
/* coverity[toctou] */
|
||||
FILE *fp = fopen(outfile, "a");
|
||||
if (!fp) {
|
||||
fprintf(stderr, "Error: cannot open output file %s\n", outfile);
|
||||
exit(1);
|
||||
}
|
||||
fclose(fp);
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!found)
|
||||
return;
|
||||
|
||||
|
||||
// build the new command line
|
||||
int len = 0;
|
||||
for (i = 0; i < argc; i++) {
|
||||
len += strlen(argv[i]) + 1; // + ' '
|
||||
}
|
||||
len += 50 + strlen(outfile); // tee command
|
||||
|
||||
char *cmd = malloc(len + 1); // + '\0'
|
||||
if (!cmd)
|
||||
errExit("malloc");
|
||||
|
||||
char *ptr = cmd;
|
||||
for (i = 0; i < argc; i++) {
|
||||
if (strncmp(argv[i], "--output=", 9) == 0)
|
||||
continue;
|
||||
ptr += sprintf(ptr, "%s ", argv[i]);
|
||||
}
|
||||
sprintf(ptr, "| %s/lib/firejail/ftee %s", PREFIX, outfile);
|
||||
|
||||
// run command
|
||||
char *a[4];
|
||||
a[0] = "/bin/bash";
|
||||
a[1] = "-c";
|
||||
a[2] = cmd;
|
||||
a[3] = NULL;
|
||||
|
||||
execvp(a[0], a);
|
||||
|
||||
perror("execvp");
|
||||
exit(1);
|
||||
}
|
||||
444
src/firejail/profile.c
Normal file
444
src/firejail/profile.c
Normal file
|
|
@ -0,0 +1,444 @@
|
|||
/*
|
||||
* Copyright (C) 2014, 2015 netblue30 (netblue30@yahoo.com)
|
||||
*
|
||||
* 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 <dirent.h>
|
||||
#include <sys/stat.h>
|
||||
|
||||
#define MAX_READ 8192 // line buffer for profile files
|
||||
|
||||
// find and read the profile specified by name from dir directory
|
||||
int profile_find(const char *name, const char *dir) {
|
||||
assert(name);
|
||||
assert(dir);
|
||||
|
||||
int rv = 0;
|
||||
DIR *dp;
|
||||
char *pname;
|
||||
if (asprintf(&pname, "%s.profile", name) == -1)
|
||||
errExit("asprintf");
|
||||
|
||||
dp = opendir (dir);
|
||||
if (dp != NULL) {
|
||||
struct dirent *ep;
|
||||
while ((ep = readdir(dp)) != NULL) {
|
||||
if (strcmp(ep->d_name, pname) == 0) {
|
||||
if (arg_debug)
|
||||
printf("Found %s profile in %s directory\n", name, dir);
|
||||
char *etcpname;
|
||||
if (asprintf(&etcpname, "%s/%s", dir, pname) == -1)
|
||||
errExit("asprintf");
|
||||
profile_read(etcpname, NULL, NULL);
|
||||
free(etcpname);
|
||||
rv = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
(void) closedir (dp);
|
||||
}
|
||||
|
||||
free(pname);
|
||||
return rv;
|
||||
}
|
||||
|
||||
|
||||
//***************************************************
|
||||
// run-time profiles
|
||||
//***************************************************
|
||||
static void check_file_name(char *ptr, int lineno) {
|
||||
if (strncmp(ptr, "${HOME}", 7) == 0)
|
||||
ptr += 7;
|
||||
else if (strncmp(ptr, "${PATH}", 7) == 0)
|
||||
ptr += 7;
|
||||
|
||||
int len = strlen(ptr);
|
||||
// file globbing ('*') is allowed
|
||||
if (strcspn(ptr, "\\&!?\"'<>%^(){}[];, ") != len) {
|
||||
if (lineno == 0)
|
||||
fprintf(stderr, "Error: \"%s\" is an invalid filename\n", ptr);
|
||||
else
|
||||
fprintf(stderr, "Error: line %d in the custom profile is invalid\n", lineno);
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// check profile line; if line == 0, this was generated from a command line option
|
||||
// return 1 if the command is to be added to the linked list of profile commands
|
||||
// return 0 if the command was already executed inside the function
|
||||
int profile_check_line(char *ptr, int lineno) {
|
||||
// seccomp, caps, private, user namespace
|
||||
if (strcmp(ptr, "noroot") == 0) {
|
||||
check_user_namespace();
|
||||
return 0;
|
||||
}
|
||||
else if (strcmp(ptr, "seccomp") == 0) {
|
||||
arg_seccomp = 1;
|
||||
return 0;
|
||||
}
|
||||
else if (strcmp(ptr, "caps") == 0) {
|
||||
arg_caps_default_filter = 1;
|
||||
return 0;
|
||||
}
|
||||
else if (strcmp(ptr, "caps.drop all") == 0) {
|
||||
arg_caps_drop_all = 1;
|
||||
return 0;
|
||||
}
|
||||
else if (strcmp(ptr, "shell none") == 0) {
|
||||
arg_shell_none = 1;
|
||||
return 0;
|
||||
}
|
||||
else if (strcmp(ptr, "private") == 0) {
|
||||
arg_private = 1;
|
||||
return 0;
|
||||
}
|
||||
else if (strcmp(ptr, "private-dev") == 0) {
|
||||
arg_private_dev = 1;
|
||||
return 0;
|
||||
}
|
||||
else if (strcmp(ptr, "nogroups") == 0) {
|
||||
arg_nogroups = 1;
|
||||
return 0;
|
||||
}
|
||||
else if (strcmp(ptr, "netfilter") == 0) {
|
||||
arg_netfilter = 1;
|
||||
return 0;
|
||||
}
|
||||
else if (strncmp(ptr, "netfilter ", 10) == 0) {
|
||||
arg_netfilter = 1;
|
||||
arg_netfilter_file = strdup(ptr + 10);
|
||||
if (!arg_netfilter_file)
|
||||
errExit("strdup");
|
||||
check_netfilter_file(arg_netfilter_file);
|
||||
return 0;
|
||||
}
|
||||
|
||||
// seccomp drop list on top of default list
|
||||
if (strncmp(ptr, "seccomp ", 8) == 0) {
|
||||
arg_seccomp = 1;
|
||||
#ifdef HAVE_SECCOMP
|
||||
arg_seccomp_list = strdup(ptr + 8);
|
||||
if (!arg_seccomp_list)
|
||||
errExit("strdup");
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
// seccomp drop list without default list
|
||||
if (strncmp(ptr, "seccomp.drop ", 13) == 0) {
|
||||
arg_seccomp = 1;
|
||||
#ifdef HAVE_SECCOMP
|
||||
arg_seccomp_list_drop = strdup(ptr + 13);
|
||||
if (!arg_seccomp_list_drop)
|
||||
errExit("strdup");
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
// seccomp keep list
|
||||
if (strncmp(ptr, "seccomp.keep ", 13) == 0) {
|
||||
arg_seccomp = 1;
|
||||
#ifdef HAVE_SECCOMP
|
||||
arg_seccomp_list_keep= strdup(ptr + 13);
|
||||
if (!arg_seccomp_list_keep)
|
||||
errExit("strdup");
|
||||
#endif
|
||||
return 0;
|
||||
}
|
||||
|
||||
// caps drop list
|
||||
if (strncmp(ptr, "caps.drop ", 10) == 0) {
|
||||
arg_caps_drop = 1;
|
||||
arg_caps_list = strdup(ptr + 10);
|
||||
if (!arg_caps_list)
|
||||
errExit("strdup");
|
||||
// verify seccomp list and exit if problems
|
||||
if (caps_check_list(arg_caps_list, NULL))
|
||||
exit(1);
|
||||
return 0;
|
||||
}
|
||||
|
||||
// caps keep list
|
||||
if (strncmp(ptr, "caps.keep ", 10) == 0) {
|
||||
arg_caps_keep = 1;
|
||||
arg_caps_list = strdup(ptr + 10);
|
||||
if (!arg_caps_list)
|
||||
errExit("strdup");
|
||||
// verify seccomp list and exit if problems
|
||||
if (caps_check_list(arg_caps_list, NULL))
|
||||
exit(1);
|
||||
return 0;
|
||||
}
|
||||
|
||||
// dns
|
||||
if (strncmp(ptr, "dns ", 4) == 0) {
|
||||
uint32_t dns;
|
||||
if (atoip(ptr + 4, &dns)) {
|
||||
fprintf(stderr, "Error: invalid DNS server IP address\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
if (cfg.dns1 == 0)
|
||||
cfg.dns1 = dns;
|
||||
else if (cfg.dns2 == 0)
|
||||
cfg.dns2 = dns;
|
||||
else if (cfg.dns3 == 0)
|
||||
cfg.dns3 = dns;
|
||||
else {
|
||||
fprintf(stderr, "Error: up to 3 DNS servers can be specified\n");
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
// cpu affinity
|
||||
if (strncmp(ptr, "cpu ", 4) == 0) {
|
||||
read_cpu_list(ptr + 4);
|
||||
return 0;
|
||||
}
|
||||
|
||||
// cgroup
|
||||
if (strncmp(ptr, "cgroup ", 7) == 0) {
|
||||
set_cgroup(ptr + 7);
|
||||
return 0;
|
||||
}
|
||||
|
||||
// private directory
|
||||
if (strncmp(ptr, "private ", 8) == 0) {
|
||||
cfg.home_private = ptr + 8;
|
||||
fs_check_private_dir();
|
||||
arg_private = 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
// private list of files and directories
|
||||
if (strncmp(ptr, "private.keep ", 13) == 0) {
|
||||
cfg.home_private_keep = ptr + 13;
|
||||
fs_check_home_list();
|
||||
arg_private = 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
// filesystem bind
|
||||
if (strncmp(ptr, "bind ", 5) == 0) {
|
||||
if (getuid() != 0) {
|
||||
fprintf(stderr, "Error: --bind option is available only if running as root\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
// extract two directories
|
||||
char *dname1 = ptr + 5;
|
||||
char *dname2 = split_comma(dname1); // this inserts a '0 to separate the two dierctories
|
||||
if (dname2 == NULL) {
|
||||
fprintf(stderr, "Error: mising second directory for bind\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
// check directories
|
||||
check_file_name(dname1, lineno);
|
||||
check_file_name(dname2, lineno);
|
||||
if (strstr(dname1, "..") || strstr(dname2, "..")) {
|
||||
fprintf(stderr, "Error: invalid file name.\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
// insert comma back
|
||||
*(dname2 - 1) = ',';
|
||||
return 1;
|
||||
}
|
||||
|
||||
// rlimit
|
||||
if (strncmp(ptr, "rlimit", 6) == 0) {
|
||||
if (strncmp(ptr, "rlimit-nofile ", 14) == 0) {
|
||||
ptr += 14;
|
||||
if (not_unsigned(ptr)) {
|
||||
fprintf(stderr, "Invalid rlimit option on line %d\n", lineno);
|
||||
exit(1);
|
||||
}
|
||||
sscanf(ptr, "%u", &cfg.rlimit_nofile);
|
||||
arg_rlimit_nofile = 1;
|
||||
}
|
||||
else if (strncmp(ptr, "rlimit-nproc ", 13) == 0) {
|
||||
ptr += 13;
|
||||
if (not_unsigned(ptr)) {
|
||||
fprintf(stderr, "Invalid rlimit option on line %d\n", lineno);
|
||||
exit(1);
|
||||
}
|
||||
sscanf(ptr, "%u", &cfg.rlimit_nproc);
|
||||
arg_rlimit_nproc = 1;
|
||||
}
|
||||
else if (strncmp(ptr, "rlimit-fsize ", 13) == 0) {
|
||||
ptr += 13;
|
||||
if (not_unsigned(ptr)) {
|
||||
fprintf(stderr, "Invalid rlimit option on line %d\n", lineno);
|
||||
exit(1);
|
||||
}
|
||||
sscanf(ptr, "%u", &cfg.rlimit_fsize);
|
||||
arg_rlimit_fsize = 1;
|
||||
}
|
||||
else if (strncmp(ptr, "rlimit-sigpending ", 18) == 0) {
|
||||
ptr += 18;
|
||||
if (not_unsigned(ptr)) {
|
||||
fprintf(stderr, "Invalid rlimit option on line %d\n", lineno);
|
||||
exit(1);
|
||||
}
|
||||
sscanf(ptr, "%u", &cfg.rlimit_sigpending);
|
||||
arg_rlimit_sigpending = 1;
|
||||
}
|
||||
else {
|
||||
fprintf(stderr, "Invalid rlimit option on line %d\n", lineno);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
// rest of filesystem
|
||||
if (strncmp(ptr, "blacklist ", 10) == 0)
|
||||
ptr += 10;
|
||||
else if (strncmp(ptr, "read-only ", 10) == 0)
|
||||
ptr += 10;
|
||||
else if (strncmp(ptr, "tmpfs ", 6) == 0)
|
||||
ptr += 6;
|
||||
else {
|
||||
if (lineno == 0)
|
||||
fprintf(stderr, "Error: \"%s\" as a command line option is invalid\n", ptr);
|
||||
else
|
||||
fprintf(stderr, "Error: line %d in the custom profile is invalid\n", lineno);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
// some characters just don't belong in filenames
|
||||
check_file_name(ptr, lineno);
|
||||
if (strstr(ptr, "..")) {
|
||||
if (lineno == 0)
|
||||
fprintf(stderr, "Error: \"%s\" is an invalid filename\n", ptr);
|
||||
else
|
||||
fprintf(stderr, "Error: line %d in the custom profile is invalid\n", lineno);
|
||||
exit(1);
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
// add a profile entry in cfg.profile list; use str to populate the list
|
||||
void profile_add(char *str) {
|
||||
ProfileEntry *prf = malloc(sizeof(ProfileEntry));
|
||||
if (!prf)
|
||||
errExit("malloc");
|
||||
prf->next = NULL;
|
||||
prf->data = str;
|
||||
|
||||
// add prf to the list
|
||||
if (cfg.profile == NULL) {
|
||||
cfg.profile = prf;
|
||||
return;
|
||||
}
|
||||
ProfileEntry *ptr = cfg.profile;
|
||||
while (ptr->next != NULL)
|
||||
ptr = ptr->next;
|
||||
ptr->next = prf;
|
||||
}
|
||||
|
||||
// read a profile file
|
||||
static int include_level = 0;
|
||||
// skip1, skip2 - if the string is found in the line, the line is not interpreted
|
||||
void profile_read(const char *fname, const char *skip1, const char *skip2) {
|
||||
// exit program if maximum include level was reached
|
||||
if (include_level > MAX_INCLUDE_LEVEL) {
|
||||
fprintf(stderr, "Error: maximum profile include level was reached\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if (strlen(fname) == 0) {
|
||||
fprintf(stderr, "Error: invalid profile file\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
// open profile file:
|
||||
FILE *fp = fopen(fname, "r");
|
||||
if (fp == NULL) {
|
||||
fprintf(stderr, "Error: cannot open profile file\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
fprintf(stderr, "Reading profile %s\n", fname);
|
||||
|
||||
// read the file line by line
|
||||
char buf[MAX_READ + 1];
|
||||
int lineno = 0;
|
||||
while (fgets(buf, MAX_READ, fp)) {
|
||||
++lineno;
|
||||
// remove empty space - ptr in allocated memory
|
||||
char *ptr = line_remove_spaces(buf);
|
||||
if (ptr == NULL)
|
||||
continue;
|
||||
|
||||
// comments
|
||||
if (*ptr == '#' || *ptr == '\0') {
|
||||
free(ptr);
|
||||
continue;
|
||||
}
|
||||
|
||||
// process include
|
||||
if (strncmp(ptr, "include ", 8) == 0) {
|
||||
include_level++;
|
||||
|
||||
// extract profile filename and new skip params
|
||||
char *newprofile = ptr + 8; // profile name
|
||||
char *newskip1 = NULL; // new skip1
|
||||
char *newskip2 = NULL; // new skip2
|
||||
char *p = newprofile;
|
||||
while (*p != '\0') {
|
||||
if (*p == ' ') {
|
||||
*p = '\0';
|
||||
if (newskip1 == NULL)
|
||||
newskip1 = p + 1;
|
||||
else if (newskip2 == NULL)
|
||||
newskip2 = p + 1;
|
||||
}
|
||||
p++;
|
||||
}
|
||||
|
||||
// recursivity
|
||||
profile_read(newprofile, newskip1, newskip2);
|
||||
include_level--;
|
||||
free(ptr);
|
||||
continue;
|
||||
}
|
||||
|
||||
// skip
|
||||
if (skip1) {
|
||||
if (strstr(ptr, skip1)) {
|
||||
free(ptr);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
if (skip2) {
|
||||
if (strstr(ptr, skip2)) {
|
||||
free(ptr);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// verify syntax, exit in case of error
|
||||
if (profile_check_line(ptr, lineno))
|
||||
profile_add(ptr);
|
||||
}
|
||||
fclose(fp);
|
||||
}
|
||||
96
src/firejail/restricted_shell.c
Normal file
96
src/firejail/restricted_shell.c
Normal file
|
|
@ -0,0 +1,96 @@
|
|||
/*
|
||||
* Copyright (C) 2014, 2015 netblue30 (netblue30@yahoo.com)
|
||||
*
|
||||
* 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"
|
||||
|
||||
#define MAX_READ 4096 // maximum line length
|
||||
char *restricted_user = NULL;
|
||||
|
||||
|
||||
int restricted_shell(const char *user) {
|
||||
assert(user);
|
||||
|
||||
// open profile file:
|
||||
FILE *fp = fopen("/etc/firejail/login.users", "r");
|
||||
if (fp == NULL)
|
||||
return 0;
|
||||
|
||||
int lineno = 0;
|
||||
char buf[MAX_READ];
|
||||
while (fgets(buf, MAX_READ, fp)) {
|
||||
lineno++;
|
||||
|
||||
// remove empty spaces at the beginning of the line
|
||||
char *ptr = buf;
|
||||
while (*ptr == ' ' || *ptr == '\t') {
|
||||
ptr++;
|
||||
}
|
||||
if (*ptr == '\n' || *ptr == '#')
|
||||
continue;
|
||||
|
||||
// parse line
|
||||
char *usr = ptr;
|
||||
char *args = strchr(usr, ':');
|
||||
if (args == NULL) {
|
||||
fprintf(stderr, "Error: users.conf line %d\n", lineno);
|
||||
exit(1);
|
||||
}
|
||||
*args = '\0';
|
||||
args++;
|
||||
ptr = strchr(args, '\n');
|
||||
if (ptr)
|
||||
*ptr = '\0';
|
||||
|
||||
if (strcmp(user, usr) == 0) {
|
||||
restricted_user = strdup(user);
|
||||
// extract program arguments
|
||||
|
||||
fullargv[0] = "firejail";
|
||||
int i;
|
||||
ptr = args;
|
||||
for (i = 1; i < MAX_ARGS; i++) {
|
||||
fullargv[i] = ptr;
|
||||
while (*ptr != ' ' && *ptr != '\t' && *ptr != '\0')
|
||||
ptr++;
|
||||
if (*ptr != '\0') {
|
||||
*ptr ='\0';
|
||||
fullargv[i] = strdup(fullargv[i]);
|
||||
if (fullargv[i] == NULL) {
|
||||
fprintf(stderr, "Error: cannot allocate memory\n");
|
||||
exit(1);
|
||||
}
|
||||
ptr++;
|
||||
while (*ptr == ' ' || *ptr == '\t')
|
||||
ptr++;
|
||||
if (*ptr != '\0')
|
||||
continue;
|
||||
}
|
||||
fullargv[i] = strdup(fullargv[i]);
|
||||
fclose(fp);
|
||||
return i + 1;
|
||||
}
|
||||
fprintf(stderr, "Error: too many program arguments in users.conf line %d\n", lineno);
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
fclose(fp);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
62
src/firejail/rlimit.c
Normal file
62
src/firejail/rlimit.c
Normal file
|
|
@ -0,0 +1,62 @@
|
|||
/*
|
||||
* Copyright (C) 2014, 2015 netblue30 (netblue30@yahoo.com)
|
||||
*
|
||||
* 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 <sys/time.h>
|
||||
#include <sys/resource.h>
|
||||
|
||||
void set_rlimits(void) {
|
||||
// resource limits
|
||||
struct rlimit rl;
|
||||
if (arg_rlimit_nofile) {
|
||||
rl.rlim_cur = (rlim_t) cfg.rlimit_nofile;
|
||||
rl.rlim_max = (rlim_t) cfg.rlimit_nofile;
|
||||
if (setrlimit(RLIMIT_NOFILE, &rl) == -1)
|
||||
errExit("setrlimit");
|
||||
if (arg_debug)
|
||||
printf("Config rlimit: number of open file descriptors %u\n", cfg.rlimit_nofile);
|
||||
}
|
||||
|
||||
if (arg_rlimit_nproc) {
|
||||
rl.rlim_cur = (rlim_t) cfg.rlimit_nproc;
|
||||
rl.rlim_max = (rlim_t) cfg.rlimit_nproc;
|
||||
if (setrlimit(RLIMIT_NPROC, &rl) == -1)
|
||||
errExit("setrlimit");
|
||||
if (arg_debug)
|
||||
printf("Config rlimit: number of processes %u\n", cfg.rlimit_nproc);
|
||||
}
|
||||
|
||||
if (arg_rlimit_fsize) {
|
||||
rl.rlim_cur = (rlim_t) cfg.rlimit_fsize;
|
||||
rl.rlim_max = (rlim_t) cfg.rlimit_fsize;
|
||||
if (setrlimit(RLIMIT_FSIZE, &rl) == -1)
|
||||
errExit("setrlimit");
|
||||
if (arg_debug)
|
||||
printf("Config rlimit: maximum file size %u\n", cfg.rlimit_fsize);
|
||||
}
|
||||
|
||||
if (arg_rlimit_sigpending) {
|
||||
rl.rlim_cur = (rlim_t) cfg.rlimit_sigpending;
|
||||
rl.rlim_max = (rlim_t) cfg.rlimit_sigpending;
|
||||
if (setrlimit(RLIMIT_SIGPENDING, &rl) == -1)
|
||||
errExit("setrlimit");
|
||||
if (arg_debug)
|
||||
printf("Config rlimit: maximum number of signals pending %u\n", cfg.rlimit_sigpending);
|
||||
}
|
||||
}
|
||||
490
src/firejail/sandbox.c
Normal file
490
src/firejail/sandbox.c
Normal file
|
|
@ -0,0 +1,490 @@
|
|||
/*
|
||||
* Copyright (C) 2014, 2015 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 <sys/mount.h>
|
||||
#include <sys/wait.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/prctl.h>
|
||||
#include <sys/time.h>
|
||||
#include <sys/resource.h>
|
||||
|
||||
#include <sched.h>
|
||||
#ifndef CLONE_NEWUSER
|
||||
#define CLONE_NEWUSER 0x10000000
|
||||
#endif
|
||||
|
||||
static void set_caps(void) {
|
||||
if (arg_caps_drop_all)
|
||||
caps_drop_all();
|
||||
else if (arg_caps_drop)
|
||||
caps_drop_list(arg_caps_list);
|
||||
else if (arg_caps_keep)
|
||||
caps_keep_list(arg_caps_list);
|
||||
else if (arg_caps_default_filter)
|
||||
caps_default_filter();
|
||||
}
|
||||
|
||||
void save_nogroups(void) {
|
||||
if (arg_nogroups == 0)
|
||||
return;
|
||||
|
||||
char *fname;
|
||||
if (asprintf(&fname, "%s/groups", MNT_DIR) == -1)
|
||||
errExit("asprintf");
|
||||
FILE *fp = fopen(fname, "w");
|
||||
if (fp) {
|
||||
fprintf(fp, "\n");
|
||||
fclose(fp);
|
||||
if (chown(fname, 0, 0) < 0)
|
||||
errExit("chown");
|
||||
}
|
||||
else {
|
||||
fprintf(stderr, "Error: cannot save nogroups state\n");
|
||||
free(fname);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
free(fname);
|
||||
}
|
||||
|
||||
static void sandbox_if_up(Bridge *br) {
|
||||
assert(br);
|
||||
if (!br->configured)
|
||||
return;
|
||||
|
||||
char *dev = br->devsandbox;
|
||||
net_if_up(dev);
|
||||
|
||||
if (br->arg_ip_none == 1); // do nothing
|
||||
else if (br->arg_ip_none == 0 && br->macvlan == 0) {
|
||||
if (br->ipsandbox == br->ip) {
|
||||
fprintf(stderr, "Error: %d.%d.%d.%d is interface %s address.\n", PRINT_IP(br->ipsandbox), br->dev);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
// just assign the address
|
||||
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);
|
||||
net_if_up(dev);
|
||||
}
|
||||
else if (br->arg_ip_none == 0 && br->macvlan == 1) {
|
||||
// reassign the macvlan address
|
||||
if (br->ipsandbox == 0)
|
||||
// ip address assigned by arp-scan for a macvlan device
|
||||
br->ipsandbox = arp_assign(dev, br); //br->ip, br->mask);
|
||||
else {
|
||||
if (br->ipsandbox == br->ip) {
|
||||
fprintf(stderr, "Error: %d.%d.%d.%d is interface %s address.\n", PRINT_IP(br->ipsandbox), br->dev);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
uint32_t rv = arp_check(dev, br->ipsandbox, br->ip);
|
||||
if (rv) {
|
||||
fprintf(stderr, "Error: the address %d.%d.%d.%d is already in use.\n", PRINT_IP(br->ipsandbox));
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
net_if_up(dev);
|
||||
}
|
||||
}
|
||||
|
||||
static void chk_chroot(void) {
|
||||
// if we are starting firejail inside some other container technology, we don't care about this
|
||||
char *mycont = getenv("container");
|
||||
if (mycont)
|
||||
return;
|
||||
|
||||
// check if this is a regular chroot
|
||||
struct stat s;
|
||||
if (stat("/", &s) == 0) {
|
||||
if (s.st_ino != 2)
|
||||
return;
|
||||
}
|
||||
|
||||
fprintf(stderr, "Error: cannot mount filesystem as slave\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
int sandbox(void* sandbox_arg) {
|
||||
pid_t child_pid = getpid();
|
||||
if (arg_debug)
|
||||
printf("Initializing child process\n");
|
||||
|
||||
// close each end of the unused pipes
|
||||
close(parent_to_child_fds[1]);
|
||||
close(child_to_parent_fds[0]);
|
||||
|
||||
// wait for parent to do base setup
|
||||
wait_for_other(parent_to_child_fds[0]);
|
||||
|
||||
if (arg_debug && child_pid == 1)
|
||||
printf("PID namespace installed\n");
|
||||
|
||||
//****************************
|
||||
// set hostname
|
||||
//****************************
|
||||
if (cfg.hostname) {
|
||||
if (sethostname(cfg.hostname, strlen(cfg.hostname)) < 0)
|
||||
errExit("sethostname");
|
||||
}
|
||||
|
||||
//****************************
|
||||
// mount namespace
|
||||
//****************************
|
||||
// mount events are not forwarded between the host the sandbox
|
||||
if (mount(NULL, "/", NULL, MS_SLAVE | MS_REC, NULL) < 0) {
|
||||
chk_chroot();
|
||||
}
|
||||
|
||||
//****************************
|
||||
// netfilter
|
||||
//****************************
|
||||
if (arg_netfilter && any_bridge_configured()) { // assuming by default the client filter
|
||||
netfilter(arg_netfilter_file);
|
||||
}
|
||||
|
||||
//****************************
|
||||
// trace pre-install
|
||||
//****************************
|
||||
if (arg_trace)
|
||||
fs_trace_preload();
|
||||
|
||||
//****************************
|
||||
// configure filesystem
|
||||
//****************************
|
||||
#ifdef HAVE_CHROOT
|
||||
if (cfg.chrootdir) {
|
||||
fs_chroot(cfg.chrootdir);
|
||||
// force caps and seccomp if not started as root
|
||||
if (getuid() != 0) {
|
||||
// force default seccomp inside the chroot, no keep or drop list
|
||||
// the list build on top of the default drop list is kept intact
|
||||
arg_seccomp = 1;
|
||||
if (arg_seccomp_list_drop) {
|
||||
free(arg_seccomp_list_drop);
|
||||
arg_seccomp_list_drop = NULL;
|
||||
}
|
||||
if (arg_seccomp_list_keep) {
|
||||
free(arg_seccomp_list_keep);
|
||||
arg_seccomp_list_keep = NULL;
|
||||
}
|
||||
|
||||
// disable all capabilities
|
||||
if (arg_caps_default_filter || arg_caps_list)
|
||||
fprintf(stderr, "Warning: all capabilities disabled for a regular user during chroot\n");
|
||||
arg_caps_drop_all = 1;
|
||||
|
||||
// drop all supplementary groups; /etc/group file inside chroot
|
||||
// is controlled by a regular usr
|
||||
arg_nogroups = 1;
|
||||
printf("Dropping all Linux capabilities and enforcing default seccomp filter\n");
|
||||
}
|
||||
|
||||
//****************************
|
||||
// trace pre-install, this time inside chroot
|
||||
//****************************
|
||||
if (arg_trace)
|
||||
fs_trace_preload();
|
||||
}
|
||||
else
|
||||
#endif
|
||||
if (arg_overlay)
|
||||
fs_overlayfs();
|
||||
else
|
||||
fs_basic_fs();
|
||||
|
||||
|
||||
//****************************
|
||||
// set hostname in /etc/hostname
|
||||
//****************************
|
||||
if (cfg.hostname) {
|
||||
fs_hostname(cfg.hostname);
|
||||
}
|
||||
|
||||
//****************************
|
||||
// apply the profile file
|
||||
//****************************
|
||||
if (cfg.profile)
|
||||
fs_blacklist(cfg.homedir);
|
||||
|
||||
//****************************
|
||||
// private mode
|
||||
//****************************
|
||||
if (arg_private) {
|
||||
if (cfg.home_private) // --private=
|
||||
fs_private_homedir();
|
||||
else if (cfg.home_private_keep) // --private.keep=
|
||||
fs_private_home_list();
|
||||
else // --private
|
||||
fs_private();
|
||||
}
|
||||
|
||||
if (arg_private_dev)
|
||||
fs_private_dev();
|
||||
|
||||
//****************************
|
||||
// install trace
|
||||
//****************************
|
||||
if (arg_trace)
|
||||
fs_trace();
|
||||
|
||||
//****************************
|
||||
// update /proc, /dev, /boot directorymy
|
||||
//****************************
|
||||
fs_proc_sys_dev_boot();
|
||||
|
||||
//****************************
|
||||
// networking
|
||||
//****************************
|
||||
if (arg_nonetwork) {
|
||||
net_if_up("lo");
|
||||
}
|
||||
else if (any_bridge_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);
|
||||
|
||||
// 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");
|
||||
}
|
||||
|
||||
if (arg_debug)
|
||||
printf("Network namespace enabled\n");
|
||||
}
|
||||
|
||||
// if any dns server is configured, it is time to set it now
|
||||
fs_resolvconf();
|
||||
|
||||
// print network configuration
|
||||
if (any_bridge_configured() || cfg.defaultgw || cfg.dns1) {
|
||||
printf("\n");
|
||||
if (any_bridge_configured())
|
||||
net_ifprint();
|
||||
if (cfg.defaultgw != 0)
|
||||
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");
|
||||
}
|
||||
|
||||
|
||||
|
||||
//****************************
|
||||
// start executable
|
||||
//****************************
|
||||
prctl(PR_SET_PDEATHSIG, SIGKILL, 0, 0, 0); // kill the child in case the parent died
|
||||
int cwd = 0;
|
||||
if (cfg.cwd) {
|
||||
if (chdir(cfg.cwd) == 0)
|
||||
cwd = 1;
|
||||
}
|
||||
|
||||
if (!cwd) {
|
||||
if (chdir("/") < 0)
|
||||
errExit("chdir");
|
||||
if (cfg.homedir) {
|
||||
struct stat s;
|
||||
if (stat(cfg.homedir, &s) == 0) {
|
||||
/* coverity[toctou] */
|
||||
if (chdir(cfg.homedir) < 0)
|
||||
errExit("chdir");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// set environment
|
||||
// fix qt 4.8
|
||||
if (setenv("QT_X11_NO_MITSHM", "1", 1) < 0)
|
||||
errExit("setenv");
|
||||
if (setenv("container", "firejail", 1) < 0) // LXC sets container=lxc,
|
||||
errExit("setenv");
|
||||
if (arg_zsh && setenv("SHELL", "/usr/bin/zsh", 1) < 0)
|
||||
errExit("setenv");
|
||||
if (arg_csh && setenv("SHELL", "/bin/csh", 1) < 0)
|
||||
errExit("setenv");
|
||||
if (cfg.shell && setenv("SHELL", cfg.shell, 1) < 0)
|
||||
errExit("setenv");
|
||||
// set prompt color to green
|
||||
//export PS1='\[\e[1;32m\][\u@\h \W]\$\[\e[0m\] '
|
||||
if (setenv("PROMPT_COMMAND", "export PS1=\"\\[\\e[1;32m\\][\\u@\\h \\W]\\$\\[\\e[0m\\] \"", 1) < 0)
|
||||
errExit("setenv");
|
||||
|
||||
|
||||
// set capabilities
|
||||
if (!arg_noroot)
|
||||
set_caps();
|
||||
|
||||
// set rlimits
|
||||
set_rlimits();
|
||||
|
||||
// set seccomp
|
||||
#ifdef HAVE_SECCOMP
|
||||
// if a keep list is available, disregard the drop list
|
||||
if (arg_seccomp == 1) {
|
||||
if (arg_seccomp_list_keep)
|
||||
seccomp_filter_keep(); // this will also save the fmyilter to MNT_DIR/seccomp file
|
||||
else
|
||||
seccomp_filter_drop(); // this will also save the filter to MNT_DIR/seccomp file
|
||||
}
|
||||
#endif
|
||||
|
||||
// set cpu affinity
|
||||
if (cfg.cpus) {
|
||||
save_cpu(); // save cpu affinity mask to MNT_DIR/cpu file
|
||||
set_cpu_affinity();
|
||||
}
|
||||
|
||||
// save cgroup in MNT_DIR/cgroup file
|
||||
if (cfg.cgroup)
|
||||
save_cgroup();
|
||||
|
||||
//****************************************
|
||||
// drop privileges or create a new user namespace
|
||||
//****************************************
|
||||
save_nogroups();
|
||||
if (arg_noroot) {
|
||||
int rv = unshare(CLONE_NEWUSER);
|
||||
if (rv == -1) {
|
||||
fprintf(stderr, "Warning: cannot mount a new user namespace\n");
|
||||
perror("unshare");
|
||||
drop_privs(arg_nogroups);
|
||||
}
|
||||
}
|
||||
else
|
||||
drop_privs(arg_nogroups);
|
||||
|
||||
// notify parent that new user namespace has been created so a proper
|
||||
// UID/GID map can be setup
|
||||
notify_other(child_to_parent_fds[1]);
|
||||
close(child_to_parent_fds[1]);
|
||||
|
||||
// wait for parent to finish setting up a proper UID/GID map
|
||||
wait_for_other(parent_to_child_fds[0]);
|
||||
close(parent_to_child_fds[0]);
|
||||
|
||||
// somehow, the new user namespace resets capabilities;
|
||||
// we need to do them again
|
||||
if (arg_noroot) {
|
||||
set_caps();
|
||||
if (arg_debug)
|
||||
printf("User namespace (noroot) installed\n");
|
||||
}
|
||||
|
||||
|
||||
//****************************************
|
||||
// start the program without using a shell
|
||||
//****************************************
|
||||
if (arg_shell_none) {
|
||||
if (arg_debug) {
|
||||
int i;
|
||||
for (i = cfg.original_program_index; i < cfg.original_argc; i++) {
|
||||
if (cfg.original_argv[i] == NULL)
|
||||
break;
|
||||
printf("execvp argument %d: %s\n", i - cfg.original_program_index, cfg.original_argv[i]);
|
||||
}
|
||||
}
|
||||
|
||||
if (!arg_command)
|
||||
printf("Child process initialized\n");
|
||||
execvp(cfg.original_argv[cfg.original_program_index], &cfg.original_argv[cfg.original_program_index + 1]);
|
||||
}
|
||||
//****************************************
|
||||
// start the program using a shell
|
||||
//****************************************
|
||||
else {
|
||||
// choose the shell requested by the user, or use bash as default
|
||||
char *sh;
|
||||
if (cfg.shell)
|
||||
sh = cfg.shell;
|
||||
else if (arg_zsh)
|
||||
sh = "/usr/bin/zsh";
|
||||
else if (arg_csh)
|
||||
sh = "/bin/csh";
|
||||
else
|
||||
sh = "/bin/bash";
|
||||
|
||||
char *arg[5];
|
||||
int index = 0;
|
||||
arg[index++] = sh;
|
||||
arg[index++] = "-c";
|
||||
assert(cfg.command_line);
|
||||
if (arg_debug)
|
||||
printf("Starting %s\n", cfg.command_line);
|
||||
if (arg_doubledash)
|
||||
arg[index++] = "--";
|
||||
arg[index++] = cfg.command_line;
|
||||
arg[index] = NULL;
|
||||
assert(index < 5);
|
||||
|
||||
if (arg_debug) {
|
||||
char *msg;
|
||||
if (asprintf(&msg, "sandbox %d, execvp into %s", sandbox_pid, cfg.command_line) == -1)
|
||||
errExit("asprintf");
|
||||
logmsg(msg);
|
||||
free(msg);
|
||||
}
|
||||
|
||||
if (arg_debug) {
|
||||
int i;
|
||||
for (i = 0; i < 5; i++) {
|
||||
if (arg[i] == NULL)
|
||||
break;
|
||||
printf("execvp argument %d: %s\n", i, arg[i]);
|
||||
}
|
||||
}
|
||||
|
||||
if (!arg_command)
|
||||
printf("Child process initialized\n");
|
||||
execvp(sh, arg);
|
||||
}
|
||||
|
||||
|
||||
perror("execvp");
|
||||
return 0;
|
||||
}
|
||||
658
src/firejail/seccomp.c
Normal file
658
src/firejail/seccomp.c
Normal file
|
|
@ -0,0 +1,658 @@
|
|||
/*
|
||||
* Copyright (C) 2014, 2015 netblue30 (netblue30@yahoo.com)
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
/* default seccomp filter
|
||||
// seccomp
|
||||
struct sock_filter filter[] = {
|
||||
VALIDATE_ARCHITECTURE,
|
||||
EXAMINE_SYSCALL,
|
||||
BLACKLIST(SYS_mount), // mount/unmount filesystems
|
||||
BLACKLIST(SYS_umount2),
|
||||
BLACKLIST(SYS_ptrace), // trace processes
|
||||
BLACKLIST(SYS_kexec_load), // loading a different kernel
|
||||
BLACKLIST(SYS_open_by_handle_at), // open by handle
|
||||
BLACKLIST(SYS_init_module), // kernel module handling
|
||||
#ifdef SYS_finit_module // introduced in 2013
|
||||
BLACKLIST(SYS_finit_module),
|
||||
#endif
|
||||
BLACKLIST(SYS_delete_module),
|
||||
BLACKLIST(SYS_iopl), // io permisions
|
||||
#ifdef SYS_ioperm
|
||||
BLACKLIST(SYS_ioperm),
|
||||
#endif
|
||||
SYS_iopl
|
||||
BLACKLIST(SYS_iopl), // io permisions
|
||||
#endif
|
||||
#ifdef SYS_ni_syscall), // new io permisions call on arm devices
|
||||
BLACKLIST(SYS_ni_syscall),
|
||||
#endif
|
||||
BLACKLIST(SYS_swapon), // swap on/off
|
||||
BLACKLIST(SYS_swapoff),
|
||||
BLACKLIST(SYS_syslog), // kernel printk control
|
||||
RETURN_ALLOW
|
||||
};
|
||||
*/
|
||||
#ifdef HAVE_SECCOMP
|
||||
#include "firejail.h"
|
||||
#include <errno.h>
|
||||
#include <linux/filter.h>
|
||||
#include <sys/syscall.h>
|
||||
#include <linux/capability.h>
|
||||
#include <linux/audit.h>
|
||||
#include <sys/stat.h>
|
||||
#include <fcntl.h>
|
||||
|
||||
#include <sys/prctl.h>
|
||||
#ifndef PR_SET_NO_NEW_PRIVS
|
||||
# define PR_SET_NO_NEW_PRIVS 38
|
||||
#endif
|
||||
|
||||
#if HAVE_SECCOMP_H
|
||||
#include <linux/seccomp.h>
|
||||
#else
|
||||
#define SECCOMP_MODE_FILTER 2
|
||||
#define SECCOMP_RET_KILL 0x00000000U
|
||||
#define SECCOMP_RET_TRAP 0x00030000U
|
||||
#define SECCOMP_RET_ALLOW 0x7fff0000U
|
||||
#define SECCOMP_RET_ERRNO 0x00050000U
|
||||
#define SECCOMP_RET_DATA 0x0000ffffU
|
||||
struct seccomp_data {
|
||||
int nr;
|
||||
__u32 arch;
|
||||
__u64 instruction_pointer;
|
||||
__u64 args[6];
|
||||
};
|
||||
#endif
|
||||
|
||||
#if defined(__i386__)
|
||||
# define ARCH_NR AUDIT_ARCH_I386
|
||||
#elif defined(__x86_64__)
|
||||
# define ARCH_NR AUDIT_ARCH_X86_64
|
||||
#elif defined(__arm__)
|
||||
# define ARCH_NR AUDIT_ARCH_ARM
|
||||
#else
|
||||
# warning "Platform does not support seccomp filter yet"
|
||||
# define ARCH_NR 0
|
||||
#endif
|
||||
|
||||
|
||||
#define VALIDATE_ARCHITECTURE \
|
||||
BPF_STMT(BPF_LD+BPF_W+BPF_ABS, (offsetof(struct seccomp_data, arch))), \
|
||||
BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, ARCH_NR, 1, 0), \
|
||||
BPF_STMT(BPF_RET+BPF_K, SECCOMP_RET_KILL)
|
||||
|
||||
#define EXAMINE_SYSCALL BPF_STMT(BPF_LD+BPF_W+BPF_ABS, \
|
||||
(offsetof(struct seccomp_data, nr)))
|
||||
|
||||
#define BLACKLIST(syscall_nr) \
|
||||
BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, syscall_nr, 0, 1), \
|
||||
BPF_STMT(BPF_RET+BPF_K, SECCOMP_RET_KILL)
|
||||
|
||||
#define WHITELIST(syscall_nr) \
|
||||
BPF_JUMP(BPF_JMP+BPF_JEQ+BPF_K, syscall_nr, 0, 1), \
|
||||
BPF_STMT(BPF_RET+BPF_K, SECCOMP_RET_ALLOW)
|
||||
|
||||
#define RETURN_ALLOW \
|
||||
BPF_STMT(BPF_RET+BPF_K, SECCOMP_RET_ALLOW)
|
||||
|
||||
#define KILL_PROCESS \
|
||||
BPF_STMT(BPF_RET+BPF_K, SECCOMP_RET_KILL)
|
||||
|
||||
#define SECSIZE 128 // initial filter size
|
||||
static struct sock_filter *sfilter = NULL;
|
||||
static int sfilter_alloc_size = 0;
|
||||
static int sfilter_index = 0;
|
||||
|
||||
// debug filter
|
||||
void filter_debug(void) {
|
||||
// start filter
|
||||
struct sock_filter filter[] = {
|
||||
VALIDATE_ARCHITECTURE,
|
||||
EXAMINE_SYSCALL
|
||||
};
|
||||
|
||||
// print sizes
|
||||
printf("SECCOMP Filter:\n");
|
||||
if (sfilter == NULL) {
|
||||
printf("SECCOMP filter not allocated\n");
|
||||
return;
|
||||
}
|
||||
if (sfilter_index < 4)
|
||||
return;
|
||||
|
||||
// test the start of the filter
|
||||
if (memcmp(sfilter, filter, sizeof(filter)) == 0) {
|
||||
printf(" VALIDATE_ARCHITECTURE\n");
|
||||
printf(" EXAMINE_SYSCAL\n");
|
||||
}
|
||||
|
||||
// loop trough blacklists
|
||||
int i = 4;
|
||||
while (i < sfilter_index) {
|
||||
// minimal parsing!
|
||||
unsigned char *ptr = (unsigned char *) &sfilter[i];
|
||||
int *nr = (int *) (ptr + 4);
|
||||
if (*ptr == 0x15 && *(ptr +14) == 0xff && *(ptr + 15) == 0x7f ) {
|
||||
printf(" WHITELIST %d %s\n", *nr, syscall_find_nr(*nr));
|
||||
i += 2;
|
||||
}
|
||||
else if (*ptr == 0x15 && *(ptr +14) == 0 && *(ptr + 15) == 0) {
|
||||
printf(" BLACKLIST %d %s\n", *nr, syscall_find_nr(*nr));
|
||||
i += 2;
|
||||
}
|
||||
else if (*ptr == 0x06 && *(ptr +6) == 0 && *(ptr + 7) == 0 ) {
|
||||
printf(" KILL_PROCESS\n");
|
||||
i++;
|
||||
}
|
||||
else if (*ptr == 0x06 && *(ptr +6) == 0xff && *(ptr + 7) == 0x7f ) {
|
||||
printf(" RETURN_ALLOW\n");
|
||||
i++;
|
||||
}
|
||||
else {
|
||||
printf(" UNKNOWN ENTRY!!!\n");
|
||||
i++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// initialize filter
|
||||
static void filter_init(void) {
|
||||
if (sfilter) {
|
||||
assert(0);
|
||||
return;
|
||||
}
|
||||
|
||||
if (arg_debug)
|
||||
printf("Initialize seccomp filter\n");
|
||||
// allocate a filter of SECSIZE
|
||||
sfilter = malloc(sizeof(struct sock_filter) * SECSIZE);
|
||||
if (!sfilter)
|
||||
errExit("malloc");
|
||||
memset(sfilter, 0, sizeof(struct sock_filter) * SECSIZE);
|
||||
sfilter_alloc_size = SECSIZE;
|
||||
|
||||
// copy the start entries
|
||||
struct sock_filter filter[] = {
|
||||
VALIDATE_ARCHITECTURE,
|
||||
EXAMINE_SYSCALL
|
||||
};
|
||||
sfilter_index = sizeof(filter) / sizeof(struct sock_filter);
|
||||
memcpy(sfilter, filter, sizeof(filter));
|
||||
}
|
||||
|
||||
static void filter_realloc(void) {
|
||||
assert(sfilter);
|
||||
assert(sfilter_alloc_size);
|
||||
assert(sfilter_index);
|
||||
if (arg_debug)
|
||||
printf("Allocating more seccomp filter entries\n");
|
||||
|
||||
// allocate the new memory
|
||||
struct sock_filter *old = sfilter;
|
||||
sfilter = malloc(sizeof(struct sock_filter) * (sfilter_alloc_size + SECSIZE));
|
||||
if (!sfilter)
|
||||
errExit("malloc");
|
||||
memset(sfilter, 0, sizeof(struct sock_filter) * (sfilter_alloc_size + SECSIZE));
|
||||
|
||||
// copy old filter
|
||||
memcpy(sfilter, old, sizeof(struct sock_filter) * sfilter_alloc_size);
|
||||
sfilter_alloc_size += SECSIZE;
|
||||
}
|
||||
|
||||
static void filter_add_whitelist(int syscall) {
|
||||
assert(sfilter);
|
||||
assert(sfilter_alloc_size);
|
||||
assert(sfilter_index);
|
||||
if (arg_debug)
|
||||
printf("Whitelisting syscall %d %s\n", syscall, syscall_find_nr(syscall));
|
||||
|
||||
if ((sfilter_index + 2) > sfilter_alloc_size)
|
||||
filter_realloc();
|
||||
|
||||
struct sock_filter filter[] = {
|
||||
WHITELIST(syscall)
|
||||
};
|
||||
#if 0
|
||||
{
|
||||
int i;
|
||||
unsigned char *ptr = (unsigned char *) &filter[0];
|
||||
for (i = 0; i < sizeof(filter); i++, ptr++)
|
||||
printf("%x, ", (*ptr) & 0xff);
|
||||
printf("\n");
|
||||
}
|
||||
#endif
|
||||
memcpy(&sfilter[sfilter_index], filter, sizeof(filter));
|
||||
sfilter_index += sizeof(filter) / sizeof(struct sock_filter);
|
||||
}
|
||||
|
||||
static void filter_add_blacklist(int syscall) {
|
||||
assert(sfilter);
|
||||
assert(sfilter_alloc_size);
|
||||
assert(sfilter_index);
|
||||
if (arg_debug)
|
||||
printf("Blacklisting syscall %d %s\n", syscall, syscall_find_nr(syscall));
|
||||
|
||||
if ((sfilter_index + 2) > sfilter_alloc_size)
|
||||
filter_realloc();
|
||||
|
||||
struct sock_filter filter[] = {
|
||||
BLACKLIST(syscall)
|
||||
};
|
||||
#if 0
|
||||
{
|
||||
int i;
|
||||
unsigned char *ptr = (unsigned char *) &filter[0];
|
||||
for (i = 0; i < sizeof(filter); i++, ptr++)
|
||||
printf("%x, ", (*ptr) & 0xff);
|
||||
printf("\n");
|
||||
}
|
||||
#endif
|
||||
memcpy(&sfilter[sfilter_index], filter, sizeof(filter));
|
||||
sfilter_index += sizeof(filter) / sizeof(struct sock_filter);
|
||||
}
|
||||
|
||||
static void filter_end_blacklist(void) {
|
||||
assert(sfilter);
|
||||
assert(sfilter_alloc_size);
|
||||
assert(sfilter_index);
|
||||
if (arg_debug)
|
||||
printf("Ending syscall filter\n");
|
||||
|
||||
if ((sfilter_index + 2) > sfilter_alloc_size)
|
||||
filter_realloc();
|
||||
|
||||
struct sock_filter filter[] = {
|
||||
RETURN_ALLOW
|
||||
};
|
||||
#if 0
|
||||
{
|
||||
int i;
|
||||
unsigned char *ptr = (unsigned char *) &filter[0];
|
||||
for (i = 0; i < sizeof(filter); i++, ptr++)
|
||||
printf("%x, ", (*ptr) & 0xff);
|
||||
printf("\n");
|
||||
}
|
||||
#endif
|
||||
memcpy(&sfilter[sfilter_index], filter, sizeof(filter));
|
||||
sfilter_index += sizeof(filter) / sizeof(struct sock_filter);
|
||||
}
|
||||
|
||||
static void filter_end_whitelist(void) {
|
||||
assert(sfilter);
|
||||
assert(sfilter_alloc_size);
|
||||
assert(sfilter_index);
|
||||
if (arg_debug)
|
||||
printf("Ending syscall filter\n");
|
||||
|
||||
if ((sfilter_index + 2) > sfilter_alloc_size)
|
||||
filter_realloc();
|
||||
|
||||
struct sock_filter filter[] = {
|
||||
KILL_PROCESS
|
||||
};
|
||||
#if 0
|
||||
{
|
||||
int i;
|
||||
unsigned char *ptr = (unsigned char *) &filter[0];
|
||||
for (i = 0; i < sizeof(filter); i++, ptr++)
|
||||
printf("%x, ", (*ptr) & 0xff);
|
||||
printf("\n");
|
||||
}
|
||||
#endif
|
||||
memcpy(&sfilter[sfilter_index], filter, sizeof(filter));
|
||||
sfilter_index += sizeof(filter) / sizeof(struct sock_filter);
|
||||
}
|
||||
|
||||
|
||||
// save seccomp filter in /tmp/firejail/mnt/seccomp
|
||||
static void write_seccomp_file(void) {
|
||||
fs_build_mnt_dir();
|
||||
assert(sfilter);
|
||||
|
||||
char *fname;
|
||||
if (asprintf(&fname, "%s/seccomp", MNT_DIR) == -1)
|
||||
errExit("asprintf");
|
||||
int fd = open(fname, O_CREAT | O_WRONLY, S_IRUSR | S_IWUSR);
|
||||
if (fd == -1)
|
||||
errExit("open");
|
||||
|
||||
if (arg_debug)
|
||||
printf("Save seccomp filter, size %lu bytes\n", sfilter_index * sizeof(struct sock_filter));
|
||||
errno = 0;
|
||||
ssize_t sz = write(fd, sfilter, sfilter_index * sizeof(struct sock_filter));
|
||||
if (sz != (sfilter_index * sizeof(struct sock_filter))) {
|
||||
fprintf(stderr, "Error: cannot save seccomp filter\n");
|
||||
exit(1);
|
||||
}
|
||||
close(fd);
|
||||
if (chown(fname, 0, 0) < 0)
|
||||
errExit("chown");
|
||||
free(fname);
|
||||
}
|
||||
|
||||
// read seccomp filter from /tmp/firejail/mnt/seccomp
|
||||
static void read_seccomp_file(char *file_name) {
|
||||
assert(sfilter == NULL && sfilter_index == 0);
|
||||
|
||||
char *fname;
|
||||
if (file_name)
|
||||
fname = file_name;
|
||||
else {
|
||||
if (asprintf(&fname, "%s/seccomp", MNT_DIR) == -1)
|
||||
errExit("asprintf");
|
||||
}
|
||||
|
||||
// check file
|
||||
struct stat s;
|
||||
if (stat(fname, &s) == -1) {
|
||||
fprintf(stderr, "Error: seccomp file not found\n");
|
||||
exit(1);
|
||||
}
|
||||
ssize_t sz = s.st_size;
|
||||
if (sz == 0 || (sz % sizeof(struct sock_filter)) != 0) {
|
||||
fprintf(stderr, "Error: invalid seccomp file\n");
|
||||
exit(1);
|
||||
}
|
||||
sfilter = malloc(sz);
|
||||
if (!sfilter)
|
||||
errExit("malloc");
|
||||
|
||||
// read file
|
||||
/* coverity[toctou] */
|
||||
int fd = open(fname,O_RDONLY);
|
||||
if (fd == -1)
|
||||
errExit("open");
|
||||
errno = 0;
|
||||
ssize_t size = read(fd, sfilter, sz);
|
||||
if (size != sz) {
|
||||
fprintf(stderr, "Error: invalid seccomp file\n");
|
||||
exit(1);
|
||||
}
|
||||
sfilter_index = sz / sizeof(struct sock_filter);
|
||||
|
||||
if (arg_debug)
|
||||
printf("Read seccomp filter, size %lu bytes\n", sfilter_index * sizeof(struct sock_filter));
|
||||
|
||||
close(fd);
|
||||
free(fname);
|
||||
|
||||
if (arg_debug)
|
||||
filter_debug();
|
||||
}
|
||||
|
||||
|
||||
// drop filter for seccomp option
|
||||
int seccomp_filter_drop(void) {
|
||||
filter_init();
|
||||
|
||||
// default seccomp
|
||||
if (arg_seccomp_list_drop == NULL) {
|
||||
#ifdef SYS_mount
|
||||
filter_add_blacklist(SYS_mount);
|
||||
#endif
|
||||
#ifdef SYS_umount2
|
||||
filter_add_blacklist(SYS_umount2);
|
||||
#endif
|
||||
#ifdef SYS_ptrace
|
||||
filter_add_blacklist(SYS_ptrace);
|
||||
#endif
|
||||
#ifdef SYS_kexec_load
|
||||
filter_add_blacklist(SYS_kexec_load);
|
||||
#endif
|
||||
#ifdef SYS_open_by_handle_at
|
||||
filter_add_blacklist(SYS_open_by_handle_at);
|
||||
#endif
|
||||
#ifdef SYS_init_module
|
||||
filter_add_blacklist(SYS_init_module);
|
||||
#endif
|
||||
#ifdef SYS_finit_module // introduced in 2013
|
||||
filter_add_blacklist(SYS_finit_module);
|
||||
#endif
|
||||
#ifdef SYS_delete_module
|
||||
filter_add_blacklist(SYS_delete_module);
|
||||
#endif
|
||||
#ifdef SYS_iopl
|
||||
filter_add_blacklist(SYS_iopl);
|
||||
#endif
|
||||
#ifdef SYS_ioperm
|
||||
filter_add_blacklist(SYS_ioperm);
|
||||
#endif
|
||||
#ifdef SYS_ni_syscall // new io permisions call on arm devices
|
||||
filter_add_blacklist(SYS_ni_syscall);
|
||||
#endif
|
||||
#ifdef SYS_swapon
|
||||
filter_add_blacklist(SYS_swapon);
|
||||
#endif
|
||||
#ifdef SYS_swapoff
|
||||
filter_add_blacklist(SYS_swapoff);
|
||||
#endif
|
||||
#ifdef SYS_syslog
|
||||
filter_add_blacklist(SYS_syslog);
|
||||
#endif
|
||||
#ifdef SYS_process_vm_readv
|
||||
filter_add_blacklist(SYS_process_vm_readv);
|
||||
#endif
|
||||
#ifdef SYS_process_vm_writev
|
||||
filter_add_blacklist(SYS_process_vm_writev);
|
||||
#endif
|
||||
#ifdef SYS_mknod
|
||||
filter_add_blacklist(SYS_mknod);
|
||||
#endif
|
||||
|
||||
// new syscalls in 0.9,23
|
||||
#ifdef SYS_sysfs
|
||||
filter_add_blacklist(SYS_sysfs);
|
||||
#endif
|
||||
#ifdef SYS__sysctl
|
||||
filter_add_blacklist(SYS__sysctl);
|
||||
#endif
|
||||
#ifdef SYS_adjtimex
|
||||
filter_add_blacklist(SYS_adjtimex);
|
||||
#endif
|
||||
#ifdef SYS_clock_adjtime
|
||||
filter_add_blacklist(SYS_clock_adjtime);
|
||||
#endif
|
||||
#ifdef SYS_lookup_dcookie
|
||||
filter_add_blacklist(SYS_lookup_dcookie);
|
||||
#endif
|
||||
#ifdef SYS_perf_event_open
|
||||
filter_add_blacklist(SYS_perf_event_open);
|
||||
#endif
|
||||
#ifdef SYS_fanotify_init
|
||||
filter_add_blacklist(SYS_fanotify_init);
|
||||
#endif
|
||||
#ifdef SYS_kcmp
|
||||
filter_add_blacklist(SYS_kcmp);
|
||||
#endif
|
||||
}
|
||||
|
||||
// default seccomp filter with additional drop list
|
||||
if (arg_seccomp_list && arg_seccomp_list_drop == NULL) {
|
||||
if (syscall_check_list(arg_seccomp_list, filter_add_blacklist)) {
|
||||
fprintf(stderr, "Error: cannot load seccomp filter\n");
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
// drop list
|
||||
else if (arg_seccomp_list == NULL && arg_seccomp_list_drop) {
|
||||
if (syscall_check_list(arg_seccomp_list_drop, filter_add_blacklist)) {
|
||||
fprintf(stderr, "Error: cannot load seccomp filter\n");
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
filter_end_blacklist();
|
||||
if (arg_debug)
|
||||
filter_debug();
|
||||
|
||||
// save seccomp filter in /tmp/firejail/mnt/seccomp
|
||||
// in order to use it in --join operations
|
||||
write_seccomp_file();
|
||||
|
||||
|
||||
struct sock_fprog prog = {
|
||||
.len = sfilter_index,
|
||||
.filter = sfilter,
|
||||
};
|
||||
|
||||
if (prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &prog) || prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0)) {
|
||||
fprintf(stderr, "Warning: seccomp disabled, it requires a Linux kernel version 3.5 or newer.\n");
|
||||
return 1;
|
||||
}
|
||||
else if (arg_debug) {
|
||||
printf("seccomp enabled\n");
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
// keep filter for seccomp option
|
||||
int seccomp_filter_keep(void) {
|
||||
filter_init();
|
||||
|
||||
// these 4 syscalls are used by firejail after the seccomp filter is initialized
|
||||
filter_add_whitelist(SYS_setuid);
|
||||
filter_add_whitelist(SYS_setgid);
|
||||
filter_add_whitelist(SYS_setgroups);
|
||||
filter_add_whitelist(SYS_dup);
|
||||
|
||||
// apply keep list
|
||||
if (arg_seccomp_list_keep) {
|
||||
if (syscall_check_list(arg_seccomp_list_keep, filter_add_whitelist)) {
|
||||
fprintf(stderr, "Error: cannot load seccomp filter\n");
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
filter_end_whitelist();
|
||||
if (arg_debug)
|
||||
filter_debug();
|
||||
|
||||
// save seccomp filter in /tmp/firejail/mnt/seccomp
|
||||
// in order to use it in --join operations
|
||||
write_seccomp_file();
|
||||
|
||||
|
||||
struct sock_fprog prog = {
|
||||
.len = sfilter_index,
|
||||
.filter = sfilter,
|
||||
};
|
||||
|
||||
if (prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &prog) || prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0)) {
|
||||
fprintf(stderr, "Warning: seccomp disabled, it requires a Linux kernel version 3.5 or newer.\n");
|
||||
return 1;
|
||||
}
|
||||
else if (arg_debug) {
|
||||
printf("seccomp enabled\n");
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
void seccomp_set(void) {
|
||||
// read seccomp filter from /tmp/firejail/mnt/seccomp
|
||||
read_seccomp_file(NULL);
|
||||
|
||||
// apply filter
|
||||
struct sock_fprog prog = {
|
||||
.len = sfilter_index,
|
||||
.filter = sfilter,
|
||||
};
|
||||
|
||||
if (prctl(PR_SET_SECCOMP, SECCOMP_MODE_FILTER, &prog) || prctl(PR_SET_NO_NEW_PRIVS, 1, 0, 0, 0)) {
|
||||
fprintf(stderr, "Warning: seccomp disabled, it requires a Linux kernel version 3.5 or newer.\n");
|
||||
return;
|
||||
}
|
||||
else if (arg_debug) {
|
||||
printf("seccomp enabled\n");
|
||||
}
|
||||
}
|
||||
|
||||
void seccomp_print_filter_name(const char *name) {
|
||||
if (!name || strlen(name) == 0) {
|
||||
fprintf(stderr, "Error: invalid sandbox name\n");
|
||||
exit(1);
|
||||
}
|
||||
pid_t pid;
|
||||
if (name2pid(name, &pid)) {
|
||||
fprintf(stderr, "Error: cannot find sandbox %s\n", name);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
seccomp_print_filter(pid);
|
||||
}
|
||||
|
||||
void seccomp_print_filter(pid_t pid) {
|
||||
// if the pid is that of a firejail process, use the pid of the first child process
|
||||
char *comm = pid_proc_comm(pid);
|
||||
if (comm) {
|
||||
// remove \n
|
||||
char *ptr = strchr(comm, '\n');
|
||||
if (ptr)
|
||||
*ptr = '\0';
|
||||
if (strcmp(comm, "firejail") == 0) {
|
||||
pid_t child;
|
||||
if (find_child(pid, &child) == 0) {
|
||||
pid = child;
|
||||
}
|
||||
}
|
||||
free(comm);
|
||||
}
|
||||
|
||||
// check privileges for non-root users
|
||||
uid_t uid = getuid();
|
||||
if (uid != 0) {
|
||||
struct stat s;
|
||||
char *dir;
|
||||
if (asprintf(&dir, "/proc/%u/ns", pid) == -1)
|
||||
errExit("asprintf");
|
||||
if (stat(dir, &s) < 0)
|
||||
errExit("stat");
|
||||
if (s.st_uid != uid) {
|
||||
printf("Error: permission denied.\n");
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// find the seccomp filter
|
||||
char *fname;
|
||||
if (asprintf(&fname, "/proc/%d/root/tmp/firejail/mnt/seccomp", pid) == -1)
|
||||
errExit("asprintf");
|
||||
|
||||
struct stat s;
|
||||
if (stat(fname, &s) == -1) {
|
||||
printf("Cannot access seccomp filter.\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
// read and print the filter
|
||||
read_seccomp_file(fname);
|
||||
drop_privs(1);
|
||||
filter_debug();
|
||||
|
||||
exit(0);
|
||||
}
|
||||
|
||||
#endif // HAVE_SECCOMP
|
||||
|
||||
98
src/firejail/shutdown.c
Normal file
98
src/firejail/shutdown.c
Normal file
|
|
@ -0,0 +1,98 @@
|
|||
/*
|
||||
* Copyright (C) 2014, 2015 netblue30 (netblue30@yahoo.com)
|
||||
*
|
||||
* 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 <sys/stat.h>
|
||||
#include <sys/wait.h>
|
||||
#include <fcntl.h>
|
||||
#include <sys/prctl.h>
|
||||
|
||||
void shut_name(const char *name) {
|
||||
if (!name || strlen(name) == 0) {
|
||||
fprintf(stderr, "Error: invalid sandbox name\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
pid_t pid;
|
||||
if (name2pid(name, &pid)) {
|
||||
fprintf(stderr, "Error: cannot find sandbox %s\n", name);
|
||||
exit(1);
|
||||
}
|
||||
|
||||
shut(pid);
|
||||
}
|
||||
|
||||
void shut(pid_t pid) {
|
||||
pid_t parent = pid;
|
||||
// if the pid is that of a firejail process, use the pid of a child process inside the sandbox
|
||||
char *comm = pid_proc_comm(pid);
|
||||
if (comm) {
|
||||
// remove \n
|
||||
char *ptr = strchr(comm, '\n');
|
||||
if (ptr)
|
||||
*ptr = '\0';
|
||||
if (strcmp(comm, "firejail") == 0) {
|
||||
pid_t child;
|
||||
if (find_child(pid, &child) == 0) {
|
||||
pid = child;
|
||||
printf("Switching to pid %u, the first child process inside the sandbox\n", (unsigned) pid);
|
||||
}
|
||||
}
|
||||
free(comm);
|
||||
}
|
||||
|
||||
// check privileges for non-root users
|
||||
uid_t uid = getuid();
|
||||
if (uid != 0) {
|
||||
struct stat s;
|
||||
char *dir;
|
||||
if (asprintf(&dir, "/proc/%u/ns", pid) == -1)
|
||||
errExit("asprintf");
|
||||
if (stat(dir, &s) < 0)
|
||||
errExit("stat");
|
||||
if (s.st_uid != uid) {
|
||||
fprintf(stderr, "Error: permission is denied to shutdown a sandbox created by a different user.\n");
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
printf("Sending SIGTERM to %u\n", pid);
|
||||
kill(pid, SIGTERM);
|
||||
sleep(2);
|
||||
|
||||
// if the process is still running, terminate it using SIGKILL
|
||||
// try to open stat file
|
||||
char *file;
|
||||
if (asprintf(&file, "/proc/%u/status", pid) == -1) {
|
||||
perror("asprintf");
|
||||
exit(1);
|
||||
}
|
||||
FILE *fp = fopen(file, "r");
|
||||
if (!fp)
|
||||
return;
|
||||
fclose(fp);
|
||||
|
||||
// kill the process and also the parent
|
||||
printf("Sending SIGKILL to %u\n", pid);
|
||||
kill(pid, SIGKILL);
|
||||
if (parent != pid) {
|
||||
printf("Sending SIGKILL to %u\n", parent);
|
||||
kill(parent, SIGKILL);
|
||||
}
|
||||
}
|
||||
4942
src/firejail/syscall.c
Normal file
4942
src/firejail/syscall.c
Normal file
File diff suppressed because it is too large
Load diff
312
src/firejail/usage.c
Normal file
312
src/firejail/usage.c
Normal file
|
|
@ -0,0 +1,312 @@
|
|||
/*
|
||||
* Copyright (C) 2014, 2015 netblue30 (netblue30@yahoo.com)
|
||||
*
|
||||
* 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"
|
||||
|
||||
void usage(void) {
|
||||
printf("firejail - version %s\n\n", VERSION);
|
||||
printf("Firejail is a SUID sandbox program that reduces the risk of security breaches by\n");
|
||||
printf("restricting the running environment of untrusted applications using Linux\n");
|
||||
printf("namespaces. It includes a sandbox profile for Mozilla Firefox.\n\n");
|
||||
printf("\n");
|
||||
printf("Usage: firejail [options] [program and arguments]\n\n");
|
||||
printf("\n");
|
||||
printf("Without any options, the sandbox consists of a filesystem chroot build from the\n");
|
||||
printf("current system directories mounted read-only, and new PID and IPC\n");
|
||||
printf("namespaces. If no program is specified as an argument, /bin/bash is started by\n");
|
||||
printf("default in the sandbox.\n\n");
|
||||
printf("\n");
|
||||
printf("Options:\n\n");
|
||||
printf("\t-- - signal the end of options and disables further option processing.\n\n");
|
||||
printf("\t--bandwidth=name - set bandwidth limits for the sandbox identified\n");
|
||||
printf("\t\tby name, see Traffic Shaping section for more details.\n\n");
|
||||
printf("\t--bandwidth=pid - set bandwidth limits for the sandbox identified\n");
|
||||
printf("\t\tby PID, see Traffic Shaping section for more details.\n\n");
|
||||
#ifdef HAVE_BIND
|
||||
printf("\t--bind=dirname1,dirname2 - mount-bind dirname1 on top of dirname2.\n\n");
|
||||
printf("\t--bind=filename1,dirname2 - mount-bind filename1 on top of filename2.\n\n");
|
||||
#endif
|
||||
printf("\t--blacklist=dirname_or_filename - blacklist directory or file.\n\n");
|
||||
printf("\t-c - execute command and exit.\n\n");
|
||||
printf("\t--caps - enable default Linux capabilities filter. The filter disables\n");
|
||||
printf("\t\tCAP_SYS_MODULE, CAP_SYS_RAWIO, CAP_SYS_BOOT, CAP_SYS_NICE,\n");
|
||||
printf("\t\tCAP_SYS_TTY_CONFIG, CAP_SYSLOG, CAP_MKNOD, CAP_SYS_ADMIN.\n\n");
|
||||
printf("\t--caps.drop=all - drop all capabilities.\n\n");
|
||||
printf("\t--caps.drop=capability,capability,capability - blacklist Linux\n");
|
||||
printf("\t\tcapabilities filter.\n\n");
|
||||
printf("\t--caps.keep=capability,capability,capability - whitelist Linux\n");
|
||||
printf("\t\tcapabilities filter.\n\n");
|
||||
printf("\t--caps.print=name - print the caps filter for the sandbox identified\n");
|
||||
printf("\t\tby name.\n\n");
|
||||
printf("\t--caps.print=pid - print the caps filter for the sandbox identified\n");
|
||||
printf("\t\tby PID.\n\n");
|
||||
printf("\t--cgroup=tasks-file - place the sandbox in the specified control group.\n");
|
||||
printf("\t\ttasks-file is the full path of cgroup tasks file.\n");
|
||||
printf("\t\tExample: --cgroup=/sys/fs/cgroup/g1/tasks\n\n");
|
||||
#ifdef HAVE_CHROOT
|
||||
printf("\t--chroot=dirname - chroot into dirname directory.\n\n");
|
||||
#endif
|
||||
printf("\t--cpu=cpu-number,cpu-number - set cpu affinity.\n");
|
||||
printf("\t\tExample: cpu=0,1,2\n\n");
|
||||
printf("\t--csh - use /bin/csh as default shell.\n\n");
|
||||
printf("\t--debug - print sandbox debug messages.\n\n");
|
||||
printf("\t--debug-syscalls - print all recognized system calls in the current\n");
|
||||
printf("\t\tFirejail software build and exit.\n\n");
|
||||
printf("\t--debug-caps - print all recognized capabilities in the current\n");
|
||||
printf("\t\tFirejail software build and exit.\n\n");
|
||||
printf("\t--defaultgw=address - use this address as default gateway in the new\n");
|
||||
printf("\t\tnetwork namespace.\n\n");
|
||||
printf("\t--dns=address - set a DNS server for the sandbox. Up to three DNS\n");
|
||||
printf("\t\tservers can be defined.\n\n");
|
||||
printf("\t--dns.print=name - print DNS configuration for the sandbox identified\n");
|
||||
printf("\t\tby name.\n\n");
|
||||
printf("\t--dns.print=pid - print DNS configuration of the sandbox identified.\n");
|
||||
printf("\t\tby PID.\n\n");
|
||||
printf("\t--help, -? - this help screen.\n\n");
|
||||
printf("\t--ip=address - set interface IP address.\n\n");
|
||||
printf("\t--ip=none - no IP address and no default gateway address are configured\n");
|
||||
printf("\t\tin the new network namespace. Use this option in case you intend\n");
|
||||
printf("\t\tto start an external DHCP client in the sandbox.\n\n");
|
||||
printf("\t--iprange=address,address - configure an IP address in this range\n\n");
|
||||
printf("\t--ipc-namespace - enable a new IPC namespace if the sandbox was started\n");
|
||||
printf("\t\tas a regular user. IPC namespace is enabled by default only if\n");
|
||||
printf("\t\tthe sandbox is started as root.\n\n");
|
||||
printf("\t--join=name - join the sandbox identified by name.\n\n");
|
||||
printf("\t--join=pid - join the sandbox identified by PID.\n\n");
|
||||
printf("\t--list - list all sandboxes.\n\n");
|
||||
printf("\t--mac=xx:xx:xx:xx:xx:xx - set interface MAC address.\n\n");
|
||||
printf("\t--name=name - set sandbox hostname.\n\n");
|
||||
printf("\t--net=bridgename - enable network namespaces and connect to this bridge\n");
|
||||
printf("\t\tdevice. Unless specified with option --ip and --defaultgw, an\n");
|
||||
printf("\t\tIP address and a default gateway will be assigned automatically\n");
|
||||
printf("\t\tto the sandbox. The IP address is checked using ARP before\n");
|
||||
printf("\t\tassignment. The IP address assigned as default gateway is the\n");
|
||||
printf("\t\tbridge device IP address. Up to four --net devices can\n");
|
||||
printf("\t\tbe defined. Mixing bridge and macvlan devices is allowed.\n\n");
|
||||
printf("\t--net=ethernet_interface - enable network namespaces and connect\n");
|
||||
printf("\t\tto this ethernet_interface using the standard Linux macvlan\n");
|
||||
printf("\t\tdriver. Unless specified with option --ip and --defaultgw, an\n");
|
||||
printf("\t\tIP address and a default gateway will be assigned automatically\n");
|
||||
printf("\t\tto the sandbox. The IP address is checked using ARP before\n");
|
||||
printf("\t\tassignment. The IP address assigned as default gateway is the\n");
|
||||
printf("\t\tdefault gateway of the host. Up to four --net devices can\n");
|
||||
printf("\t\tbe defined. Mixing bridge and macvlan devices is allowed.\n\n");
|
||||
printf("\t--net=none - enable a new, unconnected network namespace.\n\n");
|
||||
|
||||
printf("\t--netfilter - enable the default client network filter in the new\n");
|
||||
printf("\t\tnetwork namespace:\n\n");
|
||||
printf("\t\t*filter\n");
|
||||
printf("\t\t:INPUT DROP [0:0]\n");
|
||||
printf("\t\t:FORWARD DROP [0:0]\n");
|
||||
printf("\t\t:OUTPUT ACCEPT [0:0]\n");
|
||||
printf("\t\t-A INPUT -i lo -j ACCEPT\n");
|
||||
printf("\t\t-A INPUT -m state --state RELATED,ESTABLISHED -j ACCEPT\n");
|
||||
printf("\t\t-A INPUT -p icmp --icmp-type destination-unreachable -j ACCEPT\n");
|
||||
printf("\t\t-A INPUT -p icmp --icmp-type time-exceeded -j ACCEPT\n");
|
||||
printf("\t\t-A INPUT -p icmp --icmp-type echo-request -j ACCEPT \n");
|
||||
printf("\t\tCOMMIT\n\n");
|
||||
printf("\t--netfilter=filename - enable the network filter specified by\n");
|
||||
printf("\t\tfilename in the new network namespace. The filter file format\n");
|
||||
printf("\t\tis the format of iptables-save and iptable-restore commands.\n\n");
|
||||
|
||||
printf("\t--netstats - monitor network statistics for sandboxes creating a new\n");
|
||||
printf("\t\tnetwork namespace.\n\n");
|
||||
printf("\t--nogroups - disable supplementary groups. Without this option,\n");
|
||||
printf("\t\tsupplementary groups are enabled for the user starting the\n");
|
||||
printf("\t\tsandbox. For root user supplementary groups are always\n");
|
||||
printf("\t\tdisabled.\n\n");
|
||||
|
||||
printf("\t--noroot - install a user namespace with a single user - the current\n");
|
||||
printf("\t\tuser. root user does not exist in the new namespace. This option\n");
|
||||
printf("\t\tis not supported for --chroot and --overlay configurations.\n\n");
|
||||
|
||||
printf("\t--output=logfile - stdout logging and log rotation. Copy stdout to\n");
|
||||
printf("\t\tlogfile, and keep the size of the file under 500KB using log\n");
|
||||
printf("\t\trotation. Five files with prefixes .1 to .5 are used in\n");
|
||||
printf("\t\trotation.\n\n");
|
||||
printf("\t--overlay - mount a filesystem overlay on top of the current filesystem.\n");
|
||||
printf("\t\t(OverlayFS support is required in Linux kernel for this option\n");
|
||||
printf("\t\tto work)\n\n");
|
||||
|
||||
printf("\t--private - mount new /root and /home/user directories in temporary\n");
|
||||
printf("\t\tfilesystems. All modifications are discarded when the sandbox is\n");
|
||||
printf("\t\tclosed.\n\n");
|
||||
printf("\t--private=directory - use directory as user home.\n\n");
|
||||
printf("\t--private.keep=file,directory - build a new user home in a temporary\n");
|
||||
printf("\t\tfilesystem, and copy the files and directories in the list in\n");
|
||||
printf("\t\tthe new home. All modifications are discarded when the sandbox\n");
|
||||
printf("\t\tis closed.\n\n");
|
||||
printf("\t--private-dev - create a new /dev directory. Only null, full, zero, tty,\n");
|
||||
printf("\t\tpst, ptms, random, urandom and shm devices are available.\n\n");
|
||||
|
||||
printf("\t--profile=filename - use a custom profile.\n\n");
|
||||
printf("\t--read-only=dirname_or_filename - set directory or file read-only.\n\n");
|
||||
printf("\t--rlimit-fsize=number - set the maximum file size that can be created\n");
|
||||
printf("\t\tby a process.\n\n");
|
||||
printf("\t--rlimit-nofile=number - set the maximum number of files that can be\n");
|
||||
printf("\t\topened by a process.\n\n");
|
||||
printf("\t--rlimit-nproc=number - set the maximum number of processes that can be\n");
|
||||
printf("\t\tcreated for the real user ID of the calling process.\n\n");
|
||||
printf("\t--rlimit-sigpending=number - set the maximum number of pending signals\n");
|
||||
printf("\t\tfor a process.\n\n");
|
||||
|
||||
printf("\t--scan - ARP-scan all the networks from inside a network namespace.\n");
|
||||
printf("\t\tThis makes it possible to detect macvlan kernel device drivers\n");
|
||||
printf("\t\trunning on the current host.\n\n");
|
||||
|
||||
#ifdef HAVE_SECCOMP
|
||||
printf("\t--seccomp - enable seccomp filter and blacklist the syscalls in the\n");
|
||||
printf("\t\tlist. The default list is as follows: mount, umount2,\n");
|
||||
printf("\t\tptrace, kexec_load, open_by_handle_at, init_module,\n");
|
||||
printf("\t\tfinit_module, delete_module, iopl, ioperm, swapon, swapoff,\n");
|
||||
printf("\t\tmknode, syslog, process_vm_readv and process_vm_writev\n");
|
||||
printf("\t\tsysfs,_sysctl, adjtimex, clock_adjtime, lookup_dcookie,\n");
|
||||
printf("\t\tperf_event_open, fanotify_init and kcmp.\n\n");
|
||||
|
||||
printf("\t--seccomp=syscall,syscall,syscall - enable seccomp filter, blacklist the\n");
|
||||
printf("\t\tdefault syscall list and the syscalls specified by the command.\n\n");
|
||||
|
||||
printf("\t--seccomp.drop=syscall,syscall,syscall - enable seccomp filter, and\n");
|
||||
printf("\t\tblacklist the syscalls specified by the command.\n\n");
|
||||
|
||||
printf("\t--seccomp.keep=syscall,syscall,syscall - enable seccomp filter, and\n");
|
||||
printf("\t\twhitelist the syscalls specified by the command.\n\n");
|
||||
|
||||
printf("\t--seccomp.print=name - print the seccomp filter for the sandbox\n");
|
||||
printf("\t\tidentified by name.\n\n");
|
||||
printf("\t--seccomp.print=pid - print the seccomp filter for the sandbox\n");
|
||||
printf("\t\tidentified by PID.\n\n");
|
||||
#endif
|
||||
|
||||
printf("\t--shell=none - run the program directly without a user shell.\n\n");
|
||||
printf("\t--shell=program - set default user shell.\n\n");
|
||||
printf("\t--shutdown=name - shutdown the sandbox identified by name.\n\n");
|
||||
printf("\t--shutdown=pid - shutdown the sandbox identified by PID.\n\n");
|
||||
printf("\t--tmpfs=dirname - mount a tmpfs filesystem on directory dirname.\n\n");
|
||||
printf("\t--top - monitor the most CPU-intensive sandboxes.\n\n");
|
||||
printf("\t--trace - trace open, access and connect system calls.\n\n");
|
||||
printf("\t--tree - print a tree of all sandboxed processes.\n\n");
|
||||
printf("\t--version - print program version and exit.\n\n");
|
||||
printf("\t--zsh - use /usr/bin/zsh as default shell.\n\n");
|
||||
printf("\n");
|
||||
printf("\n");
|
||||
|
||||
|
||||
printf("Traffic Shaping\n\n");
|
||||
|
||||
printf("Network bandwidth is an expensive resource shared among all sandboxes\n");
|
||||
printf("running on a system. Traffic shaping allows the user to increase network\n");
|
||||
printf("performance by controlling the amount of data that flows into and out of the\n");
|
||||
printf("sandboxes. Firejail implements a simple rate-limiting shaper based on Linux\n");
|
||||
printf("command tc. The shaper works at sandbox level, and can be used only for\n");
|
||||
printf("sandboxes configured with new network namespaces.\n\n");
|
||||
|
||||
printf("Set rate-limits:\n");
|
||||
printf("\tfirejail --bandwidth={name|pid} set network-name down-speed up-speed\n\n");
|
||||
printf("Clear rate-limits:\n");
|
||||
printf("\tfirejail --bandwidth={name|pid} clear network-name\n\n");
|
||||
printf("Status:\n");
|
||||
printf("\tfirejail --bandwidth={name|pid} status\n\n");
|
||||
printf("where:\n");
|
||||
printf("\tname - sandbox name\n");
|
||||
printf("\tpid - sandbox pid\n");
|
||||
printf("\tnetwork-name - network name as used by --net option\n");
|
||||
printf("\tdown-speed - download speed in KB/s (decimal kilobyte per second)\n");
|
||||
printf("\tup-speed - upload speed in KB/s (decimal kilobyte per second)\n");
|
||||
printf("\n");
|
||||
printf("Example:\n");
|
||||
printf("\t$ firejail --name=mybrowser --net=eth0 firefox &\n");
|
||||
printf("\t$ firejail --bandwidth=mybrowser set eth0 80 20\n");
|
||||
printf("\t$ firejail --bandwidth=mybrowser status\n");
|
||||
printf("\t$ firejail --bandwidth=mybrowser clear eth0\n");
|
||||
printf("\n");
|
||||
printf("\n");
|
||||
|
||||
|
||||
|
||||
printf("Monitoring\n\n");
|
||||
|
||||
printf("Option --list prints a list of all sandboxes. The format for each entry is as\n");
|
||||
printf("follows:\n\n");
|
||||
printf("\tPID:USER:Command\n\n");
|
||||
|
||||
printf("Option --tree prints the tree of processes running in the sandbox. The format\n");
|
||||
printf("for each process entry is as follows:\n\n");
|
||||
printf("\tPID:USER:Command\n\n");
|
||||
|
||||
printf("Option --top is similar to the UNIX top command, however it applies only to\n");
|
||||
printf("sandboxes. Listed below are the available fields (columns) in alphabetical\n");
|
||||
printf("order:\n\n");
|
||||
printf("\tCommand - command used to start the sandbox.\n");
|
||||
printf("\tCPU%% - CPU usage, the sandbox share of the elapsed CPU time since the\n");
|
||||
printf("\t last screen update\n");
|
||||
printf("\tPID - Unique process ID for the task controlling the sandbox.\n");
|
||||
printf("\tPrcs - number of processes running in sandbox, including the controlling\n");
|
||||
printf("\t process.\n");
|
||||
printf("\tRES - Resident Memory Size (KiB), sandbox non-swapped physical memory.\n");
|
||||
printf("\t It is a sum of the RES values for all processes running in the\n");
|
||||
printf("\t sandbox.\n");
|
||||
printf("\tSHR - Shared Memory Size (KiB), it reflects memory shared with other\n");
|
||||
printf("\t processes. It is a sum of the SHR values for all processes running\n");
|
||||
printf("\t in the sandbox, including the controlling process.\n");
|
||||
printf("\tUptime - sandbox running time in hours:minutes:seconds format.\n");
|
||||
printf("\tUser - The owner of the sandbox.\n");
|
||||
printf("\n");
|
||||
printf("\n");
|
||||
printf("Profile files\n\n");
|
||||
printf("Several command line configuration options can be passed to the program using\n");
|
||||
printf("profile files. Default Firejail profile files are stored in /etc/firejail\n");
|
||||
printf("directory, user profile files are stored in ~/.config/firejail directory. See\n");
|
||||
printf("man 5 firejail-profile for more information.\n\n");
|
||||
printf("\n");
|
||||
printf("Restricted shell\n\n");
|
||||
printf("To configure a restricted shell, replace /bin/bash with /usr/bin/firejail i\n");
|
||||
printf("/etc/password file for each user that needs to be restricted.\n");
|
||||
printf("Alternatively, you can specify /usr/bin/firejail in adduser command:\n\n");
|
||||
printf(" adduser --shell /usr/bin/firejail username\n\n");
|
||||
printf("Arguments to be passed to firejail executable upon login are declared in\n");
|
||||
printf("/etc/firejail/login.users file.\n\n");
|
||||
printf("\n");
|
||||
printf("Examples:\n\n");
|
||||
printf(" $ firejail\n");
|
||||
printf(" start a regular /bin/bash session in sandbox\n");
|
||||
printf(" $ firejail firefox\n");
|
||||
printf(" start Mozilla Firefox\n");
|
||||
printf(" $ firejail --seccomp firefox\n");
|
||||
printf(" start Mozilla Firefox in a seccomp sandbox\n");
|
||||
printf(" $ firejail --caps firefox\n");
|
||||
printf(" start Mozilla Firefox in a Linux capabilities sandbox\n");
|
||||
printf(" $ firejail --debug firefox\n");
|
||||
printf(" debug Firefox sandbox\n");
|
||||
printf(" $ firejail --private\n");
|
||||
printf(" start a /bin/bash session with a new tmpfs home directory\n");
|
||||
printf(" $ firejail --net=br0 ip=10.10.20.10\n");
|
||||
printf(" start a /bin/bash session in a new network namespace; the session is\n");
|
||||
printf(" connected to the main network using br0 bridge device, an IP address\n");
|
||||
printf(" of 10.10.20.10 is assigned to the sandbox\n");
|
||||
printf(" $ firejail --net=br0 --net=br1 --net=br2\n");
|
||||
printf(" start a /bin/bash session in a new network namespace and connect it\n");
|
||||
printf(" to br0, br1, and br2 host bridge devices\n");
|
||||
printf(" $ firejail --list\n");
|
||||
printf(" list all running sandboxes\n");
|
||||
printf("\n");
|
||||
printf("License GPL version 2 or later\n");
|
||||
printf("Homepage: http://firejail.sourceforge.net\n");
|
||||
printf("\n");
|
||||
}
|
||||
480
src/firejail/util.c
Normal file
480
src/firejail/util.c
Normal file
|
|
@ -0,0 +1,480 @@
|
|||
/*
|
||||
* Copyright (C) 2014, 2015 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 <sys/stat.h>
|
||||
#include <fcntl.h>
|
||||
#include <syslog.h>
|
||||
#include <errno.h>
|
||||
#include <dirent.h>
|
||||
#include <grp.h>
|
||||
|
||||
#define MAX_GROUPS 1024
|
||||
// drop privileges
|
||||
// - for root group or if nogroups is set, supplementary groups are not configured
|
||||
void drop_privs(int nogroups) {
|
||||
gid_t gid = getgid();
|
||||
|
||||
// configure supplementary groups
|
||||
if (gid == 0 || nogroups) {
|
||||
if (setgroups(0, NULL) < 0)
|
||||
errExit("setgroups");
|
||||
if (arg_debug)
|
||||
printf("Username %s, no supplementary groups\n", cfg.username);
|
||||
}
|
||||
else {
|
||||
assert(cfg.username);
|
||||
gid_t groups[MAX_GROUPS];
|
||||
int ngroups = MAX_GROUPS;
|
||||
int rv = getgrouplist(cfg.username, gid, groups, &ngroups);
|
||||
|
||||
if (arg_debug && rv) {
|
||||
printf("Username %s, groups ", cfg.username);
|
||||
int i;
|
||||
for (i = 0; i < ngroups; i++)
|
||||
printf("%u, ", groups[i]);
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
if (rv == -1) {
|
||||
fprintf(stderr, "Warning: cannot extract supplementary group list, dropping them\n");
|
||||
if (setgroups(0, NULL) < 0)
|
||||
errExit("setgroups");
|
||||
}
|
||||
else {
|
||||
rv = setgroups(ngroups, groups);
|
||||
if (rv) {
|
||||
fprintf(stderr, "Warning: cannot set supplementary group list, dropping them\n");
|
||||
if (setgroups(0, NULL) < 0)
|
||||
errExit("setgroups");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// set uid/gid
|
||||
if (setgid(getgid()) < 0)
|
||||
errExit("setgid/getgid");
|
||||
if (setuid(getuid()) < 0)
|
||||
errExit("setuid/getuid");
|
||||
}
|
||||
|
||||
|
||||
void logsignal(int s) {
|
||||
if (!arg_debug)
|
||||
return;
|
||||
|
||||
openlog("firejail", LOG_NDELAY | LOG_PID, LOG_USER);
|
||||
syslog(LOG_INFO, "Signal %d caught", s);
|
||||
closelog();
|
||||
}
|
||||
|
||||
|
||||
void logmsg(const char *msg) {
|
||||
if (!arg_debug)
|
||||
return;
|
||||
|
||||
openlog("firejail", LOG_NDELAY | LOG_PID, LOG_USER);
|
||||
syslog(LOG_INFO, "%s\n", msg);
|
||||
closelog();
|
||||
}
|
||||
|
||||
|
||||
void logargs(int argc, char **argv) {
|
||||
if (!arg_debug)
|
||||
return;
|
||||
|
||||
int i;
|
||||
int len = 0;
|
||||
|
||||
// calculate message length
|
||||
for (i = 0; i < argc; i++)
|
||||
len += strlen(argv[i]) + 1; // + ' '
|
||||
|
||||
// build message
|
||||
char msg[len + 1];
|
||||
char *ptr = msg;
|
||||
for (i = 0; i < argc; i++) {
|
||||
sprintf(ptr, "%s ", argv[i]);
|
||||
ptr += strlen(ptr);
|
||||
}
|
||||
|
||||
// log message
|
||||
logmsg(msg);
|
||||
}
|
||||
|
||||
|
||||
void logerr(const char *msg) {
|
||||
if (!arg_debug)
|
||||
return;
|
||||
|
||||
openlog("firejail", LOG_NDELAY | LOG_PID, LOG_USER);
|
||||
syslog(LOG_ERR, "%s\n", msg);
|
||||
closelog();
|
||||
}
|
||||
|
||||
|
||||
// return -1 if error, 0 if no error
|
||||
int copy_file(const char *srcname, const char *destname) {
|
||||
assert(srcname);
|
||||
assert(destname);
|
||||
|
||||
// open source
|
||||
int src = open(srcname, O_RDONLY);
|
||||
if (src < 0) {
|
||||
fprintf(stderr, "Warning: cannot open %s, file not copied\n", srcname);
|
||||
return -1;
|
||||
}
|
||||
|
||||
// open destination
|
||||
int dst = open(destname, O_CREAT|O_WRONLY|O_TRUNC, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
|
||||
if (dst < 0) {
|
||||
fprintf(stderr, "Warning: cannot open %s, file not copied\n", destname);
|
||||
close(src);
|
||||
return -1;
|
||||
}
|
||||
|
||||
// copy
|
||||
ssize_t len;
|
||||
static const int BUFLEN = 1024;
|
||||
unsigned char buf[BUFLEN];
|
||||
while ((len = read(src, buf, BUFLEN)) > 0) {
|
||||
int done = 0;
|
||||
while (done != len) {
|
||||
int rv = write(dst, buf + done, len - done);
|
||||
if (rv == -1) {
|
||||
close(src);
|
||||
close(dst);
|
||||
return -1;
|
||||
}
|
||||
|
||||
done += rv;
|
||||
}
|
||||
}
|
||||
|
||||
close(src);
|
||||
close(dst);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
char *get_link(const char *fname) {
|
||||
assert(fname);
|
||||
struct stat sb;
|
||||
char *linkname;
|
||||
ssize_t r;
|
||||
|
||||
if (lstat(fname, &sb) == -1)
|
||||
return NULL;
|
||||
|
||||
linkname = malloc(sb.st_size + 1);
|
||||
if (linkname == NULL)
|
||||
return NULL;
|
||||
memset(linkname, 0, sb.st_size + 1);
|
||||
|
||||
r = readlink(fname, linkname, sb.st_size + 1);
|
||||
if (r < 0) {
|
||||
free(linkname);
|
||||
return NULL;
|
||||
}
|
||||
return linkname;
|
||||
}
|
||||
|
||||
|
||||
// return 1 if the file is a directory
|
||||
int is_dir(const char *fname) {
|
||||
assert(fname);
|
||||
if (*fname == '\0')
|
||||
return 0;
|
||||
|
||||
// if fname doesn't end in '/', add one
|
||||
int rv;
|
||||
struct stat s;
|
||||
if (fname[strlen(fname) - 1] == '/')
|
||||
rv = stat(fname, &s);
|
||||
else {
|
||||
char *tmp;
|
||||
if (asprintf(&tmp, "%s/", fname) == -1) {
|
||||
fprintf(stderr, "Error: cannot allocate memory, %s:%d\n", __FILE__, __LINE__);
|
||||
errExit("asprintf");
|
||||
}
|
||||
rv = stat(tmp, &s);
|
||||
free(tmp);
|
||||
}
|
||||
|
||||
if (rv == -1)
|
||||
return 0;
|
||||
|
||||
if (S_ISDIR(s.st_mode))
|
||||
return 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
// return 1 if the file is a link
|
||||
int is_link(const char *fname) {
|
||||
assert(fname);
|
||||
if (*fname == '\0')
|
||||
return 0;
|
||||
|
||||
struct stat s;
|
||||
if (lstat(fname, &s) == 0) {
|
||||
if (S_ISLNK(s.st_mode))
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
// remove multiple spaces and return allocated memory
|
||||
char *line_remove_spaces(const char *buf) {
|
||||
assert(buf);
|
||||
if (strlen(buf) == 0)
|
||||
return NULL;
|
||||
|
||||
// allocate memory for the new string
|
||||
char *rv = malloc(strlen(buf) + 1);
|
||||
if (rv == NULL)
|
||||
errExit("malloc");
|
||||
|
||||
// remove space at start of line
|
||||
const char *ptr1 = buf;
|
||||
while (*ptr1 == ' ' || *ptr1 == '\t')
|
||||
ptr1++;
|
||||
|
||||
// copy data and remove additional spaces
|
||||
char *ptr2 = rv;
|
||||
int state = 0;
|
||||
while (*ptr1 != '\0') {
|
||||
if (*ptr1 == '\n' || *ptr1 == '\r')
|
||||
break;
|
||||
|
||||
if (state == 0) {
|
||||
if (*ptr1 != ' ' && *ptr1 != '\t')
|
||||
*ptr2++ = *ptr1++;
|
||||
else {
|
||||
*ptr2++ = ' ';
|
||||
ptr1++;
|
||||
state = 1;
|
||||
}
|
||||
}
|
||||
else { // state == 1
|
||||
while (*ptr1 == ' ' || *ptr1 == '\t')
|
||||
ptr1++;
|
||||
state = 0;
|
||||
}
|
||||
}
|
||||
|
||||
// strip last blank character if any
|
||||
if (*(ptr2 - 1) == ' ')
|
||||
--ptr2;
|
||||
*ptr2 = '\0';
|
||||
// if (arg_debug)
|
||||
// printf("Processing line #%s#\n", rv);
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
|
||||
char *split_comma(char *str) {
|
||||
if (str == NULL || *str == '\0')
|
||||
return NULL;
|
||||
char *ptr = strchr(str, ',');
|
||||
if (!ptr)
|
||||
return NULL;
|
||||
*ptr = '\0';
|
||||
ptr++;
|
||||
if (*ptr == '\0')
|
||||
return NULL;
|
||||
return ptr;
|
||||
}
|
||||
|
||||
|
||||
int not_unsigned(const char *str) {
|
||||
int rv = 0;
|
||||
const char *ptr = str;
|
||||
while (*ptr != ' ' && *ptr != '\t' && *ptr != '\0') {
|
||||
if (!isdigit(*ptr)) {
|
||||
rv = 1;
|
||||
break;
|
||||
}
|
||||
ptr++;
|
||||
}
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
|
||||
#define BUFLEN 4096
|
||||
// find the first child for this parent; return 1 if error
|
||||
int find_child(pid_t parent, pid_t *child) {
|
||||
*child = 0; // use it to flag a found child
|
||||
|
||||
DIR *dir;
|
||||
if (!(dir = opendir("/proc"))) {
|
||||
// sleep 2 seconds and try again
|
||||
sleep(2);
|
||||
if (!(dir = opendir("/proc"))) {
|
||||
fprintf(stderr, "Error: cannot open /proc directory\n");
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
struct dirent *entry;
|
||||
char *end;
|
||||
while (*child == 0 && (entry = readdir(dir))) {
|
||||
pid_t pid = strtol(entry->d_name, &end, 10);
|
||||
if (end == entry->d_name || *end)
|
||||
continue;
|
||||
if (pid == parent)
|
||||
continue;
|
||||
|
||||
// open stat file
|
||||
char *file;
|
||||
if (asprintf(&file, "/proc/%u/status", pid) == -1) {
|
||||
perror("asprintf");
|
||||
exit(1);
|
||||
}
|
||||
FILE *fp = fopen(file, "r");
|
||||
if (!fp) {
|
||||
free(file);
|
||||
continue;
|
||||
}
|
||||
|
||||
// look for firejail executable name
|
||||
char buf[BUFLEN];
|
||||
while (fgets(buf, BUFLEN - 1, fp)) {
|
||||
if (strncmp(buf, "PPid:", 5) == 0) {
|
||||
char *ptr = buf + 5;
|
||||
while (*ptr != '\0' && (*ptr == ' ' || *ptr == '\t')) {
|
||||
ptr++;
|
||||
}
|
||||
if (*ptr == '\0') {
|
||||
fprintf(stderr, "Error: cannot read /proc file\n");
|
||||
exit(1);
|
||||
}
|
||||
if (parent == atoi(ptr))
|
||||
*child = pid;
|
||||
break; // stop reading the file
|
||||
}
|
||||
}
|
||||
fclose(fp);
|
||||
free(file);
|
||||
}
|
||||
closedir(dir);
|
||||
|
||||
return (*child)? 0:1; // 0 = found, 1 = not found
|
||||
}
|
||||
|
||||
|
||||
|
||||
void extract_command_name(const char *str) {
|
||||
assert(str);
|
||||
cfg.command_name = strdup(str);
|
||||
if (!cfg.command_name)
|
||||
errExit("strdup");
|
||||
|
||||
// restrict the command name to the first word
|
||||
char *ptr = cfg.command_name;
|
||||
while (*ptr != ' ' && *ptr != '\t' && *ptr != '\0')
|
||||
ptr++;
|
||||
*ptr = '\0';
|
||||
|
||||
// remove the path: /usr/bin/firefox becomes firefox
|
||||
ptr = strrchr(cfg.command_name, '/');
|
||||
if (ptr) {
|
||||
ptr++;
|
||||
if (*ptr == '\0') {
|
||||
fprintf(stderr, "Error: invalid command name\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
char *tmp = strdup(ptr);
|
||||
if (!tmp)
|
||||
errExit("strdup");
|
||||
free(cfg.command_name);
|
||||
cfg.command_name = tmp;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void update_map(char *mapping, char *map_file) {
|
||||
int fd, j;
|
||||
size_t map_len; /* Length of 'mapping' */
|
||||
|
||||
/* Replace commas in mapping string with newlines */
|
||||
|
||||
map_len = strlen(mapping);
|
||||
for (j = 0; j < map_len; j++)
|
||||
if (mapping[j] == ',')
|
||||
mapping[j] = '\n';
|
||||
|
||||
fd = open(map_file, O_RDWR);
|
||||
if (fd == -1) {
|
||||
fprintf(stderr, "Error: cannot open %s: %s\n", map_file, strerror(errno));
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
if (write(fd, mapping, map_len) != map_len) {
|
||||
fprintf(stderr, "Error: cannot write to %s: %s\n", map_file, strerror(errno));
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
close(fd);
|
||||
}
|
||||
|
||||
|
||||
void wait_for_other(int fd) {
|
||||
//****************************
|
||||
// wait for the parent to be initialized
|
||||
//****************************
|
||||
char childstr[BUFLEN + 1];
|
||||
int newfd = dup(fd);
|
||||
if (newfd == -1)
|
||||
errExit("dup");
|
||||
FILE* stream;
|
||||
stream = fdopen(newfd, "r");
|
||||
*childstr = '\0';
|
||||
if (fgets(childstr, BUFLEN, stream)) {
|
||||
// remove \n)
|
||||
char *ptr = childstr;
|
||||
while(*ptr !='\0' && *ptr != '\n')
|
||||
ptr++;
|
||||
if (*ptr == '\0')
|
||||
errExit("fgets");
|
||||
*ptr = '\0';
|
||||
}
|
||||
else {
|
||||
fprintf(stderr, "Error: cannot establish communication with the parent, exiting...\n");
|
||||
exit(1);
|
||||
}
|
||||
fclose(stream);
|
||||
}
|
||||
|
||||
|
||||
void notify_other(int fd) {
|
||||
FILE* stream;
|
||||
int newfd = dup(fd);
|
||||
if (newfd == -1)
|
||||
errExit("dup");
|
||||
stream = fdopen(newfd, "w");
|
||||
fprintf(stream, "%u\n", getpid());
|
||||
fflush(stream);
|
||||
fclose(stream);
|
||||
}
|
||||
191
src/firejail/veth.c
Normal file
191
src/firejail/veth.c
Normal file
|
|
@ -0,0 +1,191 @@
|
|||
/* code based on iproute2 ip/iplink.c, modified to be included in firejail project
|
||||
*
|
||||
* Original source code:
|
||||
*
|
||||
* Information:
|
||||
* http://www.linuxfoundation.org/collaborate/workgroups/networking/iproute2
|
||||
*
|
||||
* Download:
|
||||
* http://www.kernel.org/pub/linux/utils/net/iproute2/
|
||||
*
|
||||
* Repository:
|
||||
* git://git.kernel.org/pub/scm/linux/kernel/git/shemminger/iproute2.git
|
||||
*
|
||||
* License: GPL v2
|
||||
*
|
||||
* Original copyright header
|
||||
*
|
||||
* iplink.c "ip link".
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* Authors: Alexey Kuznetsov, <kuznet@ms2.inr.ac.ru>
|
||||
*
|
||||
*/
|
||||
/*
|
||||
* Copyright (C) 2014, 2015 netblue30 (netblue30@yahoo.com)
|
||||
*
|
||||
* 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/libnetlink.h"
|
||||
#include <linux/veth.h>
|
||||
|
||||
struct iplink_req
|
||||
{
|
||||
struct nlmsghdr n;
|
||||
struct ifinfomsg i;
|
||||
char buf[1024];
|
||||
};
|
||||
|
||||
static struct rtnl_handle rth = { .fd = -1 };
|
||||
|
||||
int net_create_veth(const char *dev, const char *nsdev, unsigned pid) {
|
||||
int len;
|
||||
struct iplink_req req;
|
||||
|
||||
if (arg_debug)
|
||||
printf("create veth %s/%s/%u\n", dev, nsdev, pid);
|
||||
assert(dev);
|
||||
assert(nsdev);
|
||||
assert(pid);
|
||||
|
||||
if (rtnl_open(&rth, 0) < 0) {
|
||||
fprintf(stderr, "cannot open netlink\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
memset(&req, 0, sizeof(req));
|
||||
|
||||
req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg));
|
||||
req.n.nlmsg_flags = NLM_F_REQUEST|NLM_F_CREATE|NLM_F_EXCL;
|
||||
req.n.nlmsg_type = RTM_NEWLINK;
|
||||
req.i.ifi_family = 0;
|
||||
|
||||
if (dev) {
|
||||
len = strlen(dev) + 1;
|
||||
addattr_l(&req.n, sizeof(req), IFLA_IFNAME, dev, len);
|
||||
}
|
||||
|
||||
struct rtattr *linkinfo = NLMSG_TAIL(&req.n);
|
||||
addattr_l(&req.n, sizeof(req), IFLA_LINKINFO, NULL, 0);
|
||||
addattr_l(&req.n, sizeof(req), IFLA_INFO_KIND, "veth", strlen("veth"));
|
||||
|
||||
struct rtattr * data = NLMSG_TAIL(&req.n);
|
||||
addattr_l(&req.n, sizeof(req), IFLA_INFO_DATA, NULL, 0);
|
||||
|
||||
struct rtattr * peerdata = NLMSG_TAIL(&req.n);
|
||||
addattr_l (&req.n, sizeof(req), VETH_INFO_PEER, NULL, 0);
|
||||
req.n.nlmsg_len += sizeof(struct ifinfomsg);
|
||||
|
||||
// place the link in the child namespace
|
||||
addattr_l (&req.n, sizeof(req), IFLA_NET_NS_PID, &pid, 4);
|
||||
|
||||
if (nsdev) {
|
||||
int len = strlen(nsdev) + 1;
|
||||
addattr_l(&req.n, sizeof(req), IFLA_IFNAME, nsdev, len);
|
||||
}
|
||||
peerdata->rta_len = (void *)NLMSG_TAIL(&req.n) - (void *)peerdata;
|
||||
|
||||
data->rta_len = (void *)NLMSG_TAIL(&req.n) - (void *)data;
|
||||
linkinfo->rta_len = (void *)NLMSG_TAIL(&req.n) - (void *)linkinfo;
|
||||
|
||||
// send message
|
||||
if (rtnl_talk(&rth, &req.n, 0, 0, NULL) < 0)
|
||||
exit(2);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
int net_create_macvlan(const char *dev, const char *parent, unsigned pid) {
|
||||
int len;
|
||||
struct iplink_req req;
|
||||
if (arg_debug)
|
||||
printf("create macvlan %s, parent %s\n", dev, parent);
|
||||
assert(dev);
|
||||
assert(parent);
|
||||
|
||||
if (rtnl_open(&rth, 0) < 0) {
|
||||
fprintf(stderr, "cannot open netlink\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
memset(&req, 0, sizeof(req));
|
||||
|
||||
req.n.nlmsg_len = NLMSG_LENGTH(sizeof(struct ifinfomsg));
|
||||
req.n.nlmsg_flags = NLM_F_REQUEST|NLM_F_CREATE|NLM_F_EXCL;
|
||||
req.n.nlmsg_type = RTM_NEWLINK;
|
||||
req.i.ifi_family = 0;
|
||||
|
||||
// we start with the parent
|
||||
int parent_ifindex = 2;
|
||||
addattr_l(&req.n, sizeof(req), IFLA_LINK, &parent_ifindex, 4);
|
||||
|
||||
// add new interface name
|
||||
len = strlen(dev) + 1;
|
||||
addattr_l(&req.n, sizeof(req), IFLA_IFNAME, dev, len);
|
||||
|
||||
// place the interface in child namespace
|
||||
addattr_l (&req.n, sizeof(req), IFLA_NET_NS_PID, &pid, 4);
|
||||
|
||||
|
||||
// add link info for the new interface
|
||||
struct rtattr *linkinfo = NLMSG_TAIL(&req.n);
|
||||
addattr_l(&req.n, sizeof(req), IFLA_LINKINFO, NULL, 0);
|
||||
addattr_l(&req.n, sizeof(req), IFLA_INFO_KIND, "macvlan", strlen("macvlan"));
|
||||
|
||||
// set macvlan bridge mode
|
||||
struct rtattr * data = NLMSG_TAIL(&req.n);
|
||||
addattr_l(&req.n, sizeof(req), IFLA_INFO_DATA, NULL, 0);
|
||||
int macvlan_type = MACVLAN_MODE_BRIDGE;
|
||||
addattr_l (&req.n, sizeof(req), IFLA_INFO_KIND, &macvlan_type, 4);
|
||||
|
||||
data->rta_len = (void *)NLMSG_TAIL(&req.n) - (void *)data;
|
||||
// req.n.nlmsg_len += sizeof(struct ifinfomsg);
|
||||
|
||||
|
||||
data->rta_len = (void *)NLMSG_TAIL(&req.n) - (void *)data;
|
||||
linkinfo->rta_len = (void *)NLMSG_TAIL(&req.n) - (void *)linkinfo;
|
||||
|
||||
// send message
|
||||
if (rtnl_talk(&rth, &req.n, 0, 0, NULL) < 0)
|
||||
exit(2);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
int main(int argc, char **argv) {
|
||||
printf("Hello\n");
|
||||
|
||||
|
||||
char *dev = argv[3];
|
||||
char *nsdev = argv[8];
|
||||
unsigned pid;
|
||||
sscanf(argv[10], "%u", &pid);
|
||||
|
||||
|
||||
net_create_veth(dev, nsdev, pid);
|
||||
|
||||
return 0;
|
||||
}
|
||||
*/
|
||||
24
src/firemon/Makefile.in
Normal file
24
src/firemon/Makefile.in
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
all: firemon
|
||||
|
||||
PREFIX=@prefix@
|
||||
VERSION=@PACKAGE_VERSION@
|
||||
NAME=@PACKAGE_NAME@
|
||||
|
||||
H_FILE_LIST = $(wildcard *.[h])
|
||||
C_FILE_LIST = $(wildcard *.c)
|
||||
OBJS = $(C_FILE_LIST:.c=.o)
|
||||
BINOBJS = $(foreach file, $(OBJS), $file)
|
||||
CFLAGS += -ggdb -O2 -DVERSION='"$(VERSION)"' -fstack-protector-all -D_FORTIFY_SOURCE=2 -fPIE -pie -Wformat -Wformat-security
|
||||
LDFLAGS += -pie -Wl,-z,relro -Wl,-z,now
|
||||
|
||||
%.o : %.c $(H_FILE_LIST)
|
||||
$(CC) $(CFLAGS) $(INCLUDE) -c $< -o $@
|
||||
|
||||
firemon: $(OBJS) ../lib/common.o ../lib/pid.o
|
||||
$(CC) $(LDFLAGS) -o $@ $(OBJS) ../lib/common.o ../lib/pid.o $(LIBS)
|
||||
|
||||
clean:; rm -f *.o firemon
|
||||
|
||||
distclean: clean
|
||||
rm -fr Makefile
|
||||
|
||||
99
src/firemon/arp.c
Normal file
99
src/firemon/arp.c
Normal file
|
|
@ -0,0 +1,99 @@
|
|||
/*
|
||||
* Copyright (C) 2014, 2015 netblue30 (netblue30@yahoo.com)
|
||||
*
|
||||
* 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 "firemon.h"
|
||||
#define MAXBUF 4096
|
||||
|
||||
static void print_arp(const char *fname) {
|
||||
FILE *fp = fopen(fname, "r");
|
||||
if (!fp)
|
||||
return;
|
||||
|
||||
printf(" ARP Table:\n");
|
||||
char buf[MAXBUF];
|
||||
while (fgets(buf, MAXBUF, fp)) {
|
||||
// remove blanks, \n
|
||||
char *ptr = buf;
|
||||
while (*ptr == ' ' || *ptr == '\t')
|
||||
ptr++;
|
||||
char *start = ptr;
|
||||
if (*start == '\0')
|
||||
continue;
|
||||
ptr = strchr(ptr, '\n');
|
||||
if (ptr)
|
||||
*ptr = '\0';
|
||||
|
||||
// remove table header
|
||||
//IP address HW type Flags HW address Mask Device
|
||||
if (strncmp(start, "IP address", 10) == 0)
|
||||
continue;
|
||||
|
||||
// extract data
|
||||
char ip[64];
|
||||
char type[64];
|
||||
char flags[64];
|
||||
char mac[64];
|
||||
char mask[64];
|
||||
char device[64];
|
||||
int rv = sscanf(start, "%s %s %s %s %s %s\n", ip, type, flags, mac, mask, device);
|
||||
if (rv != 6)
|
||||
continue;
|
||||
|
||||
// destination ip
|
||||
unsigned a, b, c, d;
|
||||
if (sscanf(ip, "%u.%u.%u.%u", &a, &b, &c, &d) != 4 || a > 255 || b > 255 || c > 255 || d > 255)
|
||||
continue;
|
||||
uint32_t destip = a * 0x1000000 + b * 0x10000 + c * 0x100 + d;
|
||||
if (strcmp(flags, "0x0") == 0)
|
||||
printf(" %d.%d.%d.%d dev %s FAILED\n",
|
||||
PRINT_IP(destip), device);
|
||||
else
|
||||
printf(" %d.%d.%d.%d dev %s lladdr %s REACHABLE\n",
|
||||
PRINT_IP(destip), device, mac);
|
||||
}
|
||||
|
||||
fclose(fp);
|
||||
|
||||
}
|
||||
|
||||
void arp(pid_t pid) {
|
||||
if (getuid() == 0)
|
||||
firemon_drop_privs();
|
||||
|
||||
pid_read(pid);
|
||||
|
||||
// print processes
|
||||
int i;
|
||||
for (i = 0; i < max_pids; i++) {
|
||||
if (pids[i].level == 1) {
|
||||
pid_print_list(i, 0);
|
||||
int child = find_child(i);
|
||||
if (child != -1) {
|
||||
char *fname;
|
||||
if (asprintf(&fname, "/proc/%d/net/arp", child) == -1)
|
||||
errExit("asprintf");
|
||||
print_arp(fname);
|
||||
free(fname);
|
||||
printf("\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
69
src/firemon/caps.c
Normal file
69
src/firemon/caps.c
Normal file
|
|
@ -0,0 +1,69 @@
|
|||
/*
|
||||
* Copyright (C) 2014, 2015 netblue30 (netblue30@yahoo.com)
|
||||
*
|
||||
* 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 "firemon.h"
|
||||
#define MAXBUF 4098
|
||||
|
||||
static void print_caps(int pid) {
|
||||
char *file;
|
||||
if (asprintf(&file, "/proc/%d/status", pid) == -1) {
|
||||
errExit("asprintf");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
FILE *fp = fopen(file, "r");
|
||||
if (!fp) {
|
||||
printf(" Error: cannot open %s\n", file);
|
||||
free(file);
|
||||
return;
|
||||
}
|
||||
|
||||
char buf[MAXBUF];
|
||||
while (fgets(buf, MAXBUF, fp)) {
|
||||
if (strncmp(buf, "CapBnd:", 7) == 0) {
|
||||
printf(" %s", buf);
|
||||
fflush(0);
|
||||
free(file);
|
||||
fclose(fp);
|
||||
return;
|
||||
}
|
||||
}
|
||||
fclose(fp);
|
||||
free(file);
|
||||
}
|
||||
|
||||
void caps(pid_t pid) {
|
||||
if (getuid() == 0)
|
||||
firemon_drop_privs();
|
||||
|
||||
pid_read(pid); // include all processes
|
||||
|
||||
// print processes
|
||||
int i;
|
||||
for (i = 0; i < max_pids; i++) {
|
||||
if (pids[i].level == 1) {
|
||||
pid_print_list(i, 0);
|
||||
int child = find_child(i);
|
||||
if (child != -1)
|
||||
print_caps(child);
|
||||
}
|
||||
}
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
64
src/firemon/cgroup.c
Normal file
64
src/firemon/cgroup.c
Normal file
|
|
@ -0,0 +1,64 @@
|
|||
/*
|
||||
* Copyright (C) 2014, 2015 netblue30 (netblue30@yahoo.com)
|
||||
*
|
||||
* 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 "firemon.h"
|
||||
#define MAXBUF 4098
|
||||
|
||||
static void print_cgroup(int pid) {
|
||||
char *file;
|
||||
if (asprintf(&file, "/proc/%d/cgroup", pid) == -1) {
|
||||
errExit("asprintf");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
FILE *fp = fopen(file, "r");
|
||||
if (!fp) {
|
||||
printf(" Error: cannot open %s\n", file);
|
||||
free(file);
|
||||
return;
|
||||
}
|
||||
|
||||
char buf[MAXBUF];
|
||||
if (fgets(buf, MAXBUF, fp)) {
|
||||
printf(" %s", buf);
|
||||
fflush(0);
|
||||
}
|
||||
|
||||
fclose(fp);
|
||||
free(file);
|
||||
}
|
||||
|
||||
void cgroup(pid_t pid) {
|
||||
if (getuid() == 0)
|
||||
firemon_drop_privs();
|
||||
|
||||
pid_read(pid);
|
||||
|
||||
// print processes
|
||||
int i;
|
||||
for (i = 0; i < max_pids; i++) {
|
||||
if (pids[i].level == 1) {
|
||||
pid_print_list(i, 0);
|
||||
int child = find_child(i);
|
||||
if (child != -1)
|
||||
print_cgroup(child);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
68
src/firemon/cpu.c
Normal file
68
src/firemon/cpu.c
Normal file
|
|
@ -0,0 +1,68 @@
|
|||
/*
|
||||
* Copyright (C) 2014, 2015 netblue30 (netblue30@yahoo.com)
|
||||
*
|
||||
* 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 "firemon.h"
|
||||
#define MAXBUF 4098
|
||||
|
||||
static void print_cpu(int pid) {
|
||||
char *file;
|
||||
if (asprintf(&file, "/proc/%d/status", pid) == -1) {
|
||||
errExit("asprintf");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
FILE *fp = fopen(file, "r");
|
||||
if (!fp) {
|
||||
printf(" Error: cannot open %s\n", file);
|
||||
free(file);
|
||||
return;
|
||||
}
|
||||
|
||||
char buf[MAXBUF];
|
||||
while (fgets(buf, MAXBUF, fp)) {
|
||||
if (strncmp(buf, "Cpus_allowed_list:", 18) == 0) {
|
||||
printf(" %s", buf);
|
||||
fflush(0);
|
||||
free(file);
|
||||
fclose(fp);
|
||||
return;
|
||||
}
|
||||
}
|
||||
fclose(fp);
|
||||
free(file);
|
||||
}
|
||||
|
||||
void cpu(pid_t pid) {
|
||||
if (getuid() == 0)
|
||||
firemon_drop_privs();
|
||||
|
||||
pid_read(pid);
|
||||
|
||||
// print processes
|
||||
int i;
|
||||
for (i = 0; i < max_pids; i++) {
|
||||
if (pids[i].level == 1) {
|
||||
pid_print_list(i, 0);
|
||||
int child = find_child(i);
|
||||
if (child != -1)
|
||||
print_cpu(child);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
222
src/firemon/firemon.c
Normal file
222
src/firemon/firemon.c
Normal file
|
|
@ -0,0 +1,222 @@
|
|||
/*
|
||||
* Copyright (C) 2014, 2015 netblue30 (netblue30@yahoo.com)
|
||||
*
|
||||
* 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 "firemon.h"
|
||||
#include <signal.h>
|
||||
#include <termios.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/prctl.h>
|
||||
#include <grp.h>
|
||||
|
||||
|
||||
static int arg_route = 0;
|
||||
static int arg_arp = 0;
|
||||
static int arg_tree = 0;
|
||||
static int arg_interface = 0;
|
||||
static int arg_seccomp = 0;
|
||||
static int arg_caps = 0;
|
||||
static int arg_cpu = 0;
|
||||
static int arg_cgroup = 0;
|
||||
int arg_nowrap = 0;
|
||||
|
||||
static struct termios tlocal; // startup terminal setting
|
||||
static struct termios twait; // no wait on key press
|
||||
static int terminal_set = 0;
|
||||
|
||||
static void my_handler(int s){
|
||||
if (terminal_set)
|
||||
tcsetattr(0, TCSANOW, &tlocal);
|
||||
exit(0);
|
||||
}
|
||||
|
||||
// find the first child process for the specified pid
|
||||
// return -1 if not found
|
||||
int find_child(int id) {
|
||||
int i;
|
||||
for (i = 0; i < max_pids; i++) {
|
||||
if (pids[i].level == 2 && pids[i].parent == id)
|
||||
return i;
|
||||
}
|
||||
|
||||
return -1;
|
||||
}
|
||||
|
||||
// drop privileges
|
||||
void firemon_drop_privs(void) {
|
||||
// drop privileges
|
||||
if (setgroups(0, NULL) < 0)
|
||||
errExit("setgroups");
|
||||
if (setgid(getgid()) < 0)
|
||||
errExit("setgid/getgid");
|
||||
if (setuid(getuid()) < 0)
|
||||
errExit("setuid/getuid");
|
||||
}
|
||||
|
||||
// sleep and wait for a key to be pressed
|
||||
void firemon_sleep(int st) {
|
||||
if (terminal_set == 0) {
|
||||
tcgetattr(0, &twait); // get current terminal attirbutes; 0 is the file descriptor for stdin
|
||||
memcpy(&tlocal, &twait, sizeof(tlocal));
|
||||
twait.c_lflag &= ~ICANON; // disable canonical mode
|
||||
twait.c_lflag &= ~ECHO; // no echo
|
||||
twait.c_cc[VMIN] = 1; // wait until at least one keystroke available
|
||||
twait.c_cc[VTIME] = 0; // no timeout
|
||||
terminal_set = 1;
|
||||
}
|
||||
tcsetattr(0, TCSANOW, &twait);
|
||||
|
||||
|
||||
fd_set fds;
|
||||
FD_ZERO(&fds);
|
||||
FD_SET(0,&fds);
|
||||
int maxfd = 1;
|
||||
|
||||
struct timeval ts;
|
||||
ts.tv_sec = st;
|
||||
ts.tv_usec = 0;
|
||||
|
||||
int ready = select(maxfd, &fds, (fd_set *) 0, (fd_set *) 0, &ts);
|
||||
(void) ready;
|
||||
if( FD_ISSET(0, &fds)) {
|
||||
getchar();
|
||||
tcsetattr(0, TCSANOW, &tlocal);
|
||||
printf("\n");
|
||||
exit(0);
|
||||
}
|
||||
tcsetattr(0, TCSANOW, &tlocal);
|
||||
}
|
||||
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
unsigned pid = 0;
|
||||
int i;
|
||||
|
||||
// handle CTRL-C
|
||||
signal (SIGINT, my_handler);
|
||||
signal (SIGTERM, my_handler);
|
||||
|
||||
for (i = 1; i < argc; i++) {
|
||||
// default options
|
||||
if (strcmp(argv[i], "--help") == 0 ||
|
||||
strcmp(argv[i], "-?") == 0) {
|
||||
usage();
|
||||
return 0;
|
||||
}
|
||||
else if (strcmp(argv[i], "--version") == 0) {
|
||||
printf("firemon version %s\n\n", VERSION);
|
||||
return 0;
|
||||
}
|
||||
|
||||
// options without a pid argument
|
||||
else if (strcmp(argv[i], "--top") == 0) {
|
||||
top(); // never to return
|
||||
}
|
||||
else if (strcmp(argv[i], "--list") == 0) {
|
||||
list();
|
||||
return 0;
|
||||
}
|
||||
else if (strcmp(argv[i], "--netstats") == 0) {
|
||||
netstats();
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
// cumulative options with or without a pid argument
|
||||
else if (strcmp(argv[i], "--cgroup") == 0) {
|
||||
arg_cgroup = 1;
|
||||
}
|
||||
else if (strcmp(argv[i], "--cpu") == 0) {
|
||||
arg_cpu = 1;
|
||||
}
|
||||
else if (strcmp(argv[i], "--seccomp") == 0) {
|
||||
arg_seccomp = 1;
|
||||
}
|
||||
else if (strcmp(argv[i], "--caps") == 0) {
|
||||
arg_caps = 1;
|
||||
}
|
||||
else if (strcmp(argv[i], "--tree") == 0) {
|
||||
arg_tree = 1;
|
||||
}
|
||||
else if (strcmp(argv[i], "--interface") == 0) {
|
||||
arg_interface = 1;
|
||||
}
|
||||
else if (strcmp(argv[i], "--route") == 0) {
|
||||
arg_route = 1;
|
||||
}
|
||||
else if (strcmp(argv[i], "--arp") == 0) {
|
||||
arg_arp = 1;
|
||||
}
|
||||
|
||||
else if (strncmp(argv[i], "--name=", 7) == 0) {
|
||||
char *name = argv[i] + 7;
|
||||
if (name2pid(name, (pid_t *) &pid)) {
|
||||
fprintf(stderr, "Error: cannot find sandbox %s\n", name);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
// etc
|
||||
else if (strcmp(argv[i], "--nowrap") == 0)
|
||||
arg_nowrap = 1;
|
||||
|
||||
// invalid option
|
||||
else if (*argv[i] == '-') {
|
||||
fprintf(stderr, "Error: invalid option\n");
|
||||
return 1;
|
||||
}
|
||||
|
||||
// PID argument
|
||||
else {
|
||||
// this should be a pid number
|
||||
char *ptr = argv[i];
|
||||
while (*ptr != '\0') {
|
||||
if (!isdigit(*ptr)) {
|
||||
fprintf(stderr, "Error: not a valid PID number\n");
|
||||
exit(1);
|
||||
}
|
||||
ptr++;
|
||||
}
|
||||
|
||||
sscanf(argv[i], "%u", &pid);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (arg_tree)
|
||||
tree((pid_t) pid);
|
||||
if (arg_interface)
|
||||
interface((pid_t) pid);
|
||||
if (arg_route)
|
||||
route((pid_t) pid);
|
||||
if (arg_arp)
|
||||
arp((pid_t) pid);
|
||||
if (arg_seccomp)
|
||||
seccomp((pid_t) pid);
|
||||
if (arg_caps)
|
||||
caps((pid_t) pid);
|
||||
if (arg_cpu)
|
||||
cpu((pid_t) pid);
|
||||
if (arg_cgroup)
|
||||
cgroup((pid_t) pid);
|
||||
|
||||
if (!arg_route && !arg_arp && !arg_interface && !arg_tree && !arg_caps && !arg_seccomp)
|
||||
procevent((pid_t) pid); // never to return
|
||||
|
||||
return 0;
|
||||
}
|
||||
84
src/firemon/firemon.h
Normal file
84
src/firemon/firemon.h
Normal file
|
|
@ -0,0 +1,84 @@
|
|||
/*
|
||||
* Copyright (C) 2014, 2015 netblue30 (netblue30@yahoo.com)
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
#ifndef FIREMON_H
|
||||
#define FIREMON_H
|
||||
#define _GNU_SOURCE
|
||||
#include <stdlib.h>
|
||||
#include <stdio.h>
|
||||
#include <ctype.h>
|
||||
#include <string.h>
|
||||
#include <errno.h>
|
||||
#include <stdint.h>
|
||||
#include "../include/pid.h"
|
||||
#include "../include/common.h"
|
||||
|
||||
// clear screen
|
||||
static inline void firemon_clrscr(void) {
|
||||
printf("\033[2J\033[1;1H");
|
||||
fflush(0);
|
||||
}
|
||||
|
||||
// firemon.c
|
||||
extern int arg_nowrap;
|
||||
int find_child(int id);
|
||||
void firemon_drop_privs(void);
|
||||
void firemon_sleep(int st);
|
||||
|
||||
|
||||
// procevent.c
|
||||
void procevent(pid_t pid);
|
||||
|
||||
// usage.c
|
||||
void usage(void);
|
||||
|
||||
// top.c
|
||||
void top(void);
|
||||
|
||||
// list.c
|
||||
void list(void);
|
||||
|
||||
// interface.c
|
||||
void interface(pid_t pid);
|
||||
|
||||
// arp.c
|
||||
void arp(pid_t pid);
|
||||
|
||||
// route.c
|
||||
void route(pid_t pid);
|
||||
|
||||
// caps.c
|
||||
void caps(pid_t pid);
|
||||
|
||||
// seccomp.c
|
||||
void seccomp(pid_t pid);
|
||||
|
||||
// cpu.c
|
||||
void cpu(pid_t pid);
|
||||
|
||||
// cgroup.c
|
||||
void cgroup(pid_t pid);
|
||||
|
||||
// tree.c
|
||||
void tree(pid_t pid);
|
||||
|
||||
// netstats.c
|
||||
void netstats(void);
|
||||
|
||||
#endif
|
||||
176
src/firemon/interface.c
Normal file
176
src/firemon/interface.c
Normal file
|
|
@ -0,0 +1,176 @@
|
|||
/*
|
||||
* Copyright (C) 2014, 2015 netblue30 (netblue30@yahoo.com)
|
||||
*
|
||||
* 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 "firemon.h"
|
||||
#include <sys/types.h>
|
||||
#include <sys/wait.h>
|
||||
#include <netdb.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <ifaddrs.h>
|
||||
#include <net/if.h>
|
||||
#include <linux/connector.h>
|
||||
#include <linux/netlink.h>
|
||||
#include <linux/if_link.h>
|
||||
#include <linux/sockios.h>
|
||||
#include <sys/ioctl.h>
|
||||
|
||||
//#include <net/route.h>
|
||||
//#include <linux/if_bridge.h>
|
||||
|
||||
// print IP addresses for all interfaces
|
||||
static void net_ifprint(void) {
|
||||
uint32_t ip;
|
||||
uint32_t mask;
|
||||
struct ifaddrs *ifaddr, *ifa;
|
||||
|
||||
int fd;
|
||||
if ((fd = socket(AF_INET, SOCK_DGRAM, 0)) < 0) {
|
||||
fprintf(stderr, "Error: cannot open AF_INET socket\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
if (getifaddrs(&ifaddr) == -1)
|
||||
errExit("getifaddrs");
|
||||
|
||||
// walk through the linked list
|
||||
printf(" Link status:\n");
|
||||
for (ifa = ifaddr; ifa != NULL; ifa = ifa->ifa_next) {
|
||||
if (ifa->ifa_addr == NULL)
|
||||
continue;
|
||||
|
||||
if (ifa->ifa_addr->sa_family == AF_PACKET) {
|
||||
if (ifa->ifa_flags & IFF_RUNNING && ifa->ifa_flags & IFF_UP) {
|
||||
if (ifa->ifa_data != NULL) {
|
||||
struct rtnl_link_stats *stats = ifa->ifa_data;
|
||||
|
||||
// extract mac address
|
||||
struct ifreq ifr;
|
||||
memset(&ifr, 0, sizeof(ifr));
|
||||
strncpy(ifr.ifr_name, ifa->ifa_name, IFNAMSIZ);
|
||||
int rv = ioctl (fd, SIOCGIFHWADDR, &ifr);
|
||||
|
||||
if (rv == 0)
|
||||
printf(" %s UP, %02x:%02x:%02x:%02x:%02x:%02x\n",
|
||||
ifa->ifa_name, PRINT_MAC((unsigned char *) &ifr.ifr_hwaddr.sa_data));
|
||||
else
|
||||
printf(" %s UP\n", ifa->ifa_name);
|
||||
|
||||
printf(" tx/rx: %u/%u packets, %u/%u bytes\n",
|
||||
stats->tx_packets, stats->rx_packets,
|
||||
stats->tx_bytes, stats->rx_bytes);
|
||||
}
|
||||
}
|
||||
else
|
||||
printf(" %s DOWN\n", ifa->ifa_name);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// walk through the linked list
|
||||
printf(" IPv4 status:\n");
|
||||
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);
|
||||
|
||||
char *status;
|
||||
if (ifa->ifa_flags & IFF_RUNNING && ifa->ifa_flags & IFF_UP)
|
||||
status = "UP";
|
||||
else
|
||||
status = "DOWN";
|
||||
|
||||
printf(" %s %s, %d.%d.%d.%d/%u\n",
|
||||
ifa->ifa_name, status, PRINT_IP(ip), mask2bits(mask));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// walk through the linked list
|
||||
printf(" IPv6 status:\n");
|
||||
for (ifa = ifaddr; ifa != NULL; ifa = ifa->ifa_next) {
|
||||
if (ifa->ifa_addr == NULL)
|
||||
continue;
|
||||
|
||||
if (ifa->ifa_addr->sa_family == AF_INET6) {
|
||||
char host[NI_MAXHOST];
|
||||
int s = getnameinfo(ifa->ifa_addr, sizeof(struct sockaddr_in6),
|
||||
host, NI_MAXHOST, NULL, 0, NI_NUMERICHOST);
|
||||
if (s == 0) {
|
||||
char *ptr;
|
||||
if ((ptr = strchr(host, '%')) != NULL)
|
||||
*ptr = '\0';
|
||||
char *status;
|
||||
if (ifa->ifa_flags & IFF_RUNNING && ifa->ifa_flags & IFF_UP)
|
||||
status = "UP";
|
||||
else
|
||||
status = "DOWN";
|
||||
|
||||
printf(" %s %s, %s\n", ifa->ifa_name, status, host);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
freeifaddrs(ifaddr);
|
||||
close(fd);
|
||||
}
|
||||
|
||||
static void print_sandbox(pid_t pid) {
|
||||
pid_t child = fork();
|
||||
if (child == -1)
|
||||
return;
|
||||
|
||||
if (child == 0) {
|
||||
int rv = join_namespace(pid, "net");
|
||||
if (rv)
|
||||
return;
|
||||
net_ifprint();
|
||||
printf("\n");
|
||||
exit(0);
|
||||
}
|
||||
|
||||
// wait for the child to finish
|
||||
waitpid(child, NULL, 0);
|
||||
}
|
||||
|
||||
void interface(pid_t pid) {
|
||||
if (getuid() != 0) {
|
||||
fprintf(stderr, "Error: you need to be root to run this command\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
pid_read(pid); // a pid of 0 will include all processes
|
||||
|
||||
// print processes
|
||||
int i;
|
||||
for (i = 0; i < max_pids; i++) {
|
||||
if (pids[i].level == 1) {
|
||||
pid_print_list(i, 0);
|
||||
int child = find_child(i);
|
||||
if (child != -1) {
|
||||
print_sandbox(child);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
35
src/firemon/list.c
Normal file
35
src/firemon/list.c
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
/*
|
||||
* Copyright (C) 2014, 2015 netblue30 (netblue30@yahoo.com)
|
||||
*
|
||||
* 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 "firemon.h"
|
||||
|
||||
void list(void) {
|
||||
if (getuid() == 0)
|
||||
firemon_drop_privs();
|
||||
|
||||
pid_read(0); // include all processes
|
||||
|
||||
// print processes
|
||||
int i;
|
||||
for (i = 0; i < max_pids; i++) {
|
||||
if (pids[i].level == 1)
|
||||
pid_print_list(i, 0);
|
||||
}
|
||||
}
|
||||
|
||||
214
src/firemon/netstats.c
Normal file
214
src/firemon/netstats.c
Normal file
|
|
@ -0,0 +1,214 @@
|
|||
/*
|
||||
* Copyright (C) 2014, 2015 netblue30 (netblue30@yahoo.com)
|
||||
*
|
||||
* 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 "firemon.h"
|
||||
#include <termios.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#define MAXBUF 4096
|
||||
|
||||
static char *get_header(void) {
|
||||
char *rv;
|
||||
if (asprintf(&rv, "%-5.5s %-9.9s %-10.10s %-10.10s %s",
|
||||
"PID", "User", "RX(KB/s)", "TX(KB/s)", "Command") == -1)
|
||||
errExit("asprintf");
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
void get_stats(int parent) {
|
||||
// find the first child
|
||||
int child = -1;
|
||||
for (child = parent + 1; child < max_pids; child++) {
|
||||
if (pids[child].parent == parent)
|
||||
break;
|
||||
}
|
||||
|
||||
if (child == -1)
|
||||
goto errexit;
|
||||
|
||||
// open /proc/child/net/dev file and read rx and tx
|
||||
char *fname;
|
||||
if (asprintf(&fname, "/proc/%d/net/dev", child) == -1)
|
||||
errExit("asprintf");
|
||||
FILE *fp = fopen(fname, "r");
|
||||
if (!fp) {
|
||||
free(fname);
|
||||
goto errexit;
|
||||
}
|
||||
|
||||
char buf[MAXBUF];
|
||||
long long unsigned rx = 0;
|
||||
long long unsigned tx = 0;
|
||||
while (fgets(buf, MAXBUF, fp)) {
|
||||
if (strncmp(buf, "Inter", 5) == 0)
|
||||
continue;
|
||||
if (strncmp(buf, " face", 5) == 0)
|
||||
continue;
|
||||
|
||||
char *ptr = buf;
|
||||
while (*ptr != '\0' && *ptr != ':') {
|
||||
ptr++;
|
||||
}
|
||||
|
||||
if (*ptr == '\0') {
|
||||
fclose(fp);
|
||||
free(fname);
|
||||
goto errexit;
|
||||
}
|
||||
ptr++;
|
||||
|
||||
long long unsigned rxval;
|
||||
long long unsigned txval;
|
||||
unsigned a, b, c, d, e, f, g;
|
||||
sscanf(ptr, "%llu %u %u %u %u %u %u %u %llu",
|
||||
&rxval, &a, &b, &c, &d, &e, &f, &g, &txval);
|
||||
rx += rxval;
|
||||
tx += txval;
|
||||
}
|
||||
|
||||
// store data
|
||||
pids[parent].rx_delta = rx - pids[parent].rx;
|
||||
pids[parent].rx = rx;
|
||||
pids[parent].tx_delta = tx - pids[parent].tx;
|
||||
pids[parent].tx = tx;
|
||||
|
||||
|
||||
free(fname);
|
||||
fclose(fp);
|
||||
return;
|
||||
|
||||
errexit:
|
||||
pids[parent].rx = 0;
|
||||
pids[parent].tx = 0;
|
||||
pids[parent].rx_delta = 0;
|
||||
pids[parent].tx_delta = 0;
|
||||
}
|
||||
|
||||
|
||||
static void print_proc(int index, int itv, int col) {
|
||||
// command
|
||||
char *cmd = pid_proc_cmdline(index);
|
||||
char *ptrcmd;
|
||||
if (cmd == NULL) {
|
||||
if (pids[index].zombie)
|
||||
ptrcmd = "(zombie)";
|
||||
else
|
||||
ptrcmd = "";
|
||||
}
|
||||
else
|
||||
ptrcmd = cmd;
|
||||
// if the command doesn't have a --net= option, don't print
|
||||
if (strstr(ptrcmd, "--net=") == NULL) {
|
||||
if (cmd)
|
||||
free(cmd);
|
||||
return;
|
||||
}
|
||||
|
||||
// pid
|
||||
char pidstr[10];
|
||||
snprintf(pidstr, 10, "%u", index);
|
||||
|
||||
// user
|
||||
char *user = pid_get_user_name(pids[index].uid);
|
||||
char *ptruser;
|
||||
if (user)
|
||||
ptruser = user;
|
||||
else
|
||||
ptruser = "";
|
||||
|
||||
|
||||
float rx_kbps = ((float) pids[index].rx_delta / 1000) / itv;
|
||||
char ptrrx[15];
|
||||
sprintf(ptrrx, "%.03f", rx_kbps);
|
||||
|
||||
float tx_kbps = ((float) pids[index].tx_delta / 1000) / itv;
|
||||
char ptrtx[15];
|
||||
sprintf(ptrtx, "%.03f", tx_kbps);
|
||||
|
||||
char buf[1024 + 1];
|
||||
snprintf(buf, 1024, "%-5.5s %-9.9s %-10.10s %-10.10s %s",
|
||||
pidstr, ptruser, ptrrx, ptrtx, ptrcmd);
|
||||
if (col < 1024)
|
||||
buf[col] = '\0';
|
||||
printf("%s\n", buf);
|
||||
|
||||
if (cmd)
|
||||
free(cmd);
|
||||
if (user)
|
||||
free(user);
|
||||
|
||||
}
|
||||
|
||||
void netstats(void) {
|
||||
if (getuid() == 0)
|
||||
firemon_drop_privs();
|
||||
|
||||
pid_read(0); // include all processes
|
||||
|
||||
printf("Displaying network statistics only for sandboxes using a new network namespace.\n");
|
||||
|
||||
// print processes
|
||||
while (1) {
|
||||
// set pid table
|
||||
int i;
|
||||
int itv = 5; // 5 second interval
|
||||
pid_read(0); // todo: preserve the last calculation if any, so we don't have to do get_stats()
|
||||
|
||||
// start rx/tx measurements
|
||||
for (i = 0; i < max_pids; i++) {
|
||||
if (pids[i].level == 1)
|
||||
get_stats(i);
|
||||
}
|
||||
|
||||
// wait 5 seconds
|
||||
firemon_sleep(itv);
|
||||
|
||||
// grab screen size
|
||||
struct winsize sz;
|
||||
int row = 24;
|
||||
int col = 80;
|
||||
if (!ioctl(0, TIOCGWINSZ, &sz)) {
|
||||
col = sz.ws_col;
|
||||
row = sz.ws_row;
|
||||
}
|
||||
|
||||
// start printing
|
||||
firemon_clrscr();
|
||||
char *header = get_header();
|
||||
if (strlen(header) > col)
|
||||
header[col] = '\0';
|
||||
printf("%s\n", header);
|
||||
if (row > 0)
|
||||
row--;
|
||||
free(header);
|
||||
|
||||
// start rx/tx measurements
|
||||
for (i = 0; i < max_pids; i++) {
|
||||
if (pids[i].level == 1) {
|
||||
get_stats(i);
|
||||
print_proc(i, itv, col);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
377
src/firemon/procevent.c
Normal file
377
src/firemon/procevent.c
Normal file
|
|
@ -0,0 +1,377 @@
|
|||
/*
|
||||
* Copyright (C) 2014, 2015 netblue30 (netblue30@yahoo.com)
|
||||
*
|
||||
* 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 "firemon.h"
|
||||
#include <sys/socket.h>
|
||||
#include <linux/connector.h>
|
||||
#include <linux/netlink.h>
|
||||
#include <linux/cn_proc.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
#include <arpa/inet.h>
|
||||
#include <time.h>
|
||||
#define PIDS_BUFLEN 4096
|
||||
#define SERVER_PORT 889 // 889-899 is left unassigned by IANA
|
||||
|
||||
static int pid_is_firejail(pid_t pid) {
|
||||
uid_t rv = 0;
|
||||
|
||||
// open stat file
|
||||
char *file;
|
||||
if (asprintf(&file, "/proc/%u/status", pid) == -1) {
|
||||
perror("asprintf");
|
||||
exit(1);
|
||||
}
|
||||
FILE *fp = fopen(file, "r");
|
||||
if (!fp) {
|
||||
free(file);
|
||||
return 0;
|
||||
}
|
||||
|
||||
// look for firejail executable name
|
||||
char buf[PIDS_BUFLEN];
|
||||
while (fgets(buf, PIDS_BUFLEN - 1, fp)) {
|
||||
if (strncmp(buf, "Name:", 5) == 0) {
|
||||
char *ptr = buf + 5;
|
||||
while (*ptr != '\0' && (*ptr == ' ' || *ptr == '\t')) {
|
||||
ptr++;
|
||||
}
|
||||
if (*ptr == '\0')
|
||||
goto doexit;
|
||||
if (strncmp(ptr, "firejail", 8) == 0)
|
||||
rv = 1;
|
||||
// if (strncmp(ptr, "lxc-execute", 11) == 0)
|
||||
// rv = 1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
doexit:
|
||||
fclose(fp);
|
||||
free(file);
|
||||
return rv;
|
||||
}
|
||||
|
||||
|
||||
static int procevent_netlink_setup(void) {
|
||||
// open socket for process event connector
|
||||
int sock;
|
||||
if ((sock = socket(PF_NETLINK, SOCK_DGRAM, NETLINK_CONNECTOR)) < 0) {
|
||||
fprintf(stderr, "Error: cannot open netlink socket\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
// bind socket
|
||||
struct sockaddr_nl addr;
|
||||
memset(&addr, 0, sizeof(addr));
|
||||
addr.nl_pid = getpid();
|
||||
addr.nl_family = AF_NETLINK;
|
||||
addr.nl_groups = CN_IDX_PROC;
|
||||
if (bind(sock, (struct sockaddr *)&addr, sizeof(addr)) < 0) {
|
||||
fprintf(stderr, "Error: cannot bind to netlink socket\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
// send monitoring message
|
||||
struct nlmsghdr nlmsghdr;
|
||||
memset(&nlmsghdr, 0, sizeof(nlmsghdr));
|
||||
nlmsghdr.nlmsg_len = NLMSG_LENGTH(sizeof(struct cn_msg) + sizeof(enum proc_cn_mcast_op));
|
||||
nlmsghdr.nlmsg_pid = getpid();
|
||||
nlmsghdr.nlmsg_type = NLMSG_DONE;
|
||||
|
||||
struct cn_msg cn_msg;
|
||||
memset(&cn_msg, 0, sizeof(cn_msg));
|
||||
cn_msg.id.idx = CN_IDX_PROC;
|
||||
cn_msg.id.val = CN_VAL_PROC;
|
||||
cn_msg.len = sizeof(enum proc_cn_mcast_op);
|
||||
|
||||
struct iovec iov[3];
|
||||
iov[0].iov_base = &nlmsghdr;
|
||||
iov[0].iov_len = sizeof(nlmsghdr);
|
||||
iov[1].iov_base = &cn_msg;
|
||||
iov[1].iov_len = sizeof(cn_msg);
|
||||
|
||||
enum proc_cn_mcast_op op = PROC_CN_MCAST_LISTEN;
|
||||
iov[2].iov_base = &op;
|
||||
iov[2].iov_len = sizeof(op);
|
||||
|
||||
if (writev(sock, iov, 3) == -1) {
|
||||
fprintf(stderr, "Error: cannot write to netlink socket\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
return sock;
|
||||
}
|
||||
|
||||
|
||||
static int procevent_monitor(const int sock, pid_t mypid) {
|
||||
ssize_t len;
|
||||
struct nlmsghdr *nlmsghdr;
|
||||
|
||||
// timeout in order to re-enable firejail module trace
|
||||
struct timeval tv;
|
||||
tv.tv_sec = 30;
|
||||
tv.tv_usec = 0;
|
||||
|
||||
while (1) {
|
||||
#define BUFFSIZE 4096
|
||||
char __attribute__ ((aligned(NLMSG_ALIGNTO)))buf[BUFFSIZE];
|
||||
|
||||
fd_set readfds;
|
||||
int max;
|
||||
FD_ZERO(&readfds);
|
||||
FD_SET(sock, &readfds);
|
||||
max = sock;
|
||||
max++;
|
||||
|
||||
int rv = select(max, &readfds, NULL, NULL, &tv);
|
||||
if (rv == -1) {
|
||||
fprintf(stderr, "recv: %s\n", strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
|
||||
// timeout
|
||||
if (rv == 0) {
|
||||
tv.tv_sec = 30;
|
||||
tv.tv_usec = 0;
|
||||
continue;
|
||||
}
|
||||
|
||||
|
||||
if ((len = recv(sock, buf, sizeof(buf), 0)) == 0) {
|
||||
return 0;
|
||||
}
|
||||
if (len == -1) {
|
||||
if (errno == EINTR) {
|
||||
return 0;
|
||||
} else {
|
||||
fprintf(stderr,"recv: %s\n", strerror(errno));
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
for (nlmsghdr = (struct nlmsghdr *)buf;
|
||||
NLMSG_OK (nlmsghdr, len);
|
||||
nlmsghdr = NLMSG_NEXT (nlmsghdr, len)) {
|
||||
|
||||
struct cn_msg *cn_msg;
|
||||
struct proc_event *proc_ev;
|
||||
struct tm tm;
|
||||
time_t now;
|
||||
|
||||
if ((nlmsghdr->nlmsg_type == NLMSG_ERROR) ||
|
||||
(nlmsghdr->nlmsg_type == NLMSG_NOOP))
|
||||
continue;
|
||||
|
||||
cn_msg = NLMSG_DATA(nlmsghdr);
|
||||
if ((cn_msg->id.idx != CN_IDX_PROC) ||
|
||||
(cn_msg->id.val != CN_VAL_PROC))
|
||||
continue;
|
||||
|
||||
(void)time(&now);
|
||||
(void)localtime_r(&now, &tm);
|
||||
char line[PIDS_BUFLEN];
|
||||
char *lineptr = line;
|
||||
sprintf(lineptr, "%2.2d:%2.2d:%2.2d", tm.tm_hour, tm.tm_min, tm.tm_sec);
|
||||
lineptr += strlen(lineptr);
|
||||
|
||||
proc_ev = (struct proc_event *)cn_msg->data;
|
||||
pid_t pid = 0;
|
||||
pid_t child = 0;
|
||||
int remove_pid = 0;
|
||||
switch (proc_ev->what) {
|
||||
case PROC_EVENT_FORK:
|
||||
if (proc_ev->event_data.fork.child_pid !=
|
||||
proc_ev->event_data.fork.child_tgid)
|
||||
continue; // this is a thread, not a process
|
||||
pid = proc_ev->event_data.fork.parent_tgid;
|
||||
if (pids[pid].level > 0) {
|
||||
child = proc_ev->event_data.fork.child_tgid;
|
||||
child %= max_pids;
|
||||
pids[child].level = pids[pid].level + 1;
|
||||
pids[child].uid = pid_get_uid(child);
|
||||
}
|
||||
sprintf(lineptr, " fork");
|
||||
break;
|
||||
case PROC_EVENT_EXEC:
|
||||
pid = proc_ev->event_data.exec.process_tgid;
|
||||
sprintf(lineptr, " exec");
|
||||
break;
|
||||
|
||||
case PROC_EVENT_EXIT:
|
||||
if (proc_ev->event_data.exit.process_pid !=
|
||||
proc_ev->event_data.exit.process_tgid)
|
||||
continue; // this is a thread, not a process
|
||||
|
||||
pid = proc_ev->event_data.exit.process_tgid;
|
||||
remove_pid = 1;
|
||||
sprintf(lineptr, " exit");
|
||||
break;
|
||||
|
||||
case PROC_EVENT_UID:
|
||||
pid = proc_ev->event_data.id.process_tgid;
|
||||
sprintf(lineptr, " uid ");
|
||||
break;
|
||||
|
||||
case PROC_EVENT_GID:
|
||||
pid = proc_ev->event_data.id.process_tgid;
|
||||
sprintf(lineptr, " gid ");
|
||||
break;
|
||||
|
||||
case PROC_EVENT_SID:
|
||||
pid = proc_ev->event_data.sid.process_tgid;
|
||||
sprintf(lineptr, " sid ");
|
||||
break;
|
||||
|
||||
default:
|
||||
sprintf(lineptr, "\n");
|
||||
continue;
|
||||
}
|
||||
|
||||
int add_new = 0;
|
||||
if (pids[pid].level < 0) // not a firejail process
|
||||
continue;
|
||||
else if (pids[pid].level == 0) { // new porcess, do we track it?
|
||||
if (pid_is_firejail(pid) && mypid == 0) {
|
||||
pids[pid].level = 1;
|
||||
add_new = 1;
|
||||
}
|
||||
else {
|
||||
pids[pid].level = -1;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
lineptr += strlen(lineptr);
|
||||
sprintf(lineptr, " %u", pid);
|
||||
lineptr += strlen(lineptr);
|
||||
|
||||
char *user = pids[pid].user;
|
||||
if (!user)
|
||||
user = pid_get_user_name(pids[pid].uid);
|
||||
if (user) {
|
||||
pids[pid].user = user;
|
||||
sprintf(lineptr, " (%s)", user);
|
||||
lineptr += strlen(lineptr);
|
||||
}
|
||||
|
||||
|
||||
int sandbox_closed = 0; // exit sandbox flag
|
||||
char *cmd = pids[pid].cmd;
|
||||
if (!cmd) {
|
||||
cmd = pid_proc_cmdline(pid);
|
||||
}
|
||||
if (add_new) {
|
||||
if (!cmd)
|
||||
sprintf(lineptr, " NEW SANDBOX\n");
|
||||
else
|
||||
sprintf(lineptr, " NEW SANDBOX: %s\n", cmd);
|
||||
lineptr += strlen(lineptr);
|
||||
}
|
||||
else if (proc_ev->what == PROC_EVENT_EXIT && pids[pid].level == 1) {
|
||||
sprintf(lineptr, " EXIT SANDBOX\n");
|
||||
lineptr += strlen(lineptr);
|
||||
if (mypid == pid)
|
||||
sandbox_closed = 1;
|
||||
}
|
||||
else {
|
||||
if (!cmd) {
|
||||
cmd = pid_proc_cmdline(pid);
|
||||
}
|
||||
if (cmd == NULL)
|
||||
sprintf(lineptr, "\n");
|
||||
else {
|
||||
sprintf(lineptr, " %s\n", cmd);
|
||||
free(cmd);
|
||||
}
|
||||
lineptr += strlen(lineptr);
|
||||
}
|
||||
(void) lineptr;
|
||||
|
||||
// print the event
|
||||
printf("%s", line);
|
||||
fflush(0);
|
||||
|
||||
// unflag pid for exit events
|
||||
if (remove_pid) {
|
||||
if (pids[pid].user)
|
||||
free(pids[pid].user);
|
||||
if (pids[pid].cmd)
|
||||
free(pids[pid].cmd);
|
||||
memset(&pids[pid], 0, sizeof(Process));
|
||||
}
|
||||
|
||||
// print forked child
|
||||
if (child) {
|
||||
cmd = pid_proc_cmdline(child);
|
||||
if (cmd) {
|
||||
printf("\tchild %u %s\n", child, cmd);
|
||||
free(cmd);
|
||||
}
|
||||
else
|
||||
printf("\tchild %u\n", child);
|
||||
}
|
||||
|
||||
// on uid events the uid is changing
|
||||
if (proc_ev->what == PROC_EVENT_UID) {
|
||||
if (pids[pid].user)
|
||||
free(pids[pid].user);
|
||||
pids[pid].user = 0;
|
||||
pids[pid].uid = pid_get_uid(pid);
|
||||
}
|
||||
|
||||
if (sandbox_closed)
|
||||
exit(0);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void procevent_print_pids(void) {
|
||||
// print files
|
||||
int i;
|
||||
for (i = 0; i < max_pids; i++) {
|
||||
if (pids[i].level == 1)
|
||||
pid_print_tree(i, 0, 1);
|
||||
}
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
void procevent(pid_t pid) {
|
||||
// need to be root for this
|
||||
if (getuid() != 0) {
|
||||
fprintf(stderr, "Error: you need to be root to get process events\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
// read and print sandboxed processes
|
||||
pid_read(pid);
|
||||
procevent_print_pids();
|
||||
|
||||
// monitor using netlink
|
||||
int sock = procevent_netlink_setup();
|
||||
if (sock < 0) {
|
||||
fprintf(stderr, "Error: cannot open netlink socket\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
procevent_monitor(sock, pid); // it will never return from here
|
||||
assert(0);
|
||||
close(sock); // quiet static analyzers
|
||||
}
|
||||
213
src/firemon/route.c
Normal file
213
src/firemon/route.c
Normal file
|
|
@ -0,0 +1,213 @@
|
|||
/*
|
||||
* Copyright (C) 2014, 2015 netblue30 (netblue30@yahoo.com)
|
||||
*
|
||||
* 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 "firemon.h"
|
||||
#include <assert.h>
|
||||
#include <arpa/inet.h>
|
||||
#define MAXBUF 4096
|
||||
|
||||
typedef struct iflist_t {
|
||||
struct iflist_t *next;
|
||||
uint32_t ip;
|
||||
} IfList;
|
||||
static IfList *ifs = NULL;
|
||||
static char last_start[MAXBUF + 1];
|
||||
|
||||
static IfList *list_find(uint32_t ip, uint32_t mask) {
|
||||
IfList *ptr = ifs;
|
||||
while (ptr) {
|
||||
if ((ptr->ip & mask) == (ip & mask))
|
||||
return ptr;
|
||||
ptr = ptr->next;
|
||||
}
|
||||
|
||||
return NULL;
|
||||
}
|
||||
|
||||
static void extract_if(const char *fname) {
|
||||
// clear interface list
|
||||
while (ifs) {
|
||||
IfList *tmp = ifs->next;
|
||||
free(ifs);
|
||||
ifs = tmp;
|
||||
}
|
||||
assert(ifs == NULL);
|
||||
|
||||
FILE *fp = fopen(fname, "r");
|
||||
if (!fp)
|
||||
return;
|
||||
|
||||
char buf[MAXBUF];
|
||||
int state = 0; // 0 -wait for Local
|
||||
//
|
||||
while (fgets(buf, MAXBUF, fp)) {
|
||||
// remove blanks, \n
|
||||
char *ptr = buf;
|
||||
while (*ptr == ' ' || *ptr == '\t')
|
||||
ptr++;
|
||||
char *start = ptr;
|
||||
if (*start == '\0')
|
||||
continue;
|
||||
ptr = strchr(ptr, '\n');
|
||||
if (ptr)
|
||||
*ptr = '\0';
|
||||
|
||||
if (state == 0) {
|
||||
if (strncmp(buf, "Local:", 6) == 0) {
|
||||
state = 1;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
else if (state == 1) {
|
||||
// remove broadcast addresses
|
||||
if (strstr(start,"BROADCAST"))
|
||||
continue;
|
||||
else if (*start == '+')
|
||||
continue;
|
||||
else if (*start == '|') {
|
||||
memset(last_start, 0, MAXBUF + 1);
|
||||
strncpy(last_start, start, MAXBUF);
|
||||
continue;
|
||||
}
|
||||
else if (strstr(buf, "LOCAL")) {
|
||||
// printf("%s %s\n", last_start, start);
|
||||
unsigned mbits;
|
||||
sscanf(start, "/%u", &mbits);
|
||||
if (mbits != 32)
|
||||
continue;
|
||||
|
||||
unsigned a, b, c, d;
|
||||
if (sscanf(last_start, "|-- %u.%u.%u.%u", &a, &b, &c, &d) != 4 || a > 255 || b > 255 || c > 255 || d > 255)
|
||||
continue;
|
||||
|
||||
IfList *newif = malloc(sizeof(IfList));
|
||||
if (!newif)
|
||||
errExit("malloc");
|
||||
newif->ip = a * 0x1000000 + b * 0x10000 + c * 0x100 + d;
|
||||
newif->next = ifs;
|
||||
ifs = newif;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fclose(fp);
|
||||
|
||||
|
||||
}
|
||||
|
||||
static void print_route(const char *fname) {
|
||||
FILE *fp = fopen(fname, "r");
|
||||
if (!fp)
|
||||
return;
|
||||
|
||||
printf(" Route table:\n");
|
||||
char buf[MAXBUF];
|
||||
while (fgets(buf, MAXBUF, fp)) {
|
||||
// remove blanks, \n
|
||||
char *ptr = buf;
|
||||
while (*ptr == ' ' || *ptr == '\t')
|
||||
ptr++;
|
||||
char *start = ptr;
|
||||
if (*start == '\0')
|
||||
continue;
|
||||
ptr = strchr(ptr, '\n');
|
||||
if (ptr)
|
||||
*ptr = '\0';
|
||||
|
||||
// remove table header
|
||||
//Iface Destination Gateway Flags RefCnt Use Metric Mask MTU Window IRTT
|
||||
if (strncmp(start, "Iface", 5) == 0)
|
||||
continue;
|
||||
|
||||
// extract data
|
||||
char ifname[64];
|
||||
char destination[64];
|
||||
char gateway[64];
|
||||
char flags[64];
|
||||
char refcnt[64];
|
||||
char use[64];
|
||||
char metric[64];
|
||||
char mask[64];
|
||||
int rv = sscanf(start, "%s %s %s %s %s %s %s %s\n", ifname, destination, gateway, flags, refcnt, use, metric, mask);
|
||||
if (rv != 8)
|
||||
continue;
|
||||
|
||||
// destination ip
|
||||
uint32_t destip;
|
||||
sscanf(destination, "%x", &destip);
|
||||
destip = ntohl(destip);
|
||||
uint32_t destmask;
|
||||
sscanf(mask, "%x", &destmask);
|
||||
destmask = ntohl(destmask);
|
||||
uint32_t gw;
|
||||
sscanf(gateway, "%x", &gw);
|
||||
gw = ntohl(gw);
|
||||
|
||||
// printf("#%s# #%s# #%s# #%s# #%s# #%s# #%s# #%s#\n", ifname, destination, gateway, flags, refcnt, use, metric, mask);
|
||||
if (gw != 0)
|
||||
printf(" %u.%u.%u.%u/%u via %u.%u.%u.%u, dev %s, metric %s\n",
|
||||
PRINT_IP(destip), mask2bits(destmask),
|
||||
PRINT_IP(gw),
|
||||
ifname,
|
||||
metric);
|
||||
else { // this is an interface
|
||||
IfList *ifentry = list_find(destip, destmask);
|
||||
if (ifentry) {
|
||||
printf(" %u.%u.%u.%u/%u, dev %s, scope link src %d.%d.%d.%d\n",
|
||||
PRINT_IP(destip), mask2bits(destmask),
|
||||
ifname,
|
||||
PRINT_IP(ifentry->ip));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fclose(fp);
|
||||
|
||||
}
|
||||
|
||||
void route(pid_t pid) {
|
||||
if (getuid() == 0)
|
||||
firemon_drop_privs();
|
||||
|
||||
pid_read(pid);
|
||||
|
||||
// print processes
|
||||
int i;
|
||||
for (i = 0; i < max_pids; i++) {
|
||||
if (pids[i].level == 1) {
|
||||
pid_print_list(i, 0);
|
||||
int child = find_child(i);
|
||||
if (child != -1) {
|
||||
char *fname;
|
||||
if (asprintf(&fname, "/proc/%d/net/fib_trie", child) == -1)
|
||||
errExit("asprintf");
|
||||
extract_if(fname);
|
||||
free(fname);
|
||||
|
||||
if (asprintf(&fname, "/proc/%d/net/route", child) == -1)
|
||||
errExit("asprintf");
|
||||
print_route(fname);
|
||||
free(fname);
|
||||
printf("\n");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
69
src/firemon/seccomp.c
Normal file
69
src/firemon/seccomp.c
Normal file
|
|
@ -0,0 +1,69 @@
|
|||
/*
|
||||
* Copyright (C) 2014, 2015 netblue30 (netblue30@yahoo.com)
|
||||
*
|
||||
* 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 "firemon.h"
|
||||
|
||||
#define MAXBUF 4098
|
||||
static void print_seccomp(int pid) {
|
||||
char *file;
|
||||
if (asprintf(&file, "/proc/%d/status", pid) == -1) {
|
||||
errExit("asprintf");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
FILE *fp = fopen(file, "r");
|
||||
if (!fp) {
|
||||
printf(" Error: cannot open %s\n", file);
|
||||
free(file);
|
||||
return;
|
||||
}
|
||||
|
||||
char buf[MAXBUF];
|
||||
while (fgets(buf, MAXBUF, fp)) {
|
||||
if (strncmp(buf, "Seccomp:", 8) == 0) {
|
||||
printf(" %s", buf);
|
||||
fflush(0);
|
||||
fclose(fp);
|
||||
free(file);
|
||||
return;
|
||||
}
|
||||
}
|
||||
fclose(fp);
|
||||
free(file);
|
||||
}
|
||||
|
||||
void seccomp(pid_t pid) {
|
||||
if (getuid() == 0)
|
||||
firemon_drop_privs();
|
||||
|
||||
pid_read(pid); // include all processes
|
||||
|
||||
// print processes
|
||||
int i;
|
||||
for (i = 0; i < max_pids; i++) {
|
||||
if (pids[i].level == 1) {
|
||||
pid_print_list(i, 0);
|
||||
int child = find_child(i);
|
||||
if (child != -1)
|
||||
print_seccomp(child);
|
||||
}
|
||||
}
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
297
src/firemon/top.c
Normal file
297
src/firemon/top.c
Normal file
|
|
@ -0,0 +1,297 @@
|
|||
/*
|
||||
* Copyright (C) 2014, 2015 netblue30 (netblue30@yahoo.com)
|
||||
*
|
||||
* 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 "firemon.h"
|
||||
#include <termios.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
|
||||
static unsigned pgs_rss = 0;
|
||||
static unsigned pgs_shared = 0;
|
||||
static unsigned clocktick = 0;
|
||||
static unsigned long long sysuptime = 0;
|
||||
|
||||
static char *get_header(void) {
|
||||
char *rv;
|
||||
if (asprintf(&rv, "%-5.5s %-9.9s %-8.8s %-8.8s %-5.5s %-4.4s %-9.9s %s",
|
||||
"PID", "User", "RES(KiB)", "SHR(KiB)", "CPU%", "Prcs", "Uptime", "Command") == -1)
|
||||
errExit("asprintf");
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
|
||||
// recursivity!!!
|
||||
static char *print_top(unsigned index, unsigned parent, unsigned *utime, unsigned *stime, unsigned itv, float *cpu, int *cnt) {
|
||||
char *rv = NULL;
|
||||
|
||||
char procdir[20];
|
||||
snprintf(procdir, 20, "/proc/%u", index);
|
||||
struct stat s;
|
||||
if (stat(procdir, &s) == -1)
|
||||
return NULL;
|
||||
|
||||
if (pids[index].level == 1) {
|
||||
pgs_rss = 0;
|
||||
pgs_shared = 0;
|
||||
*utime = 0;
|
||||
*stime = 0;
|
||||
*cnt = 0;
|
||||
}
|
||||
|
||||
(*cnt)++;
|
||||
pid_getmem(index, &pgs_rss, &pgs_shared);
|
||||
unsigned utmp;
|
||||
unsigned stmp;
|
||||
pid_get_cpu_time(index, &utmp, &stmp);
|
||||
*utime += utmp;
|
||||
*stime += stmp;
|
||||
|
||||
|
||||
int i;
|
||||
for (i = index + 1; i < max_pids; i++) {
|
||||
if (pids[i].parent == index)
|
||||
print_top(i, index, utime, stime, itv, cpu, cnt);
|
||||
}
|
||||
|
||||
if (pids[index].level == 1) {
|
||||
// pid
|
||||
char pidstr[10];
|
||||
snprintf(pidstr, 10, "%u", index);
|
||||
|
||||
// command
|
||||
char *cmd = pid_proc_cmdline(index);
|
||||
char *ptrcmd;
|
||||
if (cmd == NULL) {
|
||||
if (pids[index].zombie)
|
||||
ptrcmd = "(zombie)";
|
||||
else
|
||||
ptrcmd = "";
|
||||
}
|
||||
else
|
||||
ptrcmd = cmd;
|
||||
|
||||
// user
|
||||
char *user = pid_get_user_name(pids[index].uid);
|
||||
char *ptruser;
|
||||
if (user)
|
||||
ptruser = user;
|
||||
else
|
||||
ptruser = "";
|
||||
|
||||
// memory
|
||||
int pgsz = getpagesize();
|
||||
char rss[10];
|
||||
snprintf(rss, 10, "%u", pgs_rss * pgsz / 1024);
|
||||
char shared[10];
|
||||
snprintf(shared, 10, "%u", pgs_shared * pgsz / 1024);
|
||||
|
||||
// uptime
|
||||
unsigned long long uptime = pid_get_start_time(index);
|
||||
if (clocktick == 0)
|
||||
clocktick = sysconf(_SC_CLK_TCK);
|
||||
uptime /= clocktick;
|
||||
uptime = sysuptime - uptime;
|
||||
unsigned sec = uptime % 60;
|
||||
uptime -= sec;
|
||||
uptime /= 60;
|
||||
unsigned min = uptime % 60;
|
||||
uptime -= min;
|
||||
uptime /= 60;
|
||||
unsigned hour = uptime;
|
||||
char uptime_str[50];
|
||||
snprintf(uptime_str, 50, "%02u:%02u:%02u", hour, min, sec);
|
||||
|
||||
// cpu
|
||||
itv *= clocktick;
|
||||
float ud = (float) (*utime - pids[index].utime) / itv * 100;
|
||||
float sd = (float) (*stime - pids[index].stime) / itv * 100;
|
||||
float cd = ud + sd;
|
||||
*cpu = cd;
|
||||
char cpu_str[10];
|
||||
snprintf(cpu_str, 10, "%2.1f", cd);
|
||||
|
||||
// process count
|
||||
char prcs_str[10];
|
||||
snprintf(prcs_str, 10, "%d", *cnt);
|
||||
|
||||
if (asprintf(&rv, "%-5.5s %-9.9s %-8.8s %-8.8s %-5.5s %-4.4s %-9.9s %s",
|
||||
pidstr, ptruser, rss, shared, cpu_str, prcs_str, uptime_str, ptrcmd) == -1)
|
||||
errExit("asprintf");
|
||||
|
||||
if (cmd)
|
||||
free(cmd);
|
||||
if (user)
|
||||
free(user);
|
||||
|
||||
}
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
|
||||
typedef struct node_t {
|
||||
struct node_t *next;
|
||||
char *line;
|
||||
float cpu;
|
||||
} Node;
|
||||
|
||||
static Node *head = NULL;
|
||||
|
||||
static void head_clear(void) {
|
||||
Node *ptr = head;
|
||||
while (ptr) {
|
||||
if (ptr->line)
|
||||
free(ptr->line);
|
||||
Node *next = ptr->next;
|
||||
free(ptr);
|
||||
ptr = next;
|
||||
}
|
||||
|
||||
head = NULL;
|
||||
}
|
||||
|
||||
static void head_add(float cpu, char *line) {
|
||||
// allocate a new node structure
|
||||
Node *node = malloc(sizeof(Node));
|
||||
if (!node)
|
||||
errExit("malloc");
|
||||
node->line = line;
|
||||
node->cpu = cpu;
|
||||
node->next = NULL;
|
||||
|
||||
// insert in first list position
|
||||
if (head == NULL || head->cpu < cpu) {
|
||||
node->next = head;
|
||||
head = node;
|
||||
return;
|
||||
}
|
||||
|
||||
// insert in the right place
|
||||
Node *ptr = head;
|
||||
while (1) {
|
||||
// last position
|
||||
Node *current = ptr->next;
|
||||
if (current == NULL) {
|
||||
ptr->next = node;
|
||||
return;
|
||||
}
|
||||
|
||||
// current position
|
||||
if (current->cpu < cpu) {
|
||||
ptr->next = node;
|
||||
node->next = current;
|
||||
return;
|
||||
}
|
||||
|
||||
ptr = current;
|
||||
}
|
||||
}
|
||||
|
||||
void head_print(int col, int row) {
|
||||
Node *ptr = head;
|
||||
int current = 0;
|
||||
while (ptr) {
|
||||
if (current >= row)
|
||||
break;
|
||||
|
||||
if (strlen(ptr->line) > col)
|
||||
ptr->line[col] = '\0';
|
||||
|
||||
if (ptr->next == NULL || current == (row - 1)) {
|
||||
printf("%s", ptr->line);
|
||||
fflush(0);
|
||||
}
|
||||
else
|
||||
printf("%s\n", ptr->line);
|
||||
|
||||
ptr = ptr->next;
|
||||
current++;
|
||||
}
|
||||
}
|
||||
|
||||
void top(void) {
|
||||
if (getuid() == 0)
|
||||
firemon_drop_privs();
|
||||
|
||||
while (1) {
|
||||
// clear linked list
|
||||
head_clear();
|
||||
|
||||
// set pid table
|
||||
int i;
|
||||
int itv = 5; // 5 second interval
|
||||
pid_read(0);
|
||||
|
||||
// start cpu measurements
|
||||
unsigned utime = 0;
|
||||
unsigned stime = 0;
|
||||
for (i = 0; i < max_pids; i++) {
|
||||
if (pids[i].level == 1)
|
||||
pid_store_cpu(i, 0, &utime, &stime);
|
||||
}
|
||||
|
||||
// wait 5 seconds
|
||||
firemon_sleep(itv);
|
||||
|
||||
// grab screen size
|
||||
struct winsize sz;
|
||||
int row = 24;
|
||||
int col = 80;
|
||||
if (!ioctl(0, TIOCGWINSZ, &sz)) {
|
||||
col = sz.ws_col;
|
||||
row = sz.ws_row;
|
||||
}
|
||||
|
||||
// start printing
|
||||
firemon_clrscr();
|
||||
char *header = get_header();
|
||||
if (strlen(header) > col)
|
||||
header[col] = '\0';
|
||||
printf("%s\n", header);
|
||||
if (row > 0)
|
||||
row--;
|
||||
free(header);
|
||||
|
||||
// find system uptime
|
||||
FILE *fp = fopen("/proc/uptime", "r");
|
||||
if (fp) {
|
||||
float f;
|
||||
int rv = fscanf(fp, "%f", &f);
|
||||
(void) rv;
|
||||
sysuptime = (unsigned long long) f;
|
||||
fclose(fp);
|
||||
}
|
||||
|
||||
// print processes
|
||||
for (i = 0; i < max_pids; i++) {
|
||||
if (pids[i].level == 1) {
|
||||
float cpu = 0;
|
||||
int cnt = 0; // process count
|
||||
char *line = print_top(i, 0, &utime, &stime, itv, &cpu, &cnt);
|
||||
if (line)
|
||||
head_add(cpu, line);
|
||||
}
|
||||
}
|
||||
head_print(col, row);
|
||||
}
|
||||
}
|
||||
|
||||
36
src/firemon/tree.c
Normal file
36
src/firemon/tree.c
Normal file
|
|
@ -0,0 +1,36 @@
|
|||
/*
|
||||
* Copyright (C) 2014, 2015 netblue30 (netblue30@yahoo.com)
|
||||
*
|
||||
* 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 "firemon.h"
|
||||
|
||||
void tree(pid_t pid) {
|
||||
if (getuid() == 0)
|
||||
firemon_drop_privs();
|
||||
|
||||
pid_read(pid); // include all processes
|
||||
|
||||
// print processes
|
||||
int i;
|
||||
for (i = 0; i < max_pids; i++) {
|
||||
if (pids[i].level == 1)
|
||||
pid_print_tree(i, 0, arg_nowrap);
|
||||
}
|
||||
printf("\n");
|
||||
}
|
||||
|
||||
77
src/firemon/usage.c
Normal file
77
src/firemon/usage.c
Normal file
|
|
@ -0,0 +1,77 @@
|
|||
/*
|
||||
* Copyright (C) 2014, 2015 netblue30 (netblue30@yahoo.com)
|
||||
*
|
||||
* 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 "firemon.h"
|
||||
|
||||
void usage(void) {
|
||||
printf("firemon - version %s\n", VERSION);
|
||||
printf("Usage: firemon [OPTIONS] [PID]\n\n");
|
||||
printf("Monitor processes started in a Firejail sandbox. Without any PID specified,\n");
|
||||
printf("all processes started by Firejail are monitored. Descendants of these processes\n");
|
||||
printf("are also being monitored.\n\n");
|
||||
printf("Options:\n");
|
||||
printf("\t--arp - print ARP table for each sandbox.\n\n");
|
||||
printf("\t--caps - print capabilities configuration for each sandbox.\n\n");
|
||||
printf("\t--cgroup - print control group information for each sandbox.\n\n");
|
||||
printf("\t--cpu - print CPU affinity for each sandbox.\n\n");
|
||||
printf("\t--help, -? - this help screen.\n\n");
|
||||
printf("\t--interface - print network interface information for each sandbox.\n\n");
|
||||
printf("\t--list - list all sandboxes.\n\n");
|
||||
printf("\t--name=name - print information only about named sandbox.\n\n");
|
||||
printf("\t--netstats - monitor network statistics for sandboxes creating a new\n");
|
||||
printf("\t\tnetwork namespace.\n\n");
|
||||
printf("\t--route - print route table for each sandbox.\n\n");
|
||||
printf("\t--seccomp - print seccomp configuration for each sandbox.\n\n");
|
||||
printf("\t--tree - print a tree of all sandboxed processes.\n\n");
|
||||
printf("\t--top - monitor the most CPU-intensive sandboxes.\n\n");
|
||||
printf("\t--version - print program version and exit.\n\n");
|
||||
|
||||
printf("Without any options, firemon monitors all fork, exec, id change, and exit events\n");
|
||||
printf("in the sandbox. Monitoring a specific PID is also supported.\n\n");
|
||||
|
||||
printf("Option --list prints a list of all sandboxes. The format for each entry is as\n");
|
||||
printf("follows:\n\n");
|
||||
printf("\tPID:USER:Command\n\n");
|
||||
|
||||
printf("Option --tree prints the tree of processes running in the sandbox. The format\n");
|
||||
printf("for each process entry is as follows:\n\n");
|
||||
printf("\tPID:USER:Command\n\n");
|
||||
|
||||
printf("Option --top is similar to the UNIX top command, however it applies only to\n");
|
||||
printf("sandboxes. Listed below are the available fields (columns) in alphabetical\n");
|
||||
printf("order:\n\n");
|
||||
printf("\tCommand - command used to start the sandbox.\n");
|
||||
printf("\tCPU%% - CPU usage, the sandbox share of the elapsed CPU time since the\n");
|
||||
printf("\t last screen update\n");
|
||||
printf("\tPID - Unique process ID for the task controlling the sandbox.\n");
|
||||
printf("\tPrcs - number of processes running in sandbox, including the controlling\n");
|
||||
printf("\t process.\n");
|
||||
printf("\tRES - Resident Memory Size (KiB), sandbox non-swapped physical memory.\n");
|
||||
printf("\t It is a sum of the RES values for all processes running in the\n");
|
||||
printf("\t sandbox.\n");
|
||||
printf("\tSHR - Shared Memory Size (KiB), it reflects memory shared with other\n");
|
||||
printf("\t processes. It is a sum of the SHR values for all processes running\n");
|
||||
printf("\t in the sandbox, including the controlling process.\n");
|
||||
printf("\tUptime - sandbox running time in hours:minutes:seconds format.\n");
|
||||
printf("\tUser - The owner of the sandbox.\n");
|
||||
printf("\n");
|
||||
printf("License GPL version 2 or later\n");
|
||||
printf("Homepage: http://firejail.sourceforge.net\n");
|
||||
printf("\n");
|
||||
}
|
||||
69
src/fshaper/fshaper.sh
Executable file
69
src/fshaper/fshaper.sh
Executable file
|
|
@ -0,0 +1,69 @@
|
|||
#!/bin/bash
|
||||
|
||||
usage() {
|
||||
echo "Usage:"
|
||||
echo " fshaper.sh --status"
|
||||
echo " fshaper.sh --clear device"
|
||||
echo " fshaper.sh --set device download-speed upload-speed"
|
||||
}
|
||||
|
||||
if [ "$1" = "--status" ]; then
|
||||
/sbin/tc -s qdisc ls
|
||||
/sbin/tc -s class ls
|
||||
exit
|
||||
fi
|
||||
|
||||
if [ "$1" = "--clear" ]; then
|
||||
if [ $# -ne 2 ]; then
|
||||
echo "Error: invalid command"
|
||||
usage
|
||||
exit
|
||||
fi
|
||||
|
||||
DEV=$2
|
||||
echo "Removing bandwith limits"
|
||||
/sbin/tc qdisc del dev $DEV root 2> /dev/null > /dev/null
|
||||
/sbin/tc qdisc del dev $DEV ingress 2> /dev/null > /dev/null
|
||||
exit
|
||||
|
||||
fi
|
||||
|
||||
if [ "$1" = "--set" ]; then
|
||||
DEV=$2
|
||||
echo "Removing bandwith limit"
|
||||
/sbin/tc qdisc del dev $DEV ingress #2> /dev/null > /dev/null
|
||||
|
||||
if [ $# -ne 4 ]; then
|
||||
echo "Error: missing parameters"
|
||||
usage
|
||||
exit
|
||||
fi
|
||||
|
||||
DEV=$2
|
||||
echo "Configuring interface $DEV "
|
||||
|
||||
IN=$3
|
||||
IN=$((${IN} * 8))
|
||||
echo "Download speed ${IN}kbps"
|
||||
|
||||
OUT=$4
|
||||
OUT=$((${OUT} * 8))
|
||||
echo "Upload speed ${OUT}kbps"
|
||||
|
||||
echo "cleaning limits"
|
||||
/sbin/tc qdisc del dev $DEV root 2> /dev/null > /dev/null
|
||||
/sbin/tc qdisc del dev $DEV ingress 2> /dev/null > /dev/null
|
||||
|
||||
echo "configuring tc ingress"
|
||||
/sbin/tc qdisc add dev $DEV handle ffff: ingress #2> /dev/null > /dev/null
|
||||
/sbin/tc filter add dev $DEV parent ffff: protocol ip prio 50 u32 match ip src \
|
||||
0.0.0.0/0 police rate ${IN}kbit burst 10k drop flowid :1 #2> /dev/null > /dev/null
|
||||
|
||||
echo "configuring tc egress"
|
||||
/sbin/tc qdisc add dev $DEV root tbf rate ${OUT}kbit latency 25ms burst 10k #2> /dev/null > /dev/null
|
||||
exit
|
||||
fi
|
||||
|
||||
echo "Error: missing parameters"
|
||||
usage
|
||||
exit 1
|
||||
24
src/ftee/Makefile.in
Normal file
24
src/ftee/Makefile.in
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
all: ftee
|
||||
|
||||
PREFIX=@prefix@
|
||||
VERSION=@PACKAGE_VERSION@
|
||||
NAME=@PACKAGE_NAME@
|
||||
|
||||
H_FILE_LIST = $(wildcard *.[h])
|
||||
C_FILE_LIST = $(wildcard *.c)
|
||||
OBJS = $(C_FILE_LIST:.c=.o)
|
||||
BINOBJS = $(foreach file, $(OBJS), $file)
|
||||
CFLAGS += -ggdb -O2 -DVERSION='"$(VERSION)"' -DPREFIX='"$(PREFIX)"' -fstack-protector-all -D_FORTIFY_SOURCE=2 -fPIE -pie -Wformat -Wformat-security
|
||||
LDFLAGS += -pie -Wl,-z,relro -Wl,-z,now -lpthread
|
||||
|
||||
%.o : %.c $(H_FILE_LIST)
|
||||
$(CC) $(CFLAGS) $(INCLUDE) -c $< -o $@
|
||||
|
||||
ftee: $(OBJS)
|
||||
$(CC) $(LDFLAGS) -o $@ $(OBJS)
|
||||
|
||||
clean:; rm -f *.o ftee
|
||||
|
||||
distclean: clean
|
||||
rm -fr Makefile
|
||||
|
||||
24
src/ftee/ftee.h
Normal file
24
src/ftee/ftee.h
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
/*
|
||||
* Copyright (C) 2014, 2015 netblue30 (netblue30@yahoo.com)
|
||||
*
|
||||
* 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.
|
||||
*/
|
||||
#ifndef FTEE_H
|
||||
#define FTEE_H
|
||||
#include "../include/common.h"
|
||||
|
||||
#endif
|
||||
228
src/ftee/main.c
Normal file
228
src/ftee/main.c
Normal file
|
|
@ -0,0 +1,228 @@
|
|||
#include "ftee.h"
|
||||
#include <errno.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
#define MAXBUF 512
|
||||
|
||||
static unsigned char buf[MAXBUF];
|
||||
|
||||
static FILE *out_fp = NULL;
|
||||
static int out_cnt = 0;
|
||||
static int out_max = 500 * 1024;
|
||||
|
||||
static void log_close(void) {
|
||||
if (out_fp) {
|
||||
fclose(out_fp);
|
||||
out_fp = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
static void log_rotate(const char *fname) {
|
||||
struct stat s;
|
||||
int index = strlen(fname);
|
||||
char *name1 = malloc(index + 2 + 1);
|
||||
char *name2 = malloc(index + 2 + 1);
|
||||
if (!name1 || !name2)
|
||||
errExit("malloc");
|
||||
strcpy(name1, fname);
|
||||
strcpy(name2, fname);
|
||||
fflush(0);
|
||||
|
||||
// delete filename.5
|
||||
sprintf(name1 + index, ".5");
|
||||
if (stat(name1, &s) == 0) {
|
||||
int rv = unlink(name1);
|
||||
if (rv == -1)
|
||||
perror("unlink");
|
||||
}
|
||||
|
||||
// move files 1 to 4 down one position
|
||||
sprintf(name2 + index, ".4");
|
||||
if (stat(name2, &s) == 0) {
|
||||
int rv = rename(name2, name1);
|
||||
if (rv == -1)
|
||||
perror("rename");
|
||||
}
|
||||
|
||||
sprintf(name1 + index, ".3");
|
||||
if (stat(name1, &s) == 0) {
|
||||
int rv = rename(name1, name2);
|
||||
if (rv == -1)
|
||||
perror("rename");
|
||||
}
|
||||
|
||||
sprintf(name2 + index, ".2");
|
||||
if (stat(name2, &s) == 0) {
|
||||
/* coverity[toctou] */
|
||||
int rv = rename(name2, name1);
|
||||
if (rv == -1)
|
||||
perror("rename");
|
||||
}
|
||||
|
||||
sprintf(name1 + index, ".1");
|
||||
if (stat(name1, &s) == 0) {
|
||||
int rv = rename(name1, name2);
|
||||
if (rv == -1)
|
||||
perror("rename");
|
||||
}
|
||||
|
||||
// move the first file
|
||||
if (out_fp)
|
||||
fclose(out_fp);
|
||||
|
||||
out_fp = NULL;
|
||||
if (stat(fname, &s) == 0) {
|
||||
int rv = rename(fname, name1);
|
||||
if (rv == -1)
|
||||
perror("rename");
|
||||
}
|
||||
|
||||
free(name1);
|
||||
free(name2);
|
||||
}
|
||||
|
||||
static void log_write(const unsigned char *str, int len, const char *fname) {
|
||||
assert(fname);
|
||||
|
||||
if (out_fp == NULL) {
|
||||
out_fp = fopen(fname, "w");
|
||||
if (!out_fp) {
|
||||
fprintf(stderr, "Error: cannot open log file %s\n", fname);
|
||||
exit(1);
|
||||
}
|
||||
out_cnt = 0;
|
||||
}
|
||||
|
||||
// rotate files
|
||||
out_cnt += len;
|
||||
if (out_cnt >= out_max) {
|
||||
log_rotate(fname);
|
||||
|
||||
// reopen the first file
|
||||
if (out_fp)
|
||||
fclose(out_fp);
|
||||
out_fp = fopen(fname, "w");
|
||||
if (!out_fp) {
|
||||
fprintf(stderr, "Error: cannot open log file %s\n", fname);
|
||||
exit(1);
|
||||
}
|
||||
out_cnt = len;
|
||||
}
|
||||
|
||||
fwrite(str, len, 1, out_fp);
|
||||
fflush(0);
|
||||
}
|
||||
|
||||
|
||||
// return 1 if the file is a directory
|
||||
static int is_dir(const char *fname) {
|
||||
assert(fname);
|
||||
if (*fname == '\0')
|
||||
return 0;
|
||||
|
||||
// if fname doesn't end in '/', add one
|
||||
int rv;
|
||||
struct stat s;
|
||||
if (fname[strlen(fname) - 1] == '/')
|
||||
rv = stat(fname, &s);
|
||||
else {
|
||||
char *tmp;
|
||||
if (asprintf(&tmp, "%s/", fname) == -1) {
|
||||
fprintf(stderr, "Error: cannot allocate memory, %s:%d\n", __FILE__, __LINE__);
|
||||
exit(1);
|
||||
}
|
||||
rv = stat(tmp, &s);
|
||||
free(tmp);
|
||||
}
|
||||
|
||||
if (rv == -1)
|
||||
return 0;
|
||||
|
||||
if (S_ISDIR(s.st_mode))
|
||||
return 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
// return 1 if the file is a link
|
||||
static int is_link(const char *fname) {
|
||||
assert(fname);
|
||||
if (*fname == '\0')
|
||||
return 0;
|
||||
|
||||
struct stat s;
|
||||
if (lstat(fname, &s) == 0) {
|
||||
if (S_ISLNK(s.st_mode))
|
||||
return 1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
static void usage(void) {
|
||||
printf("Usage: ftee filename\n");
|
||||
}
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
if (argc < 2) {
|
||||
fprintf(stderr, "Error: please provide a filename to store the program output\n");
|
||||
usage();
|
||||
exit(1);
|
||||
}
|
||||
char *fname = argv[1];
|
||||
|
||||
|
||||
// do not accept directories, links, and files with ".."
|
||||
if (strstr(fname, "..") || is_link(fname) || is_dir(fname)) {
|
||||
fprintf(stderr, "Error: invalid output file. Links, directories and files with \"..\" are not allowed.\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
struct stat s;
|
||||
if (stat(fname, &s) == 0) {
|
||||
// check permissions
|
||||
if (s.st_uid != getuid() || s.st_gid != getgid()) {
|
||||
fprintf(stderr, "Error: the output file needs to be owned by the current user.\n");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
// check hard links
|
||||
if (s.st_nlink != 1) {
|
||||
fprintf(stderr, "Error: no hard links allowed.\n");
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
// check if we can append to this file
|
||||
/* coverity[toctou] */
|
||||
FILE *fp = fopen(fname, "a");
|
||||
if (!fp) {
|
||||
fprintf(stderr, "Error: cannot open output file %s\n", fname);
|
||||
exit(1);
|
||||
}
|
||||
fclose(fp);
|
||||
|
||||
|
||||
// preserve the last log file
|
||||
log_rotate(fname);
|
||||
|
||||
setvbuf (stdout, NULL, _IONBF, 0);
|
||||
while(1) {
|
||||
int n = read(0, buf, sizeof(buf));
|
||||
if (n < 0 && errno == EINTR)
|
||||
continue;
|
||||
if (n <= 0)
|
||||
break;
|
||||
|
||||
fwrite(buf, n, 1, stdout);
|
||||
log_write(buf, n, fname);
|
||||
}
|
||||
|
||||
log_close();
|
||||
return 0;
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show more
Loading…
Add table
Add a link
Reference in a new issue