[ previous ] [ Contents ] [ 1 ] [ 2 ] [ 3 ] [ 4 ] [ 5 ] [ 6 ] [ 7 ] [ 8 ] [ 9 ] [ 10 ] [ 11 ] [ 12 ] [ A ] [ B ] [ C ] [ D ] [ E ] [ F ] [ G ] [ H ] [ next ]
This chapter introduces some best secure coding practices for developers
writing Debian packages. If you are really interested in secure coding I
recommend you read David Wheeler's Secure Programming for Linux
and Unix HOWTO
and Secure Coding: Principles and
Practices
by Mark G. Graff and Kenneth R. van Wyk (O'Reilly,
2003).
Developers that are packaging software should make a best effort to ensure that the installation of the software, or its use, does not introduce security risks to either the system it is installed on or its users.
In order to do so, they should make their best to review the source code of the package and detect any flaws that might introduce security bugs before releasing the software or distributing a new version. It is acknowledged that the cost of fixing bugs grows for different stages of its development, so it is easier (and cheaper) to fix bugs when designing than when the software has been deployed and is in maintenance mode (some studies say that the cost in this later phase is sixty times higher). Although there are some tools that try to automatically detect these flaws, developers should strive to learn about the different kind of security flaws in order to understand them and be able to spot them in the code they (or others) have written.
The programming bugs which lead to security bugs typically include: buffer
overflows
, format string overflows, heap overflows and integer
overflows (in C/C++ programs), temporary symlink race
conditions
(in scripts), directory
traversal
and command injection (in servers) and cross-site
scripting
, and SQL injection bugs
(in the case of web-oriented applications). For a more complete information on
security bugs review Fortify's Taxonomy of Software Security
Errors
.
Some of these issues might not be easy to spot unless you are an expert in the
programming language the software uses, but some security problems are easy to
detect and fix. For example, finding temporary race conditions due to misuse
of temporary directories can easily be done just by running grep -r
"/tmp/" .. Those calls can be reviewed and replace the
hardcoded filenames using temporary directories to calls to either
mktemp
or tempfile
in shell scripts,
File::Temp(3perl)
in Perl scripts, or tmpfile(3)
in
C/C++.
There are a set of tools available to assist to the security code review phase.
These include rats
, flawfinder
and
pscan
. For more information, read the list of tools used by the
Debian Security Audit Team
.
When packaging software developers have to make sure that they follow common security principles, including:
The software runs with the minimum privileges it needs:
The package does install binaries setuid or setgid. Lintian
will
warn of setuid
,
setgid
and setuid and
setgid
binaries.
The daemons the package provide run with a low privilege user (see Creating users and groups for software daemons, Section 9.2)
Programmed (i.e., cron
) tasks running in the system do NOT run as
root or, if they do, do not implement complex tasks.
If you have to do any of the above make sure the programs that might run with
higher privileges have been audited for security bugs. If you are unsure, or
need help, contact the Debian Security Audit
team
. In the case of setuid/setgid binaries, follow the Debian
policy section regarding permissions
and owners
For more information, specific to secure programming, make sure you read (or
point your upstream to) Secure Programming for Linux
and Unix HOWTO
and the Build Security In
portal.
If your software runs a daemon that does not need root privileges, you need to
create a user for it. There are two kind of Debian users that can be used by
packages: static uids (assigned by base-passwd
, for a list of
static users in Debian see Operating
system users and groups, Section 12.1.12) and dynamic uids in the range
assigned to system users.
In the first case, you need to ask for a user or group id to the
base-passwd
. Once the user is available there the package needs
to be distributed including a proper versioned depends to the
base-passwd
package.
In the second case, you need to create the system user either in the preinst or in the postinst and make the package depend on adduser (>= 3.11).
The following example code creates the user and group the daemon will run as when the package is installed or upgraded:
[...] case "$1" in install|upgrade) # If the package has default file it could be sourced, so that # the local admin can overwrite the defaults [ -f "/etc/default/packagename" ] && . /etc/default/packagename # Sane defaults: [ -z "$SERVER_HOME" ] && SERVER_HOME=server_dir [ -z "$SERVER_USER" ] && SERVER_USER=server_user [ -z "$SERVER_NAME" ] && SERVER_NAME="Server description" [ -z "$SERVER_GROUP" ] && SERVER_GROUP=server_group # Groups that the user will be added to, if undefined, then none. ADDGROUP="" # create user to avoid running server as root # 1. create group if not existing if ! getent group | grep -q "^$SERVER_GROUP:" ; then echo -n "Adding group $SERVER_GROUP.." addgroup --quiet --system $SERVER_GROUP 2>/dev/null ||true echo "..done" fi # 2. create homedir if not existing test -d $SERVER_HOME || mkdir $SERVER_HOME # 3. create user if not existing if ! getent passwd | grep -q "^$SERVER_USER:"; then echo -n "Adding system user $SERVER_USER.." adduser --quiet \ --system \ --ingroup $SERVER_GROUP \ --no-create-home \ --disabled-password \ $SERVER_USER 2>/dev/null || true echo "..done" fi # 4. adjust passwd entry usermod -c "$SERVER_NAME" \ -d $SERVER_HOME \ -g $SERVER_GROUP \ $SERVER_USER # 5. adjust file and directory permissions if ! dpkg-statoverride --list $SERVER_HOME >/dev/null then chown -R $SERVER_USER:adm $SERVER_HOME chmod u=rwx,g=rxs,o= $SERVER_HOME fi # 6. Add the user to the ADDGROUP group if test -n $ADDGROUP then if ! groups $SERVER_USER | cut -d: -f2 | \ grep -qw $ADDGROUP; then adduser $SERVER_USER $ADDGROUP fi fi ;; configure) [...]
You have to make sure that the init.d script file:
Starts the daemon dropping privileges: if the software does not do the
setuid(2)
or seteuid(2)
call itself, you can use the
--chuid call of start-stop-daemon
.
Stops the daemon only if the user id matches, you can use the
start-stop-daemon
--user option for this.
Does not run if either the user or the group do not exist:
if ! getent passwd | grep -q "^server_user:"; then echo "Server user does not exist. Aborting" >&2 exit 1 fi if ! getent group | grep -q "^server_group:" ; then echo "Server group does not exist. Aborting" >&2 exit 1 fi
If the package creates the system user it can remove it when it is purged in
its postrm. This has some drawbacks, however. For example, files
created by it will be orphaned and might be taken over by a new system user in
the future if it is assigned the same uid[62]. Consequently, removing system users on purge is not yet
mandatory and depends on the package needs. If unsure, this action could be
handled by asking the administrator for the prefered action when the package is
installed (i.e. through debconf
).
The following example code[63] removes the user and groups created before only, and only if, the uid is in the range of dynamic assigned system uids and the gid is belongs to a system group:
case "$1" in purge) [...] # find first and last SYSTEM_UID numbers for LINE in `grep SYSTEM_UID /etc/adduser.conf | grep -v "^#"`; do case $LINE in FIRST_SYSTEM_UID*) FIST_SYSTEM_UID=`echo $LINE | cut -f2 -d '='` ;; LAST_SYSTEM_UID*) LAST_SYSTEM_UID=`echo $LINE | cut -f2 -d '='` ;; *) ;; esac done # Remove system account if necessary CREATEDUSER="server_user" if [ -n "$FIST_SYSTEM_UID" ] && [ -n "$LAST_SYSTEM_UID" ]; then if USERID=`getent passwd $CREATEDUSER | cut -f 3 -d ':'`; then if [ -n "$USERID" ]; then if [ "$FIST_SYSTEM_UID" -le "$USERID" ] && \ [ "$USERID" -le "$LAST_SYSTEM_UID" ]; then echo -n "Removing $CREATEDUSER system user.." deluser --quiet $CREATEDUSER || true echo "..done" fi fi fi fi # Remove system group if necessary CREATEDGROUP=server_group FIRST_USER_GID=`grep ^USERS_GID /etc/adduser.conf | cut -f2 -d '='` if [ -n "$FIST_USER_GID" ] then if GROUPGID=`getent group $CREATEDGROUP | cut -f 3 -d ':'`; then if [ -n "$GROUPGID" ]; then if [ "$FIST_USER_GID" -gt "$GROUPGID" ]; then echo -n "Removing $CREATEDGROUP group.." delgroup --only-if-empty $CREATEDGROUP || true echo "..done" fi fi fi fi [...]
Running programs with a user with limited privileges makes sure that any
security issue will not be able to damage the full system. It also follows the
principle of least privilege. Also consider you can limit privileges
in programs through other mechanisms besides running as non-root[64]. For more information, read
the Minimize
Privileges
chapter of the Secure Programming for Linux and Unix
HOWTO book.
[ previous ] [ Contents ] [ 1 ] [ 2 ] [ 3 ] [ 4 ] [ 5 ] [ 6 ] [ 7 ] [ 8 ] [ 9 ] [ 10 ] [ 11 ] [ 12 ] [ A ] [ B ] [ C ] [ D ] [ E ] [ F ] [ G ] [ H ] [ next ]
Securing Debian Manual
[email protected]