vsftpd 3.0.2
[tomato/tomato-dir865l.git] / release / src / router / vsftpd / privops.c
blob21d72678d2106caa1197a7b904658a139d1f58ce
1 /*
2 * Part of Very Secure FTPd
3 * License: GPL v2
4 * Author: Chris Evans
5 * privops.c
7 * Code implementing the privileged operations that the unprivileged client
8 * might request.
9 * Look for suitable paranoia in this file.
12 #include "privops.h"
13 #include "session.h"
14 #include "sysdeputil.h"
15 #include "sysutil.h"
16 #include "utility.h"
17 #include "str.h"
18 #include "tunables.h"
19 #include "defs.h"
20 #include "logging.h"
22 /* File private functions */
23 static enum EVSFPrivopLoginResult handle_anonymous_login(
24 struct vsf_session* p_sess, const struct mystr* p_pass_str);
25 static enum EVSFPrivopLoginResult handle_local_login(
26 struct vsf_session* p_sess, struct mystr* p_user_str,
27 const struct mystr* p_pass_str);
28 static void setup_username_globals(struct vsf_session* p_sess,
29 const struct mystr* p_str);
30 static enum EVSFPrivopLoginResult handle_login(
31 struct vsf_session* p_sess, struct mystr* p_user_str,
32 const struct mystr* p_pass_str);
34 int
35 vsf_privop_get_ftp_port_sock(struct vsf_session* p_sess,
36 unsigned short remote_port,
37 int use_port_sockaddr)
39 static struct vsf_sysutil_sockaddr* p_sockaddr;
40 const struct vsf_sysutil_sockaddr* p_connect_to;
41 int retval;
42 int i;
43 int s = vsf_sysutil_get_ipsock(p_sess->p_local_addr);
44 unsigned short port = 0;
45 if (p_sess->pasv_listen_fd != -1)
47 die("listed fd is active?");
49 if (vsf_sysutil_is_port_reserved(remote_port))
51 die("Illegal port request");
53 if (tunable_connect_from_port_20)
55 port = (unsigned short) tunable_ftp_data_port;
57 vsf_sysutil_activate_reuseaddr(s);
58 /* A report of failure here on Solaris, presumably buggy address reuse
59 * support? We'll retry.
61 for (i = 0; i < 2; ++i)
63 double sleep_for;
64 vsf_sysutil_sockaddr_clone(&p_sockaddr, p_sess->p_local_addr);
65 vsf_sysutil_sockaddr_set_port(p_sockaddr, port);
66 retval = vsf_sysutil_bind(s, p_sockaddr);
67 if (retval == 0)
69 break;
71 if (vsf_sysutil_get_error() != kVSFSysUtilErrADDRINUSE || i == 1)
73 die("vsf_sysutil_bind");
75 sleep_for = vsf_sysutil_get_random_byte();
76 sleep_for /= 256.0;
77 sleep_for += 1.0;
78 vsf_sysutil_sleep(sleep_for);
80 if (use_port_sockaddr)
82 p_connect_to = p_sess->p_port_sockaddr;
84 else
86 vsf_sysutil_sockaddr_set_port(p_sess->p_remote_addr, remote_port);
87 p_connect_to = p_sess->p_remote_addr;
89 retval = vsf_sysutil_connect_timeout(s, p_connect_to,
90 tunable_connect_timeout);
91 if (vsf_sysutil_retval_is_error(retval))
93 vsf_sysutil_close(s);
94 s = -1;
96 return s;
99 void
100 vsf_privop_pasv_cleanup(struct vsf_session* p_sess)
102 if (p_sess->pasv_listen_fd != -1)
104 vsf_sysutil_close(p_sess->pasv_listen_fd);
105 p_sess->pasv_listen_fd = -1;
110 vsf_privop_pasv_active(struct vsf_session* p_sess)
112 if (p_sess->pasv_listen_fd != -1)
114 return 1;
116 return 0;
119 unsigned short
120 vsf_privop_pasv_listen(struct vsf_session* p_sess)
122 static struct vsf_sysutil_sockaddr* s_p_sockaddr;
123 int bind_retries = 10;
124 unsigned short the_port;
125 /* IPPORT_RESERVED */
126 unsigned short min_port = 1024;
127 unsigned short max_port = 65535;
128 int is_ipv6 = vsf_sysutil_sockaddr_is_ipv6(p_sess->p_local_addr);
129 if (p_sess->pasv_listen_fd != -1)
131 die("listed fd already active");
134 if (tunable_pasv_min_port > min_port && tunable_pasv_min_port <= max_port)
136 min_port = (unsigned short) tunable_pasv_min_port;
138 if (tunable_pasv_max_port >= min_port && tunable_pasv_max_port < max_port)
140 max_port = (unsigned short) tunable_pasv_max_port;
143 while (--bind_retries)
145 int retval;
146 double scaled_port;
147 the_port = vsf_sysutil_get_random_byte();
148 the_port = (unsigned short) (the_port << 8);
149 the_port = (unsigned short) (the_port | vsf_sysutil_get_random_byte());
150 scaled_port = (double) min_port;
151 scaled_port += ((double) the_port / (double) 65536) *
152 ((double) max_port - min_port + 1);
153 the_port = (unsigned short) scaled_port;
154 if (is_ipv6)
156 p_sess->pasv_listen_fd = vsf_sysutil_get_ipv6_sock();
158 else
160 p_sess->pasv_listen_fd = vsf_sysutil_get_ipv4_sock();
162 vsf_sysutil_activate_reuseaddr(p_sess->pasv_listen_fd);
163 vsf_sysutil_sockaddr_clone(&s_p_sockaddr, p_sess->p_local_addr);
164 vsf_sysutil_sockaddr_set_port(s_p_sockaddr, the_port);
165 retval = vsf_sysutil_bind(p_sess->pasv_listen_fd, s_p_sockaddr);
166 if (!vsf_sysutil_retval_is_error(retval))
168 retval = vsf_sysutil_listen(p_sess->pasv_listen_fd, 1);
169 if (!vsf_sysutil_retval_is_error(retval))
171 break;
174 /* SELinux systems can give you an inopportune EACCES, it seems. */
175 if (vsf_sysutil_get_error() == kVSFSysUtilErrADDRINUSE ||
176 vsf_sysutil_get_error() == kVSFSysUtilErrACCES)
178 vsf_sysutil_close(p_sess->pasv_listen_fd);
179 p_sess->pasv_listen_fd = -1;
180 continue;
182 die("vsf_sysutil_bind / listen");
184 if (!bind_retries)
186 die("vsf_sysutil_bind");
188 return the_port;
192 vsf_privop_accept_pasv(struct vsf_session* p_sess)
194 struct vsf_sysutil_sockaddr* p_accept_addr = 0;
195 int remote_fd;
196 if (p_sess->pasv_listen_fd == -1)
198 die("listed fd not active");
200 vsf_sysutil_sockaddr_alloc(&p_accept_addr);
201 remote_fd = vsf_sysutil_accept_timeout(p_sess->pasv_listen_fd, p_accept_addr,
202 tunable_accept_timeout);
203 if (vsf_sysutil_retval_is_error(remote_fd))
205 vsf_sysutil_sockaddr_clear(&p_accept_addr);
206 return -1;
208 /* SECURITY:
209 * Reject the connection if it wasn't from the same IP as the
210 * control connection.
212 if (!tunable_pasv_promiscuous)
214 if (!vsf_sysutil_sockaddr_addr_equal(p_sess->p_remote_addr, p_accept_addr))
216 vsf_sysutil_close(remote_fd);
217 vsf_sysutil_sockaddr_clear(&p_accept_addr);
218 return -2;
221 vsf_sysutil_sockaddr_clear(&p_accept_addr);
222 return remote_fd;
225 void
226 vsf_privop_do_file_chown(struct vsf_session* p_sess, int fd)
228 static struct vsf_sysutil_statbuf* s_p_statbuf;
229 vsf_sysutil_fstat(fd, &s_p_statbuf);
230 /* Do nothing if it is already owned by the desired user. */
231 if (vsf_sysutil_statbuf_get_uid(s_p_statbuf) ==
232 p_sess->anon_upload_chown_uid)
234 return;
236 /* Drop it like a hot potato unless it's a regular file owned by
237 * the the anonymous ftp user
239 if (p_sess->anon_upload_chown_uid == -1 ||
240 !vsf_sysutil_statbuf_is_regfile(s_p_statbuf) ||
241 (vsf_sysutil_statbuf_get_uid(s_p_statbuf) != p_sess->anon_ftp_uid &&
242 vsf_sysutil_statbuf_get_uid(s_p_statbuf) != p_sess->guest_user_uid))
244 die("invalid fd in cmd_process_chown");
246 /* SECURITY! You need an OS which strips SUID/SGID bits on chown(),
247 * otherwise a compromise of the FTP user will lead to compromise of
248 * the "anon_upload_chown_uid" user (think chmod +s).
250 vsf_sysutil_fchown(fd, p_sess->anon_upload_chown_uid, -1);
253 enum EVSFPrivopLoginResult
254 vsf_privop_do_login(struct vsf_session* p_sess,
255 const struct mystr* p_pass_str)
257 enum EVSFPrivopLoginResult result =
258 handle_login(p_sess, &p_sess->user_str, p_pass_str);
259 vsf_log_start_entry(p_sess, kVSFLogEntryLogin);
260 if (result == kVSFLoginFail)
262 vsf_log_do_log(p_sess, 0);
263 if (tunable_delay_failed_login)
265 vsf_sysutil_sleep((double) tunable_delay_failed_login);
268 else
270 vsf_log_do_log(p_sess, 1);
271 if (tunable_delay_successful_login)
273 vsf_sysutil_sleep((double) tunable_delay_successful_login);
276 return result;
279 static enum EVSFPrivopLoginResult
280 handle_login(struct vsf_session* p_sess, struct mystr* p_user_str,
281 const struct mystr* p_pass_str)
283 /* Do not assume PAM can cope with dodgy input, even though it
284 * almost certainly can.
286 int anonymous_login = 0;
287 char first_char;
288 unsigned int len = str_getlen(p_user_str);
289 if (len == 0 || len > VSFTP_USERNAME_MAX)
291 return kVSFLoginFail;
293 /* Throw out dodgy start characters */
294 first_char = str_get_char_at(p_user_str, 0);
295 if (!vsf_sysutil_isalnum(first_char) &&
296 first_char != '_' &&
297 first_char != '.')
299 return kVSFLoginFail;
301 /* Throw out non-printable characters and space in username */
302 if (str_contains_space(p_user_str) ||
303 str_contains_unprintable(p_user_str))
305 return kVSFLoginFail;
307 /* Throw out excessive length passwords */
308 len = str_getlen(p_pass_str);
309 if (len > VSFTP_PASSWORD_MAX)
311 return kVSFLoginFail;
313 /* Check for an anonymous login or "real" login */
314 if (tunable_anonymous_enable)
316 struct mystr upper_str = INIT_MYSTR;
317 str_copy(&upper_str, p_user_str);
318 str_upper(&upper_str);
319 if (str_equal_text(&upper_str, "FTP") ||
320 str_equal_text(&upper_str, "ANONYMOUS"))
322 anonymous_login = 1;
324 str_free(&upper_str);
327 enum EVSFPrivopLoginResult result = kVSFLoginFail;
328 if (anonymous_login)
330 result = handle_anonymous_login(p_sess, p_pass_str);
332 else
334 if (!tunable_local_enable)
336 die("unexpected local login in handle_login");
338 result = handle_local_login(p_sess, p_user_str, p_pass_str);
340 return result;
344 static enum EVSFPrivopLoginResult
345 handle_anonymous_login(struct vsf_session* p_sess,
346 const struct mystr* p_pass_str)
348 if (!str_isempty(&p_sess->banned_email_str) &&
349 str_contains_line(&p_sess->banned_email_str, p_pass_str))
351 return kVSFLoginFail;
353 if (!str_isempty(&p_sess->email_passwords_str) &&
354 (!str_contains_line(&p_sess->email_passwords_str, p_pass_str) ||
355 str_isempty(p_pass_str)))
357 return kVSFLoginFail;
359 /* Store the anonymous identity string */
360 str_copy(&p_sess->anon_pass_str, p_pass_str);
361 if (str_isempty(&p_sess->anon_pass_str))
363 str_alloc_text(&p_sess->anon_pass_str, "?");
365 /* "Fix" any characters which might upset the log processing */
366 str_replace_char(&p_sess->anon_pass_str, ' ', '_');
367 str_replace_char(&p_sess->anon_pass_str, '\n', '?');
369 struct mystr ftp_username_str = INIT_MYSTR;
370 if (tunable_ftp_username)
372 str_alloc_text(&ftp_username_str, tunable_ftp_username);
374 setup_username_globals(p_sess, &ftp_username_str);
375 str_free(&ftp_username_str);
377 str_free(&p_sess->banned_email_str);
378 str_free(&p_sess->email_passwords_str);
379 return kVSFLoginAnon;
382 static enum EVSFPrivopLoginResult
383 handle_local_login(struct vsf_session* p_sess,
384 struct mystr* p_user_str,
385 const struct mystr* p_pass_str)
387 if (!vsf_sysdep_check_auth(p_user_str, p_pass_str, &p_sess->remote_ip_str))
389 return kVSFLoginFail;
391 setup_username_globals(p_sess, p_user_str);
392 return kVSFLoginReal;
395 static void
396 setup_username_globals(struct vsf_session* p_sess, const struct mystr* p_str)
398 str_copy(&p_sess->user_str, p_str);
399 if (tunable_setproctitle_enable)
401 struct mystr prefix_str = INIT_MYSTR;
402 str_copy(&prefix_str, &p_sess->remote_ip_str);
403 str_append_char(&prefix_str, '/');
404 str_append_str(&prefix_str, p_str);
405 vsf_sysutil_set_proctitle_prefix(&prefix_str);
406 str_free(&prefix_str);