1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311
|
<!-- CVS revision of this document "$Revision: 1.4 $" -->
<chapt>Developer's Best Practices for OS Security
<!-- This chapter is based on the patch I submitted to the Developer's
Reference, see #337086: [BPP] Best practices for security design and review -->
<p>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 <url id="http://www.dwheeler.com/secure-programs/"
name="Secure Programming for Linux and Unix HOWTO"> and
<url id="http://www.securecoding.org" name="Secure Coding: Principles and Practices">
by Mark G. Graff and Kenneth R. van Wyk (O'Reilly, 2003).
<sect id="bpp-devel-design">
<heading>Best practices for security review and design</heading>
<p>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.</p>
<p>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 <strong>sixty</strong> 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.
<p>The programming bugs
which lead to security bugs typically include: <url
id="http://en.wikipedia.org/wiki/Buffer_overflow" name="buffer
overflows">,
format string overflows,
heap overflows and
integer overflows (in C/C++ programs), temporary <url
id="http://en.wikipedia.org/wiki/Symlink_race" name="symlink race
conditions"> (in scripts), <url
id="http://en.wikipedia.org/wiki/Directory_traversal" name="directory
traversal"> and command injection (in servers) and <url
id="http://en.wikipedia.org/wiki/Cross_site_scripting"
name="cross-site scripting">, and <url
id="http://en.wikipedia.org/wiki/SQL_injection" name="SQL
injection bugs"> (in the case of web-oriented applications).
For a more complete information on security bugs review
Fortify's <url id="http://vulncat.fortifysoftware.com/" name="Taxonomy of
Software Security Errors">.
</p>
<p>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
<tt>grep -r "/tmp/" .</tt>. Those calls can be reviewed and replace
the hardcoded filenames using temporary directories to calls to either
<prgn>mktemp</prgn> or <prgn>tempfile</prgn> in shell
scripts, <manref name="File::Temp" section="3perl"> in Perl scripts,
or <manref name="tmpfile" section="3"> in C/C++.</p>
<p>There are a set of tools available to assist to the security code review phase.
These include <package>rats</package>, <package>flawfinder</package> and
<package>pscan</package>. For more information, read the
<url id="http://www.debian.org/security/audit/tools" name="list of tools used
by the Debian Security Audit Team">.
<p>When packaging software developers have to make sure that they follow
common security principles, including:
<list>
<item>The software runs with the minimum privileges it needs:
<list>
<item>The package does install binaries setuid or setgid.
<prgn>Lintian</prgn> will warn of <url id="
http://lintian.debian.org/reports/Tsetuid-binary.html" name="setuid">,
<url id="http://lintian.debian.org/reports/Tsetgid-binary.html"
name="setgid"> and <url
id="http://lintian.debian.org/reports/Tsetuid-gid-binary.html"
name="setuid and setgid"> binaries.
<item>The daemons the package provide run with a
low privilege user (see <ref id="bpp-lower-privs">)
</list>
<item>Programmed (i.e., <prgn>cron</prgn>) tasks running in the
system do NOT run as root or, if they do, do not implement complex
tasks.
</list>
<p>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 <url
id="http://www.debian.org/security/audit/" name="Debian Security Audit
team">. In the case of setuid/setgid binaries, follow the Debian
policy section regarding
<url id="http://www.debian.org/doc/debian-policy/ch-files.html#s10.9"
name="permissions and owners">
</p>
<p>For more information, specific to secure programming, make sure you
read (or point your upstream to) <url
id="http://www.dwheeler.com/secure-programs/" name="Secure Programming
for Linux and Unix HOWTO"> and the <url
id="https://buildsecurityin.us-cert.gov/portal/" name="Build Security
In"> portal.
</p>
</sect>
<!-- This should be explained here until #291177 gets fixed and this is
added to poliy -->
<sect id="bpp-lower-privs">
<heading>Creating users and groups for software daemons
<p>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
<package>base-passwd</package>, for a list of static users in Debian
see <ref id="faq-os-users">) and dynamic uids in the range assigned
to system users.
<p>In the first case, you need to ask for a user or group id to the
<package>base-passwd</package>. Once the user is available there
the package needs to be distributed including a proper versioned depends to the
<package>base-passwd</package> package.
<p>In the second case, you need to create the system user either in
the <em>preinst</em> or in the <em>postinst</em> and make the package
depend on <tt>adduser (>= 3.11)</tt>.
<p>The following example code creates the user and group the daemon
will run as when the package is installed or upgraded:
<example>
[...]
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/<var>packagename</var>" ] && . /etc/default/<var>packagename</var>
# Sane defaults:
[ -z "$SERVER_HOME" ] && SERVER_HOME=<var>server_dir</var>
[ -z "$SERVER_USER" ] && SERVER_USER=<var>server_user</var>
[ -z "$SERVER_NAME" ] && SERVER_NAME="<var>Server description</var>"
[ -z "$SERVER_GROUP" ] && SERVER_GROUP=<var>server_group</var>
# 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)
[...]
</example>
<p>You have to make sure that the init.d script file:
<list>
<item>Starts the daemon dropping privileges: if the software does not
do the <manref name="setuid" section="2"> or <manref name="seteuid"
section="2"> call itself, you can use the <tt>--chuid</tt>
call of <prgn>start-stop-daemon</prgn>.
<item>Stops the daemon only if the user id matches, you can use the
<prgn>start-stop-daemon</prgn> <tt>--user</tt> option
for this.
<item>Does not run if either the user or the group do not exist:
<example>
if ! getent passwd | grep -q "^<var>server_user</var>:"; then
echo "Server user does not exist. Aborting" >&2
exit 1
fi
if ! getent group | grep -q "^<var>server_group</var>:" ; then
echo "Server group does not exist. Aborting" >&2
exit 1
fi
</example>
</list>
<p>If the package creates the system user it can remove it when it is
purged in its <em>postrm</em>. 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<footnote>Some relevant threads discussing these drawbacks
include <url id="http://lists.debian.org/debian-mentors/2004/10/msg00338.html">
and <url id="http://lists.debian.org/debian-devel/2004/05/msg01156.html">
</footnote>. 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 <prgn>debconf</prgn>).
<p>The following example code<footnote><p>This might eventually
be introduced as a <prgn>dh_adduser</prgn> in debhelper. See
<url id="http://bugs.debian.org/81697" name="#81967">,
<url id="http://bugs.debian.org/291177" name="#291177"> and
<url id="http://bugs.debian.org/118787" name="#118787">.</p></footnote>
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:
<example>
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="<var>server_user</var>"
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=<var>server_group</var>
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
[...]
</example>
<p>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 <em>least privilege</em>. Also consider you can
limit privileges in programs through other mechanisms besides running as
non-root<footnote><p>You can even provide a SELinux policy for
it</p></footnote>. For more information, read the <url
id="http://www.dwheeler.com/secure-programs/Secure-Programs-HOWTO/minimize-privileges.html"
name="Minimize Privileges"> chapter of the <em>Secure Programming for
Linux and Unix HOWTO</em> book.
</sect>
</chapt>
|