This page describes methods and techniques relevant to package maintainers.
For users and system engineers wishing to improve the security of running Debian systems, start from: SecurityManagement
For package maintainers wishing to sandbox services using systemd, see: ServiceSandboxing
Contents
- Using Hardening Options
-
Validation
- DEB_BUILD_HARDENING_FORMAT (gcc/g++ -Wformat -Wformat-security -Werror=format-security)
- DEB_BUILD_HARDENING_FORTIFY (gcc/g++ -D_FORTIFY_SOURCE=2)
- DEB_BUILD_HARDENING_STACKPROTECTOR (gcc/g++ -fstack-protector-strong)
- DEB_BUILD_HARDENING_PIE (gcc/g++ -fPIE -pie)
- DEB_BUILD_HARDENING_RELRO (ld -z relro)
- DEB_BUILD_HARDENING_BINDNOW (ld -z now)
- State of implementation
- Notes on Memory Corruption Mitigation Methods
- Runtime hardening
Using Hardening Options
Several compile-time options (detailed below) can be used to help harden a resulting binary against memory corruption attacks, or provide additional warning messages during compiles. Using "dpkg-buildflags" is the recommended way to incorporate the build flags in Debian.
See ReleaseGoals/SecurityHardeningBuildFlags for additional information.
For a step-by-step guide, see the HardeningWalkthrough.
dpkg-buildflags
To use "dpkg-buildflags", either switch to dh(1) to do builds (requires debhelper compat level >= 9), or use it directly in your builds to set the default compiler and linker flags:
CPPFLAGS:=$(shell dpkg-buildflags --get CPPFLAGS) CFLAGS:=$(shell dpkg-buildflags --get CFLAGS) CXXFLAGS:=$(shell dpkg-buildflags --get CXXFLAGS) LDFLAGS:=$(shell dpkg-buildflags --get LDFLAGS) hello.o: hello.c $(CC) $(CPPFLAGS) $(CFLAGS) $(LDFLAGS) -o hello.o hello.c
Or you can use the new buildflags.mk file (dpkg-dev >= 1.16.1~) to set all *FLAGS:
DPKG_EXPORT_BUILDFLAGS = 1 include /usr/share/dpkg/buildflags.mk
buildflags.mk overwrites the *FLAGS, so additions to the flags must happen after the include. Make sure to append to the *FLAGS instead of overwriting them, e.g. use CFLAGS += -Wextra instead of CFLAGS = -Wextra:
DPKG_EXPORT_BUILDFLAGS = 1 include /usr/share/dpkg/buildflags.mk CFLAGS += -Wextra
This also works with DEB_BUILD_MAINT_OPTIONS, just declare it before the include (needs dpkg-dev >= 1.16.1.1):
export DEB_BUILD_MAINT_OPTIONS = hardening=+all DPKG_EXPORT_BUILDFLAGS = 1 include /usr/share/dpkg/buildflags.mk
When building programs that handle untrusted data (parsers, network listeners, etc.), or run with elevated privileges (PAM, X, etc.), please enable "PIE" and "BINDNOW" in the build. The "all" option enables "PIE" and "BINDNOW" and future hardening flags:
export DEB_BUILD_MAINT_OPTIONS = hardening=+all
Enable or disable certain hardening features separately
Each hardening feature can be enabled and disabled in the DEB_BUILD_MAINT_OPTIONS environment variable's hardening value with the "+" and "-" modifier. For example, to enable the "pie" feature and disable the "fortify" feature you can do this in debian/rules:
export DEB_BUILD_MAINT_OPTIONS=hardening=+pie,-fortify
CDBS packages automatically export all dpkg-buildflags (Bug #651964 was fixed). Due to #651966 it used to not be possible to use DEB_BUILD_MAINT_OPTIONS directly. To enable "PIE" and "BINDNOW" use DEB_BUILD_MAINT_OPTIONS in combination with buildflags.mk as explained above.
See the "FEATURE AREAS" section and "Hardening" subsection of the dpkg-buildflags man page for more details.
Notes for packages using QMake
The easiest way is to find the main .pro file, and add those lines:
QMAKE_CPPFLAGS *= $(shell dpkg-buildflags --get CPPFLAGS) QMAKE_CFLAGS *= $(shell dpkg-buildflags --get CFLAGS) QMAKE_CXXFLAGS *= $(shell dpkg-buildflags --get CXXFLAGS) QMAKE_LFLAGS *= $(shell dpkg-buildflags --get LDFLAGS)
(Yes, there is no QMAKE_LDFLAGS.)
Notes for packages using Qbs
Qbs will not just take the $(CFLAGS), $(LDFLAGS) etc variables 'as is'. Some options translate to Qbs properties (like -g (cpp.debugInformation) and -O2 (cpp.optimization:fast)). Others map to cpp.*Flags properties, but syntax changes are required, because it's not shell or make, it's javascript. There is not yet a provided mechanism for translation, so here is a static example:
These dpkg-buildoptions:
CFLAGS=-g -O2 -fdebug-prefix-map=/path/to/package/build/dir=. -fstack-protector-strong -Wformat -Werror=format-security CPPFLAGS=-Wdate-time -D_FORTIFY_SOURCE=2 CXXFLAGS=-g -O2 -fdebug-prefix-map=/path/to/package/build/dir=. -fstack-protector-strong -Wformat -Werror=format-security LDFLAGS=-Wl,-z,relro
map as follows:
qbs config --settings-dir /tmp profiles.debian.cpp.debugInformation true qbs config --settings-dir /tmp profiles.debian.cpp.optimization fast qbs config --settings-dir /tmp profiles.debian.cpp.commonCompilerFlags -Wdate-time qbs config --settings-dir /tmp profiles.debian.cpp.defines '"FORTIFY_SOURCE=2"' qbs config --settings-dir /tmp profiles.debian.cpp.cFlags '[ "-fdebug-prefix-map=$(CURDIR)=.", "-fstack-protector-strong", "-Wformat", "-Werror=format-security" qbs config --settings-dir /tmp profiles.debian.cpp.cxxFlags '[ "-fdebug-prefix-map=$(CURDIR)=.", "-fstack-protector-strong", "-Wformat", "-Werror=format-security" ]' qbs config --settings-dir /tmp profiles.debian.cpp.linkerFlags "-z,relro"
As you can see the -Wl escape must be removed from cpp.Linkerflags, and values with an '=' in need to be double-quoted to avoid the shell messing with them. Things with multiple flag setting need to be entered as lists.
This assumes that a profile 'debian' will be used for the build.
See Qbs for more details.
Notes for packages using CMake
CMake silently ignores CPPFLAGS during build, this renders FORTIFY hardening isn't working as expected if you have just done like above. The question is not going to be easily answered because dpkg-buildflags set the relevant flag (-D_FORTIFY_SOURCE=2) in CPPFLAGS (bug 643632), while some CMake users rely on the behaviour of ignoring CPPFLAGS (comment from CMake bug 12928). The issue has been reported to Debian's CMake maintainers as (bug 653916), but anyway it's hard to resolve.
Debhelper (since 9.20120417, only with compat=9 and dh_auto* commands!) and cdbs (since 0.4.110) automatically append CPPFLAGS to CFLAGS and CXXFLAGS. The following workaround is not necessary anymore if the package uses debhelper (compat=9, dh_auto*) or cdbs (packages might require a new upload though).
A workaround is injecting CPPFLAGS to CFLAGS and CXXFLAGS in debian/rules.
- If you are calling dpkg-buildflags directly to set build flags, then:
CPPFLAGS:=$(shell dpkg-buildflags --get CPPFLAGS) CFLAGS:=$(shell dpkg-buildflags --get CFLAGS) $(CPPFLAGS) CXXFLAGS:=$(shell dpkg-buildflags --get CXXFLAGS) $(CPPFLAGS) LDFLAGS:=$(shell dpkg-buildflags --get LDFLAGS)
- If you are including "/usr/share/dpkg/buildflags.mk" in debian/rules:
DPKG_EXPORT_BUILDFLAGS = 1 include /usr/share/dpkg/buildflags.mk CFLAGS+=$(CPPFLAGS) CXXFLAGS+=$(CPPFLAGS)
Notes for packages using Vala
Use the following line, to tell the Vala compiler, that it should forward CPPFLAGS, CFLAGS and LDFLAGS to the C compiler:
VALAFLAGS:=$(foreach w,$(CPPFLAGS) $(CFLAGS) $(LDFLAGS),-X $(w))
Validation
To verify that the resulting binary does, in fact, have hardening features enabled, you can use "hardening-check" from the "devscripts" package to test each ELF binary:
$ hardening-check /usr/sbin/sshd /usr/sbin/sshd: Position Independent Executable: yes Stack protected: yes Fortify Source functions: yes (some protected functions found) Read-only relocations: yes Immediate binding: yes
If your binary does not make use of character arrays on the stack, it's possible that "Stack protected" will report "no", since there was no stack it found to protect. If you absolutely want to protect all stacks, you can add "-fstack-protector-all", but this tends not to be needed, and there are some trade-offs on speed.
If your binary does not make use of FORTIFY_SOURCE-protected glibc routines, it's possible that "Fortify Source functions" will report "no", since there were no functions used that included the glibc fortification routines.
DEB_BUILD_HARDENING_FORMAT (gcc/g++ -Wformat -Wformat-security -Werror=format-security)
Quoting the gcc man page:
If -Wformat is specified, also warn about uses of format functions that represent possible security problems. At present, this warns about calls to printf and scanf functions where the format string is not a string literal and there are no format arguments, as in printf (foo);. This may be a security hole if the format string came from untrusted input and contains %n.
Default compile:
$ make trivial cc -Wall -O2 trivial.c -o trivial
Hardened compile:
$ DEB_BUILD_HARDENING=1 make trivial cc -Wall -O2 trivial.c -o trivial trivial.c: In function 'main': trivial.c:16: warning: format not a string literal and no format arguments
Known problems: (Common build failures, non-availability on some archs)
- None so far.
DEB_BUILD_HARDENING_FORTIFY (gcc/g++ -D_FORTIFY_SOURCE=2)
During code generation the compiler knows a great deal of information about buffer sizes (where possible), and attempts to replace insecure unlimited length buffer function calls with length-limited ones. This is especially useful for old, crufty code. Additionally, format strings in writable memory that contain '%n' are blocked. If an application depends on such a format string, it will need to be worked around.
Note that for this feature to be fully enabled, the source must also be compiled with -O1 or higher.
Default build:
$ make trivial cc -Wall -O2 trivial.c -o trivial $ ./trivial $(perl -e 'print "A"x100') Your first argument was: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA Segmentation fault (core dumped)
Hardened build:
$ DEB_BUILD_HARDENING=1 make trivial cc -Wall -O2 trivial.c -o trivial trivial.c: In function 'main': trivial.c:16: warning: format not a string literal and no format arguments $ ./trivial $(perl -e 'print "A"x100') *** buffer overflow detected ***: ./trivial terminated
Known problems: (Common build failures, non-availability on some archs)
Code compiled with -Werror and using memcpy/strcpy with qualifier overrides will fail with FORTIFY enabled. See https://launchpad.net/bugs/217481
DEB_BUILD_HARDENING_STACKPROTECTOR (gcc/g++ -fstack-protector-strong)
Stack protector is a mainline GCC feature, which adds safety checks against stack overwrites. This renders many potential code injection attacks into aborting situations. In the best case this turns code injection vulnerabilities into denial of service or into non-issues (depending on the application). Stack-smashing_protection
Prior to GCC 4.9, `-fstack-protector --param ssp-buffer-size=4' is used to cover functions that defines a 4 or more byte local character array, which is an okay balance for security and performance. For those who want to protect all the functions then -fstack-protector-all is recommended.
Since GCC 4.9, -fstack-protector-strong, an improved version of -fstack-protector is introduced, which covers all the more paranoid conditions that might lead to a stack overflow but not trade performance like -fstack-protector-all, thus it becomes default.
Default build:
$ make trivial cc -Wall -O2 trivial.c -o trivial $ ./trivial $(perl -e 'print "A"x100') Your first argument was: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA Segmentation fault (core dumped)
Hardened build (with FORTIFY disabled, since it catches the stack overflow before it happens):
$ DEB_BUILD_HARDENING=1 DEB_BUILD_HARDENING_FORTIFY=0 make trivial cc -Wall -O2 trivial.c -o trivial trivial.c: In function 'main': trivial.c:16: warning: format not a string literal and no format arguments $ ./trivial $(perl -e 'print "A"x100') Your first argument was: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA *** stack smashing detected ***: ./trivial terminated
Known problems: (Common build failures, non-availability on some archs)
- Not supported on ia64 and alpha. (disabled for these archs by default in hardening-wrapper 1.8)
warning: -fstack-protector not supported for this target
- Not supported on mips and hppa. (disabled for these archs by default in hardening-wrapper 1.10)
warning: -fstack-protector not supported for this target
DEB_BUILD_HARDENING_PIE (gcc/g++ -fPIE -pie)
Position Independent Executable are needed to take advantage of Address Space Layout Randomization, supported by some kernel versions. While ASLR can already be enforced for data areas in the stack and heap (brk and mmap), the code areas must be compiled as position-independent. Shared libraries already do this (-fPIC), so they gain ASLR automatically, but binary .text regions need to be build PIE to gain ASLR. When this happens, ROP attacks are much harder since there are no static locations to bounce off of during a memory corruption attack.
Default build:
$ make trivial cc -Wall -O2 trivial.c -o trivial $ file trivial trivial: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), for GNU/Linux 2.6.8, dynamically linked (uses shared libs), not stripped
Hardened build:
$ DEB_BUILD_HARDENING=1 make trivial cc -Wall -O2 trivial.c -o trivial trivial.c: In function 'main': trivial.c:16: warning: format not a string literal and no format arguments $ file trivial trivial: ELF 64-bit LSB shared object, x86-64, version 1 (SYSV), for GNU/Linux 2.6.8, not stripped
Known problems: (Common build failures, non-availability on some archs)
- Doesn't work on hppa and m68k (disabled for these archs by default in hardening-wrapper 1.6)
- Doesn't work on some assembler, due to lack of registers. e.g.:
vf_decimate.c:26: error: can't find a register in class 'BREG' while reloading 'asm'
DEB_BUILD_HARDENING_RELRO (ld -z relro)
During program load, several ELF memory sections need to be written to by the linker, but can be turned read-only before turning over control to the program. This prevents some GOT (and .dtors) overwrite attacks, but at least the part of the GOT used by the dynamic linker (.got.plt) is still vulnerable.
Default build:
$ make trivial cc -Wall -O2 trivial.c -o trivial $ objdump -x trivial | grep RELRO
Hardened build:
$ DEB_BUILD_HARDENING=1 make trivial cc -Wall -O2 trivial.c -o trivial trivial.c: In function 'main': trivial.c:16: warning: format not a string literal and no format arguments $ objdump -x trivial | grep RELRO RELRO off 0x0000000000000de8 vaddr 0x0000000000200de8 paddr 0x0000000000200de8 align 2**0
Known problems: (Common build failures, non-availability on some archs)
- Unimplemented on ia64 (ld silently ignores the option).
DEB_BUILD_HARDENING_BINDNOW (ld -z now)
During program load, all dynamic symbols are resolved, allowing for the complete GOT to be marked read-only (due to -z relro above). This prevents GOT overwrite attacks. For very large application, this can incur some performance loss during initial load while symbols are resolved, but this shouldn't be an issue for daemons.
Default build:
$ make trivial cc -Wall -O2 trivial.c -o trivial $ readelf -d trivial | grep BIND
Hardened build:
$ DEB_BUILD_HARDENING=1 make trivial cc -Wall -O2 trivial.c -o trivial trivial.c: In function 'main': trivial.c:16: warning: format not a string literal and no format arguments $ readelf -d trivial | grep BIND 0x0000000000000018 (BIND_NOW)
Known problems: (Common build failures, non-availability on some archs)
- None.
State of implementation
An email was sent to the debian-devel-announce list in 2006 introducing the hardening-wrapper package and describing the way to integrate this hardening features in Debian.
A discussion has been fired on the debian-gcc list, as well as several bugs (bug 552688, bug 489771) reported to decide the best way to enable hardening compiler options.
Packages that have a bug report asking for the inclusion of this features can be found on the bug tracker.
A list of packages including hardening-wrapper and hardening-includes in their build-deps can be retrieved with the folowing commands:
- reverse-build-depends --only-main --distribution unstable hardening-wrapper
- reverse-build-depends --only-main --distribution unstable hardening-includes
After their meeting on the 14-16 January 2011, the debian security team announced in an email they intend to push the inclusion of hardening features for the wheezy release. A Birds of a Feather-session will be organized during the 2011 debconf to setup a process.
Notes on Memory Corruption Mitigation Methods
User Space
Stack Protector
gcc's -fstack-protector attempts to detect when a stack has been overwritten and aborts the program. Ubuntu has had this enabled by default since Edgy. Some programs do not play nice with it, and can be worked around with -fno-stack-protector. It would be nice to enable this by default, and for gcc to only attempt to use it when libc is being linked against.
Already done in sendmail.
heap protection
In glibc2.5, no additional work needed.
libc pointer encryption
In mainline glibc, as PTR_MANGLE.
gcc -D_FORTIFY_SOURCE=2 -O1
Compile-time protection against static sized buffer overflows. No known regressions or performance loss. This should be enabled system-wide
gcc -Wformat -Wformat-security
While not all programs correctly implement the printf hints (like glib's G_GNUC_PRINTF macro), adding this will at least call out simple printf format string vulnerabilities. Any programs whose builds become "noisy" as a result, should be fixed anyway.
gcc -pie -fPIE
This is especially difficult to plumb into packaging in a safe way, since it requires the executable be built with -fPIE for any .o files that are linked at the end with -pie. There is some amount of performance loss, but only due to the -fPIE, which is already true for all the linked libraries (via their -fPIC).
Already done with openssh, sendmail.
ld -z relro
(Or via gcc with -Wl,-z,relro) Already done with sendmail.
ld -z now
(Or via gcc with -Wl,-z,now).
Kernel Space
non-exec memory segmentation (ExecShield)
Stops execution of code in heap/stack. i386 specific (nx already does this for amd64), and introduces some small level of performance loss (5% for CPU-bound). Some people have worked on getting it pushed into the mainline kernel. Current state unknown -- would be very handy to have due to the popularity of i386. Marcus Better may be willing to continue to maintain the patchset for Debian.
Some applications appear to break when run in the protected memory layout. Most of these issues should be fixed due to RH (and SUSE?) already running with these protections.
Additional work for user-space is identifying programs that build assembly but fail to explicitly mark their stack as non-exec (gnupg, for example).
-fstack-protector
Is available for amd64 builds:
- config CC_STACKPROTECTOR
runtime memory allocation validation
Detect double-frees in kernel space. No idea where it stands.
Address Space Layout Randomization
- mmap: in mainline
- stack: in mainline
- vdso: in since 2.6.18 (COMPAT_VDSO disables it)
- heap/exec: in -mm, 2.6.24
- brk: 2.6.25
Having heap/exec ASLR is a prerequisite for -pie being useful. Presently, openssh is compiled with -pie.
/proc/$pid/maps protection
Present in 2.6.22; requires sysctl toggle (kernel.maps_protect = 1). Became non-optional in 2.6.27
/dev/mem protection
Included in 2.6.25.
link protections
In Linux 3.6, and enabled by default! https://git.kernel.org/?p=linux/kernel/git/torvalds/linux.git;a=commitdiff;h=800179c9b8a1e796e441674776d11cd4c05d61d7
in sysctl via protected_hardlinks and protected_symlinks
From the GRSecurity patchset, protections against hardlink/symlink creation/following in world-writable areas. (Solves tmp races.) May potentially break things like postfix that manipulation hardlinks? Breaks POSIX. Getting taken in mainline may be possible with a build-time or proc toggle.
https://lkml.org/lkml/2005/3/10/101 https://lkml.org/lkml/2005/4/18/167
chroot, dmesg, fifo protections
Also from GRSecurity patchset.
Documentation
http://insecure.org/sploits/non-executable.stack.problems.html
http://www.coresecurity.com/files/attachments/Richarte_Stackguard_2002.pdf
http://www.redhat.com/archives/fedora-tools-list/2004-September/msg00002.html
http://www.gentoo.org/proj/en/hardened/hardened-toolchain.xml
http://labs.mwrinfosecurity.com/notices/assessing_the_tux_strength_part_2_into_the_kernel/
Runtime hardening
Mounting /proc with hidepid
/proc filesystem can be mounted with the hidepid option in order to limit exposure of /proc/<pid> files
hidepid=0 is the default and has the same behavior as previously
hidepid=1 means users can only access their own processes /proc/<pid>/ folders
hidepid=2 means users can only see their own processes /proc/<pid>
Privileged processes (with CAP_SYS_PTRACE) are not subject to these restrictions.
Interaction with systemd-logind
systemd-logind needs access to processes information for managing permissions. systemd-logind runs as root but drops CAP_SYS_PTRACE so is not authorized to see processes information. A symptom of this problem is that users are not authorized to mount USB keys.
In order to fix this, /proc can be mounted with the gid= option (which indicates a group with specific permissions to read processes information) and systemd-login started with supplementary groups. This can be achieved with a /etc/systemd/system/systemd-logind.service.d/hidepid.conf file with the following content:
[Service] SupplementaryGroups=<gid>
Interaction with polkitd
polkitd from policykit-1 0.105 runs as root with CAP_SYS_PTRACE and has access to /proc/*/status.
polkitd from policykit-1 0.115 starts as root but then drops privileges to the polkitd users and nogroup groups, so it loses access to /proc/*/status which is also required to check permissions (for example for shutdown/restart of the computer).
In order to fix this, it is not possible to use the same snippet as systemd-logind (since polkitd will do the user change, not systemd). One solution is to add the polkitd user to the relevant group:
adduser polkitd <gid>