1 /*
2 * Part of Very Secure FTPd
3 * Licence: GPL v2
4 * Author: Dmitriy Balashov
5 * http.c
6 *
7 * Routines to handle HTTP.
8 */
9
10 #include "session.h"
11 #include "access.h"
12 #include "tunables.h"
13 #include "charconv.h"
14 #include "filestr.h"
15 #include "str.h"
16 #include "strlist.h"
17 #include "ftpcmdio.h"
18 #include "ls.h"
19 #include "sysutil.h"
20 #include "sysstr.h"
21 #include "logging.h"
22 #include "defs.h"
23 #include "utility.h"
24 #include "postlogin.h"
25 #include "http_msg.h"
26 #include "http_str.h"
27
28 enum EResourceType
29 {
30 ResTypeNotFound = -1,
31 ResTypeFile,
32 ResType_Dir,
33 ResTypeDir
34 };
35
36 static void http_error(struct vsf_session* p_sess, const enum EVSFHttpMsgType p_type);
37 static void http_html_content(struct vsf_session* p_sess, const char* response, const struct mystr* body);
38 static void http_header_print(struct vsf_session* p_sess, const char* response);
39 static enum EResourceType http_check_resource(struct vsf_session* p_sess, filesize_t* p_size);
40 static enum EResourceType http_request_resource_type(struct vsf_session* p_sess, filesize_t* p_size);
41 static void http_handle_send(struct vsf_session* p_sess, filesize_t p_size);
42 static void http_handle_browse(struct vsf_session* p_sess);
43
44 static char* err_403_forbidden = "403 Forbidden";
45 static char* err_404_not_found = "404 Not Found";
46 static char* err_200_ok = "200 OK";
47
48 void
handle_http(struct vsf_session * p_sess)49 handle_http(struct vsf_session* p_sess)
50 {
51 /* Warning: Doesn't respect cmds_allowed etc. because there is currently only
52 * one command (GET)!
53 * HTTP likely doesn't respect other important FTP options. I don't think
54 * logging works.
55 */
56 if (!tunable_download_enable)
57 {
58 bug("HTTP needs download - fix your config");
59 }
60 /* Eat the HTTP headers, which we don't care about. */
61
62 do
63 {
64 vsf_cmdio_get_cmd_and_arg(p_sess, &p_sess->ftp_cmd_str,
65 &p_sess->ftp_arg_str, 1, 1);
66 }
67 while (!str_isempty(&p_sess->ftp_cmd_str) ||
68 !str_isempty(&p_sess->ftp_arg_str));
69
70 if (!p_sess->is_anonymous)
71 {
72 http_error(p_sess, kVSFHttpMsgType403Serv);
73 }
74
75 /* Split the path from the HTTP/1.x */
76 str_split_char(&p_sess->http_get_arg, &p_sess->ftp_arg_str, ' ');
77 str_unescape(&p_sess->http_get_arg);
78
79 static filesize_t p_size;
80 enum EResourceType p_res_type = http_check_resource(p_sess, &p_size);
81
82 switch (p_res_type)
83 {
84 case ResTypeDir:
85 if (!tunable_http_browse || str_chdir(&p_sess->http_get_arg) != 0)
86 {
87 http_error(p_sess, kVSFHttpMsgType403);
88 }
89 break;
90 case ResTypeNotFound:
91 http_error(p_sess, kVSFHttpMsgType404);
92 break;
93 default:
94 break;
95 }
96
97 if (p_res_type == ResTypeDir)
98 {
99 http_handle_browse(p_sess);
100 }
101 else
102 {
103 http_handle_send(p_sess, p_size);
104 }
105
106 if (vsf_log_entry_pending(p_sess))
107 {
108 vsf_log_do_log(p_sess, 0);
109 }
110 vsf_sysutil_exit(0);
111 }
112
113 static void
http_error(struct vsf_session * p_sess,const enum EVSFHttpMsgType p_type)114 http_error(struct vsf_session* p_sess, const enum EVSFHttpMsgType p_type)
115 {
116 static char* response;
117 static struct mystr p_tpl = INIT_MYSTR;
118 static struct mystr p_res = INIT_MYSTR;
119
120 switch (p_type)
121 {
122 case kVSFHttpMsgType403Serv:
123 response = err_403_forbidden;
124 break;
125 case kVSFHttpMsgType403:
126 response = err_403_forbidden;
127 break;
128 case kVSFHttpMsgType404:
129 response = err_404_not_found;
130 break;
131 default:
132 bug("Bad HTTP error code");
133 break;
134 }
135
136 str_alloc_text(&p_tpl, vsf_http_messages_get(p_type));
137 str_process_template(p_sess, &p_res, &p_tpl, 0, 0, vsf_sysutil_get_time_sec(), 0);
138 http_html_content(p_sess, response, &p_res);
139 }
140
141 static void
http_html_content(struct vsf_session * p_sess,const char * response,const struct mystr * body)142 http_html_content(struct vsf_session* p_sess,
143 const char* response,
144 const struct mystr* body)
145 {
146 http_header_print(p_sess, response);
147 vsf_cmdio_write_raw_quiet(p_sess, "Content-Type: text/html\r\n");
148
149 vsf_cmdio_write_raw_quiet(p_sess, "Content-Length: ");
150 vsf_cmdio_write_raw_quiet(p_sess, vsf_sysutil_filesize_t_to_str(str_getlen(body)));
151 vsf_cmdio_write_raw_quiet(p_sess, "\r\n\r\n");
152 vsf_cmdio_write_raw_quiet(p_sess, str_getbuf(body));
153
154 vsf_sysutil_exit(0);
155 }
156
157
158 static void
http_header_print(struct vsf_session * p_sess,const char * response)159 http_header_print(struct vsf_session* p_sess, const char* response)
160 {
161 static const char* hdr = "\r\n"
162 "Server: vsftpd\r\n"
163 "Connection: close\r\n"
164 "X-Frame-Options: SAMEORIGIN\r\n"
165 "X-Content-Type-Options: nosniff\r\n";
166
167 vsf_cmdio_write_raw_quiet(p_sess, "HTTP/1.0 ");
168 vsf_cmdio_write_raw_quiet(p_sess, response);
169 vsf_cmdio_write_raw_quiet(p_sess, hdr);
170 }
171
172 static enum EResourceType
http_check_resource(struct vsf_session * p_sess,filesize_t * p_size)173 http_check_resource(struct vsf_session* p_sess, filesize_t* p_size)
174 {
175 enum EResourceType ret = http_request_resource_type(p_sess, p_size);
176
177 if (ret == ResType_Dir)
178 {
179 str_chdir(&p_sess->http_get_arg);
180 str_getcwd(&p_sess->http_get_arg);
181 http_header_print(p_sess, "302 Found");
182 vsf_cmdio_write_raw_quiet(p_sess, "Location: ");
183 vsf_cmdio_write_raw_quiet(p_sess, str_getbuf(&p_sess->http_get_arg));
184 vsf_cmdio_write_raw_quiet(p_sess, "/\r\n\r\n");
185
186 vsf_sysutil_exit(0);
187 }
188
189 return ret;
190 }
191
192 static enum EResourceType
http_request_resource_type(struct vsf_session * p_sess,filesize_t * p_size)193 http_request_resource_type(struct vsf_session* p_sess, filesize_t* p_size)
194 {
195 static struct vsf_sysutil_statbuf* p_statbuf;
196 static struct mystr path = INIT_MYSTR;
197 int retval = 0;
198 enum EResourceType ret = ResTypeFile;
199
200 if (str_equal_text(&p_sess->http_get_arg, "/"))
201 {
202 ret = ResTypeDir;
203 }
204 else
205 {
206 if (str_get_char_at(&p_sess->http_get_arg, str_getlen(&p_sess->http_get_arg) - 1) == '/')
207 {
208 ret++; // _Dir
209 str_left(&p_sess->http_get_arg, &path, str_getlen(&p_sess->http_get_arg) - 1);
210 }
211 else
212 {
213 str_copy(&path, &p_sess->http_get_arg);
214 }
215
216 retval = str_stat(&path, &p_statbuf);
217 if (retval == 0)
218 {
219 if (vsf_sysutil_statbuf_is_dir(p_statbuf))
220 {
221 ret++;
222 struct str_locate_result locate = str_locate_text(&path, "..");
223 if (locate.found && ret == ResTypeDir)
224 {
225 ret--;
226 }
227 }
228 else
229 {
230 if (ret != ResTypeFile)
231 {
232 ret = ResTypeNotFound;
233 }
234 else
235 {
236 *p_size = vsf_sysutil_statbuf_get_size(p_statbuf);
237 }
238 }
239 }
240 else
241 {
242 ret = ResTypeNotFound;
243 }
244 }
245
246 if ((ret == ResTypeDir || ret == ResTypeFile) &&
247 !vsf_access_check_file(&p_sess->http_get_arg))
248 {
249 http_error(p_sess, kVSFHttpMsgType403);
250 }
251 if (ret == ResTypeDir && tunable_http_default_index)
252 {
253 struct mystr old = INIT_MYSTR;
254 str_alloc_text(&old, str_getbuf(&p_sess->http_get_arg));
255 str_append_text(&p_sess->http_get_arg, tunable_http_default_index);
256 ret = http_request_resource_type(p_sess, p_size);
257 if (ret != ResTypeFile)
258 {
259 ret = ResTypeDir;
260 str_alloc_text(&p_sess->http_get_arg, str_getbuf(&old));
261 }
262 str_empty(&old);
263 }
264 if (ret == ResTypeFile)
265 {
266 int opened_file;
267 opened_file = str_open(&p_sess->http_get_arg, kVSFSysStrOpenReadOnly);
268 if (vsf_sysutil_retval_is_error(opened_file))
269 {
270 http_error(p_sess, kVSFHttpMsgType403);
271 }
272 vsf_sysutil_close(opened_file);
273 }
274
275 str_empty(&path);
276
277 return ret;
278 }
279
280 static void
http_handle_send(struct vsf_session * p_sess,filesize_t p_size)281 http_handle_send(struct vsf_session* p_sess, filesize_t p_size)
282 {
283 http_header_print(p_sess, err_200_ok);
284 str_copy(&p_sess->ftp_arg_str, &p_sess->http_get_arg);
285
286 {
287 struct str_locate_result locate;
288 locate = str_locate_text_reverse(&p_sess->ftp_arg_str, ".");
289 if (locate.found == 1)
290 {
291 str_right(&p_sess->ftp_arg_str, &p_sess->ftp_cmd_str, str_getlen(&p_sess->ftp_arg_str) - locate.index - 1);
292 }
293 else
294 {
295 str_empty(&p_sess->ftp_cmd_str);
296 }
297 }
298
299 str_upper(&p_sess->ftp_cmd_str);
300 if (str_equal_text(&p_sess->ftp_cmd_str, "HTML") ||
301 str_equal_text(&p_sess->ftp_cmd_str, "HTM"))
302 {
303 vsf_cmdio_write_raw_quiet(p_sess, "Content-Type: text/html\r\n");
304 }
305 else
306 {
307 vsf_cmdio_write_raw_quiet(p_sess, "Content-Type: dunno\r\n");
308 }
309 vsf_cmdio_write_raw_quiet(p_sess, "Content-Length: ");
310 vsf_cmdio_write_raw_quiet(p_sess, vsf_sysutil_filesize_t_to_str(p_size));
311 vsf_cmdio_write_raw_quiet(p_sess, "\r\n\r\n");
312 p_sess->is_ascii = 0;
313 p_sess->restart_pos = 0;
314 process_http_retr(p_sess);
315 }
316
http_handle_browse(struct vsf_session * p_sess)317 static void http_handle_browse(struct vsf_session* p_sess)
318 {
319 struct vsf_sysutil_dir* p_dir = 0;
320 static struct vsf_sysutil_statbuf* s_p_dirstat;
321 int dir_allow_read = 1;
322 struct mystr_list dir_list = INIT_STRLIST;
323
324 p_dir = str_opendir(&p_sess->http_get_arg);
325 if (p_dir && tunable_anon_world_readable_only)
326 {
327 vsf_sysutil_dir_stat(p_dir, &s_p_dirstat);
328 if (!vsf_sysutil_statbuf_is_readable_other(s_p_dirstat))
329 {
330 dir_allow_read = 0;
331 }
332 }
333 if (p_dir != 0 && dir_allow_read)
334 {
335 static struct mystr s_option_str = INIT_MYSTR;
336 static struct mystr s_filter_str = INIT_MYSTR;
337
338 vsf_ls_populate_dir_list(p_sess, &dir_list, 0, p_dir, &p_sess->http_get_arg,
339 &s_option_str, &s_filter_str, 1, 1);
340 }
341 if (p_dir)
342 {
343 vsf_sysutil_closedir(p_dir);
344 }
345 if (!dir_allow_read)
346 {
347 http_error(p_sess, kVSFHttpMsgType403);
348 }
349 else
350 {
351 static struct mystr p_tpl = INIT_MYSTR;
352 static struct mystr p_res = INIT_MYSTR;
353
354 str_alloc_text(&p_tpl, vsf_http_messages_get(kVSFHttpMsgType200Browse));
355 str_process_template(p_sess, &p_res, &p_tpl, 0, 0, vsf_sysutil_get_time_sec(), &dir_list);
356 str_list_free(&dir_list);
357 http_html_content(p_sess, err_200_ok, &p_res);
358 }
359 }
360