1/*
2 * Part of Very Secure FTPd
3 * Licence: GPL v2
4 * Author: Chris Evans
5 * main.c
6 */
7
8#include "session.h"
9#include "utility.h"
10#include "tunables.h"
11#include "logging.h"
12#include "str.h"
13#include "filestr.h"
14#include "ftpcmdio.h"
15#include "sysutil.h"
16#include "sysdeputil.h"
17#include "defs.h"
18#include "parseconf.h"
19#include "oneprocess.h"
20#include "twoprocess.h"
21#include "standalone.h"
22#include "tcpwrap.h"
23#include "vsftpver.h"
24#include "ssl.h"
25#include <stdio.h>
26
27/*
28 * Forward decls of helper functions
29 */
30static void die_unless_privileged(void);
31static void do_sanity_checks(void);
32static void session_init(struct vsf_session* p_sess);
33static void env_init(void);
34
35int
36main(int argc, const char* argv[])
37{
38  struct vsf_session the_session =
39  {
40    /* Control connection */
41    0, 0, 0,
42    /* Data connection */
43    -1, 0, -1, 0, 0, 0, 0,
44    /* Login */
45    1, INIT_MYSTR, INIT_MYSTR,
46    /* Protocol state */
47    0, 1, INIT_MYSTR, 0, 0,
48    /* Session state */
49    0,
50    /* Userids */
51    -1, -1, -1,
52    /* Pre-chroot() cache */
53    INIT_MYSTR, INIT_MYSTR, INIT_MYSTR, INIT_MYSTR, 1,
54    /* Logging */
55    -1, -1, INIT_MYSTR, 0, 0, 0, INIT_MYSTR, 0,
56    /* Buffers */
57    INIT_MYSTR, INIT_MYSTR, 0, 0,
58    /* Parent <-> child comms */
59    -1, -1,
60    /* Number of clients */
61    0, 0,
62    /* Home directory */
63    INIT_MYSTR,
64    /* Secure connection state */
65    0, 0, 0, 0, 0, 0, -1, -1,
66    /* write_enable */
67    0
68  };
69  int config_specified = 0;
70  const char* p_config_name = VSFTP_DEFAULT_CONFIG;
71  /* Zero or one argument supported. If one argument is passed, it is the
72   * path to the config file
73   */
74  if (argc > 2)
75  {
76    die("vsftpd: too many arguments (I take an optional config file only)");
77  }
78  else if (argc == 0)
79  {
80    die("vsftpd: missing argv[0]");
81  }
82  if (argc == 2)
83  {
84    if (!vsf_sysutil_strcmp(argv[1], "-v"))
85    {
86      vsf_exit("vsftpd: version " VSF_VERSION "\n");
87    }
88    p_config_name = argv[1];
89    config_specified = 1;
90  }
91  /* This might need to open /dev/zero on systems lacking MAP_ANON. Needs
92   * to be done early (i.e. before config file parse, which may use
93   * anonymous pages
94   */
95  vsf_sysutil_map_anon_pages_init();
96  /* Parse config file if it's there */
97  {
98    struct vsf_sysutil_statbuf* p_statbuf = 0;
99    int retval = vsf_sysutil_stat(p_config_name, &p_statbuf);
100    if (!vsf_sysutil_retval_is_error(retval))
101    {
102      vsf_parseconf_load_file(p_config_name, 1);
103    }
104    else if (config_specified)
105    {
106      die2("vsftpd: cannot open config file:", p_config_name);
107    }
108    vsf_sysutil_free(p_statbuf);
109  }
110  /* Resolve pasv_address if required */
111  if (tunable_pasv_address && tunable_pasv_addr_resolve)
112  {
113    struct vsf_sysutil_sockaddr* p_addr = 0;
114    const char* p_numeric_addr;
115    vsf_sysutil_dns_resolve(&p_addr, tunable_pasv_address);
116    vsf_sysutil_free((char*) tunable_pasv_address);
117    p_numeric_addr = vsf_sysutil_inet_ntop(p_addr);
118    tunable_pasv_address = vsf_sysutil_strdup(p_numeric_addr);
119    vsf_sysutil_free(p_addr);
120  }
121  if (!tunable_run_as_launching_user)
122  {
123    /* Just get out unless we start with requisite privilege */
124    die_unless_privileged();
125  }
126  if (tunable_setproctitle_enable)
127  {
128    /* Warning -- warning -- may nuke argv, environ */
129    vsf_sysutil_setproctitle_init(argc, argv);
130  }
131  /* Initialize the SSL system here if needed - saves the overhead of each
132   *  child doing this itself.
133   */
134  if (tunable_ssl_enable)
135  {
136    ssl_init(&the_session);
137  }
138  if (tunable_listen || tunable_listen_ipv6)
139  {
140    /* Standalone mode */
141    struct vsf_client_launch ret = vsf_standalone_main();
142    the_session.num_clients = ret.num_children;
143    the_session.num_this_ip = ret.num_this_ip;
144  }
145  if (tunable_tcp_wrappers)
146  {
147    the_session.tcp_wrapper_ok = vsf_tcp_wrapper_ok(VSFTP_COMMAND_FD);
148  }
149  {
150    const char* p_load_conf = vsf_sysutil_getenv("VSFTPD_LOAD_CONF");
151    if (p_load_conf)
152    {
153      vsf_parseconf_load_file(p_load_conf, 1);
154    }
155  }
156  /* Sanity checks - exit with a graceful error message if our STDIN is not
157   * a socket. Also check various config options don't collide.
158   */
159  do_sanity_checks();
160  /* Initializes session globals - e.g. IP addr's etc. */
161  session_init(&the_session);
162  /* Set up "environment", e.g. process group etc. */
163  env_init();
164  /* Set up logging - must come after global init because we need the remote
165   * address to convert into text
166   */
167  vsf_log_init(&the_session);
168  str_alloc_text(&the_session.remote_ip_str,
169                 vsf_sysutil_inet_ntop(the_session.p_remote_addr));
170  /* Set up options on the command socket */
171  vsf_cmdio_sock_setup();
172  if (tunable_setproctitle_enable)
173  {
174    vsf_sysutil_set_proctitle_prefix(&the_session.remote_ip_str);
175    vsf_sysutil_setproctitle("connected");
176  }
177  /* We might chroot() very soon (one process model), so we need to open
178   * any required config files here.
179   */
180  /* SSL may have been enabled by a per-IP configuration.. */
181  if (tunable_ssl_enable)
182  {
183    ssl_init(&the_session);
184  }
185  if (tunable_deny_email_enable)
186  {
187    int retval = str_fileread(&the_session.banned_email_str,
188                              tunable_banned_email_file, VSFTP_CONF_FILE_MAX);
189    if (vsf_sysutil_retval_is_error(retval))
190    {
191      die2("cannot open anon e-mail list file:", tunable_banned_email_file);
192    }
193  }
194  if (tunable_banner_file)
195  {
196    int retval = str_fileread(&the_session.banner_str, tunable_banner_file,
197                              VSFTP_CONF_FILE_MAX);
198    if (vsf_sysutil_retval_is_error(retval))
199    {
200      die2("cannot open banner file:", tunable_banner_file);
201    }
202  }
203  if (tunable_secure_email_list_enable)
204  {
205    int retval = str_fileread(&the_session.email_passwords_str,
206                              tunable_email_password_file,
207                              VSFTP_CONF_FILE_MAX);
208    if (vsf_sysutil_retval_is_error(retval))
209    {
210      die2("cannot open email passwords file:", tunable_email_password_file);
211    }
212  }
213  /* Special case - can force one process model if we've got a setup
214   * needing _no_ privs
215   */
216  if (!tunable_local_enable && !tunable_connect_from_port_20 &&
217      !tunable_chown_uploads)
218  {
219    tunable_one_process_model = 1;
220  }
221  if (tunable_run_as_launching_user)
222  {
223    tunable_one_process_model = 1;
224    if (!vsf_sysutil_running_as_root())
225    {
226      tunable_connect_from_port_20 = 0;
227      tunable_chown_uploads = 0;
228    }
229  }
230  if (tunable_one_process_model)
231  {
232    vsf_one_process_start(&the_session);
233  }
234  else
235  {
236    vsf_two_process_start(&the_session);
237  }
238  /* NOTREACHED */
239  bug("should not get here: main");
240  return 1;
241}
242
243static void
244die_unless_privileged(void)
245{
246  if (!vsf_sysutil_running_as_root())
247  {
248    die("vsftpd: must be started as root (see run_as_launching_user option)");
249  }
250}
251
252static void
253do_sanity_checks(void)
254{
255  {
256    struct vsf_sysutil_statbuf* p_statbuf = 0;
257    vsf_sysutil_fstat(VSFTP_COMMAND_FD, &p_statbuf);
258    if (!vsf_sysutil_statbuf_is_socket(p_statbuf))
259    {
260      die("vsftpd: not configured for standalone, must be started from inetd");
261    }
262    vsf_sysutil_free(p_statbuf);
263  }
264  if (tunable_one_process_model)
265  {
266    if (tunable_local_enable)
267    {
268      die("vsftpd: security: 'one_process_model' is anonymous only");
269    }
270    if (!vsf_sysdep_has_capabilities_as_non_root())
271    {
272      die("vsftpd: security: 'one_process_model' needs a better OS");
273    }
274  }
275  if (!tunable_local_enable && !tunable_anonymous_enable)
276  {
277    die("vsftpd: both local and anonymous access disabled!");
278  }
279}
280
281static void
282env_init(void)
283{
284  vsf_sysutil_make_session_leader();
285  /* Set up a secure umask - we'll set the proper one after login */
286  vsf_sysutil_set_umask(VSFTP_SECURE_UMASK);
287  /* Fire up libc's timezone initialisation, before we chroot()! */
288  vsf_sysutil_tzset();
289  /* Signals. We'll always take -EPIPE rather than a rude signal, thanks */
290  vsf_sysutil_install_null_sighandler(kVSFSysUtilSigPIPE);
291}
292
293static void
294session_init(struct vsf_session* p_sess)
295{
296  /* Get the addresses of the control connection */
297  vsf_sysutil_getpeername(VSFTP_COMMAND_FD, &p_sess->p_remote_addr);
298  vsf_sysutil_getsockname(VSFTP_COMMAND_FD, &p_sess->p_local_addr);
299
300  /* If anonymous mode is active, fetch the uid of the anonymous user */
301  if (tunable_anonymous_enable)
302  {
303    const struct vsf_sysutil_user* p_user = vsf_sysutil_getpwnam(tunable_ftp_username);
304
305    if (p_user == 0)
306    {
307      die2("vsftpd: cannot locate user specified in 'ftp_username':",
308           tunable_ftp_username);
309    }
310    p_sess->anon_ftp_uid = vsf_sysutil_user_getuid(p_user);
311  }
312
313  if (tunable_guest_enable)
314  {
315    const struct vsf_sysutil_user* p_user = vsf_sysutil_getpwnam(tunable_guest_username);
316
317    if (p_user == 0)
318    {
319      die2("vsftpd: cannot locate user specified in 'guest_username':",
320           tunable_guest_username);
321    }
322    p_sess->guest_user_uid = vsf_sysutil_user_getuid(p_user);
323  }
324
325  if (tunable_chown_uploads)
326  {
327    const struct vsf_sysutil_user* p_user = vsf_sysutil_getpwnam(tunable_chown_username);
328
329    if (p_user == 0)
330    {
331      die2("vsftpd: cannot locate user specified in 'chown_username':",
332           tunable_chown_username);
333    }
334    p_sess->anon_upload_chown_uid = vsf_sysutil_user_getuid(p_user);
335  }
336}
337