1 This document explains the design goals and decisions behind vsftpd.
3 The importance of a secure design
4 =================================
6 In a world full of good, careful coders who do not make mistakes, a secure
7 design would not be necessary. After all, in the absence of any programming
8 errors, security would not differ no matter how the program is arranged.
10 Unfortunately, this is not an ideal world, and coders make plenty of mistakes.
11 Even the careful coders make mistakes. Code auditing is important, and goes
12 some way towards eliminating coding mistakes after the fact. However, we
13 have no guarantee that an audit will catch all the flaws.
15 So, a secure design acknowledges the possibility of undiscovered flaws, and
16 takes steps to minimise the security impact these flaws can have. An obvious
17 example of something we want to do is to apply the principle of least
18 privilege, which ensure that every part of the program runs with the privilege
21 An example of insecure design
22 =============================
24 Examples of insecure design may be found in most other ftpd's. That's one of
25 the reasons vsftpd has been written. We'll pick on wu-ftpd as a specific
26 example, since it is rumoured to run about half of all ftp services.
28 If I log on to wu-ftpd as an anonymous user, a process is run on my behalf to
29 serve my ftp session. Unfortunately, this process typically runs with full
30 root privileges on the remote machine. This means that any security flaw
31 present in parsing the copious ftp protocol will lead to full compromise of
32 that machine. Two concrete examples are the recent wu-ftpd format string bug
33 (June 1999), and a buffer overflow dealing with large paths a few months
36 Even OpenBSD's ftpd-BSD had a format string bug leading to remote root
37 compromise of the affected machine, illustrating an earlier point about the
38 requirement for secure design even in the presence of heavy auditing.
40 Secure design under UNIX
41 ========================
43 vsftpd is written to run under UNIX-like operating systems, and so its secure
44 design is constrained by the facilities offered by UNIX. Ideally, UNIX would
45 have a proper security model which would offer fine grained access control
46 to all system interactions (files, network, etc). It doesn't, but it does
47 offer some useful and often overlooked facilities which help us to implement
48 the principle of least privilege:
50 - Strong inter-process communication facilities
52 In UNIX, the process is a strongly defined boundary. Different privilege
53 credentials may be assigned to different processes, which are not able to
54 interfere with each other. This is a very basic facility of UNIX.
56 It makes sense to use this facility to totally separate parts of a program
57 which do not need to be privileged (most) from those parts that do (typically
60 The privileged and unprivileged parts of the program then communicate via
61 one of many UNIX IPC mechanisms - perhaps a socketpair or IPC (the former
62 is attractive because UNIX lets you pass file handles over a socket).
64 The minimal privileged process exercises the "principle of distrust" - it
65 carefully filters what the unprivileged process asks it to do, so that even
66 if the unprivileged process is compromised, it cannot ask the privileged
67 process to do anything we don't want to allow.
71 chroot() is an often overlooked but useful tool. It can be used very
72 effectively as a damage limitation tool.
74 Imagine a remotely compromised process which does not run as root, but also
75 does not use chroot(). Now look at what the attacker can do. Amongst the worst
76 items are pilfering of all publicly readable files, and also attempting to
77 execute any publicly executable suid-root programs to try and elevate
80 Now imagine the same compromised process with a chroot() to an empty directory.
81 The attackers options to do unpleasant things are substantially diminished.
83 No, chroot() is not the ideal way to do what we have just accomplished, but
84 it is what we have got to work with. In an ideal environment with fine
85 grained security, we would default to having access to _no_ files at all, and
86 deliberately not ask for access to any.
88 - Capabilities (Linux 2.2+)
90 Like chroot(), capabilities are essentially a damage limitation excercise.
91 They are also much less widespread than the other UNIX facilities detailled
92 above. Nonetheless, they warrant mentioning because Linux has them, and they
93 are used in vsftpd because that is the primary devlopment platform.
95 Capabilities split up the all powerful root privilege into lots of sometimes
96 orthogonal privileges. Some of the capabilities represent privileges which
97 are often the basis for requiring a program to run with full root privileges.
98 Examples include CAP_NET_RAW (ping, traceroute) and CAP_NET_BIND_SERVICE
101 By using capabilities to ensure we only have the privilege we need (within
102 the somewhat disappointing granularity they offer), we again limit the
103 potential damage of security holes.
105 Presenting vsftpd's secure design
106 =================================
108 vsftpd employs a secure design. The UNIX facilities outlined above are used
109 to good effect. The design decisions taken are as follows:
111 1) All parsing and acting on potentially malicious remote network data is
112 done in a process running as an unprivileged user. Furthermore, this process
113 runs in a chroot() jail, ensuring only the ftp files area is accessible.
115 2) Any privileged operations are handled in a privileged parent process. The
116 code for this privileged parent process is as small as possible for safety.
118 3) This same privileged parent process receives requests from the unprivileged
119 child over a socket. All requests are distrusted. Here are example requests:
120 - Login request. The child sends username and password. Only if the details
121 are correct does the privileged parent launch a new child with the appropriate
123 - chown() request. The child may request a recently uploaded file gets
124 chown'ed() to root for security purposes. The parent is careful to only allow
125 chown() to root, and only from files owned by the ftp user.
126 - Get privileged socket request. The ftp protocol says we are supposed to
127 emit data connections from port 20. This requires privilege. The privileged
128 parent process creates the privileged socket and passes it to child over
131 4) This same privileged parent process makes use of capabilities and chroot(),
132 to run with the least privilege required. After login, depending on what
133 options have been selected, the privileged parent dynamically calculates what
134 privileges it requires. In some cases, this amounts to no privilege, and the
135 privileged parent just exits, leaving no part of vsftpd running with
138 5) vsftpd-2.0.0 introduces SSL / TLS support using OpenSSL. ALL SSL
139 protocol parsing is performed in a chroot() jail, running under an unprivileged
140 user. This means both pre-authenticated and post-authenticated SSL protocol
141 parsing; it's actually quite hard to do, but vsftpd manages it in the name of
142 being secure. I'm unaware of any other FTP server which supports both SSL / TLS
143 and privilege separation, and gets this right.
145 Comments on this document are welcomed.