[ 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
Chapter 9 - Developer's Best Practices for OS Security


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).


9.1 Best practices for security review and design

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:

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.


9.2 Creating users and groups for software daemons

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:

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


Version: 3.12, Mon, 07 Jan 2008 19:32:39 +0100

Javier Fernández-Sanguino Peña [email protected]
Authors, Section 1.1