1 /* vi:ai:et:ts=8 sw=2
2 */
3 /*
4 * wzdftpd - a modular and cool ftp server
5 * Copyright (C) 2002-2004 Pierre Chifflier
6 *
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License
9 * as published by the Free Software Foundation; either version 2
10 * of the License, or (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
20 *
21 * As a special exemption, Pierre Chifflier
22 * and other respective copyright holders give permission to link this program
23 * with OpenSSL, and distribute the resulting executable, without including
24 * the source code for OpenSSL in the source distribution.
25 */
26 /** \file wzd_ClientThread.h
27 * \brief Main loop of wzdftpd client.
28 *
29 * This file contains the code which is executed by threads, and
30 * most of the core FTP functions (see RFC 959).
31 */
32
33 #include "wzd_all.h"
34
35 #ifdef WIN32
36
37 #include <winsock2.h>
38 #include <ws2tcpip.h>
39 #include <io.h>
40 #include <sys/utime.h>
41
42 #else
43
44 #include <sys/types.h>
45 #include <sys/socket.h>
46 #include <netinet/in.h>
47 #include <arpa/inet.h>
48
49 #include <netdb.h> /* gethostbyaddr */
50
51 #endif /* WIN32 */
52
53 /** \todo XXX FIXME remove this line and use correct types !!!!
54 * this is used to convert char* to struct in6_addr
55 */
56 #define PORCUS_CAST(x) ( ((struct in6_addr*)(x)) )
57
58 #include <stdio.h>
59 #include <stdlib.h>
60 #include <string.h>
61 #include <time.h>
62 #include <sys/types.h>
63 #include <sys/stat.h>
64 #include <errno.h>
65 #include <fcntl.h>
66
67 #ifdef HAVE_UTIME_H
68 # include <utime.h>
69 #endif
70
71 #ifndef WIN32
72 #include <unistd.h>
73 #include <pthread.h>
74 #endif
75
76 #ifndef HAVE_STRTOK_R
77 # include "libwzd-base/wzd_strtok_r.h"
78 #endif
79
80 #include "wzd_structs.h"
81
82 #include "wzd_fs.h"
83 #include "wzd_ip.h"
84 #include "wzd_log.h"
85 #include "wzd_misc.h"
86 #include "wzd_mod.h"
87 #include "wzd_data.h"
88 #include "wzd_messages.h"
89 #include "wzd_vfs.h"
90 #include "wzd_configfile.h"
91 #include "wzd_crc32.h"
92 #include "wzd_events.h"
93 #include "wzd_file.h"
94 #include "wzd_group.h"
95 #include "wzd_libmain.h"
96 #include "wzd_list.h"
97 #include "wzd_login.h"
98 #include "wzd_perm.h"
99 #include "wzd_protocol.h"
100 #include "wzd_ratio.h"
101 #include "wzd_section.h"
102 #include "wzd_site.h"
103 #include "wzd_string.h"
104 #include "wzd_socket.h"
105 #include "wzd_threads.h"
106 #include "wzd_tls.h"
107 #include "wzd_user.h"
108 #include "wzd_utf8.h"
109 #include "wzd_ClientThread.h"
110
111 #include <libwzd-auth/wzd_base64.h>
112 #include <libwzd-auth/wzd_md5.h>
113
114 #include "wzd_debug.h"
115
116 #define TELNET_SYNCH 242
117 #define TELNET_IP 244
118
119 #define BUFFER_LEN 4096
120
121
122 static int test_fxp(const char * remote_ip, net_family_t family, wzd_context_t * context);
123
124 static int fxp_is_denied(wzd_user_t * user);
125
126 static struct thread_key_t * _key_context = NULL;
127
128
129 /*************** clear_read **************************/
130
131 /** \brief Non-blocking read function
132 *
133 * Try to read length bytes in non-blocking mode for timeout seconds
134 * max. If timeout is null, performs a blocking read.
135 */
clear_read(fd_t sock,char * msg,size_t length,UNUSED int flags,unsigned int timeout,UNUSED void * vcontext)136 int clear_read(fd_t sock, char *msg, size_t length, UNUSED int flags, unsigned int timeout, UNUSED void * vcontext)
137 {
138 /* wzd_context_t * context = (wzd_context_t*)vcontext;*/
139 int ret;
140 int save_errno;
141 fd_set fds, efds;
142 struct timeval tv;
143
144 if (timeout==0)
145 ret = recv(sock,msg,length,0);
146 else {
147 while (1) {
148 FD_ZERO(&fds);
149 FD_ZERO(&efds);
150 FD_SET(sock,&fds);
151 FD_SET(sock,&efds);
152 tv.tv_sec = timeout; tv.tv_usec = 0;
153
154 #if defined(_MSC_VER)
155 ret = select(0,&fds,NULL,&efds,&tv);
156 #else
157 ret = select(sock+1,&fds,NULL,&efds,&tv);
158 #endif
159 save_errno = errno;
160
161 if (FD_ISSET(sock,&fds)) /* ok */
162 break;
163 if (FD_ISSET(sock,&efds)) {
164 if (save_errno == EINTR) continue;
165 out_log(LEVEL_CRITICAL,"Error during recv: %s\n",strerror(save_errno));
166 return -1;
167 }
168 if (!FD_ISSET(sock,&fds)) /* timeout */
169 return 0;
170 break;
171 }
172 ret = recv(sock,msg,length,0);
173 } /* timeout */
174
175 return ret;
176 }
177
178 /*************** clear_write *************************/
179
180 /** \brief Non-blocking write function
181 *
182 * Try to write length bytes in non-blocking mode for timeout seconds
183 * max. If timeout is null, performs a blocking write.
184 */
clear_write(fd_t sock,const char * msg,size_t length,UNUSED int flags,unsigned int timeout,UNUSED void * vcontext)185 int clear_write(fd_t sock, const char *msg, size_t length, UNUSED int flags, unsigned int timeout, UNUSED void * vcontext)
186 {
187 /* wzd_context_t * context = (wzd_context_t*)vcontext;*/
188 int ret;
189 int done;
190 int save_errno;
191 fd_set fds, efds;
192 struct timeval tv;
193
194 done=0;
195 while (length>0) {
196 if (timeout==0)
197 ret = send(sock,msg+done,length,0);
198 else {
199 while (1) {
200 FD_ZERO(&fds);
201 FD_ZERO(&efds);
202 FD_SET(sock,&fds);
203 FD_SET(sock,&efds);
204 tv.tv_sec = timeout; tv.tv_usec = 0;
205
206 #if defined(_MSC_VER)
207 ret = select(0,NULL,&fds,&efds,&tv);
208 #else
209 ret = select(sock+1,NULL,&fds,&efds,&tv);
210 #endif
211 save_errno = errno;
212
213 if (FD_ISSET(sock,&fds)) /* break */
214 break;
215 if (FD_ISSET(sock,&efds)) {
216 if (save_errno == EINTR) continue;
217 out_log(LEVEL_CRITICAL,"Error during send: %s\n",strerror(save_errno));
218 return -1;
219 }
220 if (!FD_ISSET(sock,&fds)) /* timeout */
221 {
222 out_log(LEVEL_CRITICAL,"Timeout during send\n");
223 return 0;
224 }
225 break;
226 }
227 ret = send(sock,msg+done,length,0);
228 if (ret==-1) return ret;
229 } /* timeout */
230 done += ret;
231 length -= ret;
232 }
233
234 return done;
235 }
236
237 /***************** client_die ************************/
238 /** \brief Cleanup code
239 *
240 * Called whenever a connection with a client is closed (for any reason).
241 * Closes all files/sockets.
242 */
client_die(wzd_context_t * context)243 void client_die(wzd_context_t * context)
244 {
245 #ifdef DEBUG
246 out_log(LEVEL_FLOOD,"client_die(context = %p)\n",context);
247 #endif
248
249 if (context == NULL) return;
250
251 if (context->magic != CONTEXT_MAGIC) {
252 #ifdef DEBUG
253 out_err(LEVEL_HIGH,"clientThread: context->magic is invalid at exit\n");
254 #endif
255 return;
256 }
257
258 /* close opened files */
259 if (context->current_action.current_file != (fd_t)-1) {
260 data_end_transfer( (context->current_action.token == TOK_STOR) /* is_upload */, 0 /* end_ok */, context);
261 }
262
263 {
264 wzd_user_t * user = GetUserByID(context->userid);
265 wzd_string_t * event_args = NULL;
266
267 if (user) {
268 event_args = STR(user->username);
269 event_send(mainConfig->event_mgr, EVENT_LOGOUT, 0, event_args, context);
270 str_deallocate(event_args);
271 }
272 }
273
274
275 out_log(LEVEL_INFO,"Client dying (socket %d)\n",context->controlfd);
276 /* close existing pasv connections */
277 if (context->pasvsock != (fd_t)-1) {
278 socket_close(context->pasvsock);
279 FD_UNREGISTER(context->pasvsock,"Client PASV socket");
280 context->pasvsock = -1;
281 }
282 if (context->datafd != (fd_t)-1) {
283 #if defined(HAVE_OPENSSL) || defined(HAVE_GNUTLS)
284 /* if TLS, shutdown TLS before closing data connection */
285 tls_close_data(context);
286 #endif
287 socket_close(context->datafd);
288 FD_UNREGISTER(context->datafd,"Client data fd");
289 }
290 context->datafd = -1;
291 #if defined(HAVE_OPENSSL) || defined(HAVE_GNUTLS)
292 /* if TLS, shutdown TLS before closing control connection */
293 tls_free(context);
294 #endif
295 socket_close(context->controlfd);
296 FD_UNREGISTER(context->controlfd,"Client socket");
297 context->controlfd = -1;
298
299 wzd_tls_free(_key_context);
300 _key_context = NULL;
301
302 context_remove(context_list,context);
303 }
304
305 /*************** check_timeout ***********************/
306
check_timeout(wzd_context_t * context)307 int check_timeout(wzd_context_t * context)
308 {
309 time_t t, delay;
310 wzd_group_t *gptr;
311 unsigned int i;
312 int ret;
313 wzd_user_t * user;
314
315 user = GetUserByID(context->userid);
316
317 WZD_ASSERT( user != NULL);
318 if (user == NULL) return 0;
319
320 /* reset global ul/dl counters */
321 mainConfig->global_ul_limiter.bytes_transfered = 0;
322 #ifndef _MSC_VER
323 gettimeofday(&(mainConfig->global_ul_limiter.current_time),NULL);
324 mainConfig->global_dl_limiter.bytes_transfered = 0;
325 gettimeofday(&(mainConfig->global_dl_limiter.current_time),NULL);
326 #else
327 _ftime(&(mainConfig->global_ul_limiter.current_time));
328 mainConfig->global_dl_limiter.bytes_transfered = 0;
329 _ftime(&(mainConfig->global_dl_limiter.current_time));
330 #endif
331
332 /* check the timeout of control connection */
333 t = time(NULL);
334 delay = t - context->idle_time_start;
335
336 /* check timeout if transfer in progress ? */
337 if (context->current_action.token == TOK_STOR || context->current_action.token == TOK_RETR)
338 {
339 time_t data_delay;
340 data_delay = t - context->idle_time_data_start;
341 if (data_delay > HARD_XFER_TIMEOUT) {
342 /* send events here allow sfv checker to mark file as bad if
343 * partially uploaded
344 */
345 {
346 wzd_string_t * event_args = str_allocate();
347 str_sprintf(event_args,"%s %s",user->username,context->current_action.arg);
348 event_send(mainConfig->event_mgr, EVENT_POSTUPLOAD, 0, event_args, context);
349 str_deallocate(event_args);
350 }
351 file_close(context->current_action.current_file,context);
352 FD_UNREGISTER(context->current_action.current_file,"Client file (RETR or STOR)");
353 context->current_action.current_file = -1;
354 context->current_action.bytesnow = 0;
355 context->current_action.token = TOK_UNKNOWN;
356 data_close(context);
357 ret = send_message(426,context);
358 /* limiter_free(context->current_limiter);
359 context->current_limiter = NULL;*/
360 }
361 /* during a xfer, connection timeouts are not checked */
362 return 0;
363 }
364
365 /* if user has 'idle' flag we check nothing */
366 if (user->flags && strchr(user->flags,FLAG_IDLE))
367 return 0;
368
369 /* first we check user specific timeout */
370 if (user->max_idle_time>0) {
371 if (delay > (time_t)user->max_idle_time) {
372 /* TIMEOUT ! */
373 send_message_with_args(421,context,"Timeout, closing connection");
374 {
375 char inet_str[256];
376 int af = (context->family == WZD_INET6) ? AF_INET6 : AF_INET;
377 inet_str[0] = '\0';
378 inet_ntop(af,context->hostip,inet_str,sizeof(inet_str));
379 log_message("TIMEOUT","%s (%s) timed out after being idle %d seconds",
380 user->username,
381 inet_str,
382 (int)delay
383 );
384 }
385 context->exitclient = 1;
386 #ifdef WZD_MULTIPROCESS
387 exit(0);
388 #else /* WZD_MULTIPROCESS */
389 return 0;
390 #endif /* WZD_MULTIPROCESS */
391 }
392 }
393
394 /* next we check for all groups */
395 for (i=0; i<user->group_num; i++) {
396 gptr = GetGroupByID(user->groups[i]);
397 if (gptr && gptr->max_idle_time > 0) {
398 if (delay > (time_t)gptr->max_idle_time) {
399 /* TIMEOUT ! */
400 send_message_with_args(421,context,"Timeout, closing connection");
401 {
402 char inet_str[256];
403 int af = (context->family == WZD_INET6) ? AF_INET6 : AF_INET;
404 inet_str[0] = '\0';
405 inet_ntop(af,context->hostip,inet_str,sizeof(inet_str));
406 log_message("TIMEOUT","%s (%s) timed out after being idle %d seconds",
407 user->username,
408 inet_str,
409 (int)delay
410 );
411 }
412 context->exitclient = 1;
413 #ifdef WZD_MULTIPROCESS
414 exit(0);
415 #else /* WZD_MULTIPROCESS */
416 return 1;
417 #endif /* WZD_MULTIPROCESS */
418 }
419 } /* if max_idle_time*/
420 }
421
422 return 0;
423 }
424
425 /*************** do_chdir ****************************/
426
do_chdir(const char * wanted_path,wzd_context_t * context)427 int do_chdir(const char * wanted_path, wzd_context_t *context)
428 {
429 int ret;
430 char allowed[WZD_MAX_PATH],path[WZD_MAX_PATH], * ptr;
431 fs_filestat_t buf;
432 wzd_user_t * user;
433
434 user = GetUserByID(context->userid);
435
436 if ( !(user->userperms & RIGHT_CWD) ) return E_NOPERM;
437
438 if (!wanted_path) return E_WRONGPATH;
439 ret = checkpath_new(wanted_path,path,context);
440 if (ret) return ret;
441 snprintf(allowed,WZD_MAX_PATH,"%s/",user->rootpath);
442
443 /* deny retrieve to permissions file */
444 if (is_hidden_file(path))
445 return E_FILE_FORBIDDEN;
446
447 REMOVE_TRAILING_SLASH(path);
448
449 if (!fs_file_stat(path,&buf)) {
450 if (S_ISDIR(buf.mode)) {
451 char buffer[WZD_MAX_PATH], buffer2[WZD_MAX_PATH];
452 if (wanted_path[0] == '/') { /* absolute path */
453 wzd_strncpy(buffer,wanted_path,WZD_MAX_PATH);
454 } else {
455 wzd_strncpy(buffer,context->currentpath,WZD_MAX_PATH);
456 if (buffer[strlen(buffer)-1] != '/')
457 strlcat(buffer,"/",WZD_MAX_PATH);
458 strlcat(buffer,wanted_path,WZD_MAX_PATH);
459 }
460 stripdir(buffer,buffer2,WZD_MAX_PATH-1);
461 /*out_err(LEVEL_INFO,"DIR: %s NEW DIR: %s\n",buffer,buffer2);*/
462 wzd_strncpy(context->currentpath,buffer2,WZD_MAX_PATH-1);
463 }
464 else return E_NOTDIR;
465 }
466 else return E_FILE_NOEXIST;
467
468 ptr = stripdir(context->currentpath,path,sizeof(path));
469 if (ptr) {
470 wzd_strncpy(context->currentpath,path,WZD_MAX_PATH-1);
471 }
472
473 return E_OK;
474 }
475
476 /*************** waitaccept **************************/
477
waitaccept(wzd_context_t * context)478 int waitaccept(wzd_context_t * context)
479 {
480 fd_set fds;
481 struct timeval tv;
482 fd_t sock;
483 unsigned char remote_host[16];
484 unsigned int remote_port;
485 wzd_user_t * user;
486
487 user = GetUserByID(context->userid);
488
489 {
490 if (user && strchr(user->flags,FLAG_TLS_DATA) && context->tls_data_mode != TLS_PRIV) {
491 send_message_with_args(501,context,"Your class must use encrypted data connections");
492 return -1;
493 }
494 }
495
496 sock = context->pasvsock;
497 do {
498 FD_ZERO(&fds);
499 FD_SET(sock,&fds);
500 tv.tv_sec=HARD_XFER_TIMEOUT; tv.tv_usec=0L; /* FIXME - HARD_XFER_TIMEOUT should be a variable */
501
502 if (select(sock+1,&fds,NULL,NULL,&tv) <= 0) {
503 out_err(LEVEL_FLOOD,"accept timeout to client %s:%d.\n",__FILE__,__LINE__);
504 FD_UNREGISTER(context->pasvsock,"Client PASV socket");
505 socket_close(context->pasvsock);
506 context->pasvsock = -1;
507 send_message_with_args(501,context,"PASV timeout");
508 return -1;
509 }
510 } while (!FD_ISSET(sock,&fds));
511
512 sock = socket_accept(context->pasvsock, remote_host, &remote_port, &context->datafamily);
513 if (sock == (fd_t)-1) {
514 out_err(LEVEL_FLOOD,"accept failed to client %s:%d.\n",__FILE__,__LINE__);
515 out_err(LEVEL_FLOOD,"errno is %d:%s.\n",errno,strerror(errno));
516 FD_UNREGISTER(context->pasvsock,"Client PASV socket");
517 socket_close(context->pasvsock);
518 context->pasvsock = -1;
519 send_message_with_args(501,context,"PASV timeout");
520 return -1;
521 }
522
523 if (fxp_is_denied(user) && test_fxp((const char*)remote_host,context->datafamily,context) != 0) {
524 memset(context->dataip,0,16);
525 FD_UNREGISTER(context->pasvsock,"Client PASV socket");
526 socket_close(context->pasvsock);
527 context->pasvsock = -1;
528 socket_close(sock);
529 sock = -1;
530 send_message_with_args(501,context,"FXP not allowed");
531 return -1;
532 }
533
534 /** \todo check destination port for security: >= 1024 */
535
536
537 #if defined(HAVE_OPENSSL) || defined(HAVE_GNUTLS)
538 if (context->tls_data_mode == TLS_PRIV) {
539 int ret;
540 ret = tls_init_datamode(sock, context);
541 if (ret) {
542 out_err(LEVEL_INFO,"WARNING TLS data negotiation failed with client %s:%d.\n",__FILE__,__LINE__);
543 FD_UNREGISTER(context->pasvsock,"Client PASV socket");
544 socket_close(context->pasvsock);
545 context->pasvsock = -1;
546 socket_close(sock);
547 sock = -1;
548 send_message_with_args(426,context,"Data connection closed (SSL/TLS negotiation failed).");
549 return -1;
550 }
551 }
552 #endif
553
554 socket_close (context->pasvsock);
555 FD_UNREGISTER(context->pasvsock,"Client PASV socket");
556 context->pasvsock = sock;
557
558 context->datafd = sock;
559 context->datamode = DATA_PASV;
560
561 return sock;
562 }
563
564 /*************** waitconnect *************************/
565
waitconnect(wzd_context_t * context)566 int waitconnect(wzd_context_t * context)
567 {
568 int sock;
569 int ret;
570
571 {
572 wzd_user_t * user;
573 user = GetUserByID(context->userid);
574 if (user && strchr(user->flags,FLAG_TLS_DATA) && context->tls_data_mode != TLS_PRIV) {
575 send_message_with_args(501,context,"Your class must use encrypted data connections");
576 return -1;
577 }
578 }
579
580 if (context->datafamily == WZD_INET4)
581 {
582
583 /** \todo TODO XXX FIXME check ipv4 IP at this point ! */
584
585 ret = send_message(150,context); /* about to open data connection */
586 sock = socket_connect(context->dataip,context->datafamily,context->dataport,context->localport-1,context->controlfd,HARD_XFER_TIMEOUT);
587 if (sock == -1) {
588 ret = send_message(425,context);
589 return -1;
590 }
591
592 #if defined(HAVE_OPENSSL) || defined(HAVE_GNUTLS)
593 if (context->tls_data_mode == TLS_PRIV) {
594 ret = tls_init_datamode(sock, context);
595 if (ret) {
596 send_message_with_args(426,context,"Data connection closed (SSL/TLS negotiation failed).");
597 return -1;
598 }
599 }
600 #endif
601
602 } /* context->datafamily == WZD_INET4 */
603 #if defined(IPV6_SUPPORT)
604 else if (context->datafamily == WZD_INET6)
605 {
606
607 /** \todo TODO XXX FIXME check ipv6 IP at this point ! */
608
609 ret = send_message(150,context); /* about to open data connection */
610 sock = socket_connect(context->dataip,context->datafamily,context->dataport,context->localport-1,context->controlfd,HARD_XFER_TIMEOUT);
611 if (sock == -1) {
612 out_log(LEVEL_FLOOD,"Error establishing PORT connection: %s (%d)\n",strerror(errno),errno);
613 ret = send_message(425,context);
614 return -1;
615 }
616
617 #if defined(HAVE_OPENSSL) || defined(HAVE_GNUTLS)
618 if (context->tls_data_mode == TLS_PRIV) {
619 ret = tls_init_datamode(sock, context);
620 if (ret) {
621 send_message_with_args(426,context,"Data connection closed (SSL/TLS negotiation failed).");
622 return -1;
623 }
624 }
625 #endif
626
627 } /* context->datafamily == WZD_INET6 */
628 #endif /* IPV6_SUPPORT */
629 else
630 {
631 out_err(LEVEL_CRITICAL,"Invalid protocol %s:%d\n",__FILE__,__LINE__);
632 ret = send_message(425,context);
633 return -1;
634 }
635
636 return sock;
637 }
638
639 /*************** list_callback ***********************/
640
list_callback(fd_t sock,wzd_context_t * context,char * line)641 int list_callback(fd_t sock, wzd_context_t * context, char *line)
642 {
643 fd_set fds;
644 struct timeval tv;
645
646 do {
647 FD_ZERO(&fds);
648 FD_SET(sock,&fds);
649 tv.tv_sec=HARD_XFER_TIMEOUT; tv.tv_usec=0L; /* FIXME - HARD_XFER_TIMEOUT should be a variable */
650
651 if (select(sock+1,NULL,&fds,NULL,&tv) <= 0) {
652 out_err(LEVEL_FLOOD,"LIST timeout to client.\n");
653 socket_close(sock);
654 send_message_with_args(501,context,"LIST timeout");
655 return 0;
656 }
657 } while (!FD_ISSET(sock,&fds));
658
659 #if defined(HAVE_OPENSSL) || defined(HAVE_GNUTLS)
660 if (context->tls_data_mode == TLS_CLEAR)
661 clear_write(sock,line,strlen(line),0,HARD_XFER_TIMEOUT,context);
662 else
663 #endif
664 (context->write_fct)(sock,line,strlen(line),0,HARD_XFER_TIMEOUT,context);
665
666 return 1;
667 }
668
669 /*************** do_list *****************************/
670
do_list(wzd_string_t * name,wzd_string_t * arg,wzd_context_t * context)671 int do_list(wzd_string_t *name, wzd_string_t *arg, wzd_context_t * context)
672 {
673 char mask[1024],cmd[WZD_MAX_PATH], *path;
674 int ret,n;
675 fd_t sock;
676 char nullch[8];
677 char * cmask;
678 const char * param;
679 wzd_user_t * user;
680 enum list_type_t listtype;
681
682 user = GetUserByID(context->userid);
683
684 if ( !(user->userperms & RIGHT_LIST) ) {
685 ret = send_message_with_args(550,context,"LIST","No access");
686 return E_NOPERM;
687 }
688
689 if (!str_checklength(arg, 0, WZD_MAX_PATH-10))
690 {
691 ret = send_message_with_args(501,context,"Argument or parameter too big.");
692 return E_PARAM_BIG;
693 }
694 param = str_tochar(arg);
695
696 if (context->pasvsock == (fd_t)-1 && context->dataport == 0)
697 {
698 ret = send_message_with_args(501,context,"No data connection available.");
699 return E_NO_DATA_CTX;
700 }
701 if (context->state == STATE_XFER) {
702 ret = send_message(491,context);
703 return E_XFER_PROGRESS;
704 }
705
706 if (strcasecmp(str_tochar(name),"nlst")==0)
707 listtype = LIST_TYPE_SHORT;
708 else
709 listtype = LIST_TYPE_LONG;
710
711 context->resume = 0;
712
713 strcpy(nullch,".");
714 mask[0] = '\0';
715 if (param) {
716
717 while (param[0]=='-') {
718 n=1;
719 while (param[n]!=' ' && param[n]!=0) {
720 switch (param[n]) {
721 case 'a':
722 listtype |= LIST_SHOW_HIDDEN;
723 }
724 n++;
725 }
726 if (param[n]==' ') param = param+n+1;
727 else param = param+n;
728 }
729
730 wzd_strncpy(cmd,param,sizeof(cmd));
731 if (cmd[0] != '\0' && cmd[strlen(cmd)-1]=='/') cmd[strlen(cmd)-1]='\0';
732
733 if (strrchr(cmd,'*') || strrchr(cmd,'?')) /* wildcards */
734 {
735 char *ptr;
736 if (strrchr(cmd,'/')) { /* probably not in current path - need to readjust path */
737 if (strrchr(cmd,'/') > strrchr(cmd,'*')) {
738 /* char / is AFTER *, dir style: toto / * / .., we refuse */
739 ret = send_message_with_args(501,context,"You can't put wildcards in the middle of path, only in the last part.");
740 return 1;
741 }
742 ptr = strrchr(cmd,'/');
743 strncpy(cmd,ptr+1,WZD_MAX_PATH);
744 *ptr = '\0';
745 } else { /* simple wildcard */
746 strncpy(mask,cmd,sizeof(mask));
747 cmd[0] = '\0';
748 }
749 }
750 if (strrchr(cmd,'*') || strrchr(cmd,'?')) { /* wildcards in path ? ough */
751 ret = send_message_with_args(501,context,"You can't put wildcards in the middle of path, only in the last part.");
752 return E_PARAM_INVALID;
753 }
754 } else { /* no param, assume list of current dir */
755 cmd[0] = '\0';
756 param = nullch;
757 }
758
759 if (param[0]=='/') param++;
760 if (param[0]=='/') {
761 ret = send_message_with_args(501,context,"Too many / in the path - is it a joke?");
762 return E_PARAM_INVALID;
763 }
764
765 cmask = strrchr(mask,'/');
766 if (cmask) { /* search file in path (with /), but without wildcards */
767 *cmask='\0';
768 strlcat(cmd,"/",WZD_MAX_PATH);
769 strlcat(cmd,mask,WZD_MAX_PATH);
770 strncpy(mask,cmask,sizeof(mask));
771 }
772
773 /*#ifdef DEBUG
774 printf("path before: '%s'\n",cmd);
775 #endif*/
776
777 path = wzd_malloc(WZD_MAX_PATH+1);
778 if ((ret = checkpath_new(cmd,path,context)) || !strncmp(mask,"..",2)) {
779 switch (ret) {
780 case E_NOTDIR:
781 /* return 501 for syntax error, see rfc3659 at section 7.2.1 */
782 ret = send_message_with_args(501,context,"Not a directory");
783 break;
784 case E_WRONGPATH:
785 ret = send_message_with_args(550,context,"LIST","Invalid path");
786 break;
787 case E_FILE_NOEXIST:
788 ret = send_message_with_args(550,context,"LIST","No such file or directory (no access?)");
789 break;
790 case E_FILE_FORBIDDEN:
791 case E_NOPERM:
792 ret = send_message_with_args(550,context,"LIST","Negative on that, Houston (access denied)");
793 break;
794 default:
795 ret = send_message_with_args(501,context,"LIST failed (syntax error?)");
796 break;
797 }
798 wzd_free(path);
799 return E_PARAM_INVALID;
800 }
801
802 REMOVE_TRAILING_SLASH(path);
803
804 /*#ifdef DEBUG
805 printf("path: '%s'\n",path);
806 #endif*/
807
808 /* CHECK PERM */
809 ret = _checkPerm(path,RIGHT_LIST,user);
810
811 if (ret) { /* no access */
812 ret = send_message_with_args(550,context,"LIST","No access");
813 wzd_free(path);
814 return E_NOPERM;
815 }
816
817 if (context->pasvsock == (fd_t)-1) { /* PORT ! */
818
819 /** \todo TODO check that ip is correct - no trying to fxp LIST ??!! */
820
821 sock = waitconnect(context);
822 if (sock == (fd_t)-1) {
823 /* note: reply is done in waitconnect() */
824 wzd_free(path);
825 return E_CONNECTTIMEOUT;
826 }
827
828 } else { /* PASV ! */
829 ret = send_message(150,context); /* about to open data connection */
830 if ((sock=waitaccept(context)) == (fd_t)-1) {
831 /* note: reply is done in waitaccept() */
832 wzd_free(path);
833 return E_PASV_FAILED;
834 }
835 context->pasvsock = -1;
836 }
837 FD_REGISTER(sock,"Client LIST socket");
838
839 context->state = STATE_XFER;
840
841 if (strlen(mask)==0) strcpy(mask,"*");
842
843 if (list(sock,context,listtype,path,mask,list_callback))
844 ret = send_message(226,context);
845 else
846 ret = send_message_with_args(501,context,"Error processing list");
847
848 wzd_free(path);
849
850 #if defined(HAVE_OPENSSL) || defined(HAVE_GNUTLS)
851 if (context->tls_data_mode == TLS_PRIV)
852 ret = tls_close_data(context);
853 #endif
854 ret = socket_close(sock);
855 FD_UNREGISTER(sock,"Client LIST socket");
856 context->datafd = -1;
857 context->idle_time_start = time(NULL);
858 context->state = STATE_UNKNOWN;
859
860 return E_OK;
861 }
862
863
864 /*************** do_mlsd *****************************/
865
do_mlsd(UNUSED wzd_string_t * name,wzd_string_t * param,wzd_context_t * context)866 int do_mlsd(UNUSED wzd_string_t *name, wzd_string_t *param, wzd_context_t * context)
867 {
868 int ret;
869 wzd_user_t * user;
870 fd_t sock;
871 char * path;
872
873 user = GetUserByID(context->userid);
874
875 if ( !(user->userperms & RIGHT_LIST) ) {
876 ret = send_message_with_args(550,context,"MLSD","No access");
877 return E_NOPERM;
878 }
879
880 if (context->pasvsock == (fd_t)-1 && context->dataport == 0)
881 {
882 ret = send_message_with_args(501,context,"No data connection available.");
883 return E_NO_DATA_CTX;
884 }
885 if (context->state == STATE_XFER) {
886 ret = send_message(491,context);
887 return E_XFER_PROGRESS;
888 }
889
890 path = wzd_malloc(WZD_MAX_PATH+1);
891 ret = checkpath_new(str_tochar(param),path,context);
892 if (ret != 0) {
893 switch (ret) {
894 case E_NOTDIR:
895 /* return 501 for syntax error, see rfc3659 at section 7.2.1 */
896 ret = send_message_with_args(501,context,"Not a directory");
897 break;
898 case E_WRONGPATH:
899 ret = send_message_with_args(550,context,"MLSD","Invalid path");
900 break;
901 case E_FILE_NOEXIST:
902 ret = send_message_with_args(550,context,"MLSD","No such file or directory (no access?)");
903 break;
904 case E_FILE_FORBIDDEN:
905 case E_NOPERM:
906 ret = send_message_with_args(550,context,"MLSD","Negative on that, Houston (access denied)");
907 break;
908 default:
909 ret = send_message_with_args(501,context,"MLSD failed (syntax error?)");
910 break;
911 }
912 wzd_free(path);
913 return E_PARAM_INVALID;
914 }
915
916 REMOVE_TRAILING_SLASH(path);
917
918 /* CHECK PERM */
919 ret = _checkPerm(path,RIGHT_LIST,user);
920
921 if (ret) { /* no access */
922 ret = send_message_with_args(550,context,"LIST","No access");
923 wzd_free(path);
924 return E_NOPERM;
925 }
926
927 if (context->pasvsock == (fd_t)-1) { /* PORT ! */
928
929 /** \todo TODO check that ip is correct - no trying to fxp LIST ??!! */
930
931 sock = waitconnect(context);
932 if (sock == (fd_t)-1) {
933 /* note: reply is done in waitconnect() */
934 wzd_free(path);
935 return E_CONNECTTIMEOUT;
936 }
937
938 } else { /* PASV ! */
939 ret = send_message(150,context); /* about to open data connection */
940 if ((sock=waitaccept(context)) == (fd_t)-1) {
941 /* note: reply is done in waitaccept() */
942 wzd_free(path);
943 return E_PASV_FAILED;
944 }
945 context->pasvsock = -1;
946 }
947 FD_REGISTER(sock,"Client MLSD socket");
948
949 context->state = STATE_XFER;
950
951
952
953 if (!mlsd_directory(path,sock,list_callback,context))
954 ret = send_message(226,context);
955 else
956 ret = send_message_with_args(501,context,"Error processing list");
957
958
959 wzd_free(path);
960
961 #if defined(HAVE_OPENSSL) || defined(HAVE_GNUTLS)
962 if (context->tls_data_mode == TLS_PRIV)
963 ret = tls_close_data(context);
964 #endif
965 ret = socket_close(sock);
966 FD_UNREGISTER(sock,"Client MLSD socket");
967 context->datafd = -1;
968 context->idle_time_start = time(NULL);
969 context->state = STATE_UNKNOWN;
970
971 return E_OK;
972 }
973
974 /*************** do_mlst *****************************/
975
do_mlst(UNUSED wzd_string_t * name,wzd_string_t * param,wzd_context_t * context)976 int do_mlst(UNUSED wzd_string_t *name, wzd_string_t *param, wzd_context_t * context)
977 {
978 int ret;
979 wzd_user_t * user;
980 char * path;
981 char * str;
982
983 user = GetUserByID(context->userid);
984
985 /* stat has the same behaviour as LIST */
986 if ( !(user->userperms & RIGHT_LIST) ) {
987 ret = send_message_with_args(550,context,"MLST","No access");
988 return E_NOPERM;
989 }
990
991 if (!param || strlen(str_tochar(param))==0)
992 {
993 ret = send_message_with_args(501,context,"Usage: MLST filename");
994 return E_PARAM_BIG;
995 }
996
997 if (!str_checklength(param, 1, WZD_MAX_PATH-10))
998 {
999 ret = send_message_with_args(501,context,"Argument or parameter too big.");
1000 return E_PARAM_BIG;
1001 }
1002
1003 context->state = STATE_COMMAND;
1004
1005 path = wzd_malloc(WZD_MAX_PATH+1);
1006 ret = checkpath_new(str_tochar(param),path,context);
1007 if (ret != 0) {
1008 switch (ret) {
1009 /* \todo enable MLST command to work on files and not just directories */
1010 case E_NOTDIR:
1011 /* return 501 for syntax error, see rfc3659 at section 7.2.1 */
1012 ret = send_message_with_args(501,context,"Not a directory");
1013 break;
1014 case E_WRONGPATH:
1015 ret = send_message_with_args(550,context,"MLST","Invalid path");
1016 break;
1017 case E_FILE_NOEXIST:
1018 ret = send_message_with_args(550,context,"MLST","No such file or directory (no access?)");
1019 break;
1020 case E_FILE_FORBIDDEN:
1021 case E_NOPERM:
1022 ret = send_message_with_args(550,context,"MLST","Negative on that, Houston (access denied)");
1023 break;
1024 default:
1025 ret = send_message_with_args(501,context,"MLST failed (syntax error?)");
1026 break;
1027 }
1028 wzd_free(path);
1029 return E_PARAM_INVALID;
1030 }
1031
1032 REMOVE_TRAILING_SLASH(path);
1033
1034 if ( (str = mlst_single_file(path, context)) == NULL) {
1035 ret = send_message_with_args(501,context,"Error occurred");
1036 wzd_free(path);
1037 return E_PARAM_INVALID;
1038 }
1039
1040 strcat(str,"\r\n"); /* TODO check size */
1041
1042
1043 {
1044 wzd_string_t * buffer = str_allocate();
1045 str_sprintf(buffer,"250- Listing %s\r\n",str_tochar(param));
1046 send_message_raw(str_tochar(buffer),context);
1047 str_deallocate(buffer);
1048 }
1049
1050 send_message_raw(str,context);
1051
1052 ret = send_message_raw("250 End\r\n",context);
1053
1054 context->idle_time_start = time(NULL);
1055 context->state = STATE_UNKNOWN;
1056
1057 wzd_free(path);
1058 wzd_free(str);
1059
1060 return E_OK;
1061 }
1062
1063 /*************** do_opts *****************************/
do_opts(UNUSED wzd_string_t * name,wzd_string_t * param,wzd_context_t * context)1064 int do_opts(UNUSED wzd_string_t *name, wzd_string_t *param, wzd_context_t * context)
1065 {
1066 const char *ptr;
1067 int ret;
1068
1069 ptr = str_tochar(param);
1070
1071 if (strncasecmp(ptr,"UTF8",4)==0)
1072 {
1073 ptr += 4;
1074 if (*ptr++ != ' ') goto label_opts_error;
1075
1076 #ifdef HAVE_UTF8
1077 if (strncasecmp(ptr,"ON",2)==0)
1078 {
1079 context->connection_flags |= CONNECTION_UTF8;
1080 ret = send_message_with_args(200, context, "UTF8 OPTS ON");
1081 return 0;
1082 }
1083 else if (strncasecmp(ptr,"OFF",2)==0)
1084 {
1085 context->connection_flags &= ~(CONNECTION_UTF8);
1086 ret = send_message_with_args(200, context, "UTF8 OPTS OFF");
1087 return 0;
1088 }
1089 #endif
1090 /* let it go to error return */
1091 } /* UTF8 */
1092 if (strncasecmp(ptr,"MLST",4)==0)
1093 {
1094 /** \todo XXX FIXME implement options support for MLST */
1095 ret = send_message_with_args(200, context, "MLST OPTS Type;Size;Modify;Perm;UNIX.mode;");
1096 return 0;
1097 } /* MLST */
1098
1099 label_opts_error:
1100 ret = send_message_with_args(501,context,"OPTS option not recognized");
1101
1102 return 0;
1103 }
1104
1105 /*************** do_stat *****************************/
1106
do_stat(UNUSED wzd_string_t * name,wzd_string_t * arg,wzd_context_t * context)1107 int do_stat(UNUSED wzd_string_t *name, wzd_string_t *arg, wzd_context_t * context)
1108 {
1109 char mask[1024],cmd[WZD_MAX_PATH], *path;
1110 int ret,n;
1111 fd_t sock;
1112 char nullch[8];
1113 char * cmask;
1114 const char *param;
1115 wzd_user_t * user;
1116 enum list_type_t listtype;
1117 tls_data_mode_t old_data_mode;
1118
1119 user = GetUserByID(context->userid);
1120
1121 /* stat has the same behaviour as LIST */
1122 if ( !(user->userperms & RIGHT_LIST) ) {
1123 ret = send_message_with_args(550,context,"LIST","No access");
1124 return E_NOPERM;
1125 }
1126
1127
1128 if (!str_checklength(arg, 1, WZD_MAX_PATH-10))
1129 {
1130 ret = send_message_with_args(501,context,"Argument or parameter too big.");
1131 return E_PARAM_BIG;
1132 }
1133 param = str_tochar(arg);
1134
1135 listtype = LIST_TYPE_LONG;
1136
1137 context->resume = 0;
1138 context->state = STATE_COMMAND;
1139
1140 strcpy(nullch,".");
1141 mask[0] = '\0';
1142 if (param) {
1143
1144 while (param[0]=='-') {
1145 n=1;
1146 while (param[n]!=' ' && param[n]!=0) {
1147 switch (param[n]) {
1148 case 'a':
1149 listtype |= LIST_SHOW_HIDDEN;
1150 }
1151 n++;
1152 }
1153 if (param[n]==' ') param = param+n+1;
1154 else param = param+n;
1155 }
1156
1157 wzd_strncpy(cmd,param,sizeof(cmd));
1158 if (cmd[strlen(cmd)-1]=='/') cmd[strlen(cmd)-1]='\0';
1159
1160 if (strrchr(cmd,'*') || strrchr(cmd,'?')) /* wildcards */
1161 {
1162 char *ptr;
1163 if (strrchr(cmd,'/')) { /* probably not in current path - need to readjust path */
1164 if (strrchr(cmd,'/') > strrchr(cmd,'*')) {
1165 /* char / is AFTER *, dir style: toto / * / .., we refuse */
1166 ret = send_message_with_args(501,context,"You can't put wildcards in the middle of path, only in the last part.");
1167 return 1;
1168 }
1169 ptr = strrchr(cmd,'/');
1170 strncpy(cmd,ptr+1,WZD_MAX_PATH);
1171 *ptr = '\0';
1172 } else { /* simple wildcard */
1173 strncpy(mask,cmd,sizeof(mask));
1174 cmd[0] = '\0';
1175 }
1176 }
1177 if (strrchr(cmd,'*') || strrchr(cmd,'?')) { /* wildcards in path ? ough */
1178 ret = send_message_with_args(501,context,"You can't put wildcards in the middle of path, only in the last part.");
1179 return E_PARAM_INVALID;
1180 }
1181 } else { /* no param, assume list of current dir */
1182 cmd[0] = '\0';
1183 param = nullch;
1184 }
1185
1186 if (param[0]=='/') param++;
1187 if (param[0]=='/') {
1188 ret = send_message_with_args(501,context,"Too many / in the path - is it a joke?");
1189 return E_PARAM_INVALID;
1190 }
1191
1192 cmask = strrchr(mask,'/');
1193 if (cmask) { /* search file in path (with /), but without wildcards */
1194 *cmask='\0';
1195 strlcat(cmd,"/",WZD_MAX_PATH);
1196 strlcat(cmd,mask,WZD_MAX_PATH);
1197 strncpy(mask,cmask,sizeof(mask));
1198 }
1199
1200 /*#ifdef DEBUG
1201 printf("path before: '%s'\n",cmd);
1202 #endif*/
1203
1204 path = wzd_malloc(WZD_MAX_PATH + 1);
1205 if (checkpath_new(cmd,path,context) || !strncmp(mask,"..",2)) {
1206 ret = send_message_with_args(501,context,"Invalid filter/path");
1207 wzd_free(path);
1208 return E_PARAM_INVALID;
1209 }
1210
1211 REMOVE_TRAILING_SLASH(path);
1212
1213 /*#ifdef DEBUG
1214 printf("path: '%s'\n",path);
1215 #endif*/
1216
1217 /* CHECK PERM */
1218 ret = _checkPerm(path,RIGHT_LIST,user);
1219
1220 if (ret) { /* no access */
1221 ret = send_message_with_args(550,context,"STAT","No access");
1222 wzd_free(path);
1223 return E_NOPERM;
1224 }
1225
1226 sock = context->controlfd;
1227
1228 if (strlen(mask)==0) strcpy(mask,"*");
1229
1230 /* \todo XXX FIXME horrible workaround to avoid sending clear data inside ssl stream */
1231 old_data_mode = context->tls_data_mode;
1232 context->tls_data_mode = (context->connection_flags & CONNECTION_TLS) ? TLS_PRIV : TLS_CLEAR;
1233
1234 send_message_raw("213-Status of .:\r\n",context);
1235 send_message_raw("total 0\r\n",context);
1236 if (list(sock,context,listtype,path,mask,list_callback))
1237 ret = send_message_raw("213 End of Status\r\n",context);
1238 else
1239 ret = send_message_raw("213 Error processing list\r\n",context);
1240
1241 context->idle_time_start = time(NULL);
1242 context->state = STATE_UNKNOWN;
1243 context->tls_data_mode = old_data_mode;
1244
1245 wzd_free(path);
1246
1247 return E_OK;
1248 }
1249
1250 /*************** do_mkdir ****************************/
1251
do_mkdir(UNUSED wzd_string_t * name,wzd_string_t * arg,wzd_context_t * context)1252 int do_mkdir(UNUSED wzd_string_t *name, wzd_string_t *arg, wzd_context_t * context)
1253 {
1254 char * cmd = NULL, * path = NULL;
1255 char * buffer = NULL;
1256 int ret;
1257 wzd_user_t * user;
1258 const char *param;
1259
1260 if (!str_checklength(arg,1,WZD_MAX_PATH-1))
1261 {
1262 ret = send_message_with_args(501,context,"Invalid path");
1263 return E_PARAM_INVALID;
1264 }
1265 param = str_tochar(arg);
1266
1267 cmd = wzd_malloc(WZD_MAX_PATH+1);
1268 path = wzd_malloc(WZD_MAX_PATH+1);
1269 buffer = wzd_malloc(WZD_MAX_PATH+1);
1270
1271 user = GetUserByID(context->userid);
1272
1273 if ( !(user->userperms & RIGHT_MKDIR) ) { ret = E_NOPERM; goto label_error_mkdir; }
1274
1275 if (strcmp(param,"/")==0) { ret = E_WRONGPATH; goto label_error_mkdir; }
1276
1277 if (param[0] != '/') {
1278 strcpy(cmd,".");
1279 if (checkpath_new(cmd,path,context)) { ret = E_WRONGPATH; goto label_error_mkdir; }
1280 if (path[strlen(path)-1]!='/') strcat(path,"/");
1281 strlcat(path,param,WZD_MAX_PATH);
1282 } else {
1283 wzd_strncpy(cmd,param,WZD_MAX_PATH);
1284 ret = checkpath_new(cmd,path,context);
1285 if (ret != E_FILE_NOEXIST) { ret = E_WRONGPATH; goto label_error_mkdir; }
1286 if (path[strlen(path)-1]!='/') strcat(path,"/");
1287 /* if (path[strlen(path)-1]=='/') path[strlen(path)-1]='\0';*/
1288 }
1289 REMOVE_TRAILING_SLASH(path);
1290
1291 ret = checkpath_new(param,buffer,context);
1292 if (ret != E_FILE_NOEXIST) goto label_error_mkdir;
1293
1294 #if DEBUG
1295 if (ret || errno) {
1296 if (ret != E_FILE_NOEXIST)
1297 out_err(LEVEL_FLOOD,"Making directory '%s' (%d, %s %d %d)\n",buffer,ret,strerror(errno),errno,ENOENT);
1298 switch (ret) {
1299 case E_USER_IDONTEXIST: out_log(LEVEL_HIGH,"mkdir: user does not exist !\n"); break;
1300 case E_PARAM_NULL: out_log(LEVEL_HIGH,"mkdir: no input parameter\n"); break;
1301 case E_PARAM_BIG: out_log(LEVEL_HIGH,"mkdir: parameter too long\n"); break;
1302 case E_WRONGPATH: out_log(LEVEL_HIGH,"mkdir: wrong path\n"); break;
1303 case E_FILE_NOEXIST: break; /* not an error ! */
1304 case E_NOPERM: out_log(LEVEL_HIGH,"mkdir: no permission\n"); break;
1305 default:
1306 break;
1307 }
1308 }
1309 else
1310 out_err(LEVEL_FLOOD,"Making directory '%s' (%d)\n",buffer,ret);
1311 #endif
1312
1313 {
1314 wzd_string_t * event_args = str_allocate();
1315 str_sprintf(event_args,"%s %s",user->username,buffer);
1316 ret = event_send(mainConfig->event_mgr, EVENT_PREMKDIR, 0, event_args, context);
1317 str_deallocate(event_args);
1318 }
1319
1320 if (ret != EVENT_OK && ret != EVENT_BREAK) {
1321 out_log(LEVEL_NORMAL, "MKDIR denied by hook (returned %d)\n", ret);
1322 ret = send_message_with_args(501,context,"MKDIR denied");
1323 return E_XFER_REJECTED;
1324 }
1325
1326
1327 if (buffer[strlen(buffer)-1]=='/')
1328 buffer[strlen(buffer)-1]='\0';
1329
1330 /* deny retrieve to permissions file */
1331 if (is_hidden_file(path)) {
1332 wzd_free(buffer);
1333 wzd_free(path);
1334 wzd_free(cmd);
1335 ret = send_message_with_args(553,context,"Forbidden!");
1336 return E_FILE_FORBIDDEN;
1337 }
1338
1339 /** \bug why this test ? it breaks mkdir inside symlinks ! */
1340 /* if (strcmp(path,buffer) != 0) { ret = E_MKDIR_PARSE; goto label_error_mkdir; }*/
1341
1342 /* check section path-filter */
1343 {
1344 char *ptr;
1345 wzd_section_t * section;
1346 wzd_strncpy(path,buffer,WZD_MAX_PATH);
1347 ptr = strrchr(path,'/');
1348 if (ptr && ptr!=&path[0]) {
1349 *ptr='\0';
1350 /* we can reuse cmd */
1351 if (param[0] != '/') {
1352 unsigned int length;
1353 strncpy(cmd,context->currentpath,WZD_MAX_PATH-1-strlen(param));
1354 length = strlen(cmd);
1355 if (cmd[length-1]!='/') {
1356 cmd[length++] = '/';
1357 }
1358 strncpy(cmd+length,param,WZD_MAX_PATH-1-length);
1359 } else {
1360 strncpy(cmd,param,WZD_MAX_PATH);
1361 }
1362 /* we need to give the ftp-relative path here */
1363 section = section_find(mainConfig->section_list,cmd);
1364 if (section && !section_check_filter(section,ptr+1))
1365 {
1366 out_err(LEVEL_FLOOD,"path [%s] does not match path-filter\n",ptr+1);
1367 ret = send_message_with_args(553,context,"Dirname does not match pathfilter");
1368 wzd_free(buffer);
1369 wzd_free(path);
1370 wzd_free(cmd);
1371 return E_MKDIR_PATHFILTER;
1372 }
1373 }
1374 }
1375
1376 context->current_action.token = TOK_MKD;
1377 strncpy(context->current_action.arg,buffer,HARD_LAST_COMMAND_LENGTH);
1378 context->current_action.current_file = -1;
1379
1380 ret = file_mkdir(buffer,0755,context); /* TODO umask ? - should have a variable here */
1381
1382 if (ret) {
1383 if (ret != E_NOPERM)
1384 out_err(LEVEL_FLOOD,"MKDIR returned %d (%s)\n",errno,strerror(errno));
1385 goto label_error_mkdir; /* keep current ret value for later use */
1386 } else {
1387 const char *groupname=NULL;
1388 if (user->group_num > 0) {
1389 groupname = GetGroupByID(user->groups[0])->groupname;
1390 }
1391 file_chown(buffer,user->username,groupname,context);
1392
1393 /* send message header */
1394 send_message_raw("257- Command okay\r\n",context);
1395 {
1396 wzd_string_t * event_args = STR(buffer);
1397 event_send(mainConfig->event_mgr, EVENT_MKDIR, 257, event_args, context);
1398 str_deallocate(event_args);
1399 }
1400 ret = send_message_with_args(257,context,param,"created");
1401
1402 if (param[0] != '/') {
1403 strcpy(buffer,context->currentpath);
1404 strlcat(buffer,"/",WZD_MAX_PATH);
1405 strlcat(buffer,param,WZD_MAX_PATH);
1406 } else {
1407 strcpy(buffer,param);
1408 }
1409 stripdir(buffer,path,WZD_MAX_PATH-1);
1410
1411 log_message("NEWDIR","\"%s\" \"%s\" \"%s\" \"%s\"",
1412 path, /* ftp-absolute path */
1413 user->username,
1414 (groupname)?groupname:"No Group",
1415 user->tagline
1416 );
1417 }
1418 context->idle_time_start = time(NULL);
1419 wzd_free(buffer);
1420 wzd_free(path);
1421 wzd_free(cmd);
1422
1423 return E_OK;
1424
1425 label_error_mkdir:
1426 if (ret == E_NOPERM)
1427 snprintf(buffer,WZD_MAX_PATH-1,"Could not create dir: permission denied");
1428 else
1429 snprintf(buffer,WZD_MAX_PATH-1,"Could not create dir '%s' (%d)",(param)?param:"(NULL)",ret);
1430 send_message_with_args(553,context,buffer);
1431 wzd_free(buffer);
1432 wzd_free(path);
1433 wzd_free(cmd);
1434 return ret;
1435 }
1436
1437 /*************** do_rmdir ****************************/
1438
do_rmdir(UNUSED wzd_string_t * name,wzd_string_t * arg,wzd_context_t * context)1439 int do_rmdir(UNUSED wzd_string_t *name, wzd_string_t * arg, wzd_context_t * context)
1440 {
1441 char path[WZD_MAX_PATH], buffer[WZD_MAX_PATH];
1442 fs_filestat_t s;
1443 int ret;
1444 wzd_user_t * user;
1445 const char *param;
1446
1447 if (!str_checklength(arg,1,WZD_MAX_PATH-1))
1448 {
1449 ret = send_message_with_args(501,context,"Invalid path");
1450 return E_PARAM_INVALID;
1451 }
1452 param = str_tochar(arg);
1453
1454 user = GetUserByID(context->userid);
1455
1456 if ( !(user->userperms & RIGHT_RMDIR) ) { ret = E_NOPERM;; goto label_error_rmdir; }
1457
1458
1459 if (checkpath_new(param,path,context)) { ret = E_WRONGPATH; goto label_error_rmdir; }
1460
1461 /* if path is / terminated, lstat will return the dir itself in case
1462 * of a symlink
1463 */
1464 if (path[strlen(path)-1]=='/')
1465 path[strlen(path)-1]='\0';
1466
1467 /* deny retrieve to permissions file */
1468 if (is_hidden_file(path)) {
1469 ret = send_message_with_args(553,context,"Forbidden!");
1470 return E_FILE_FORBIDDEN;
1471 }
1472
1473 if (fs_file_lstat(path,&s)) { ret = E_FILE_NOEXIST; goto label_error_rmdir; }
1474 if (!S_ISDIR(s.mode)) {
1475 ret = send_message_with_args(553,context,"Not a directory");
1476 return E_NOTDIR;
1477 }
1478
1479 /* check permissions */
1480 ret = file_rmdir(path,context);
1481
1482 if (ret) {
1483 out_err(LEVEL_FLOOD,"RMDIR returned %d (%s)\n",errno,strerror(errno));
1484 ret = E_PARAM_INVALID; goto label_error_rmdir;
1485 } else {
1486 /* send message header */
1487 send_message_raw("258- Command okay\r\n",context);
1488 {
1489 wzd_string_t * event_args = str_allocate();
1490 str_sprintf(event_args,"%s %s",user->username,path);
1491 event_send(mainConfig->event_mgr, EVENT_RMDIR, 258, event_args, context);
1492 str_deallocate(event_args);
1493 }
1494 ret = send_message_with_args(258,context,param,"Removed");
1495
1496 {
1497 const char *groupname=NULL;
1498 char tbuf[WZD_MAX_PATH], path[WZD_MAX_PATH];
1499
1500 if (user->group_num > 0) {
1501 groupname = GetGroupByID(user->groups[0])->groupname;
1502 }
1503
1504 if (param[0] != '/') {
1505 strcpy(tbuf,context->currentpath);
1506 strlcat(tbuf,"/",WZD_MAX_PATH);
1507 strlcat(tbuf,param,WZD_MAX_PATH);
1508 } else {
1509 strcpy(tbuf,param);
1510 }
1511 stripdir(tbuf,path,WZD_MAX_PATH-1);
1512
1513 log_message("DELDIR","\"%s\" \"%s\" \"%s\" \"%s\"",
1514 path, /* ftp-absolute path */
1515 user->username,
1516 (groupname)?groupname:"No Group",
1517 user->tagline
1518 );
1519 }
1520
1521 }
1522
1523 context->idle_time_start = time(NULL);
1524
1525 return E_OK;
1526
1527 label_error_rmdir:
1528 snprintf(buffer,WZD_MAX_PATH-1,"Could not delete dir '%s'",(param)?param:"(NULL)");
1529 send_message_with_args(553,context,buffer);
1530 return ret;
1531 }
1532
1533 /*************** do_port *****************************/
do_port(UNUSED wzd_string_t * name,wzd_string_t * args,wzd_context_t * context)1534 int do_port(UNUSED wzd_string_t *name, wzd_string_t *args, wzd_context_t * context)
1535 {
1536 int a0,a1,a2,a3;
1537 unsigned int p1, p2;
1538 int ret;
1539 wzd_user_t * user;
1540
1541 if (context->pasvsock != (fd_t)-1) {
1542 socket_close(context->pasvsock);
1543 context->pasvsock = -1;
1544 }
1545 if (!args) {
1546 ret = send_message_with_args(501,context,"Invalid parameters");
1547 return E_PARAM_NULL;
1548 }
1549 if ((sscanf(str_tochar(args),"%d,%d,%d,%d,%d,%d",
1550 &a0,&a1,&a2,&a3,
1551 &p1,&p2))<6) {
1552 ret = send_message(502,context);
1553 return E_PARAM_INVALID;
1554 }
1555
1556 context->dataip[0] = (unsigned char)a0;
1557 context->dataip[1] = (unsigned char)a1;
1558 context->dataip[2] = (unsigned char)a2;
1559 context->dataip[3] = (unsigned char)a3;
1560
1561 user = GetUserByID(context->userid);
1562
1563 if (fxp_is_denied(user) && test_fxp((const char*)context->dataip,WZD_INET4,context) != 0) {
1564 memset(context->dataip,0,16);
1565 ret = send_message_with_args(501,context,"FXP not allowed");
1566 return E_NOPERM;
1567 }
1568
1569 /** \todo check destination port for security: >= 1024 */
1570
1571 context->dataport = ((p1&0xff)<<8) | (p2&0xff);
1572 context->datafamily = WZD_INET4;
1573 ret = send_message_with_args(200,context,"Command okay");
1574 return E_OK;
1575 }
1576
1577 /*************** do_pasv *****************************/
do_pasv(UNUSED wzd_string_t * name,UNUSED wzd_string_t * args,wzd_context_t * context)1578 int do_pasv(UNUSED wzd_string_t *name, UNUSED wzd_string_t *args, wzd_context_t * context)
1579 {
1580 int ret;
1581 unsigned long addr;
1582 unsigned int size,port;
1583 struct sockaddr_in sai;
1584 unsigned char *myip;
1585 unsigned char pasv_bind_ip[16];
1586 unsigned char buffer[16];
1587 int offset=0;
1588 int count=0;
1589 int all_ports_used=1;
1590
1591 size = sizeof(struct sockaddr_in);
1592 port = mainConfig->pasv_low_range; /* use pasv range min */
1593
1594 /* close existing pasv connections */
1595 if (context->pasvsock != (fd_t)-1) {
1596 socket_close(context->pasvsock);
1597 FD_UNREGISTER(context->pasvsock,"Client PASV socket");
1598 /* port = context->pasvsock+1; *//* FIXME force change of socket */
1599 context->pasvsock = -1;
1600 }
1601
1602 /* create socket */
1603 if ((context->pasvsock=socket(AF_INET,SOCK_STREAM,0)) == (fd_t)-1) {
1604 context->pasvsock = -1;
1605 ret = send_message(425,context);
1606 return E_NO_DATA_CTX;
1607 }
1608
1609 myip = getmyip(context->controlfd, context->family, buffer); /* FIXME use a variable to get pasv ip ? */
1610
1611 if (mainConfig->pasv_ip[0] == 0) {
1612 #if defined(IPV6_SUPPORT)
1613 if (IN6_IS_ADDR_V4MAPPED(PORCUS_CAST(myip)) )
1614 memcpy(pasv_bind_ip,myip+12,4);
1615 else
1616 #endif /* IPV6_SUPPORT */
1617 memcpy(pasv_bind_ip,myip,4);
1618 } else {
1619 #if defined(IPV6_SUPPORT)
1620 if (IN6_IS_ADDR_V4MAPPED(PORCUS_CAST(context->hostip)))
1621 offset = 12;
1622 #endif
1623 /* do NOT send pasv_ip if used from private network */
1624 if (context->hostip[offset+0]==10 ||
1625 (context->hostip[offset+0] == 172 && context->hostip[offset+1] == 16) ||
1626 (context->hostip[offset+0] == 192 && context->hostip[offset+1] == 168 && context->hostip[offset+2] == 0) ||
1627 (context->hostip[offset+0] == 127 && context->hostip[offset+1] == 0 && context->hostip[offset+2] == 0 && context->hostip[offset+3] == 1))
1628 {
1629 #if defined(IPV6_SUPPORT)
1630 if (IN6_IS_ADDR_V4MAPPED(PORCUS_CAST(myip)))
1631 memcpy(pasv_bind_ip,myip+12,4);
1632 else
1633 #endif /* IPV6_SUPPORT */
1634 memcpy(pasv_bind_ip,myip,4);
1635 }
1636 else
1637 #if defined(IPV6_SUPPORT)
1638 if (IN6_IS_ADDR_V4MAPPED(PORCUS_CAST(mainConfig->pasv_ip)))
1639 memcpy(pasv_bind_ip,mainConfig->pasv_ip+12,4);
1640 else
1641 #endif /* IPV6_SUPPORT */
1642 memcpy(pasv_bind_ip,mainConfig->pasv_ip,4);
1643 }
1644 /* out_err(LEVEL_CRITICAL,"PASV_IP: %d.%d.%d.%d\n",
1645 pasv_bind_ip[0], pasv_bind_ip[1], pasv_bind_ip[2], pasv_bind_ip[3]);*/
1646
1647 port = mainConfig->pasv_low_range; /* use pasv range min */
1648 count = mainConfig->pasv_high_range - mainConfig->pasv_low_range + 1;
1649 #ifndef WIN32
1650 port = port + (random()) % count; /* we try to change starting port for random */
1651 #else
1652 port = port + (rand()) % count; /* we try to change starting port for random */
1653 #endif
1654 while (count-- > 0) {
1655 memset(&sai,0,size);
1656
1657 sai.sin_family = AF_INET;
1658 sai.sin_port = htons((unsigned short)port);
1659 /* XXX TODO FIXME bind to specific address works, but not for NAT */
1660 /* XXX TODO FIXME always bind to 'myip' ?! */
1661 addr = INADDR_ANY;
1662 /* memcpy( (void*)&addr, pasv_bind_ip, sizeof(unsigned long));*/
1663
1664 memcpy(&sai.sin_addr.s_addr,&addr,sizeof(unsigned long));
1665
1666 if (bind(context->pasvsock,(struct sockaddr *)&sai,size)==0) {
1667 /* found a free port, stop searching */
1668 all_ports_used = 0;
1669 break;
1670 }
1671 port++; /* retry with next port */
1672
1673 if (port > mainConfig->pasv_high_range) {
1674 /* reached the top of the range, continue searching from the bottom */
1675 port = mainConfig->pasv_low_range;
1676 }
1677 }
1678
1679 if (all_ports_used) {
1680 /* all ports are in use, return an error */
1681 out_log(LEVEL_HIGH, "PASV: all possible PASV ports are in use\n");
1682 socket_close(context->pasvsock);
1683 context->pasvsock = -1;
1684 ret = send_message(425, context);
1685 return E_NO_DATA_CTX;
1686 }
1687
1688 /* sanity check */
1689 if (port < mainConfig->pasv_low_range || port > mainConfig->pasv_high_range)
1690 {
1691 out_log(LEVEL_CRITICAL, "PASV: attempted to bind to port out of range (%d not in [%d , %d])\n",
1692 port, mainConfig->pasv_low_range, mainConfig->pasv_high_range);
1693 socket_close(context->pasvsock);
1694 context->pasvsock = -1;
1695 ret = send_message(425, context);
1696 return E_NO_DATA_CTX;
1697 }
1698
1699 /* sanity check */
1700 if (port >= 65536) {
1701 out_log(LEVEL_CRITICAL, "PASV: attempted to bind to invalid port 65536\n");
1702 socket_close(context->pasvsock);
1703 context->pasvsock = -1;
1704 ret = send_message(425, context);
1705 return E_NO_DATA_CTX;
1706 }
1707
1708 if (listen(context->pasvsock,1)<0) {
1709 out_log(LEVEL_CRITICAL,"Major error during listen: errno %d error %s\n",errno,strerror(errno));
1710 socket_close(context->pasvsock);
1711 context->pasvsock = -1;
1712 ret = send_message(425, context);
1713 return E_NO_DATA_CTX;
1714 }
1715
1716 FD_REGISTER(context->pasvsock,"Client PASV socket");
1717
1718 context->datafamily = WZD_INET4;
1719 myip = getmyip(context->controlfd, context->family, buffer); /* FIXME use a variable to get pasv ip ? */
1720
1721 ret = send_message_with_args(227,context,pasv_bind_ip[0], pasv_bind_ip[1], pasv_bind_ip[2], pasv_bind_ip[3],(port>>8)&0xff, port&0xff);
1722
1723 #if 0
1724 if (mainConfig->pasv_ip[0] == 0) {
1725 ret = send_message_with_args(227,context,myip[0], myip[1], myip[2], myip[3],(port>>8)&0xff, port&0xff);
1726 } else {
1727 /* do NOT send pasv_ip if used from private network */
1728 if (context->hostip[0]==10 ||
1729 (context->hostip[0] == 172 && context->hostip[1] == 16) ||
1730 (context->hostip[0] == 192 && context->hostip[1] == 168 && context->hostip[2] == 0) ||
1731 (context->hostip[0] == 127 && context->hostip[1] == 0 && context->hostip[2] == 0 && context->hostip[3] == 1))
1732 ret = send_message_with_args(227,context,myip[0], myip[1], myip[2], myip[3],(port>>8)&0xff, port&0xff);
1733 else
1734 ret = send_message_with_args(227,context,mainConfig->pasv_ip[0], mainConfig->pasv_ip[1],
1735 mainConfig->pasv_ip[2], mainConfig->pasv_ip[3],(port>>8)&0xff, port&0xff);
1736 }
1737 #endif
1738
1739 if (strcasecmp("cpsv",str_tochar(name))==0)
1740 context->tls_role = TLS_CLIENT_MODE;
1741
1742 return E_OK;
1743 }
1744
1745 /*************** do_eprt *****************************/
do_eprt(UNUSED wzd_string_t * name,wzd_string_t * arg,wzd_context_t * context)1746 int do_eprt(UNUSED wzd_string_t *name, wzd_string_t *arg, wzd_context_t * context)
1747 {
1748 #if defined(IPV6_SUPPORT)
1749 int ret;
1750 char sep;
1751 char net_prt;
1752 char * net_addr, * s_tcp_port;
1753 char * ptr;
1754 unsigned int tcp_port;
1755 struct in_addr addr4;
1756 struct in6_addr addr6;
1757 char * param, * orig_param;
1758 wzd_user_t * user;
1759
1760 if (context->pasvsock != (fd_t)-1) {
1761 socket_close(context->pasvsock);
1762 context->pasvsock = -1;
1763 }
1764 /* context->resume = 0; */
1765 if (!arg || strlen(str_tochar(arg)) <= 7) {
1766 ret = send_message(502,context);
1767 ret = send_message_with_args(501,context,"Invalid argument");
1768 return E_PARAM_INVALID;
1769 }
1770
1771 orig_param = param = strdup(str_tochar(arg));
1772
1773 sep = *param++;
1774 net_prt = *param++;
1775 if ( (*param++) != sep || (net_prt != '1' && net_prt != '2') ) {
1776 ret = send_message_with_args(501,context,"Invalid argument");
1777 free(orig_param);
1778 return E_PARAM_INVALID;
1779 }
1780
1781 net_addr = param;
1782 while (*param && (*param) != sep ) param++;
1783 if ( !*param ) {
1784 ret = send_message_with_args(501,context,"Invalid argument");
1785 free(orig_param);
1786 return E_PARAM_INVALID;
1787 }
1788
1789 *param = '\0';
1790 param++;
1791
1792 s_tcp_port = param;
1793 while (*param && (*param) != sep ) param++;
1794 if ( !*param || *param != sep ) {
1795 ret = send_message_with_args(501,context,"Invalid argument");
1796 free(orig_param);
1797 return E_PARAM_INVALID;
1798 }
1799
1800 *param = '\0';
1801
1802 tcp_port = strtoul(s_tcp_port,&ptr,0);
1803 if (*ptr) {
1804 ret = send_message_with_args(501,context,"Invalid port");
1805 free(orig_param);
1806 return E_PARAM_INVALID;
1807 }
1808
1809 /* resolve net_addr to context->dataip */
1810 switch (net_prt) {
1811 case '1':
1812 if ( (ret=inet_pton(AF_INET,net_addr,&addr4)) <= 0 )
1813 {
1814 ret = send_message_with_args(501,context,"Invalid host");
1815 free(orig_param);
1816 return E_PARAM_INVALID;
1817 }
1818 memcpy(context->dataip,(const char *)&addr4.s_addr,4);
1819 context->datafamily = WZD_INET4;
1820 break;
1821 case '2':
1822 if ( (ret=inet_pton(AF_INET6,net_addr,&addr6)) <= 0 )
1823 {
1824 ret = send_message_with_args(501,context,"Invalid host");
1825 free(orig_param);
1826 return E_PARAM_INVALID;
1827 }
1828 memcpy(context->dataip,addr6.s6_addr,16);
1829 context->datafamily = WZD_INET6;
1830 break;
1831 default:
1832 ret = send_message_with_args(501,context,"Invalid protocol");
1833 free(orig_param);
1834 return E_PARAM_INVALID;
1835 }
1836
1837 context->dataport = tcp_port;
1838
1839 user = GetUserByID(context->userid);
1840
1841 if (fxp_is_denied(user) && test_fxp((const char*)context->dataip,context->datafamily,context) != 0) {
1842 memset(context->dataip,0,16);
1843 ret = send_message_with_args(501,context,"FXP not allowed");
1844 free(orig_param);
1845 return E_NOPERM;
1846 }
1847
1848 /** \todo check destination port for security: >= 1024 */
1849
1850 free(orig_param);
1851 ret = send_message_with_args(200,context,"Command okay");
1852 #else /* defined(IPV6_SUPPORT) */
1853 send_message(502,context);
1854 #endif
1855 return E_OK;
1856 }
1857
1858 /*************** do_epsv *****************************/
do_epsv(UNUSED wzd_string_t * name,UNUSED wzd_string_t * arg,wzd_context_t * context)1859 int do_epsv(UNUSED wzd_string_t *name, UNUSED wzd_string_t *arg, wzd_context_t * context)
1860 {
1861 int ret;
1862 unsigned int size,port;
1863 #if defined(IPV6_SUPPORT)
1864 struct sockaddr_in6 sai6;
1865 #else
1866 struct sockaddr_in sai;
1867 unsigned long addr;
1868 #endif
1869 unsigned char *myip;
1870 unsigned char pasv_bind_ip[16];
1871 unsigned char buffer[16];
1872
1873 #if !defined(IPV6_SUPPORT)
1874 size = sizeof(struct sockaddr_in);
1875 #else
1876 size = sizeof(struct sockaddr_in6);
1877 #endif
1878 port = mainConfig->pasv_low_range; /* use pasv range min */
1879
1880 /* close existing pasv connections */
1881 if (context->pasvsock != (fd_t)-1) {
1882 socket_close(context->pasvsock);
1883 /* port = context->pasvsock+1; *//* FIXME force change of socket */
1884 context->pasvsock = -1;
1885 }
1886
1887 /* create socket */
1888 #if !defined(IPV6_SUPPORT)
1889 if ((context->pasvsock = socket(PF_INET,SOCK_STREAM,0)) == (fd_t)-1)
1890 #else
1891 if ((context->pasvsock = socket(PF_INET6,SOCK_STREAM,0)) == (fd_t)-1)
1892 #endif
1893 {
1894 context->pasvsock = -1;
1895 ret = send_message(425,context);
1896 return E_NO_DATA_CTX;
1897 }
1898
1899 myip = getmyip(context->controlfd, context->family, buffer); /* FIXME use a variable to get pasv ip ? */
1900
1901 if (mainConfig->pasv_ip[0] == 0) {
1902 memcpy(pasv_bind_ip,myip,sizeof(pasv_bind_ip));
1903 } else {
1904 /* do NOT send pasv_ip if used from private network */
1905 /** \todo TODO XXX FIXME private networks are not the same in ipv6 */
1906 if (context->hostip[0]==10 ||
1907 (context->hostip[0] == 172 && context->hostip[1] == 16) ||
1908 (context->hostip[0] == 192 && context->hostip[1] == 168 && context->hostip[2] == 0) ||
1909 (context->hostip[0] == 127 && context->hostip[1] == 0 && context->hostip[2] == 0 && context->hostip[3] == 1))
1910 memcpy(pasv_bind_ip,myip,sizeof(pasv_bind_ip));
1911 else
1912 memcpy(pasv_bind_ip,mainConfig->pasv_ip,sizeof(pasv_bind_ip));
1913 }
1914 /* out_err(LEVEL_CRITICAL,"PASV_IP: %d.%d.%d.%d\n",
1915 pasv_bind_ip[0], pasv_bind_ip[1], pasv_bind_ip[2], pasv_bind_ip[3]);*/
1916
1917 while (port <= mainConfig->pasv_high_range) { /* use pasv range max */
1918 #if !defined(IPV6_SUPPORT)
1919 memset(&sai,0,size);
1920
1921 sai.sin_family = AF_INET;
1922 sai.sin_port = htons((unsigned short)port);
1923 /* XXX TODO FIXME bind to specific address works, but not for NAT */
1924 /* XXX TODO FIXME always bind to 'myip' ?! */
1925 addr = INADDR_ANY;
1926 /* memcpy( (void*)&addr, pasv_bind_ip, sizeof(unsigned long));*/
1927
1928 memcpy(&sai.sin_addr.s_addr,&addr,sizeof(unsigned long));
1929
1930 if (bind(context->pasvsock,(struct sockaddr *)&sai,size)==0) break;
1931 #else /* IPV6_SUPPORT */
1932 memset(&sai6,0,size);
1933
1934 sai6.sin6_family = AF_INET6;
1935 sai6.sin6_port = htons(port);
1936 sai6.sin6_flowinfo = 0;
1937 /* sai6.sin6_addr = in6addr_any;*/ /* FIXME VISUAL */
1938 memset(&sai6.sin6_addr,0,16);
1939 /* XXX TODO FIXME bind to specific address works, but not for NAT */
1940 /* XXX TODO FIXME always bind to 'myip' ?! */
1941 /* addr = INADDR_ANY;*/
1942
1943 /* memcpy(&sai.sin_addr.s_addr,&addr,sizeof(unsigned long));*/
1944
1945 if (bind(context->pasvsock,(struct sockaddr *)&sai6,size)==0) break;
1946
1947 #endif /* IPV6_SUPPORT */
1948 port++; /* retry with next port */
1949 }
1950 if (port > mainConfig->pasv_high_range || port >= 65536) {
1951 out_log(LEVEL_CRITICAL,"EPSV: could not find any available port for binding");
1952 socket_close(context->pasvsock);
1953 context->pasvsock = -1;
1954 ret = send_message(425,context);
1955 return E_NO_DATA_CTX;
1956 }
1957
1958 if (listen(context->pasvsock,1)<0) {
1959 out_log(LEVEL_CRITICAL,"EPSV: could not listen on port %d: errno %d error %s\n",port,errno,strerror(errno));
1960 socket_close(context->pasvsock);
1961 context->pasvsock = -1;
1962 ret = send_message(425,context);
1963 return E_NO_DATA_CTX;
1964 }
1965
1966 FD_REGISTER(context->pasvsock,"Client PASV socket");
1967
1968 myip = getmyip(context->controlfd, context->family, buffer); /* FIXME use a variable to get pasv ip ? */
1969
1970 #if !defined(IPV6_SUPPORT)
1971 context->datafamily = WZD_INET4;
1972 ret = send_message_with_args(227,context,pasv_bind_ip[0], pasv_bind_ip[1], pasv_bind_ip[2], pasv_bind_ip[3],(port>>8)&0xff, port&0xff);
1973 #else
1974 context->datafamily = WZD_INET6;
1975 {
1976 char buf[256];
1977 snprintf(buf,256,"229 Entering Passive Mode (|||%d|)\r\n",port);
1978 ret = send_message_raw(buf,context);
1979 }
1980 #endif
1981
1982 #if 0
1983 if (mainConfig->pasv_ip[0] == 0) {
1984 ret = send_message_with_args(227,context,myip[0], myip[1], myip[2], myip[3],(port>>8)&0xff, port&0xff);
1985 } else {
1986 /* do NOT send pasv_ip if used from private network */
1987 if (context->hostip[0]==10 ||
1988 (context->hostip[0] == 172 && context->hostip[1] == 16) ||
1989 (context->hostip[0] == 192 && context->hostip[1] == 168 && context->hostip[2] == 0) ||
1990 (context->hostip[0] == 127 && context->hostip[1] == 0 && context->hostip[2] == 0 && context->hostip[3] == 1))
1991 ret = send_message_with_args(227,context,myip[0], myip[1], myip[2], myip[3],(port>>8)&0xff, port&0xff);
1992 else
1993 ret = send_message_with_args(227,context,mainConfig->pasv_ip[0], mainConfig->pasv_ip[1],
1994 mainConfig->pasv_ip[2], mainConfig->pasv_ip[3],(port>>8)&0xff, port&0xff);
1995 }
1996 #endif
1997 return E_OK;
1998 }
1999
2000 /*************** do_retr *****************************/
2001 /** \brief Prepares a data retrieval transfer.
2002 *
2003 * Ensures that a data connection is available, checks user permissions,
2004 * sends EVENT_PREDOWNLOAD, and opens file.
2005 * The real transfer is handled by data_execute().
2006 */
do_retr(UNUSED wzd_string_t * name,wzd_string_t * arg,wzd_context_t * context)2007 int do_retr(UNUSED wzd_string_t *name, wzd_string_t *arg, wzd_context_t * context)
2008 {
2009 char path[WZD_MAX_PATH];
2010 int fd;
2011 u64_t bytestot, bytesnow, byteslast;
2012 fd_t sock;
2013 int ret;
2014 wzd_user_t * user;
2015 const char *param;
2016 connection_state_t restorestate;
2017
2018 param = str_tochar(arg);
2019 user = GetUserByID(context->userid);
2020
2021 if ( !(user->userperms & RIGHT_RETR) ) {
2022 ret = send_message_with_args(550,context,"RETR","No access");
2023 return E_NOPERM;
2024 }
2025
2026 /* TODO FIXME send all error or any in this function ! */
2027 /* we must have a data connetion */
2028 if ((context->pasvsock == (fd_t)-1) && (context->dataport == 0)) {
2029 ret = send_message_with_args(501,context,"No data connection available - issue PORT or PASV first");
2030 return E_NO_DATA_CTX;
2031 }
2032 if (context->state == STATE_XFER) {
2033 ret = send_message(491,context);
2034 return E_XFER_PROGRESS;
2035 }
2036
2037 if (!param || strlen(param)==0) {
2038 ret = send_message_with_args(501,context,"Incorrect filename");
2039 return E_PARAM_INVALID;
2040 }
2041
2042 if (strlen(param)>WZD_MAX_PATH-1) {
2043 ret = send_message_with_args(501,context,"Filename too long");
2044 return E_PARAM_BIG;
2045 }
2046
2047 /*
2048 * Ignore some checkpath_new errorst for the moment,
2049 * test_path will do this after the predownload hook runs
2050 * in case the hook changes something
2051 */
2052 ret = checkpath_new(param,path,context);
2053
2054 if ((ret != 0) && (ret != E_NOPERM) && (ret != E_FILE_NOEXIST))
2055 {
2056 ret = send_message_with_args(501,context,"Invalid file name");
2057 return E_PARAM_INVALID;
2058 }
2059
2060 /* we need to put context into TOK_RETR state before the hook runs
2061 * so that any cookie parsing in the hook works correctly
2062 */
2063 restorestate = context->current_action.token;
2064 context->current_action.token = TOK_RETR;
2065 strncpy(context->current_action.arg,path,HARD_LAST_COMMAND_LENGTH);
2066
2067
2068 {
2069 wzd_string_t * event_args = str_allocate();
2070 str_sprintf(event_args,"%s %s",user->username,path);
2071 ret = event_send(mainConfig->event_mgr, EVENT_PREDOWNLOAD, 0, event_args, context);
2072 str_deallocate(event_args);
2073 }
2074
2075 if (ret != EVENT_OK && ret != EVENT_BREAK) {
2076 out_log(LEVEL_NORMAL, "Download denied by hook (returned %d)\n", ret);
2077 ret = send_message_with_args(501,context,"Download denied");
2078 context->current_action.token = restorestate;
2079 return E_XFER_REJECTED;
2080 }
2081
2082 /* restore the context state in case we exit before downloading*/
2083 context->current_action.token = restorestate;
2084
2085 if (test_path(path,context)) {
2086 ret = send_message_with_args(501,context,"Invalid file name");
2087 return E_PARAM_INVALID;
2088 }
2089
2090
2091 /* trailing / ? */
2092 if (path[strlen(path)-1]=='/')
2093 path[strlen(path)-1] = '\0';
2094
2095 /* deny retrieve to permissions file */
2096 if (is_hidden_file(path)) {
2097 ret = send_message_with_args(501,context,"Forbidden");
2098 return E_FILE_FORBIDDEN;
2099 }
2100
2101 /* check user ratio */
2102 if (ratio_check_download(path,context)) {
2103 ret = send_message_with_args(501,context,"Insufficient credits - Upload first");
2104 return E_CREDS_INSUFF;
2105 }
2106
2107
2108 if ((fd=file_open(path,O_RDONLY,RIGHT_RETR,context))==-1) { /* XXX allow access to files being uploaded ? */
2109 ret = send_message_with_args(550,context,param,"nonexistant file or permission denied");
2110 /* socket_close(sock);*/
2111 return E_FILE_NOEXIST;
2112 }
2113 FD_REGISTER(fd,"Client file (RETR)");
2114
2115 /* get length */
2116 bytestot = file_seek(fd,0,SEEK_END);
2117 if ((off_t)bytestot == (off_t)-1) /* happens with 0-length files */
2118 bytestot = 0;
2119 bytesnow = byteslast=context->resume;
2120
2121 if (context->pasvsock == (fd_t)-1) { /* PORT ! */
2122
2123 /* \todo TODO IP-check needed (FXP ?!) */
2124 sock = waitconnect(context);
2125 if (sock == (fd_t)-1) {
2126 file_close(fd,context);
2127 FD_UNREGISTER(fd,"Client file (RETR)");
2128 /* note: reply is done in waitconnect() */
2129 return E_CONNECTTIMEOUT;
2130 }
2131
2132 } else { /* PASV ! */
2133 /* FIXME */
2134 /* sprintf(cmd, "150 Opening BINARY data connection for '%s' (%ld bytes).\r\n",
2135 param, bytestot);*/
2136 ret = send_message(150,context);
2137 if ((sock=waitaccept(context)) == (fd_t)-1) {
2138 file_close(fd,context);
2139 FD_UNREGISTER(fd,"Client file (RETR)");
2140 /* note: reply is done in waitaccept() */
2141 return E_PASV_FAILED;
2142 }
2143 }
2144 FD_REGISTER(sock,"Client data socket (RETR)");
2145
2146 context->datafd = sock;
2147
2148 file_seek(fd,(fs_off_t)context->resume,SEEK_SET);
2149
2150 out_log(LEVEL_FLOOD,"Download: User %s starts downloading %s (%" PRIu64 " bytes)\n", user->username,param,bytestot);
2151
2152 context->state = STATE_XFER;
2153 context->current_action.token = TOK_RETR;
2154 strncpy(context->current_action.arg,path,HARD_LAST_COMMAND_LENGTH);
2155 context->current_action.current_file = fd;
2156 context->current_action.bytesnow = 0;
2157 context->idle_time_data_start = context->current_action.tm_start = time(NULL);
2158 gettimeofday(&context->current_action.tv_start,NULL);
2159
2160 /* if (user->max_dl_speed)
2161 context->current_limiter = limiter_new(user->max_dl_speed);
2162 else
2163 context->current_limiter = NULL;*/
2164
2165 /* if (user->max_dl_speed)
2166 {*/
2167 context->current_dl_limiter.maxspeed = user->max_dl_speed;
2168 context->current_dl_limiter.bytes_transfered = 0;
2169 #ifndef _MSC_VER
2170 gettimeofday(&context->current_dl_limiter.current_time,NULL);
2171 #else
2172 _ftime(&context->current_dl_limiter.current_time);
2173 #endif
2174 /* }
2175 else
2176 context->current_dl_limiter.maxspeed = 0;*/
2177
2178 /* we increment the counter of downloaded files at the beggining
2179 * of the download
2180 */
2181 user->stats.files_dl_total++;
2182
2183 context->resume=0;
2184 context->idle_time_start = time(NULL);
2185
2186 if (CFG_GET_OPTION(mainConfig,CFG_OPT_EXPERIMENTAL)) {
2187 if (context->transfer_thread != NULL) {
2188 out_log(LEVEL_HIGH,"ERROR a transfer thread is already started\n");
2189 data_end_transfer(0 /* is_upload */, 0 /* end_ok */, context);
2190 ret = send_message(426,context);
2191 return E_XFER_PROGRESS;
2192 }
2193 context->is_transferring = 1;
2194 ret = data_start_thread_retr(context);
2195 /* ret = do_local_retr(context);*/
2196 }
2197
2198 return E_OK;
2199 }
2200
2201 /*************** do_stor *****************************/
2202 /** \brief Store file on the FTP server
2203 *
2204 * Check permissions, open a data connection and stores data
2205 * in a file on the server. If the file does not exist, it is created,
2206 * otherwise it depends if a resume marker was received (see REST).
2207 *
2208 * The corresponding FTP commands are STOR (RFC959 p29) and APPE (RFC959 p29)
2209 */
do_stor(wzd_string_t * name,wzd_string_t * arg,wzd_context_t * context)2210 int do_stor(wzd_string_t *name, wzd_string_t *arg, wzd_context_t * context)
2211 {
2212 char path[WZD_MAX_PATH],path2[WZD_MAX_PATH];
2213 int fd;
2214 u64_t bytesnow, byteslast;
2215 fd_t sock;
2216 int ret;
2217 wzd_user_t * user;
2218 const char *param;
2219 unsigned long open_flags;
2220
2221 param = str_tochar(arg);
2222
2223 user = GetUserByID(context->userid);
2224
2225 if ( !(user->userperms & RIGHT_STOR) ) {
2226 ret = send_message_with_args(550,context,"STOR","No access");
2227 return E_NOPERM;
2228 }
2229
2230 /* TODO FIXME send all error or any in this function ! */
2231 /* we must have a data connection */
2232 if ((context->pasvsock == (fd_t)-1) && (context->dataport == 0)) {
2233 ret = send_message_with_args(503,context,"Issue PORT or PASV First");
2234 return E_NO_DATA_CTX;
2235 }
2236 if (context->state == STATE_XFER) {
2237 ret = send_message(491,context);
2238 return E_XFER_PROGRESS;
2239 }
2240
2241 if (!param || strlen(param)==0) {
2242 ret = send_message_with_args(501,context,"Incorrect filename");
2243 return E_PARAM_INVALID;
2244 }
2245
2246 if (strlen(param)>WZD_MAX_PATH-1) {
2247 ret = send_message_with_args(501,context,"Filename too long");
2248 return E_PARAM_BIG;
2249 }
2250
2251 if (param[0]=='/') { /* absolute path */
2252 strcpy(path,user->rootpath);
2253 } else { /* absolute path */
2254 /* FIXME these 2 lines forbids STOR dir/filename style - normal ? */
2255 /* XXX if (strrchr(param,'/'))
2256 param = strrchr(param,'/')+1; XXX */
2257
2258 strcpy(path2,".");
2259 if (checkpath_new(path2,path,context)) {
2260 ret = send_message_with_args(501,context,"Incorrect filename");
2261 return E_PARAM_INVALID;
2262 }
2263 if (path[strlen(path)-1] != '/') strcat(path,"/");
2264 } /* absolute path */
2265 strlcat(path,param,WZD_MAX_PATH);
2266
2267 /* TODO call checkpath again ? see do_mkdir */
2268
2269 /* deny retrieve to permissions file */
2270 if (is_hidden_file(path)) {
2271 ret = send_message_with_args(501,context,"Forbidden");
2272 return E_FILE_FORBIDDEN;
2273 }
2274
2275 {
2276 wzd_string_t * event_args = str_allocate();
2277 str_sprintf(event_args,"%s %s",user->username,path);
2278 ret = event_send(mainConfig->event_mgr, EVENT_PREUPLOAD, 0, event_args, context);
2279 str_deallocate(event_args);
2280 }
2281
2282 if (ret != EVENT_OK && ret != EVENT_BREAK) {
2283 out_log(LEVEL_NORMAL, "Upload denied by hook (returned %d)\n", ret);
2284 ret = send_message_with_args(501,context,"Upload denied");
2285 return E_XFER_REJECTED;
2286 }
2287
2288
2289 /* overwrite protection */
2290 /* TODO make permissions per-dir + per-group + per-user ? */
2291 /* if (context->userinfo.perms & PERM_OVERWRITE) {
2292 fp=file_open(path,"r",RIGHT_STOR,context),
2293 if (!fp) {
2294 fclose(fp);
2295 return 2;
2296 }
2297 }*/
2298 if (strcasecmp(str_tochar(name),"appe")==0)
2299 context->resume = (unsigned long)-1;
2300
2301 open_flags = O_WRONLY|O_CREAT;
2302
2303 if ((fd=file_open(path,open_flags,RIGHT_STOR,context))==-1) {
2304 ret = send_message_with_args(501,context,"Nonexistant file or permission denied");
2305 return E_FILE_NOEXIST;
2306 }
2307 FD_REGISTER(fd,"Client file (STOR)");
2308
2309 if (context->pasvsock == (fd_t)-1) { /* PORT ! */
2310 sock = waitconnect(context);
2311 if (sock == (fd_t)-1) {
2312 file_close(fd,context);
2313 FD_UNREGISTER(fd,"Client file (STOR)");
2314 /* note: reply is done in waitconnect() */
2315 return E_CONNECTTIMEOUT;
2316 }
2317 } else { /* PASV ! */
2318 ret = send_message(150,context);
2319 if ((sock=waitaccept(context)) == (fd_t)-1) {
2320 file_close(fd,context);
2321 FD_UNREGISTER(fd,"Client file (STOR)");
2322 /* note: reply is done in waitaccept() */
2323 return E_PASV_FAILED;
2324 }
2325 }
2326 FD_REGISTER(sock,"Client data socket (STOR)");
2327
2328 context->datafd = sock;
2329
2330 /* set owner */
2331 {
2332 const char *groupname=NULL;
2333 if (user->group_num > 0) {
2334 groupname = GetGroupByID(user->groups[0])->groupname;
2335 }
2336 file_chown (path,user->username,groupname,context);
2337 }
2338
2339 bytesnow = byteslast = 0;
2340 if (context->resume == (unsigned long)-1)
2341 file_seek(fd,0,SEEK_END);
2342 else
2343 file_seek(fd,(fs_off_t)context->resume,SEEK_SET);
2344
2345 out_err(LEVEL_FLOOD,"Download: User %s starts uploading %s\n",
2346 user->username,param);
2347
2348 context->state = STATE_XFER;
2349 context->current_action.token = TOK_STOR;
2350 strncpy(context->current_action.arg,path,HARD_LAST_COMMAND_LENGTH);
2351 context->current_action.current_file = fd;
2352 context->current_action.bytesnow = 0;
2353 context->idle_time_data_start = context->current_action.tm_start = time(NULL);
2354 gettimeofday(&context->current_action.tv_start,NULL);
2355
2356 context->current_ul_limiter.maxspeed = user->max_ul_speed;
2357 context->current_ul_limiter.bytes_transfered = 0;
2358 #ifndef WIN32 /* FIXME VISUAL */
2359 gettimeofday(&context->current_ul_limiter.current_time,NULL);
2360 #else
2361 _ftime(&context->current_ul_limiter.current_time);
2362 #endif
2363
2364 context->resume=0;
2365 context->idle_time_start = time(NULL);
2366
2367 if (CFG_GET_OPTION(mainConfig,CFG_OPT_EXPERIMENTAL)) {
2368 if (context->transfer_thread != NULL) {
2369 out_log(LEVEL_HIGH,"ERROR a transfer thread is already started\n");
2370 data_end_transfer(0 /* is_upload */, 0 /* end_ok */, context);
2371 ret = send_message(426,context);
2372 return E_XFER_PROGRESS;
2373 }
2374 context->is_transferring = 1;
2375 ret = data_start_thread_stor(context);
2376 /* ret = do_local_stor(context);*/
2377 }
2378
2379 return E_OK;
2380 }
2381
2382 /*************** do_mdtm *****************************/
do_mdtm(UNUSED wzd_string_t * name,wzd_string_t * param,wzd_context_t * context)2383 int do_mdtm(UNUSED wzd_string_t *name, wzd_string_t *param, wzd_context_t * context)
2384 {
2385 char path[WZD_MAX_PATH], tm[32];
2386 fs_filestat_t s;
2387 int ret;
2388
2389 if (!str_checklength(param,1,WZD_MAX_PATH-1)) {
2390 ret = send_message_with_args(501,context,"Incorrect argument");
2391 return E_PARAM_INVALID;
2392 }
2393
2394 if (!checkpath_new(str_tochar(param),path,context)) {
2395 if (path[strlen(path)-1]=='/')
2396 path[strlen(path)-1]='\0';
2397
2398 /* deny retrieve to permissions file */
2399 if (is_hidden_file(path)) {
2400 ret = send_message_with_args(501,context,"Forbidden");
2401 return E_FILE_FORBIDDEN;
2402 }
2403
2404 if (fs_file_stat(path,&s)==0) {
2405 context->resume = 0L;
2406 strftime(tm,sizeof(tm),"%Y%m%d%H%M%S",gmtime(&s.mtime));
2407 ret = send_message_with_args(213,context,tm);
2408 return E_OK;
2409 }
2410 }
2411 ret = send_message_with_args(501,context,"File inexistent or no access?");
2412 return E_FILE_NOEXIST;
2413 }
2414
2415 /*************** do_size *****************************/
do_moda(UNUSED wzd_string_t * name,wzd_string_t * param,wzd_context_t * context)2416 int do_moda(UNUSED wzd_string_t *name, wzd_string_t *param, wzd_context_t * context)
2417 {
2418 #ifdef HAVE_STRPTIME
2419 extern char *strptime (__const char *__restrict __s,
2420 __const char *__restrict __fmt, struct tm *__tp);
2421 #endif
2422 int ret, command_ok=0;
2423 char * facts, * fact, * value, * ptr;
2424 struct tm tm_atime, tm_mtime;
2425 struct utimbuf utime_buf = {0, 0};
2426 char * filename;
2427 char path[WZD_MAX_PATH];
2428
2429 if (!param) {
2430 ret = send_message_with_args(501,context,"Invalid syntax");
2431 return E_PARAM_INVALID;
2432 }
2433
2434 facts = strdup(str_tochar(param));
2435 filename = strstr(facts,"; ");
2436 if (!filename) {
2437 free(facts);
2438 ret = send_message_with_args(501,context,"Invalid syntax");
2439 return E_PARAM_INVALID;
2440 }
2441 filename++; /* skip ';' */
2442 *filename++ = '\0';
2443
2444 if (checkpath_new(filename,path,context)) {
2445 free(facts);
2446 ret = send_message_with_args(501,context,"Invalid filename");
2447 return E_PARAM_INVALID;
2448 }
2449 if (path[strlen(path)-1]=='/')
2450 path[strlen(path)-1]='\0';
2451
2452 /** \todo XXX open file to avoid race conditions */
2453
2454 fact = strtok_r(facts,"=",&ptr);
2455 if (!fact) {
2456 free(facts);
2457 ret = send_message_with_args(501,context,"Invalid syntax");
2458 return E_PARAM_INVALID;
2459 }
2460
2461 while (fact) {
2462 value = strtok_r(NULL,";",&ptr);
2463 if (!value) {
2464 free(facts);
2465 ret = send_message_with_args(501,context,"Invalid syntax");
2466 return E_PARAM_INVALID;
2467 }
2468
2469 /* test 'fact' and make action */
2470 /** \todo XXX it would be a good idea to make 'atomic' modifications, or to lock file ! */
2471
2472 /**** accessed *******/
2473 if (strcmp(fact,"accessed")==0) {
2474 memset(&tm_atime,0,sizeof(struct tm));
2475 ptr=strptime(value,"%Y%m%d%H%M%S",&tm_atime);
2476 if (ptr == NULL || *ptr != '\0') {
2477 snprintf(path,WZD_MAX_PATH,"Invalid value for fact '%s', aborting",fact);
2478 ret = send_message_with_args(501,context,path);
2479 return E_PARAM_INVALID;
2480 }
2481
2482 utime_buf.actime = mktime(&tm_atime);
2483 ret = utime(path,&utime_buf);
2484
2485 if (ret) {
2486 snprintf(path,WZD_MAX_PATH,"Error in fact %s: '%s', aborting",fact,value);
2487 free(facts);
2488 ret = send_message_with_args(501,context,path);
2489 return E_PARAM_INVALID;
2490 }
2491 command_ok++;
2492
2493 } else
2494 /**** modify *******/
2495 if (strcmp(fact,"modify")==0) {
2496 memset(&tm_mtime,0,sizeof(struct tm));
2497 ptr=strptime(value,"%Y%m%d%H%M%S",&tm_mtime);
2498 if (ptr == NULL || *ptr != '\0') {
2499 snprintf(path,WZD_MAX_PATH,"Invalid value for fact '%s', aborting",fact);
2500 ret = send_message_with_args(501,context,path);
2501 return E_PARAM_INVALID;
2502 }
2503
2504 utime_buf.modtime = mktime(&tm_mtime);
2505 ret = utime(path,&utime_buf);
2506
2507 if (ret) {
2508 snprintf(path,WZD_MAX_PATH,"Error in fact %s: '%s', aborting",fact,value);
2509 free(facts);
2510 ret = send_message_with_args(501,context,path);
2511 return E_PARAM_INVALID;
2512 }
2513 command_ok++;
2514
2515 } else
2516 /**** unknown *******/
2517 {
2518 snprintf(path,WZD_MAX_PATH,"Unsupported fact '%s', aborting",fact);
2519 free(facts);
2520 ret = send_message_with_args(501,context,path);
2521 return E_PARAM_INVALID;
2522 }
2523
2524 fact = strtok_r(NULL,"=",&ptr);
2525 }
2526
2527 free(facts);
2528
2529 if (command_ok)
2530 ret = send_message_with_args(200,context,"Command okay");
2531 else
2532 ret = send_message_with_args(501,context,"Not yet implemented");
2533
2534 return E_PARAM_INVALID;
2535 }
2536
2537 /*************** do_size *****************************/
do_size(UNUSED wzd_string_t * name,wzd_string_t * param,wzd_context_t * context)2538 int do_size(UNUSED wzd_string_t *name, wzd_string_t *param, wzd_context_t * context)
2539 {
2540 char path[WZD_MAX_PATH];
2541 char buffer[1024];
2542 fs_filestat_t s;
2543 int ret;
2544
2545 if (!str_checklength(param,1,WZD_MAX_PATH-1)) {
2546 ret = send_message_with_args(501,context,"Incorrect argument");
2547 return E_PARAM_INVALID;
2548 }
2549 if (!checkpath_new(str_tochar(param),path,context)) {
2550 if (path[strlen(path)-1]=='/')
2551 path[strlen(path)-1]='\0';
2552
2553 /* deny retrieve to permissions file */
2554 if (is_hidden_file(path)) {
2555 ret = send_message_with_args(501,context,"Forbidden");
2556 return E_FILE_FORBIDDEN;
2557 }
2558
2559
2560 if (fs_file_stat(path,&s)==0) {
2561 snprintf(buffer,1024,"%" PRIu64,s.size);
2562 ret = send_message_with_args(213,context,buffer);
2563 return E_OK;
2564 }
2565 }
2566 ret = send_message_with_args(501,context,"File inexistent or no access?");
2567 return E_FILE_NOEXIST;
2568 }
2569
2570 /*************** do_abor *****************************/
2571 /** \brief Abort current transfer
2572 *
2573 * Abort current service command and any associated transfer of data.
2574 * The command connection is not closed, but the data connection is closed.
2575 *
2576 * The corresponding FTP command is ABOR (RFC959 p30)
2577 */
do_abor(UNUSED wzd_string_t * name,UNUSED wzd_string_t * arg,wzd_context_t * context)2578 int do_abor(UNUSED wzd_string_t *name, UNUSED wzd_string_t *arg, wzd_context_t * context)
2579 {
2580 int ret;
2581 wzd_user_t * user;
2582
2583 user = GetUserByID(context->userid);
2584
2585 if (context->pasvsock != (fd_t)-1 && context->datafd != context->pasvsock) {
2586 socket_close(context->pasvsock);
2587 FD_UNREGISTER(context->pasvsock,"Client PASV socket");
2588 context->pasvsock=-1;
2589 }
2590 if (context->current_action.current_file != (fd_t)-1) {
2591 /* transfer aborted, we should send a 426 */
2592 ret = send_message(426,context);
2593 out_xferlog(context, 0 /* incomplete */);
2594
2595 if (context->current_action.token == TOK_STOR || context->current_action.token == TOK_RETR) {
2596 data_end_transfer((context->current_action.token == TOK_STOR) ? 1:0 /* is_upload */, 0 /* end_ok */, context);
2597 }
2598
2599 }
2600 ret = send_message(226,context);
2601 return E_OK;
2602 }
2603
2604 /*************** do_cwd ******************************/
do_cwd(wzd_string_t * name,wzd_string_t * arg,wzd_context_t * context)2605 int do_cwd(wzd_string_t *name, wzd_string_t *arg, wzd_context_t * context)
2606 {
2607 int ret;
2608 const char *param;
2609
2610 param = str_tochar(arg);
2611 context->resume = 0;
2612 if (strcmp(str_tochar(name),"cdup")==0) param="..";
2613
2614 if (param == NULL) {
2615 param = "/";
2616 }
2617 /* avoir error if current is "/" and action is ".." */
2618 if (!strcmp("..",param)
2619 && ( !strcmp("/",context->currentpath)
2620 || ( (strlen(context->currentpath)<=3) && (context->currentpath[2]==':') ) )
2621 ) {
2622 ret = send_message_with_args(250,context,context->currentpath," now current directory.");
2623 return E_OK;
2624 }
2625 if ( (ret=do_chdir(param,context)) ) {
2626 switch (ret) {
2627 case E_NOTDIR:
2628 /* return 501 for syntax error, see rfc3659 at section 7.2.1 */
2629 ret = send_message_with_args(501,context,param?param:"(null)","Not a directory");
2630 break;
2631 case E_WRONGPATH:
2632 ret = send_message_with_args(550,context,param?param:"(null)","Invalid path");
2633 break;
2634 case E_FILE_NOEXIST:
2635 ret = send_message_with_args(550,context,param?param:"(null)","No such file or directory (no access?)");
2636 break;
2637 case E_FILE_FORBIDDEN:
2638 case E_NOPERM:
2639 ret = send_message_with_args(550,context,param?param:"(null)","Negative on that, Houston (access denied)");
2640 break;
2641 default:
2642 ret = send_message_with_args(501,context,param?param:"(null)","CWD failed (syntax error?)");
2643 break;
2644 }
2645 return E_OK;
2646 }
2647 /** \bug we have to ensure that the reply is RFC compliant */
2648 ret = send_message_with_args(250,context,context->currentpath," now current directory.");
2649 return E_OK;
2650 }
2651
2652 /*************** do_dele *****************************/
do_dele(UNUSED wzd_string_t * name,wzd_string_t * param,wzd_context_t * context)2653 int do_dele(UNUSED wzd_string_t *name, wzd_string_t *param, wzd_context_t * context)
2654 {
2655 char path[WZD_MAX_PATH];
2656 int ret;
2657 fs_filestat_t s;
2658 u64_t file_size;
2659 wzd_user_t * user, * owner;
2660
2661 if (!str_checklength(param,1,WZD_MAX_PATH-1)) {
2662 ret = send_message_with_args(501,context,"Syntax error");
2663 return E_PARAM_INVALID;
2664 }
2665
2666 user = GetUserByID(context->userid);
2667 if (!user) {
2668 ret = send_message_with_args(501,context,"Mama says I don't exist!");
2669 return E_USER_IDONTEXIST;
2670 }
2671
2672 if ( !(user->userperms & RIGHT_DELE) ) {
2673 ret = send_message_with_args(501,context,"Permission denied");
2674 return E_NOPERM;
2675 }
2676
2677 if (checkpath_new(str_tochar(param),path,context)) {
2678 ret = send_message_with_args(501,context,"Permission denied or inexistant file");
2679 return E_FILE_NOEXIST;
2680 }
2681
2682 if (path[strlen(path)-1]=='/') path[strlen(path)-1]='\0';
2683
2684 /* deny retrieve to permissions file */
2685 if (is_hidden_file(path)) {
2686 ret = send_message_with_args(501,context,"Forbidden");
2687 return E_FILE_FORBIDDEN;
2688 }
2689
2690 if (fs_file_lstat(path,&s)) {
2691 /* non-existent file ? */
2692 ret = send_message_with_args(501,context,"File does not exist");
2693 return E_FILE_NOEXIST;
2694 }
2695 if (S_ISDIR(s.mode)) {
2696 ret = send_message_with_args(501,context,"This is a directory!");
2697 return E_ISDIR;
2698 }
2699 if (S_ISREG(s.mode))
2700 file_size = s.size;
2701 else
2702 file_size = 0;
2703 owner = file_getowner(path,context);
2704
2705 context->current_action.token = TOK_DELE;
2706 out_err(LEVEL_FLOOD,"Removing file '%s'\n",path);
2707
2708 ret = file_remove(path,context);
2709
2710 /* decrement user credits and upload stats */
2711 /* we should adjust stats for REAL OWNER of file */
2712 if (!ret && file_size)
2713 {
2714 if (owner) {
2715 if (strcmp(owner->username,"nobody"))
2716 {
2717 if (owner->ratio) {
2718 if (owner->credits > owner->ratio*file_size)
2719 owner->credits -= (owner->ratio * file_size);
2720 else
2721 owner->credits = 0;
2722 }
2723 if (owner->stats.bytes_ul_total > file_size)
2724 owner->stats.bytes_ul_total -= file_size;
2725 else
2726 owner->stats.bytes_ul_total = 0;
2727 if (owner->stats.files_ul_total)
2728 owner->stats.files_ul_total--;
2729 }
2730 }
2731 }
2732
2733 if (!ret) {
2734 {
2735 wzd_string_t * event_args = STR(path);
2736 event_send(mainConfig->event_mgr, EVENT_DELE, 250, event_args, context);
2737 str_deallocate(event_args);
2738 }
2739 ret = send_message_with_args(250,context,"DELE"," command successful");
2740
2741 context->idle_time_start = time(NULL);
2742 } else
2743 ret = send_message_with_args(501,context,"DELE failed");
2744
2745 context->current_action.token = TOK_UNKNOWN;
2746 return ret;
2747 }
2748
2749 /*************** do_pret *****************************/
do_pret(UNUSED wzd_string_t * name,UNUSED wzd_string_t * param,wzd_context_t * context)2750 int do_pret(UNUSED wzd_string_t *name, UNUSED wzd_string_t *param, wzd_context_t * context)
2751 {
2752 int ret;
2753
2754 /* TODO XXX FIXME PRET *MUST* be sent before the PASV command */
2755
2756 /* TODO check next token (RETR STOR STOU LIST NLST APPE) and
2757 * run specific commands ...
2758 */
2759 /* e.g: if RETR, open file to have it in cache ? */
2760
2761 ret = send_message_with_args(200,context,"Command okay");
2762 return E_OK;
2763 }
2764
2765 /*************** do_print_message ********************/
do_print_message(UNUSED wzd_string_t * name,UNUSED wzd_string_t * filename,wzd_context_t * context)2766 int do_print_message(UNUSED wzd_string_t *name, UNUSED wzd_string_t *filename, wzd_context_t * context)
2767 {
2768 int cmd;
2769 int ret;
2770 char buffer[WZD_BUFFER_LEN];
2771 wzd_string_t * str;
2772
2773 cmd = identify_token(str_tochar(name));
2774 switch (cmd) {
2775 case TOK_PWD:
2776 context->resume = 0;
2777 /** \todo allow msg 257 customization */
2778 /*ret = send_message(257,context);*/
2779 str = str_allocate();
2780 str_sprintf(str,"257 \"%s\" is current directory.\r\n",context->currentpath);
2781 #ifdef HAVE_UTF8
2782 if (context->connection_flags & CONNECTION_UTF8)
2783 {
2784 if (!str_is_valid_utf8(str))
2785 str_local_to_utf8(str,local_charset());
2786 }
2787 #endif
2788 ret = send_message_raw(str_tochar(str),context);
2789 str_deallocate(str);
2790 break;
2791 case TOK_ALLO:
2792 case TOK_NOOP:
2793 ret = send_message_with_args(200,context,"Command okay");
2794 break;
2795 case TOK_FEAT:
2796 snprintf(buffer,sizeof(buffer),"Extensions supported:\n%s",SUPPORTED_FEATURES);
2797 ret = send_message_with_args(211,context,buffer);
2798 break;
2799 case TOK_SYST:
2800 context->resume = 0;
2801 ret = send_message(215,context);
2802 break;
2803 }
2804 return E_OK;
2805 }
2806
2807 #if defined(HAVE_OPENSSL) || defined(HAVE_GNUTLS)
2808 /*************** do_pbsz *****************************/
do_pbsz(UNUSED wzd_string_t * name,wzd_string_t * param,wzd_context_t * context)2809 int do_pbsz(UNUSED wzd_string_t *name, wzd_string_t *param, wzd_context_t * context)
2810 {
2811 int ret;
2812 const char *arg;
2813
2814 arg = str_tochar(param);
2815 /** \todo TOK_BSZ: if user is NOT in TLS mode, insult him */
2816 /** \todo TOK_BSZ: use argument */
2817 ret = send_message_with_args(200,context,"PBSZ command okay");
2818 return E_OK;
2819 }
2820 #else
do_pbsz(wzd_string_t * name,wzd_string_t * arg,wzd_context_t * context)2821 int do_pbsz(wzd_string_t *name, wzd_string_t *arg, wzd_context_t * context)
2822 {
2823 send_message(502,context);
2824 return E_PARAM_INVALID;
2825 }
2826 #endif
2827
2828 #if defined(HAVE_OPENSSL) || defined(HAVE_GNUTLS)
2829 /*************** do_prot *****************************/
do_prot(UNUSED wzd_string_t * name,wzd_string_t * param,wzd_context_t * context)2830 int do_prot(UNUSED wzd_string_t *name, wzd_string_t *param, wzd_context_t * context)
2831 {
2832 int ret;
2833 const char *arg;
2834
2835 arg = str_tochar(param);
2836 /** \todo TOK_PROT: if user is NOT in TLS mode, insult him */
2837 if (strcasecmp("P",arg)==0)
2838 context->tls_data_mode = TLS_PRIV;
2839 else if (strcasecmp("C",arg)==0)
2840 context->tls_data_mode = TLS_CLEAR;
2841 else {
2842 ret = send_message_with_args(550,context,"PROT","must be C or P");
2843 return E_PARAM_INVALID;
2844 }
2845 ret = send_message_with_args(200,context,"PROT command okay");
2846 return E_OK;
2847 }
2848 #else
do_prot(wzd_string_t * name,wzd_string_t * arg,wzd_context_t * context)2849 int do_prot(wzd_string_t *name, wzd_string_t *arg, wzd_context_t * context)
2850 {
2851 send_message(502,context);
2852 return E_PARAM_INVALID;
2853 }
2854 #endif
2855
2856 #if defined(HAVE_OPENSSL) || defined(HAVE_GNUTLS)
2857 /*************** do_sscn *****************************/
do_sscn(UNUSED wzd_string_t * name,wzd_string_t * param,wzd_context_t * context)2858 int do_sscn(UNUSED wzd_string_t *name, wzd_string_t *param, wzd_context_t * context)
2859 {
2860 int ret;
2861 const char *arg;
2862
2863 arg = str_tochar(param);
2864 if (!arg || strlen(arg)==0 || strcasecmp(arg,"off")==0) {
2865 context->tls_role = TLS_SERVER_MODE;
2866 ret = send_message_with_args(200,context,"SSCN:SERVER METHOD");
2867 return E_OK;
2868 }
2869 if (strcasecmp(arg,"on")==0) {
2870 context->tls_role = TLS_CLIENT_MODE;
2871 ret = send_message_with_args(200,context,"SSCN:CLIENT METHOD");
2872 return E_OK;
2873 }
2874
2875 ret = send_message_with_args(550,context,"SSCN","Invalid argument");
2876 return E_PARAM_INVALID;
2877 }
2878 #else
do_sscn(wzd_string_t * name,wzd_string_t * arg,wzd_context_t * context)2879 int do_sscn(wzd_string_t *name, wzd_string_t *arg, wzd_context_t * context)
2880 {
2881 return E_PARAM_INVALID;
2882 }
2883 #endif
2884
2885 /*************** do_quit *****************************/
do_quit(UNUSED wzd_string_t * name,UNUSED wzd_string_t * arg,wzd_context_t * context)2886 int do_quit(UNUSED wzd_string_t *name, UNUSED wzd_string_t *arg, wzd_context_t * context)
2887 {
2888 int ret;
2889
2890 ret = send_message(221,context);
2891 {
2892 const char * groupname = NULL;
2893 wzd_user_t * user;
2894 const char * remote_host;
2895 struct hostent *h;
2896 char inet_str[256];
2897 int af = (context->family == WZD_INET6) ? AF_INET6 : AF_INET;
2898
2899 user = GetUserByID(context->userid);
2900
2901 if (user->group_num > 0) groupname = GetGroupByID(user->groups[0])->groupname;
2902 inet_str[0] = '\0';
2903 inet_ntop(af,context->hostip,inet_str,sizeof(inet_str));
2904 h = gethostbyaddr((char*)&context->hostip,sizeof(context->hostip),af);
2905 if (h==NULL)
2906 remote_host = inet_str;
2907 else
2908 remote_host = h->h_name;
2909 log_message("LOGOUT","%s (%s) \"%s\" \"%s\" \"%s\"",
2910 remote_host,
2911 inet_str,
2912 user->username,
2913 (groupname)?groupname:"No Group",
2914 user->tagline
2915 );
2916 }
2917 context->exitclient=1;
2918 /* check if pending xfers */
2919
2920 return E_OK;
2921 }
2922
2923 /*************** do_rest *****************************/
do_rest(UNUSED wzd_string_t * name,wzd_string_t * arg,wzd_context_t * context)2924 int do_rest(UNUSED wzd_string_t *name, wzd_string_t *arg, wzd_context_t * context)
2925 {
2926 int ret;
2927 u64_t ull;
2928 char *ptr;
2929
2930 if (!arg) {
2931 ret = send_message_with_args(501,context,"Invalid REST marker");
2932 return E_PARAM_INVALID;
2933 }
2934 ull = strtoull(str_tochar(arg), &ptr, 0);
2935 if (ptr==str_tochar(arg) || *ptr!='\0')
2936 {
2937 ret = send_message_with_args(501,context,"Invalid REST marker");
2938 return E_PARAM_INVALID;
2939 } else {
2940 char buf[256];
2941 snprintf(buf,256,"Restarting at %" PRIu64 ". Send STORE or RETRIEVE.",ull);
2942 ret = send_message_with_args(350,context,buf);
2943 context->resume = ull;
2944 }
2945 return E_OK;
2946 }
2947
2948 /*************** do_rnfr *****************************/
do_rnfr(UNUSED wzd_string_t * name,wzd_string_t * filename,wzd_context_t * context)2949 int do_rnfr(UNUSED wzd_string_t *name, wzd_string_t *filename, wzd_context_t * context)
2950 {
2951 char path[WZD_MAX_PATH];
2952 int ret;
2953 wzd_user_t * user;
2954
2955 user = GetUserByID(context->userid);
2956
2957 if (!user || !(user->userperms & RIGHT_RNFR)) {
2958 ret = send_message_with_args(550,context,"RNFR","permission denied");
2959 return E_FILE_NOEXIST;
2960 }
2961
2962
2963 if (!filename || strlen(str_tochar(filename))==0 || strlen(str_tochar(filename))>=WZD_MAX_PATH || checkpath_new(str_tochar(filename),path,context)) {
2964 ret = send_message_with_args(550,context,"RNFR","file does not exist");
2965 return E_FILE_NOEXIST;
2966 }
2967
2968 if (path[strlen(path)-1]=='/') path[strlen(path)-1]='\0';
2969
2970 /* deny retrieve to permissions file */
2971 if (is_hidden_file(path)) {
2972 ret = send_message_with_args(501,context,"Forbidden");
2973 return E_FILE_FORBIDDEN;
2974 }
2975
2976 context->current_action.token = TOK_RNFR;
2977 strncpy(context->current_action.arg,path,HARD_LAST_COMMAND_LENGTH);
2978 context->current_action.current_file = -1;
2979 context->current_action.bytesnow = 0;
2980 context->current_action.tm_start = time(NULL);
2981
2982 ret = send_message_with_args(350,context,"OK, send RNTO");
2983 return E_OK;
2984 }
2985
2986 /*************** do_rnto *****************************/
do_rnto(UNUSED wzd_string_t * name,wzd_string_t * filename,wzd_context_t * context)2987 int do_rnto(UNUSED wzd_string_t *name, wzd_string_t *filename, wzd_context_t * context)
2988 {
2989 char path[WZD_MAX_PATH];
2990 int ret;
2991 wzd_user_t * user;
2992
2993 user = GetUserByID(context->userid);
2994
2995 if (!user || !(user->userperms & RIGHT_RNFR)) {
2996 ret = send_message_with_args(550,context,"RNTO","permission denied");
2997 return E_FILE_NOEXIST;
2998 }
2999
3000
3001 if (!filename || strlen(str_tochar(filename))==0 || strlen(str_tochar(filename))>=WZD_MAX_PATH) {
3002 ret = send_message_with_args(553,context,"RNTO","wrong file name?");
3003 return E_PARAM_INVALID;
3004 }
3005 if (context->current_action.token != TOK_RNFR) {
3006 ret = send_message_with_args(553,context,"RNTO","send RNFR before!");
3007 return E_PARAM_INVALID;
3008 }
3009
3010 checkpath_new(str_tochar(filename),path,context);
3011 if (path[strlen(path)-1]=='/') path[strlen(path)-1]='\0';
3012
3013 /* deny retrieve to permissions file */
3014 if (is_hidden_file(path)) {
3015 ret = send_message_with_args(501,context,"Forbidden");
3016 return E_FILE_FORBIDDEN;
3017 }
3018 context->current_action.token = TOK_UNKNOWN;
3019 context->current_action.current_file = -1;
3020 context->current_action.bytesnow = 0;
3021
3022 ret = file_rename(context->current_action.arg,path,context);
3023 if (ret) {
3024 ret = send_message_with_args(550,context,"RNTO","command failed");
3025 } else {
3026 ret = send_message_with_args(250,context,"RNTO"," command okay");
3027 context->idle_time_start = time(NULL);
3028 }
3029 return E_OK;
3030 }
3031
3032 /*************** do_type *****************************/
do_type(UNUSED wzd_string_t * name,wzd_string_t * param,wzd_context_t * context)3033 int do_type(UNUSED wzd_string_t *name, wzd_string_t *param, wzd_context_t * context)
3034 {
3035 int ret;
3036
3037 context->resume = 0;
3038 if (!param) {
3039 ret = send_message_with_args(501,context,"Invalid TYPE marker");
3040 return E_PARAM_INVALID;
3041 }
3042 if (strcasecmp(str_tochar(param),"I")==0)
3043 context->current_xfer_type = BINARY;
3044 else if (strcasecmp(str_tochar(param),"A")==0)
3045 context->current_xfer_type = ASCII;
3046 else {
3047 ret = send_message(502,context);
3048 return E_PARAM_INVALID;
3049 }
3050 ret = send_message_with_args(200,context,"Command okay");
3051 return E_OK;
3052 }
3053
3054 /*************** do_xcrc *****************************/
do_xcrc(UNUSED wzd_string_t * name,wzd_string_t * arg,wzd_context_t * context)3055 int do_xcrc(UNUSED wzd_string_t *name, wzd_string_t *arg, wzd_context_t * context)
3056 {
3057 char path[WZD_MAX_PATH];
3058 char buffer[1024];
3059 const char * ptr;
3060 char * ptest;
3061 fs_filestat_t s;
3062 int ret;
3063 unsigned long crc = 0;
3064 unsigned long startpos = 0;
3065 unsigned long length = (unsigned long)-1;
3066 const char *param;
3067
3068 if (!str_checklength(arg,1,WZD_MAX_PATH-1)) {
3069 ret = send_message_with_args(501,context,"Syntax error");
3070 return E_PARAM_INVALID;
3071 }
3072 param = str_tochar(arg);
3073
3074 /* get filename and args:
3075 * "filename" must be quoted
3076 * startpos and length are optional
3077 */
3078 ptr = param;
3079 if (*ptr == '"') {
3080 ptr++;
3081 while (*ptr && *ptr != '"') ptr++;
3082 if (!*ptr) {
3083 ret = send_message_with_args(501,context,"Syntax error");
3084 return E_PARAM_INVALID;
3085 }
3086 memcpy(buffer,param+1,ptr-param-1);
3087 buffer[ptr-param-1] = '\0';
3088 ptr++;
3089 /* optional: read startpos AND length */
3090 startpos = strtoul(ptr,&ptest,0);
3091 if (ptest && ptest != ptr)
3092 {
3093 ptr = ptest;
3094 length = strtoul(ptr,&ptest,0);
3095 if (!ptest || ptest == ptr) {
3096 ret = send_message_with_args(501,context,"Syntax error");
3097 return E_PARAM_INVALID;
3098 } else { /* optional: read start checksum */
3099 ptr = ptest;
3100 crc = strtoul(ptr,&ptest,16);
3101 if (!ptest || ptest == ptr)
3102 crc = 0;
3103 }
3104 } else
3105 startpos = 0;
3106 param = buffer;
3107 }
3108
3109 if (!checkpath_new(param,path,context)) {
3110 if (path[strlen(path)-1]=='/')
3111 path[strlen(path)-1]='\0';
3112
3113 /* deny retrieve to permissions file */
3114 if (is_hidden_file(path)) {
3115 ret = send_message_with_args(501,context,"Forbidden");
3116 return E_FILE_FORBIDDEN;
3117 }
3118
3119
3120 if (fs_file_stat(path,&s)==0) {
3121 ret = calc_crc32(path,&crc,startpos,length);
3122 snprintf(buffer,1024,"%lX",crc);
3123 /* snprintf(buffer,1024,"%d %lX\r\n",250,crc);*/
3124 /* ret = send_message_raw(buffer,context);*/
3125 ret = send_message_with_args(250,context,buffer,"");
3126 return E_OK;
3127 }
3128 }
3129 ret = send_message_with_args(550,context,"XCRC","File inexistent or no access?");
3130 return E_FILE_NOEXIST;
3131 }
3132
3133 /*************** do_xmd5 *****************************/
do_xmd5(UNUSED wzd_string_t * name,wzd_string_t * arg,wzd_context_t * context)3134 int do_xmd5(UNUSED wzd_string_t *name, wzd_string_t *arg, wzd_context_t * context)
3135 {
3136 char path[WZD_MAX_PATH];
3137 char buffer[1024];
3138 const char * ptr;
3139 char * ptest;
3140 fs_filestat_t s;
3141 int ret;
3142 unsigned char crc[16];
3143 char md5str[33];
3144 unsigned long startpos = 0;
3145 unsigned long length = (unsigned long)-1;
3146 unsigned int i;
3147 const char *param;
3148
3149 if (!str_checklength(arg,1,WZD_MAX_PATH-1)) {
3150 ret = send_message_with_args(501,context,"Syntax error");
3151 return E_PARAM_INVALID;
3152 }
3153 param = str_tochar(arg);
3154
3155 for (i=0; i<16; i++)
3156 crc[i] = 0;
3157
3158 /* get filename and args:
3159 * "filename" must be quoted
3160 * startpos and length are optional
3161 */
3162 ptr = param;
3163 if (*ptr == '"') {
3164 ptr++;
3165 while (*ptr && *ptr != '"') ptr++;
3166 if (!*ptr) {
3167 ret = send_message_with_args(501,context,"Syntax error");
3168 return E_PARAM_INVALID;
3169 }
3170 memcpy(buffer,param+1,ptr-param-1);
3171 buffer[ptr-param-1] = '\0';
3172 ptr++;
3173 /* optional: read startpos AND length */
3174 startpos = strtoul(ptr,&ptest,0);
3175 if (ptest && ptest != ptr)
3176 {
3177 ptr = ptest;
3178 length = strtoul(ptr,&ptest,0);
3179 if (!ptest || ptest == ptr) {
3180 ret = send_message_with_args(501,context,"Syntax error");
3181 return E_PARAM_INVALID;
3182 } else { /* optional: read start checksum */
3183 ptr = ptest;
3184 strtomd5((char*)ptr,&ptest,crc);
3185 if (!ptest || ptest == ptr)
3186 memset(crc,0,16);
3187 }
3188 } else
3189 startpos = 0;
3190 param = buffer;
3191 }
3192
3193 if (!checkpath_new(param,path,context)) {
3194 if (path[strlen(path)-1]=='/')
3195 path[strlen(path)-1]='\0';
3196
3197 /* deny retrieve to permissions file */
3198 if (is_hidden_file(path)) {
3199 ret = send_message_with_args(501,context,"Forbidden");
3200 return E_FILE_FORBIDDEN;
3201 }
3202
3203
3204 if (fs_file_stat(path,&s)==0) {
3205 ret = calc_md5(path,crc,startpos,length);
3206 for (i=0; i<16; i++)
3207 snprintf(md5str+i*2,3,"%02x",crc[i]);
3208 ret = send_message_with_args(250,context,md5str,"");
3209 return E_OK;
3210 }
3211 }
3212 ret = send_message_with_args(550,context,"XMD5","File inexistent or no access?");
3213 return E_FILE_NOEXIST;
3214 }
3215
3216 /*************** do_help *****************************/
do_help(UNUSED wzd_string_t * name,UNUSED wzd_string_t * arg,wzd_context_t * context)3217 int do_help(UNUSED wzd_string_t *name, UNUSED wzd_string_t *arg, wzd_context_t * context)
3218 {
3219 /* TODO maybe add HELP SITE? */
3220 send_message_with_args(214,context);
3221
3222 return E_OK;
3223 }
3224
3225
3226 /*****************************************************/
3227 /*************** client main proc ********************/
3228 /*****************************************************/
3229 /** @brief Client main loop
3230 *
3231 * Calls do_login(context) to handle the login, and then enters the main
3232 * loop.
3233 *
3234 * Each loop consist of checking if the control connection is ready for
3235 * reading, and if data connection is ready for reading/writing. If both
3236 * are ready, the control connection is always handled first.
3237 * Data are handled in the separate function data_execute().
3238 *
3239 * Control data are first translated to current charset if needed, then the
3240 * first token is parsed and sent to commands_find() to identify the command.
3241 *
3242 * The exit is done using client_die().
3243 */
clientThreadProc(void * arg)3244 void * clientThreadProc(void *arg)
3245 {
3246 struct timeval tv;
3247 fd_set fds_r,fds_w,efds;
3248 unsigned long max_wait_time;
3249 wzd_context_t * context;
3250 char *buffer = NULL;
3251 int save_errno;
3252 fd_t sockfd;
3253 int ret;
3254 wzd_user_t * user;
3255 wzd_command_t * command;
3256 wzd_string_t * command_buffer;
3257 struct ftp_command_t * ftp_command;
3258 #ifndef _MSC_VER
3259 int oldtype;
3260 #endif
3261
3262 context = arg;
3263 sockfd = context->controlfd;
3264 context->last_file.name[0] = '\0';
3265 context->last_file.token = TOK_UNKNOWN;
3266 context->data_buffer = wzd_malloc(mainConfig->data_buffer_length);
3267
3268 #ifdef WIN32
3269 context->thread_id = GetCurrentThreadId();
3270 #else
3271 context->thread_id = pthread_self();
3272 #endif
3273 _tls_store_context(context);
3274
3275 out_log(LEVEL_INFO,"Client speaking to socket %d\n",sockfd);
3276 #ifndef WIN32
3277 #ifdef WZD_MULTITHREAD
3278 pthread_setcanceltype(PTHREAD_CANCEL_ASYNCHRONOUS, &oldtype);
3279 pthread_cleanup_push((void (*) (void *))client_die, (void *) context);
3280 #endif /* WZD_MULTITHREAD */
3281 #endif
3282
3283 ret = do_login(context);
3284
3285 if (ret) {
3286 #if defined (WIN32)
3287 client_die(context);
3288 #else
3289 /* on other platforms, the cleanup function will be executed
3290 * using pthread_cleanup_pop
3291 */
3292 pthread_exit(NULL);
3293 #endif /* WIN32 */
3294 return NULL;
3295 }
3296
3297 context->state = STATE_COMMAND;
3298
3299 user = GetUserByID(context->userid);
3300
3301 /* user+pass ok */
3302 send_message_raw("230- Command okay\r\n",context);
3303 {
3304 wzd_string_t * event_args = STR(user->username);
3305 event_send(mainConfig->event_mgr, EVENT_LOGIN, 230, event_args, context);
3306 str_deallocate(event_args);
3307 }
3308 ret = send_message(230,context);
3309
3310 /* update last login time */
3311 time(&user->last_login);
3312
3313 context->control_buffer = buffer = malloc(WZD_BUFFER_LEN);
3314
3315 /* get value for server tick */
3316 max_wait_time = config_get_integer(mainConfig->cfg_file, "GLOBAL", "client tick", &ret);
3317 if (ret != CF_OK) {
3318 max_wait_time = DEFAULT_CLIENT_TICK;
3319 }
3320
3321 /* main loop */
3322 context->exitclient=0;
3323 context->idle_time_start = time(NULL);
3324
3325 user = GetUserByID(context->userid);
3326 while (!context->exitclient) {
3327 #ifdef DEBUG
3328 if (check_context(context) != 0) {
3329 out_log(LEVEL_CRITICAL,"CRITICAL check_context failed\n");
3330 context->exitclient = 1;
3331 break;
3332 }
3333 #endif /* DEBUG */
3334
3335 /* check for finished transfers
3336 * Remember that this will be checked every max_wait_time seconds
3337 * (default: DEFAULT_CLIENT_TICK = 10), so at this point the transfer
3338 * can be finished while the thread is waiting to be joined
3339 */
3340 if (context->transfer_thread != NULL &&
3341 context->is_transferring == 0) {
3342 void * return_value;
3343
3344 out_log(LEVEL_FLOOD,"DEBUG waiting for transfer thread\n");
3345
3346 wzd_thread_join(context->transfer_thread,&return_value);
3347 context->transfer_thread = NULL;
3348
3349 free(context->transfer_thread);
3350 context->transfer_thread = NULL;
3351 }
3352
3353 save_errno = 666;
3354 /* 1. read */
3355 FD_ZERO(&fds_r);
3356 FD_ZERO(&fds_w);
3357 FD_ZERO(&efds);
3358 /* set control fd */
3359 FD_SET(sockfd,&fds_r);
3360 FD_SET(sockfd,&efds);
3361 /* set data fd */
3362 if (context->transfer_thread == NULL) {
3363 ret = data_set_fd(context,&fds_r,&fds_w,&efds);
3364 }
3365 if ((signed)sockfd > ret) ret = sockfd;
3366
3367 tv.tv_sec=max_wait_time; tv.tv_usec=0L;
3368 /* bug in windows implementation of select(): when aborting a data connection,
3369 * next calls to select() always return immediatly, causing wzdftpd
3370 * to use 100% cpu (infinite loop).
3371 * The solution is not to use efds
3372 */
3373 /* ret = select(ret+1,&fds_r,&fds_w,&efds,&tv);*/
3374 ret = select(ret+1,&fds_r,&fds_w,NULL,&tv);
3375 FD_ZERO(&efds);
3376 save_errno = errno;
3377
3378 if (ret==-1) {
3379 if (errno == EINTR) continue;
3380 else {
3381 out_log(LEVEL_CRITICAL,"Major error during recv: control fd %d errno %d error %s\n",sockfd,save_errno,strerror(save_errno));
3382 context->exitclient = 1;
3383 }
3384 }
3385 /* TODO XXX FIXME is this empty if() intentional ?? */
3386 if (FD_ISSET(sockfd,&efds)) {
3387 /* if (save_errno == EINTR) continue;*/
3388 /* out_log(LEVEL_CRITICAL,"Major error during recv: errno %d error %s\n",save_errno,strerror(save_errno));*/
3389 /*out_err(LEVEL_CRITICAL,"ret %d sockfd: %d %d datafd %d %d\n",ret,sockfd,FD_ISSET(sockfd,&efds),context->datafd,FD_ISSET(context->datafd,&efds));
3390 out_err(LEVEL_CRITICAL,"read %d %d write %d %d error %d %d\n",FD_ISSET(sockfd,&fds_r),FD_ISSET(context->datafd,&fds_r),
3391 FD_ISSET(sockfd,&fds_w),FD_ISSET(context->datafd,&fds_w),
3392 FD_ISSET(sockfd,&efds),FD_ISSET(context->datafd,&efds));*/
3393 /* continue;*/
3394 }
3395 if (context->transfer_thread == NULL) {
3396 ret = data_check_fd(context,&fds_r,&fds_w,&efds);
3397 }
3398 if (ret == -1) {
3399 /* we had an error reading data connection */
3400 /** \todo should be remove data descriptors and so ? */
3401 }
3402
3403 if (!FD_ISSET(sockfd,&fds_r)) {
3404 /* we check for data iff control is not set - control is prior */
3405 if (ret==1) {
3406 if (context->current_action.token == TOK_UNKNOWN) {
3407 /* we are receiving / sending data without RETR/STOR */
3408 continue;
3409 }
3410 /* we have data ready */
3411 ret = data_execute(context,user,&fds_r,&fds_w);
3412 continue;
3413 }
3414 /* nothing to read */
3415 /* XXX CHECK FOR TIMEOUT: control & data if needed */
3416 /* check timeout */
3417 if (check_timeout(context)) break;
3418 continue;
3419 }
3420 ret = (context->read_fct)(sockfd,buffer,WZD_BUFFER_LEN-1,0,0,context); /* timeout = 0, we know there's something to read */
3421
3422 /* remote host has closed session */
3423 if (ret==0 || ret==-1) {
3424 out_log(LEVEL_FLOOD,"Host disconnected improperly!\n");
3425 context->exitclient=1;
3426 break;
3427 }
3428
3429 /* this replace the memset (bzero ?) some lines before */
3430 buffer[ret] = '\0';
3431
3432 cleanup_ftp_command(buffer,ret);
3433
3434 if (buffer[0]=='\0') continue;
3435
3436 command_buffer = STR(buffer);
3437
3438 str_trim_right(command_buffer);
3439
3440 set_action(context,str_tochar(command_buffer));
3441
3442 /* context->idle_time_start = time(NULL);*/
3443 #ifdef DEBUG
3444 out_err(LEVEL_FLOOD,"<thread %ld> <- '%s'\n",(unsigned long)context->pid_child,str_tochar(command_buffer));
3445 #endif
3446
3447 /* reset current reply */
3448 reply_clear(context);
3449
3450 /* parse and identify command */
3451 ftp_command = parse_ftp_command(command_buffer);
3452
3453 if (ftp_command != NULL) {
3454 command = ftp_command->command;
3455
3456 /** For FTP commands, the default permission (if not specified)
3457 * is to ALLOW users to use command, unless restricted !
3458 */
3459 if (command->perms && commands_check_permission(command,context)) {
3460 ret = send_message_with_args(501,context,"Permission Denied");
3461 free_ftp_command(ftp_command);
3462 continue;
3463 }
3464
3465 if (command->command)
3466 ret = (*(command->command))(ftp_command->command_name,ftp_command->args,context);
3467 else { /* external command */
3468 char buffer_command[4096];
3469 wzd_group_t * group = NULL;
3470
3471 if (user->group_num > 0) group = GetGroupByID(user->groups[0]);
3472 cookie_parse_buffer(str_tochar(command->external_command), user, group, context, buffer_command, sizeof(buffer_command));
3473 chop(buffer_command);
3474
3475 /* add arguments given on CLI to event */
3476 if (str_length(ftp_command->args)>0) {
3477 strlcat(buffer_command, " ", sizeof(buffer_command));
3478 strlcat(buffer_command, str_tochar(ftp_command->args), sizeof(buffer_command));
3479 }
3480
3481 ret = event_exec(buffer_command,context);
3482 }
3483
3484 /** \todo When all functions use reply_push, test reply and send error if -1 */
3485 ret = reply_send(context);
3486 } else { /* no command found */
3487 ret = send_message(502,context);
3488 str_deallocate(command_buffer);
3489 }
3490 free_ftp_command(ftp_command);
3491
3492 } /* while (!exitclient) */
3493
3494
3495 #ifdef WZD_MULTITHREAD
3496 #ifndef WIN32
3497 pthread_cleanup_pop(1); /* 1 means the cleanup fct is executed !*/
3498 #else
3499 client_die(context);
3500 #endif /* _MSC_VER */
3501 #else /* WZD_MULTITHREAD */
3502 client_die(context);
3503 #endif /* WZD_MULTITHREAD */
3504
3505 return NULL;
3506 }
3507
3508 /** \brief Test if remote address is different from the connected client (i.e,
3509 * if client is trying to transfer files to use site-to-site transfer)
3510 *
3511 * \return 1 if transfer is FXP
3512 */
test_fxp(const char * remote_ip,net_family_t family,wzd_context_t * context)3513 static int test_fxp(const char * remote_ip, net_family_t family, wzd_context_t * context)
3514 {
3515 switch (family) {
3516 case WZD_INET4:
3517 return (memcmp(remote_ip,context->hostip,4) != 0);
3518 case WZD_INET6:
3519 return (memcmp(remote_ip,context->hostip,16) != 0);
3520 default:
3521 out_log(LEVEL_HIGH,"ERROR test_fxp called with invalid family\n");
3522 };
3523 return -1;
3524 }
3525
fxp_is_denied(wzd_user_t * user)3526 static int fxp_is_denied(wzd_user_t * user)
3527 {
3528 return (strchr(user->flags,FLAG_FXP_DISABLE) != NULL);
3529 }
3530
3531 /** \brief Store context in Thread Local Store (TLS)
3532 *
3533 * \param[in] context Client context
3534 * \return 0 if ok
3535 */
_tls_store_context(wzd_context_t * context)3536 int _tls_store_context(wzd_context_t * context)
3537 {
3538 if (_key_context == NULL) {
3539 _key_context = wzd_tls_allocate();
3540 if (_key_context == NULL) return -1;
3541 }
3542
3543 if (wzd_tls_setspecific(_key_context, context) != 0) {
3544 out_log(LEVEL_HIGH,"ERROR Could not store context in TLS\n");
3545 wzd_tls_free(_key_context);
3546 _key_context = NULL;
3547 return -1;
3548 }
3549 return 0;
3550 }
3551
3552 /** \brief Get current context from Thread Local Storage (TLS)
3553 *
3554 * \return
3555 * - a valid wzd_context_t structure if found
3556 * - NULL if the value was not found in TLS
3557 */
_tls_get_context(void)3558 wzd_context_t * _tls_get_context(void)
3559 {
3560 if (_key_context != NULL)
3561 return wzd_tls_getspecific(_key_context);
3562
3563 return NULL;
3564 }
3565
3566 /** \brief Remove current context from Thread Local Storage (TLS)
3567 *
3568 * This is used by threads to release properly TLS resources used
3569 * to store context pointer.
3570 *
3571 * \return 0 if ok
3572 */
_tls_remove_context(void)3573 int _tls_remove_context(void)
3574 {
3575 if (_key_context != NULL)
3576 return wzd_tls_remove(_key_context);
3577
3578 return 0;
3579 }
3580
3581
3582
3583