- Best practices for security review and design
-
-When you are packaging software for other users you 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.
-
-You should make your best to review the source code of the package and
-detect issues that might introduce security bugs. The programming bugs
-which lead to security bugs typically include: , , and (in C/C++ programs), temporary (in scripts), and command injection (in servers) and , and (in the case of web-oriented applications).
-
-Some of these issues might not be easy to spot unless you are an
-expert in the programming language the program uses, but some security
-problems are easy to detect and fix. For example, finding temporary
-race conditions in the source code can easily be done by running
-grep -r "/tmp/" . in the source code replace
-hardcoded filenames using temporary directories to calls to either
-mktemp or tempfile in shell
-scripts, in Perl scripts,
-and in C/C++. You can also use
- to assist to the security code review phase.
-
-When packaging software make sure that:
-
-
-
-- The software runs with the minimum privileges it needs:
-
-
-- The package does install binaries setuid or setgid.
-Lintian will warn of ,
- and binaries.
-
-
- The daemons the package provide run with a
-low privilege user (see
[)
-
-]
-
-- 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 . In the case of setuid/setgid binaries, follow the Debian
-policy section regarding
-
-
-
-For more information, specific to secure programming, make sure you
-read (or point your upstream to) and the portal. For more information specific to Debian security you can
-read the
-
-
-
-
-
- System 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) 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, and a proper versioned depends to the
-base-passwd package that provides the user.
-
-
In the second case, you need to create the system user through maintainer
-scripts.
-
-
Running programs with a user with limited privileges makes sure
-that any security issue with the program makes limited damaged to the
-system and follows the principle of least privilege you can
-limit privileges in programs through other mechanisms besides running
-as non-root. Fore more information, read the chapter of the Secure Programming for
-Linux and Unix HOWTO book.
-
-
- Creating system users and groups
-
-If you want to create system groups on package installatino you
-need to create it in either the preinst or in the postinst
-and have 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
- # Notice that the package could handle this defaults through
- # debconf so that the local admin could select a different
- # user name for the system user than the one hardcoded in the
- # package
-
- [ -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.
- # Some daemons might need additional privileges and those can be
- # granted by adding it to additional groups.
- 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 it does not exist
- test -d $SERVER_HOME || mkdir $SERVER_HOME
- # 3. create user if it does not exist
- 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"
- # 4. adjust passwd entry, only do this if the package
- # creates the user
- usermod -c "$SERVER_NAME" \
- -d $SERVER_HOME \
- -g $SERVER_GROUP \
- $SERVER_USER
- else
- # The package might want to check if the user already exists
- # and it is *not* a system user, in this case it should abort
- # the installation (like in this example) or ask the administrator
- # since otherwrise it might have unexpected consequences.
- # Some packages try to prevent collision by using a prefix such as 'Debian-'
- 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
- # Abort package installation if the user has not been created by
- # us.
- if [ -n "$FIST_SYSTEM_UID" ] && [ -n "$LAST_SYSTEM_UID" ]; then
- if USERID=`getent passwd $SERVER_USER | cut -f 3 -d ':'`; then
- if [ -n "$USERID" ]; then
- if [ "$FIST_SYSTEM_UID" -le "$USERID" ] && \
- [ "$USERID" -le "$LAST_SYSTEM_UID" ]; then
- echo "The user $SERVER_USER already exists as a non system user!" >&2
- echo "Aborting package installation" >&2
- exit 1
- fi
- fi
- fi
- fi
- fi
-
- # 5. adjust file and directory permissions
- # The example below sets the server home as 750 as it
- # contains (hypothetically) sensible information.
- 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 | grep -q $ADDGROUP; then
- adduser $SERVER_USER $ADDGROUP
- fi
- fi
- ;;
- configure)
-
-[...]
-
-
-
- Using system users
-
-In order to make use of the system user you have to make sure that the
-init.d script file:
-
-
-- Starts the daemon dropping privileges, if the software does not
-do the or 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
-
-
-
-
-
File ownerships of files shipped by the package will need to be adjusted:
-
-
-- Configuration files should be readable by the system user, if they
-contain sensitive information the system user should not own them unless there
-is a need for it to write to its own configuration files. Typically this means
-that the configuration files are owned by group, belong to the group of the
-system user and are mode 0640.
-
-
- The system user if it generates state files (such as pidfiles) should
-have a directory under /var/run owned by it. This directory should be
-recreated by the init.d script since the state directory might be wiped out
-after a system boot.
-
-
- If the daemon logs directly to /var/log logfiles should be
-writable by the system user but, once rotated, they should not be either owned
-or writable by it to prevent it from overwritting old log entries if a security
-vulnerability in the software were to be used. If the daemon logs to a
-directory under /var/log/ then it should be owned by the system user
-and rotated log files need not be changed ownership.
-
-
-
-
- Removing system users
-
-If the package creates the system user it can remove it when it is
-purged in its postrm script. This currently not recommended
-since it has a few known
-
-Some relevant threads discussing these issues include:
-,
-
-and
-.
-
-drawbacks. For example, files created by the daemon (or by an admin
-impersonating it) either on the local filesystem or in backup files will be
-orphaned and might be taken over by a new system user in the future if it is
-assigned the same uid. On the other hand, an unused local system user can be
-used to access even if the account has been locked (as some authentication
-systems might not use PAM or shadow authentication).
-
-
If you want to remove a system user and there is a possibility of it
-leaving orphaned files, the administrator should be asked for the preferred
-action either when the package is installed or when it is removed (see [).
-
-]The following example code 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
- if [ -r /etc/adduser.conf ] ; then
- 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
- else
- # Sane defaults
- FIRST_SYSTEM_UID=100
- LAST_SYSTEM_UID=499
- fi
- # Remove system account if it is a system user
- 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 is a system group
- CREATEDGROUP=server_group
- if [ -r /etc/adduser.conf ] ; then
- FIRST_USER_GID=`grep ^USERS_GID /etc/adduser.conf | cut -f2 -d '='`
- else
- # Sane defaults
- FIRST_USER_GID=1000
- fi
- 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
-[...]
-
-
-
Other possibilities, are to make sure the account is locked (has an invalid
-password and /bin/false as a shell) and modify the GECOS field
-pointing out that the account is no longer used.
-
-
-
-