File: developer.sgml

package info (click to toggle)
harden-doc 3.13.3
  • links: PTS, VCS
  • area: main
  • in suites: squeeze
  • size: 8,908 kB
  • ctags: 25
  • sloc: sh: 789; makefile: 174; xml: 105; perl: 86
file content (311 lines) | stat: -rw-r--r-- 12,284 bytes parent folder | download | duplicates (5)
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>