1 /*
2 * Part of Very Secure FTPd
3 * Licence: GPL v2
4 * Author: Chris Evans
5 * ftpcmdio.c
6 *
7 * Routines applicable to reading and writing the FTP command stream.
8 */
9
10 #include "ftpcmdio.h"
11 #include "ftpcodes.h"
12 #include "str.h"
13 #include "netstr.h"
14 #include "sysutil.h"
15 #include "tunables.h"
16 #include "defs.h"
17 #include "secbuf.h"
18 #include "utility.h"
19 #include "logging.h"
20 #include "session.h"
21 #include "readwrite.h"
22 #include "charconv.h"
23
24 /* Internal functions */
25 static int control_getline(struct mystr* p_str, struct vsf_session* p_sess);
26 static void ftp_write_text_common(struct vsf_session* p_sess, int status,
27 const char* p_text, char sep);
28 static void ftp_write_str_common(struct vsf_session* p_sess, int status,
29 char sep, const struct mystr* p_str);
30 static void handle_alarm_timeout(void* p_private);
31
32 void
vsf_cmdio_sock_setup(void)33 vsf_cmdio_sock_setup(void)
34 {
35 vsf_sysutil_activate_keepalive(VSFTP_COMMAND_FD);
36 vsf_sysutil_set_nodelay(VSFTP_COMMAND_FD);
37 vsf_sysutil_activate_oobinline(VSFTP_COMMAND_FD);
38 }
39
40 static void
handle_alarm_timeout(void * p_private)41 handle_alarm_timeout(void* p_private)
42 {
43 struct vsf_session* p_sess = (struct vsf_session*) p_private;
44 p_sess->idle_timeout = 1;
45 vsf_sysutil_activate_noblock(VSFTP_COMMAND_FD);
46 vsf_sysutil_shutdown_read_failok(VSFTP_COMMAND_FD);
47 }
48
49 void
vsf_cmdio_write(struct vsf_session * p_sess,int status,const char * p_text)50 vsf_cmdio_write(struct vsf_session* p_sess, int status, const char* p_text)
51 {
52 ftp_write_text_common(p_sess, status, p_text, ' ');
53 }
54
55 void
vsf_cmdio_write_hyphen(struct vsf_session * p_sess,int status,const char * p_text)56 vsf_cmdio_write_hyphen(struct vsf_session* p_sess, int status,
57 const char* p_text)
58 {
59 ftp_write_text_common(p_sess, status, p_text, '-');
60 }
61
62 void
vsf_cmdio_write_raw(struct vsf_session * p_sess,const char * p_text)63 vsf_cmdio_write_raw(struct vsf_session* p_sess, const char* p_text)
64 {
65 static struct mystr s_the_str;
66 int retval;
67 str_alloc_text(&s_the_str, p_text);
68 if (tunable_log_ftp_protocol)
69 {
70 vsf_log_line(p_sess, kVSFLogEntryFTPOutput, &s_the_str);
71 }
72 retval = ftp_write_str(p_sess, &s_the_str, kVSFRWControl);
73 if (retval != 0)
74 {
75 die("ftp_write_str");
76 }
77 }
78
79 void
vsf_cmdio_write_raw_quiet(struct vsf_session * p_sess,const char * p_text)80 vsf_cmdio_write_raw_quiet(struct vsf_session* p_sess, const char* p_text)
81 {
82 static struct mystr s_the_str;
83 int retval;
84 str_alloc_text(&s_the_str, p_text);
85 retval = ftp_write_str(p_sess, &s_the_str, kVSFRWControl);
86 if (retval != 0)
87 {
88 die("ftp_write_str");
89 }
90 }
91
92 void
vsf_cmdio_write_exit(struct vsf_session * p_sess,int status,const char * p_text,int exit_val)93 vsf_cmdio_write_exit(struct vsf_session* p_sess, int status, const char* p_text,
94 int exit_val)
95 {
96 /* Unblock any readers on the dying control channel. This is needed for SSL
97 * connections, where the SSL control channel slave is in a separate
98 * process.
99 */
100 vsf_sysutil_activate_noblock(VSFTP_COMMAND_FD);
101 vsf_sysutil_shutdown_read_failok(VSFTP_COMMAND_FD);
102 vsf_cmdio_write(p_sess, status, p_text);
103 vsf_sysutil_shutdown_failok(VSFTP_COMMAND_FD);
104 vsf_sysutil_exit(exit_val);
105 }
106
107 static void
ftp_write_text_common(struct vsf_session * p_sess,int status,const char * p_text,char sep)108 ftp_write_text_common(struct vsf_session* p_sess, int status,
109 const char* p_text, char sep)
110 {
111 /* XXX - could optimize */
112 static struct mystr s_the_str;
113 str_alloc_text(&s_the_str, p_text);
114 ftp_write_str_common(p_sess, status, sep, &s_the_str);
115 }
116
117 void
vsf_cmdio_write_str_hyphen(struct vsf_session * p_sess,int status,const struct mystr * p_str)118 vsf_cmdio_write_str_hyphen(struct vsf_session* p_sess, int status,
119 const struct mystr* p_str)
120 {
121 ftp_write_str_common(p_sess, status, '-', p_str);
122 }
123
124 void
vsf_cmdio_write_str(struct vsf_session * p_sess,int status,const struct mystr * p_str)125 vsf_cmdio_write_str(struct vsf_session* p_sess, int status,
126 const struct mystr* p_str)
127 {
128 ftp_write_str_common(p_sess, status, ' ', p_str);
129 }
130
131 static void
ftp_write_str_common(struct vsf_session * p_sess,int status,char sep,const struct mystr * p_str)132 ftp_write_str_common(struct vsf_session* p_sess, int status, char sep,
133 const struct mystr* p_str)
134 {
135 static struct mystr s_write_buf_str;
136 static struct mystr s_text_mangle_str;
137 int retval;
138 if (tunable_log_ftp_protocol)
139 {
140 str_alloc_ulong(&s_write_buf_str, (unsigned long) status);
141 str_append_char(&s_write_buf_str, sep);
142 str_append_str(&s_write_buf_str, p_str);
143 vsf_log_line(p_sess, kVSFLogEntryFTPOutput, &s_write_buf_str);
144 }
145 str_copy(&s_text_mangle_str, p_str);
146 vsf_charconv_convert(p_sess, &s_text_mangle_str, VSFTP_CONVDIRECT_FORWARD);
147 /* Process the output response according to the specifications.. */
148 /* Escape telnet characters properly */
149 if (tunable_double_377)
150 {
151 str_replace_text(&s_text_mangle_str, "\377", "\377\377");
152 }
153 /* Change \n for \0 in response */
154 str_replace_char(&s_text_mangle_str, '\n', '\0');
155 /* Build string to squirt down network */
156 str_alloc_ulong(&s_write_buf_str, (unsigned long) status);
157 str_append_char(&s_write_buf_str, sep);
158 str_append_str(&s_write_buf_str, &s_text_mangle_str);
159 str_append_text(&s_write_buf_str, "\r\n");
160 retval = ftp_write_str(p_sess, &s_write_buf_str, kVSFRWControl);
161 if (retval != 0)
162 {
163 die("ftp_write");
164 }
165 }
166
167 void
vsf_cmdio_set_alarm(struct vsf_session * p_sess)168 vsf_cmdio_set_alarm(struct vsf_session* p_sess)
169 {
170 if (tunable_idle_session_timeout > 0)
171 {
172 vsf_sysutil_install_sighandler(kVSFSysUtilSigALRM,
173 handle_alarm_timeout,
174 p_sess,
175 1);
176 vsf_sysutil_set_alarm(tunable_idle_session_timeout);
177 }
178 }
179
180 void
vsf_cmdio_get_cmd_and_arg(struct vsf_session * p_sess,struct mystr * p_cmd_str,struct mystr * p_arg_str,int set_alarm,int is_quiet)181 vsf_cmdio_get_cmd_and_arg(struct vsf_session* p_sess, struct mystr* p_cmd_str,
182 struct mystr* p_arg_str, int set_alarm, int is_quiet)
183 {
184 int ret;
185 /* Prepare an alarm to timeout the session.. */
186 if (set_alarm)
187 {
188 vsf_cmdio_set_alarm(p_sess);
189 }
190 /* Blocks */
191 ret = control_getline(p_cmd_str, p_sess);
192 if (p_sess->idle_timeout)
193 {
194 vsf_cmdio_write_exit(p_sess, FTP_IDLE_TIMEOUT, "Timeout.", 1);
195 }
196 if (ret == 0)
197 {
198 /* Remote end hung up without a polite QUIT. The shutdown is to make
199 * sure buggy clients don't ever see an OOPS message.
200 */
201 vsf_sysutil_shutdown_failok(VSFTP_COMMAND_FD);
202 vsf_sysutil_exit(1);
203 }
204 /* View a single space as a command of " ", which although a useless command,
205 * permits the caller to distinguish input of "" from " ".
206 */
207 if (str_getlen(p_cmd_str) == 1 && str_get_char_at(p_cmd_str, 0) == ' ')
208 {
209 str_empty(p_arg_str);
210 }
211 else
212 {
213 str_split_char(p_cmd_str, p_arg_str, ' ');
214 }
215 str_upper(p_cmd_str);
216 if (tunable_log_ftp_protocol && !is_quiet)
217 {
218 static struct mystr s_log_str;
219 if (str_equal_text(p_cmd_str, "PASS"))
220 {
221 str_alloc_text(&s_log_str, "PASS <password>");
222 }
223 else
224 {
225 str_copy(&s_log_str, p_cmd_str);
226 if (!str_isempty(p_arg_str))
227 {
228 str_append_char(&s_log_str, ' ');
229 str_append_str(&s_log_str, p_arg_str);
230 }
231 }
232 vsf_log_line(p_sess, kVSFLogEntryFTPInput, &s_log_str);
233 }
234 }
235
236 static int
control_getline(struct mystr * p_str,struct vsf_session * p_sess)237 control_getline(struct mystr* p_str, struct vsf_session* p_sess)
238 {
239 int ret;
240 if (p_sess->p_control_line_buf == 0)
241 {
242 vsf_secbuf_alloc(&p_sess->p_control_line_buf, VSFTP_MAX_COMMAND_LINE);
243 }
244 ret = ftp_getline(p_sess, p_str, p_sess->p_control_line_buf);
245 if (ret == 0)
246 {
247 return ret;
248 }
249 else if (ret < 0)
250 {
251 vsf_cmdio_write_exit(p_sess, FTP_BADCMD, "Input line too long.", 1);
252 }
253 /* As mandated by the FTP specifications.. */
254 str_replace_char(p_str, '\0', '\n');
255 /* If the last character is a \r, strip it */
256 {
257 unsigned int len = str_getlen(p_str);
258 while (len > 0 && str_get_char_at(p_str, len - 1) == '\r')
259 {
260 str_trunc(p_str, len - 1);
261 --len;
262 }
263 }
264 vsf_charconv_convert(p_sess, p_str, VSFTP_CONVDIRECT_BACKWARD);
265 return 1;
266 }
267
268