2 * Part of Very Secure FTPd
7 * Code implementing the privileged operations that the unprivileged client
9 * Look for suitable paranoia in this file.
14 #include "sysdeputil.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
);
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
;
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
)
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
);
71 if (vsf_sysutil_get_error() != kVSFSysUtilErrADDRINUSE
|| i
== 1)
73 die("vsf_sysutil_bind");
75 sleep_for
= vsf_sysutil_get_random_byte();
78 vsf_sysutil_sleep(sleep_for
);
80 if (use_port_sockaddr
)
82 p_connect_to
= p_sess
->p_port_sockaddr
;
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
))
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)
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
)
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
;
156 p_sess
->pasv_listen_fd
= vsf_sysutil_get_ipv6_sock();
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
))
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;
182 die("vsf_sysutil_bind / listen");
186 die("vsf_sysutil_bind");
192 vsf_privop_accept_pasv(struct vsf_session
* p_sess
)
194 struct vsf_sysutil_sockaddr
* p_accept_addr
= 0;
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
);
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
);
221 vsf_sysutil_sockaddr_clear(&p_accept_addr
);
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
)
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
);
270 vsf_log_do_log(p_sess
, 1);
271 if (tunable_delay_successful_login
)
273 vsf_sysutil_sleep((double) tunable_delay_successful_login
);
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;
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
) &&
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"))
324 str_free(&upper_str
);
327 enum EVSFPrivopLoginResult result
= kVSFLoginFail
;
330 result
= handle_anonymous_login(p_sess
, p_pass_str
);
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
);
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
;
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
);