1 /*
2 * ProFTPD - FTP server daemon
3 * Copyright (c) 1997, 1998 Public Flood Software
4 * Copyright (c) 1999, 2000 MacGyver aka Habeeb J. Dihu <macgyver@tos.net>
5 * Copyright (c) 2001-2020 The ProFTPD Project team
6 *
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (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., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, USA.
20 *
21 * As a special exemption, Public Flood Software/MacGyver aka Habeeb J. Dihu
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
27 /* Data transfer module for ProFTPD */
28
29 #include "conf.h"
30 #include "privs.h"
31 #include "error.h"
32
33 #ifdef HAVE_SYS_SENDFILE_H
34 # include <sys/sendfile.h>
35 #endif
36
37 /* Minimum priority a process can have. */
38 #ifndef PRIO_MIN
39 # define PRIO_MIN -20
40 #endif
41
42 /* Maximum priority a process can have. */
43 #ifndef PRIO_MAX
44 # define PRIO_MAX 20
45 #endif
46
47 extern module auth_module;
48 extern pid_t mpid;
49
50 /* Variables for this module */
51 static pr_fh_t *retr_fh = NULL;
52 static pr_fh_t *stor_fh = NULL;
53 static pr_fh_t *displayfilexfer_fh = NULL;
54
55 static unsigned char have_rfc2228_data = FALSE;
56 static unsigned char have_type = FALSE;
57 static unsigned char have_zmode = FALSE;
58 static unsigned char use_sendfile = TRUE;
59 static off_t use_sendfile_len = 0;
60 static float use_sendfile_pct = -1.0;
61
62 static int xfer_check_limit(cmd_rec *);
63
64 /* TransferOptions */
65 #define PR_XFER_OPT_HANDLE_ALLO 0x0001
66 #define PR_XFER_OPT_IGNORE_ASCII 0x0002
67 static unsigned long xfer_opts = PR_XFER_OPT_HANDLE_ALLO;
68
69 static void xfer_exit_ev(const void *, void *);
70 static void xfer_sigusr2_ev(const void *, void *);
71 static void xfer_timeout_session_ev(const void *, void *);
72 static void xfer_timeout_stalled_ev(const void *, void *);
73 static int xfer_sess_init(void);
74
75 /* Used for MaxTransfersPerHost and TransferRate */
76 static int xfer_parse_cmdlist(const char *, config_rec *, char *);
77
78 module xfer_module;
79
80 static int xfer_logged_sendfile_decline_msg = FALSE;
81
82 static const char *trace_channel = "xfer";
83
find_max_nbytes(char * directive)84 static off_t find_max_nbytes(char *directive) {
85 config_rec *c = NULL;
86 unsigned int ctxt_precedence = 0;
87 unsigned char have_user_limit, have_group_limit, have_class_limit,
88 have_all_limit;
89 off_t max_nbytes = 0UL;
90
91 have_user_limit = have_group_limit = have_class_limit =
92 have_all_limit = FALSE;
93
94 c = find_config(CURRENT_CONF, CONF_PARAM, directive, FALSE);
95 while (c) {
96
97 /* This check is for more than three arguments: one argument is the
98 * classifier (i.e. "user", "group", or "class"), one argument is
99 * the precedence, one is the number of bytes; the remaining arguments
100 * are the individual items in the configured expression.
101 */
102
103 if (c->argc > 3) {
104 if (strncmp(c->argv[2], "user", 5) == 0) {
105
106 if (pr_expr_eval_user_or((char **) &c->argv[3]) == TRUE) {
107 if (*((unsigned int *) c->argv[1]) > ctxt_precedence) {
108
109 /* Set the context precedence */
110 ctxt_precedence = *((unsigned int *) c->argv[1]);
111
112 max_nbytes = *((off_t *) c->argv[0]);
113
114 have_group_limit = have_class_limit = have_all_limit = FALSE;
115 have_user_limit = TRUE;
116 }
117 }
118
119 } else if (strncmp(c->argv[2], "group", 6) == 0) {
120
121 if (pr_expr_eval_group_or((char **) &c->argv[3]) == TRUE) {
122 if (*((unsigned int *) c->argv[1]) > ctxt_precedence) {
123
124 /* Set the context precedence */
125 ctxt_precedence = *((unsigned int *) c->argv[1]);
126
127 max_nbytes = *((off_t *) c->argv[0]);
128
129 have_user_limit = have_class_limit = have_all_limit = FALSE;
130 have_group_limit = TRUE;
131 }
132 }
133
134 } else if (strncmp(c->argv[2], "class", 6) == 0) {
135
136 if (pr_expr_eval_class_or((char **) &c->argv[3]) == TRUE) {
137 if (*((unsigned int *) c->argv[1]) > ctxt_precedence) {
138
139 /* Set the context precedence */
140 ctxt_precedence = *((unsigned int *) c->argv[1]);
141
142 max_nbytes = *((off_t *) c->argv[0]);
143
144 have_user_limit = have_group_limit = have_all_limit = FALSE;
145 have_class_limit = TRUE;
146 }
147 }
148 }
149
150 } else {
151
152 if (*((unsigned int *) c->argv[1]) > ctxt_precedence) {
153
154 /* Set the context precedence. */
155 ctxt_precedence = *((unsigned int *) c->argv[1]);
156
157 max_nbytes = *((off_t *) c->argv[0]);
158
159 have_user_limit = have_group_limit = have_class_limit = FALSE;
160 have_all_limit = TRUE;
161 }
162 }
163
164 c = find_config_next(c, c->next, CONF_PARAM, directive, FALSE);
165 }
166
167 /* Print out some nice debugging information. */
168 if (max_nbytes > 0 &&
169 (have_user_limit || have_group_limit ||
170 have_class_limit || have_all_limit)) {
171 pr_log_debug(DEBUG5, "%s (%" PR_LU " bytes) in effect for %s",
172 directive, (pr_off_t) max_nbytes,
173 have_user_limit ? "user " : have_group_limit ? "group " :
174 have_class_limit ? "class " : "all");
175 }
176
177 return max_nbytes;
178 }
179
_log_transfer(char direction,char abort_flag)180 static void _log_transfer(char direction, char abort_flag) {
181 struct timeval end_time;
182 char *fullpath = NULL;
183
184 memset(&end_time, '\0', sizeof(end_time));
185
186 if (session.xfer.start_time.tv_sec != 0) {
187 gettimeofday(&end_time, NULL);
188 end_time.tv_sec -= session.xfer.start_time.tv_sec;
189
190 if (end_time.tv_usec >= session.xfer.start_time.tv_usec) {
191 end_time.tv_usec -= session.xfer.start_time.tv_usec;
192
193 } else {
194 end_time.tv_usec = 1000000L - (session.xfer.start_time.tv_usec -
195 end_time.tv_usec);
196 end_time.tv_sec--;
197 }
198 }
199
200 fullpath = dir_abs_path(session.xfer.p, session.xfer.path, TRUE);
201
202 if ((session.sf_flags & SF_ANON) != 0) {
203 xferlog_write(end_time.tv_sec, pr_netaddr_get_sess_remote_name(),
204 session.xfer.total_bytes, fullpath,
205 (session.sf_flags & SF_ASCII ? 'a' : 'b'), direction,
206 'a', session.anon_user, abort_flag, "_");
207
208 } else {
209 xferlog_write(end_time.tv_sec, pr_netaddr_get_sess_remote_name(),
210 session.xfer.total_bytes, fullpath,
211 (session.sf_flags & SF_ASCII ? 'a' : 'b'), direction,
212 'r', session.user, abort_flag, "_");
213 }
214
215 pr_log_debug(DEBUG1, "Transfer %s %" PR_LU " bytes in %ld.%02lu seconds",
216 abort_flag == 'c' ? "completed:" : "aborted after",
217 (pr_off_t) session.xfer.total_bytes, (long) end_time.tv_sec,
218 (unsigned long)(end_time.tv_usec / 10000));
219 }
220
221 /* Code borrowed from src/dirtree.c's get_word() -- modified to separate
222 * words on commas as well as spaces.
223 */
get_cmd_from_list(char ** list)224 static char *get_cmd_from_list(char **list) {
225 char *res = NULL, *dst = NULL;
226 unsigned char quote_mode = FALSE;
227
228 while (**list && PR_ISSPACE(**list)) {
229 (*list)++;
230 }
231
232 if (!**list)
233 return NULL;
234
235 res = dst = *list;
236
237 if (**list == '\"') {
238 quote_mode = TRUE;
239 (*list)++;
240 }
241
242 while (**list && **list != ',' &&
243 (quote_mode ? (**list != '\"') : (!PR_ISSPACE(**list)))) {
244
245 if (**list == '\\' && quote_mode) {
246
247 /* escaped char */
248 if (*((*list) + 1))
249 *dst = *(++(*list));
250 }
251
252 *dst++ = **list;
253 ++(*list);
254 }
255
256 if (**list)
257 (*list)++;
258
259 *dst = '\0';
260
261 return res;
262 }
263
xfer_check_limit(cmd_rec * cmd)264 static int xfer_check_limit(cmd_rec *cmd) {
265 config_rec *c = NULL;
266 const char *client_addr = pr_netaddr_get_ipstr(session.c->remote_addr);
267 char server_addr[128];
268
269 memset(server_addr, '\0', sizeof(server_addr));
270 pr_snprintf(server_addr, sizeof(server_addr)-1, "%s:%d",
271 pr_netaddr_get_ipstr(main_server->addr), main_server->ServerPort);
272 server_addr[sizeof(server_addr)-1] = '\0';
273
274 c = find_config(CURRENT_CONF, CONF_PARAM, "MaxTransfersPerHost", FALSE);
275 while (c) {
276 char *xfer_cmd = NULL, **cmdlist = (char **) c->argv[0];
277 unsigned char matched_cmd = FALSE;
278 unsigned int curr = 0, max = 0;
279 pr_scoreboard_entry_t *score = NULL;
280
281 pr_signals_handle();
282
283 /* Does this MaxTransfersPerHost apply to the current command? Note: this
284 * could be made more efficient by using bitmasks rather than string
285 * comparisons.
286 */
287 for (xfer_cmd = *cmdlist; xfer_cmd; xfer_cmd = *(cmdlist++)) {
288 if (strcasecmp(xfer_cmd, cmd->argv[0]) == 0) {
289 matched_cmd = TRUE;
290 break;
291 }
292 }
293
294 if (!matched_cmd) {
295 c = find_config_next(c, c->next, CONF_PARAM, "MaxTransfersPerHost",
296 FALSE);
297 continue;
298 }
299
300 max = *((unsigned int *) c->argv[1]);
301
302 /* Count how many times the current IP address is logged in, AND how
303 * many of those other logins are currently using this command.
304 */
305
306 (void) pr_rewind_scoreboard();
307 while ((score = pr_scoreboard_entry_read()) != NULL) {
308 pr_signals_handle();
309
310 /* Scoreboard entry must match local server address and remote client
311 * address to be counted.
312 */
313 if (strcmp(score->sce_server_addr, server_addr) != 0)
314 continue;
315
316 if (strcmp(score->sce_client_addr, client_addr) != 0)
317 continue;
318
319 if (strcmp(score->sce_cmd, xfer_cmd) == 0)
320 curr++;
321 }
322
323 pr_restore_scoreboard();
324
325 if (curr >= max) {
326 char maxn[20];
327
328 char *maxstr = "Sorry, the maximum number of data transfers (%m) from "
329 "your host are currently being used.";
330
331 if (c->argv[2] != NULL)
332 maxstr = c->argv[2];
333
334 pr_event_generate("mod_xfer.max-transfers-per-host", session.c);
335
336 memset(maxn, '\0', sizeof(maxn));
337 pr_snprintf(maxn, sizeof(maxn)-1, "%u", max);
338 pr_response_send(R_451, "%s", sreplace(cmd->tmp_pool, maxstr, "%m",
339 maxn, NULL));
340 pr_log_debug(DEBUG4, "MaxTransfersPerHost %u exceeded for %s for "
341 "client '%s'", max, xfer_cmd, client_addr);
342
343 return -1;
344 }
345
346 c = find_config_next(c, c->next, CONF_PARAM, "MaxTransfersPerHost", FALSE);
347 }
348
349 c = find_config(CURRENT_CONF, CONF_PARAM, "MaxTransfersPerUser", FALSE);
350 while (c) {
351 char *xfer_cmd = NULL, **cmdlist = (char **) c->argv[0];
352 unsigned char matched_cmd = FALSE;
353 unsigned int curr = 0, max = 0;
354 pr_scoreboard_entry_t *score = NULL;
355
356 pr_signals_handle();
357
358 /* Does this MaxTransfersPerUser apply to the current command? Note: this
359 * could be made more efficient by using bitmasks rather than string
360 * comparisons.
361 */
362 for (xfer_cmd = *cmdlist; xfer_cmd; xfer_cmd = *(cmdlist++)) {
363 if (strcasecmp(xfer_cmd, cmd->argv[0]) == 0) {
364 matched_cmd = TRUE;
365 break;
366 }
367 }
368
369 if (!matched_cmd) {
370 c = find_config_next(c, c->next, CONF_PARAM, "MaxTransfersPerUser",
371 FALSE);
372 continue;
373 }
374
375 max = *((unsigned int *) c->argv[1]);
376
377 /* Count how many times the current user is logged in, AND how many of
378 * those other logins are currently using this command.
379 */
380
381 (void) pr_rewind_scoreboard();
382 while ((score = pr_scoreboard_entry_read()) != NULL) {
383 pr_signals_handle();
384
385 if (strcmp(score->sce_server_addr, server_addr) != 0)
386 continue;
387
388 if (strcmp(score->sce_user, session.user) != 0)
389 continue;
390
391 if (strcmp(score->sce_cmd, xfer_cmd) == 0)
392 curr++;
393 }
394
395 pr_restore_scoreboard();
396
397 if (curr >= max) {
398 char maxn[20];
399
400 char *maxstr = "Sorry, the maximum number of data transfers (%m) from "
401 "this user are currently being used.";
402
403 if (c->argv[2] != NULL)
404 maxstr = c->argv[2];
405
406 pr_event_generate("mod_xfer.max-transfers-per-user", session.user);
407
408 memset(maxn, '\0', sizeof(maxn));
409 pr_snprintf(maxn, sizeof(maxn)-1, "%u", max);
410 pr_response_send(R_451, "%s", sreplace(cmd->tmp_pool, maxstr, "%m",
411 maxn, NULL));
412 pr_log_debug(DEBUG4, "MaxTransfersPerUser %u exceeded for %s for "
413 "user '%s'", max, xfer_cmd, session.user);
414
415 return -1;
416 }
417
418 c = find_config_next(c, c->next, CONF_PARAM, "MaxTransfersPerUser", FALSE);
419 }
420
421 return 0;
422 }
423
xfer_displayfile(void)424 static void xfer_displayfile(void) {
425
426 if (displayfilexfer_fh) {
427 if (pr_display_fh(displayfilexfer_fh, session.vwd, R_226, 0) < 0) {
428 pr_log_debug(DEBUG6, "unable to display DisplayFileTransfer "
429 "file '%s': %s", displayfilexfer_fh->fh_path, strerror(errno));
430 }
431
432 /* Rewind the filehandle, so that it can be used again. */
433 if (pr_fsio_lseek(displayfilexfer_fh, 0, SEEK_SET) < 0) {
434 pr_log_debug(DEBUG6, "error rewinding DisplayFileTransfer "
435 "file '%s': %s", displayfilexfer_fh->fh_path, strerror(errno));
436 }
437
438 } else {
439 char *displayfilexfer;
440
441 displayfilexfer = get_param_ptr(main_server->conf, "DisplayFileTransfer",
442 FALSE);
443 if (displayfilexfer) {
444 if (pr_display_file(displayfilexfer, session.vwd, R_226, 0) < 0) {
445 pr_log_debug(DEBUG6, "unable to display DisplayFileTransfer "
446 "file '%s': %s", displayfilexfer, strerror(errno));
447 }
448 }
449 }
450 }
451
xfer_parse_cmdlist(const char * name,config_rec * c,char * cmdlist)452 static int xfer_parse_cmdlist(const char *name, config_rec *c,
453 char *cmdlist) {
454 char *cmd = NULL;
455 array_header *cmds = NULL;
456
457 /* Allocate an array_header. */
458 cmds = make_array(c->pool, 0, sizeof(char *));
459
460 /* Add each command to the array, checking for invalid commands or
461 * duplicates.
462 */
463 while ((cmd = get_cmd_from_list(&cmdlist)) != NULL) {
464
465 /* Is the given command a valid one for this directive? */
466 if (strcasecmp(cmd, C_APPE) != 0 &&
467 strcasecmp(cmd, C_RETR) != 0 &&
468 strcasecmp(cmd, C_STOR) != 0 &&
469 strcasecmp(cmd, C_STOU) != 0) {
470 pr_log_debug(DEBUG0, "invalid %s command: %s", name, cmd);
471 errno = EINVAL;
472 return -1;
473 }
474
475 *((char **) push_array(cmds)) = pstrdup(c->pool, cmd);
476 }
477
478 /* Terminate the array with a NULL. */
479 *((char **) push_array(cmds)) = NULL;
480
481 /* Store the array of commands in the config_rec. */
482 c->argv[0] = (void *) cmds->elts;
483
484 return 0;
485 }
486
transmit_normal(pool * p,char * buf,size_t bufsz)487 static int transmit_normal(pool *p, char *buf, size_t bufsz) {
488 int xerrno;
489 long nread;
490 size_t read_len;
491 pr_error_t *err = NULL;
492
493 read_len = bufsz;
494 if (session.range_len > 0) {
495 if (((off_t) read_len) > session.range_len) {
496 read_len = session.range_len;
497 }
498 }
499
500 nread = pr_fsio_read_with_error(p, retr_fh, buf, read_len, &err);
501 xerrno = errno;
502
503 while (nread < 0) {
504 if (xerrno == EINTR) {
505 /* Interrupted by signal; handle it, and try again. */
506 errno = EINTR;
507 pr_signals_handle();
508
509 nread = pr_fsio_read_with_error(p, retr_fh, buf, read_len, &err);
510 xerrno = errno;
511 continue;
512 }
513
514 pr_error_set_where(err, &xfer_module, __FILE__, __LINE__ - 4);
515 pr_error_set_why(err, pstrcat(p, "normal download of '", retr_fh->fh_path,
516 "'", NULL));
517
518 (void) pr_trace_msg("fileperms", 1, "RETR, user '%s' (UID %s, GID %s): "
519 "error reading from '%s': %s", session.user,
520 pr_uid2str(p, session.uid), pr_gid2str(p, session.gid),
521 retr_fh->fh_path, strerror(xerrno));
522
523 if (err != NULL) {
524 pr_log_debug(DEBUG9, "%s", pr_error_strerror(err, 0));
525 pr_error_destroy(err);
526 err = NULL;
527 }
528
529 errno = xerrno;
530 return -1;
531 }
532
533 if (nread == 0) {
534 return 0;
535 }
536
537 return pr_data_xfer(buf, nread);
538 }
539
540 #ifdef HAVE_SENDFILE
transmit_sendfile(off_t data_len,off_t * data_offset,pr_sendfile_t * sent_len)541 static int transmit_sendfile(off_t data_len, off_t *data_offset,
542 pr_sendfile_t *sent_len) {
543 off_t send_len;
544
545 /* We don't use sendfile() if:
546 * - We're using bandwidth throttling.
547 * - We're transmitting an ASCII file.
548 * - We're using RFC2228 data channel protection
549 * - We're using MODE Z compression
550 * - There's no data left to transmit.
551 * - UseSendfile is set to off.
552 */
553 if (pr_throttle_have_rate() ||
554 !(session.xfer.file_size - data_len) ||
555 (session.sf_flags & (SF_ASCII|SF_ASCII_OVERRIDE)) ||
556 have_rfc2228_data || have_zmode ||
557 !use_sendfile) {
558
559 if (!xfer_logged_sendfile_decline_msg) {
560 if (!use_sendfile) {
561 pr_log_debug(DEBUG10, "declining use of sendfile due to UseSendfile "
562 "configuration setting");
563
564 } else if (pr_throttle_have_rate()) {
565 pr_log_debug(DEBUG10, "declining use of sendfile due to TransferRate "
566 "restrictions");
567
568 } else if (session.sf_flags & (SF_ASCII|SF_ASCII_OVERRIDE)) {
569 pr_log_debug(DEBUG10, "declining use of sendfile for ASCII data");
570
571 } else if (have_rfc2228_data) {
572 pr_log_debug(DEBUG10, "declining use of sendfile due to RFC2228 data "
573 "channel protections");
574
575 } else if (have_zmode) {
576 pr_log_debug(DEBUG10, "declining use of sendfile due to MODE Z "
577 "restrictions");
578
579 } else {
580 pr_log_debug(DEBUG10, "declining use of sendfile due to lack of data "
581 "to transmit");
582 }
583
584 xfer_logged_sendfile_decline_msg = TRUE;
585 }
586
587 return 0;
588 }
589
590 pr_log_debug(DEBUG10, "using sendfile capability for transmitting data");
591
592 /* Determine how many bytes to send using sendfile(2). By default,
593 * we want to send all of the remaining bytes.
594 *
595 * However, the admin may have configured either a length in bytes, or
596 * a percentage, using the UseSendfile directive. We will send the smaller
597 * of the remaining size, or the length/percentage.
598 */
599
600 if (session.range_len > 0) {
601 send_len = session.range_len;
602
603 } else {
604 send_len = session.xfer.file_size - data_len;
605 }
606
607 if (use_sendfile_len > 0 &&
608 send_len > use_sendfile_len) {
609 pr_log_debug(DEBUG10, "using sendfile with configured UseSendfile length "
610 "(%" PR_LU " bytes)", (pr_off_t) use_sendfile_len);
611 send_len = use_sendfile_len;
612
613 } else if (use_sendfile_pct > 0.0) {
614 off_t pct_len;
615
616 pct_len = (off_t) (session.xfer.file_size * use_sendfile_pct);
617 if (send_len > pct_len) {
618 pr_log_debug(DEBUG10, "using sendfile with configured UseSendfile "
619 "percentage %0.0f%% (%" PR_LU " bytes)", use_sendfile_pct * 100.0,
620 (pr_off_t) pct_len);
621 send_len = pct_len;
622 }
623 }
624
625 retry:
626 *sent_len = pr_data_sendfile(PR_FH_FD(retr_fh), data_offset, send_len);
627
628 if (*sent_len == -1) {
629 int xerrno = errno;
630
631 switch (xerrno) {
632 case EAGAIN:
633 case EINTR:
634 if (XFER_ABORTED) {
635 pr_log_pri(PR_LOG_NOTICE, "sendfile transmission aborted: %s",
636 strerror(xerrno));
637 errno = xerrno;
638 return -1;
639 }
640
641 /* Interrupted call, or the other side wasn't ready yet. */
642 pr_signals_handle();
643 goto retry;
644
645 case EPIPE:
646 case ECONNRESET:
647 case ETIMEDOUT:
648 case EHOSTUNREACH:
649 /* Other side broke the connection. */
650 break;
651
652 #ifdef ENOSYS
653 case ENOSYS:
654 #endif /* ENOSYS */
655
656 #ifdef EOVERFLOW
657 case EOVERFLOW:
658 #endif /* EOVERFLOW */
659
660 case EINVAL:
661 /* No sendfile support, apparently. Try it the normal way. */
662 return 0;
663 break;
664
665 default:
666 pr_log_pri(PR_LOG_WARNING, "error using sendfile(): [%d] %s", xerrno,
667 strerror(xerrno));
668 errno = xerrno;
669 return -1;
670 }
671 }
672
673 return 1;
674 }
675 #endif /* HAVE_SENDFILE */
676
677 /* Note: the data_len and data_offset arguments are only for the benefit of
678 * transmit_sendfile(), if sendfile support is enabled. The transmit_normal()
679 * function only needs/uses buf and bufsz.
680 */
transmit_data(pool * p,off_t data_len,off_t * data_offset,char * buf,size_t bufsz)681 static long transmit_data(pool *p, off_t data_len, off_t *data_offset,
682 char *buf, size_t bufsz) {
683 long res;
684 int xerrno = 0;
685
686 #ifdef HAVE_SENDFILE
687 pr_sendfile_t sent_len;
688 int ret;
689 #endif /* HAVE_SENDFILE */
690
691 if (pr_inet_set_proto_cork(PR_NETIO_FD(session.d->outstrm), 1) < 0) {
692 pr_log_pri(PR_LOG_NOTICE, "error corking socket fd %d: %s",
693 PR_NETIO_FD(session.d->outstrm), strerror(errno));
694 }
695
696 #ifdef HAVE_SENDFILE
697 ret = transmit_sendfile(data_len, data_offset, &sent_len);
698 if (ret > 0) {
699 /* sendfile() was used, so return the value of sent_len. */
700 res = (long) sent_len;
701
702 } else if (ret == 0) {
703 /* sendfile() should not be used for some reason, fallback to using
704 * normal data transmission methods.
705 */
706 res = transmit_normal(p, buf, bufsz);
707 xerrno = errno;
708
709 } else {
710 /* There was an error with sendfile(); do NOT attempt to re-send the
711 * data using normal data transmission methods, unless the cause
712 * of the error is one of an accepted few cases.
713 */
714 # ifdef EOVERFLOW
715 pr_log_debug(DEBUG10, "use of sendfile(2) failed due to %s (%d), "
716 "falling back to normal data transmission", strerror(errno),
717 errno);
718 res = transmit_normal(p, buf, bufsz);
719 xerrno = errno;
720
721 # else
722 if (session.d != NULL) {
723 (void) pr_inet_set_proto_cork(PR_NETIO_FD(session.d->outstrm), 0);
724 }
725
726 errno = EIO;
727 res = -1;
728 # endif
729 }
730
731 #else
732 res = transmit_normal(p, buf, bufsz);
733 xerrno = errno;
734 #endif /* HAVE_SENDFILE */
735
736 if (session.d != NULL) {
737 /* The session.d struct can become null after transmit_normal() if the
738 * client aborts the transfer, thus we need to check for this.
739 */
740 if (pr_inet_set_proto_cork(PR_NETIO_FD(session.d->outstrm), 0) < 0) {
741 if (errno != EINVAL) {
742 pr_log_pri(PR_LOG_NOTICE, "error uncorking socket fd %d: %s",
743 PR_NETIO_FD(session.d->outstrm), strerror(errno));
744 }
745 }
746 }
747
748 errno = xerrno;
749 return res;
750 }
751
stor_chown(pool * p)752 static void stor_chown(pool *p) {
753 struct stat st;
754 const char *xfer_path = NULL;
755
756 if (session.xfer.xfer_type == STOR_HIDDEN) {
757 xfer_path = session.xfer.path_hidden;
758
759 } else {
760 xfer_path = session.xfer.path;
761 }
762
763 /* session.fsgid defaults to -1, so chown(2) won't chgrp unless specifically
764 * requested via GroupOwner.
765 */
766 if (session.fsuid != (uid_t) -1 &&
767 xfer_path != NULL) {
768 int res, xerrno = 0;
769 pr_error_t *err = NULL;
770
771 PRIVS_ROOT
772 res = pr_fsio_lchown_with_error(p, xfer_path, session.fsuid, session.fsgid,
773 &err);
774 xerrno = errno;
775 PRIVS_RELINQUISH
776
777 if (res < 0) {
778 pr_error_set_where(err, &xfer_module, __FILE__, __LINE__ - 6);
779 pr_error_set_why(err, pstrcat(p, "set UserOwner of '", xfer_path,
780 "'", NULL));
781
782 if (err != NULL) {
783 pr_log_pri(PR_LOG_WARNING, "%s", pr_error_strerror(err, 0));
784 pr_error_destroy(err);
785 err = NULL;
786
787 } else {
788 pr_log_pri(PR_LOG_WARNING, "lchown(%s) as root failed: %s", xfer_path,
789 strerror(xerrno));
790 }
791
792 } else {
793 if (session.fsgid != (gid_t) -1) {
794 pr_log_debug(DEBUG2, "root lchown(%s) to UID %s, GID %s successful",
795 xfer_path, pr_uid2str(p, session.fsuid),
796 pr_gid2str(p, session.fsgid));
797
798 } else {
799 pr_log_debug(DEBUG2, "root lchown(%s) to UID %s successful", xfer_path,
800 pr_uid2str(p, session.fsuid));
801 }
802
803 pr_fs_clear_cache2(xfer_path);
804 if (pr_fsio_stat(xfer_path, &st) < 0) {
805 pr_log_debug(DEBUG0,
806 "'%s' stat(2) error during root chmod: %s", xfer_path,
807 strerror(errno));
808 }
809
810 /* The chmod happens after the chown because chown will remove
811 * the S{U,G}ID bits on some files (namely, directories); the subsequent
812 * chmod is used to restore those dropped bits. This makes it
813 * necessary to use root privs when doing the chmod as well (at least
814 * in the case of chown'ing the file via root privs) in order to ensure
815 * that the mode can be set (a file might be being "given away", and if
816 * root privs aren't used, the chmod() will fail because the old owner/
817 * session user doesn't have the necessary privileges to do so).
818 */
819 xerrno = 0;
820 PRIVS_ROOT
821 res = pr_fsio_chmod_with_error(p, xfer_path, st.st_mode, &err);
822 xerrno = errno;
823 PRIVS_RELINQUISH
824
825 if (res < 0) {
826 pr_error_set_where(err, &xfer_module, __FILE__, __LINE__ - 5);
827 pr_error_set_why(err, pstrcat(p, "restore SUID/SGID on '", xfer_path,
828 "'", NULL));
829
830 if (err != NULL) {
831 pr_log_debug(DEBUG0, "%s", pr_error_strerror(err, 0));
832 pr_error_destroy(err);
833 err = NULL;
834
835 } else {
836 pr_log_debug(DEBUG0, "root chmod(%s) to %04o failed: %s", xfer_path,
837 (unsigned int) st.st_mode, strerror(xerrno));
838 }
839
840 } else {
841 pr_log_debug(DEBUG2, "root chmod(%s) to %04o successful", xfer_path,
842 (unsigned int) st.st_mode);
843 }
844 }
845
846 } else if (session.fsgid != (gid_t) -1 &&
847 xfer_path != NULL) {
848 register unsigned int i;
849 int res, use_root_privs = TRUE, xerrno = 0;
850 pr_error_t *err = NULL;
851
852 /* Check if session.fsgid is in session.gids. If not, use root privs. */
853 for (i = 0; i < session.gids->nelts; i++) {
854 gid_t *group_ids = session.gids->elts;
855
856 if (group_ids[i] == session.fsgid) {
857 use_root_privs = FALSE;
858 break;
859 }
860 }
861
862 if (use_root_privs) {
863 PRIVS_ROOT
864 }
865
866 res = pr_fsio_lchown_with_error(p, xfer_path, (uid_t) -1, session.fsgid,
867 &err);
868 xerrno = errno;
869
870 if (use_root_privs) {
871 PRIVS_RELINQUISH
872 }
873
874 if (res < 0) {
875 pr_error_set_where(err, &xfer_module, __FILE__, __LINE__ - 9);
876 pr_error_set_why(err, pstrcat(p, "set GroupOwner of '", xfer_path, "'",
877 NULL));
878
879 if (err != NULL) {
880 pr_log_pri(PR_LOG_WARNING, "%s", pr_error_strerror(err, 0));
881 pr_error_destroy(err);
882 err = NULL;
883
884 } else {
885 pr_log_pri(PR_LOG_WARNING, "%slchown(%s) failed: %s",
886 use_root_privs ? "root " : "", xfer_path, strerror(xerrno));
887 }
888
889 } else {
890 pr_log_debug(DEBUG2, "%slchown(%s) to GID %s successful",
891 use_root_privs ? "root " : "", xfer_path,
892 pr_gid2str(p, session.fsgid));
893
894 pr_fs_clear_cache2(xfer_path);
895 if (pr_fsio_stat(xfer_path, &st) < 0) {
896 pr_log_debug(DEBUG0,
897 "'%s' stat(2) error during %schmod: %s", xfer_path,
898 use_root_privs ? "root " : "", strerror(errno));
899 }
900
901 if (use_root_privs) {
902 PRIVS_ROOT
903 }
904
905 res = pr_fsio_chmod_with_error(p, xfer_path, st.st_mode, &err);
906 xerrno = errno;
907
908 if (use_root_privs) {
909 PRIVS_RELINQUISH
910 }
911
912 if (res < 0) {
913 pr_error_set_where(err, &xfer_module, __FILE__, __LINE__ - 8);
914 pr_error_set_why(err, pstrcat(p, "restore SUID/SGID of '", xfer_path,
915 "'", NULL));
916
917 if (err != NULL) {
918 pr_log_debug(DEBUG0, "%s", pr_error_strerror(err, 0));
919 pr_error_destroy(err);
920 err = NULL;
921
922 } else {
923 pr_log_debug(DEBUG0, "%schmod(%s) to %04o failed: %s",
924 use_root_privs ? "root " : "", xfer_path, (unsigned int) st.st_mode,
925 strerror(xerrno));
926 }
927 }
928 }
929 }
930 }
931
retr_abort(pool * p)932 static void retr_abort(pool *p) {
933 /* Isn't necessary to send anything here, just cleanup */
934
935 if (retr_fh) {
936 pr_fsio_close(retr_fh);
937 retr_fh = NULL;
938 }
939
940 _log_transfer('o', 'i');
941 }
942
retr_complete(pool * p)943 static void retr_complete(pool *p) {
944 pr_fsio_close(retr_fh);
945 retr_fh = NULL;
946 }
947
stor_abort(pool * p)948 static void stor_abort(pool *p) {
949 int res, xerrno = 0;
950 pool *tmp_pool;
951 pr_error_t *err = NULL;
952 unsigned char *delete_stores = NULL;
953
954 tmp_pool = make_sub_pool(p);
955
956 if (stor_fh != NULL) {
957 res = pr_fsio_close_with_error(tmp_pool, stor_fh, &err);
958 xerrno = errno;
959
960 if (res < 0) {
961 pr_error_set_where(err, &xfer_module, __FILE__, __LINE__ - 4);
962 pr_error_set_why(err, pstrcat(tmp_pool, "close file '", stor_fh->fh_path,
963 "'", NULL));
964
965 if (err != NULL) {
966 pr_log_pri(PR_LOG_NOTICE, "%s", pr_error_strerror(err, 0));
967 pr_error_destroy(err);
968 err = NULL;
969
970 } else {
971 pr_log_pri(PR_LOG_NOTICE, "notice: error closing '%s': %s",
972 stor_fh->fh_path, strerror(xerrno));
973 }
974
975 errno = xerrno;
976 }
977
978 stor_fh = NULL;
979 }
980
981 delete_stores = get_param_ptr(CURRENT_CONF, "DeleteAbortedStores", FALSE);
982
983 if (session.xfer.xfer_type == STOR_HIDDEN) {
984 if (delete_stores == NULL ||
985 *delete_stores == TRUE) {
986 /* If a hidden store was aborted, remove only hidden file, not real
987 * one.
988 */
989 if (session.xfer.path_hidden) {
990 pr_log_debug(DEBUG5, "removing aborted HiddenStores file '%s'",
991 session.xfer.path_hidden);
992
993 res = pr_fsio_unlink_with_error(tmp_pool, session.xfer.path_hidden,
994 &err);
995 xerrno = errno;
996
997 if (res < 0) {
998 pr_error_set_where(err, &xfer_module, __FILE__, __LINE__ - 5);
999 pr_error_set_why(err, pstrcat(tmp_pool, "delete HiddenStores file '",
1000 session.xfer.path_hidden, "'", NULL));
1001
1002 if (xerrno != ENOENT) {
1003 if (err != NULL) {
1004 pr_log_debug(DEBUG0, "%s", pr_error_strerror(err, 0));
1005
1006 } else {
1007 pr_log_debug(DEBUG0, "error deleting HiddenStores file '%s': %s",
1008 session.xfer.path_hidden, strerror(xerrno));
1009 }
1010 }
1011
1012 pr_error_destroy(err);
1013 err = NULL;
1014 }
1015 }
1016 }
1017 }
1018
1019 if (session.xfer.path != NULL) {
1020 if (delete_stores != NULL &&
1021 *delete_stores == TRUE) {
1022 pr_log_debug(DEBUG5, "removing aborted file '%s'", session.xfer.path);
1023
1024 res = pr_fsio_unlink_with_error(tmp_pool, session.xfer.path, &err);
1025 xerrno = errno;
1026
1027 if (res < 0) {
1028 pr_error_set_where(err, &xfer_module, __FILE__, __LINE__ - 4);
1029 pr_error_set_why(err, pstrcat(tmp_pool, "delete aborted file '",
1030 session.xfer.path, "'", NULL));
1031
1032 if (err != NULL) {
1033 pr_log_debug(DEBUG0, "%s", pr_error_strerror(err, 0));
1034 pr_error_destroy(err);
1035 err = NULL;
1036
1037 } else {
1038 pr_log_debug(DEBUG0, "error deleting aborted file '%s': %s",
1039 session.xfer.path, strerror(xerrno));
1040 }
1041 }
1042 }
1043 }
1044
1045 destroy_pool(tmp_pool);
1046 _log_transfer('i', 'i');
1047 }
1048
stor_complete(pool * p)1049 static int stor_complete(pool *p) {
1050 int res, xerrno = 0;
1051 pool *tmp_pool;
1052 pr_error_t *err = NULL;
1053
1054 tmp_pool = make_sub_pool(p);
1055 res = pr_fsio_close_with_error(tmp_pool, stor_fh, &err);
1056 xerrno = errno;
1057
1058 if (res < 0) {
1059 pr_error_set_where(err, &xfer_module, __FILE__, __LINE__ - 4);
1060 pr_error_set_why(err, pstrcat(tmp_pool, "close uploaded file '",
1061 stor_fh->fh_path, "'", NULL));
1062
1063 if (err != NULL) {
1064 pr_log_pri(PR_LOG_NOTICE, "%s", pr_error_strerror(err, 0));
1065 pr_error_destroy(err);
1066 err = NULL;
1067
1068 } else {
1069 pr_log_pri(PR_LOG_NOTICE, "notice: error closing '%s': %s",
1070 stor_fh->fh_path, strerror(xerrno));
1071 }
1072
1073 /* We will unlink failed writes, but only if it's a HiddenStores file.
1074 * Other files will need to be explicitly deleted/removed by the client.
1075 */
1076 if (session.xfer.xfer_type == STOR_HIDDEN) {
1077 if (session.xfer.path_hidden) {
1078 pr_log_debug(DEBUG5, "failed to close HiddenStores file '%s', removing",
1079 session.xfer.path_hidden);
1080
1081 res = pr_fsio_unlink_with_error(tmp_pool, session.xfer.path_hidden,
1082 &err);
1083 xerrno = errno;
1084
1085 if (res < 0) {
1086 pr_error_set_where(err, &xfer_module, __FILE__, __LINE__ - 4);
1087 pr_error_set_why(err, pstrcat(tmp_pool, "close HiddenStores file '",
1088 session.xfer.path_hidden, "'", NULL));
1089
1090 if (xerrno != ENOENT) {
1091 if (err != NULL) {
1092 pr_log_debug(DEBUG0, "%s", pr_error_strerror(err, 0));
1093
1094 } else {
1095 pr_log_debug(DEBUG0, "error deleting HiddenStores file '%s': %s",
1096 session.xfer.path_hidden, strerror(xerrno));
1097 }
1098 }
1099
1100 pr_error_destroy(err);
1101 err = NULL;
1102 }
1103 }
1104 }
1105
1106 errno = xerrno;
1107 res = -1;
1108 }
1109
1110 destroy_pool(tmp_pool);
1111 stor_fh = NULL;
1112 return res;
1113 }
1114
get_hidden_store_path(cmd_rec * cmd,const char * path,const char * prefix,const char * suffix)1115 static int get_hidden_store_path(cmd_rec *cmd, const char *path,
1116 const char *prefix, const char *suffix) {
1117 const char *c = NULL;
1118 char *hidden_path, *parent_dir = NULL;
1119 int dotcount = 0, found_slash = FALSE, basenamestart = 0, maxlen;
1120
1121 /* We have to also figure out the temporary hidden file name for receiving
1122 * this transfer. Length is +(N+M) due to prepended prefix and suffix.
1123 */
1124
1125 /* Figure out where the basename starts */
1126 for (c = path; *c; ++c) {
1127
1128 if (*c == '/') {
1129 found_slash = TRUE;
1130 basenamestart = dotcount = 0;
1131
1132 } else if (*c == '.') {
1133 ++dotcount;
1134
1135 /* Keep track of leading dots, ... is normal, . and .. are special.
1136 * So if we exceed ".." it becomes a normal file. Retroactively consider
1137 * this the possible start of the basename.
1138 */
1139 if ((dotcount > 2) &&
1140 !basenamestart) {
1141 basenamestart = ((unsigned long) c - (unsigned long) path) - dotcount;
1142 }
1143
1144 } else {
1145
1146 /* We found a nonslash, nondot character; if this is the first time
1147 * we found one since the last slash, remember this as the possible
1148 * start of the basename.
1149 */
1150 if (!basenamestart) {
1151 basenamestart = ((unsigned long) c - (unsigned long) path) - dotcount;
1152 }
1153 }
1154 }
1155
1156 if (!basenamestart) {
1157 session.xfer.xfer_type = STOR_DEFAULT;
1158
1159 pr_log_debug(DEBUG6, "could not determine HiddenStores path for '%s'",
1160 path);
1161
1162 /* This probably shouldn't happen */
1163 pr_response_add_err(R_451, _("%s: Bad file name"), path);
1164 errno = EINVAL;
1165 return -1;
1166 }
1167
1168 /* Add N+M for the prefix and suffix characters, plus one for a terminating
1169 * NUL.
1170 */
1171 maxlen = strlen(prefix) + strlen(path) + strlen(suffix) + 1;
1172
1173 if (maxlen > PR_TUNABLE_PATH_MAX) {
1174 session.xfer.xfer_type = STOR_DEFAULT;
1175
1176 pr_log_pri(PR_LOG_NOTICE, "making path '%s' a hidden path exceeds max "
1177 "path length (%u)", path, PR_TUNABLE_PATH_MAX);
1178
1179 /* This probably shouldn't happen */
1180 pr_response_add_err(R_451, _("%s: File name too long"), path);
1181 errno = EPERM;
1182 return -1;
1183 }
1184
1185 if (pr_table_add(cmd->notes, "mod_xfer.store-hidden-path", NULL, 0) < 0) {
1186 if (errno != EEXIST) {
1187 pr_log_pri(PR_LOG_NOTICE,
1188 "notice: error adding 'mod_xfer.store-hidden-path': %s",
1189 strerror(errno));
1190 }
1191 }
1192
1193 if (found_slash == FALSE) {
1194
1195 /* Simple local file name */
1196 hidden_path = pstrcat(cmd->tmp_pool, prefix, path, suffix, NULL);
1197
1198 pr_log_debug(DEBUG2, "HiddenStore: local path, will rename %s to %s",
1199 hidden_path, path);
1200
1201 } else {
1202
1203 /* Complex relative path or absolute path */
1204 hidden_path = pstrndup(cmd->pool, path, maxlen);
1205 hidden_path[basenamestart] = '\0';
1206
1207 hidden_path = pstrcat(cmd->pool, hidden_path, prefix,
1208 path + basenamestart, suffix, NULL);
1209
1210 pr_log_debug(DEBUG2, "HiddenStore: complex path, will rename %s to %s",
1211 hidden_path, path);
1212 }
1213
1214 pr_fs_clear_cache2(hidden_path);
1215 if (file_mode2(cmd->tmp_pool, hidden_path)) {
1216 session.xfer.xfer_type = STOR_DEFAULT;
1217
1218 pr_log_debug(DEBUG3, "HiddenStore path '%s' already exists",
1219 hidden_path);
1220
1221 pr_response_add_err(R_550, _("%s: Temporary hidden file %s already exists"),
1222 cmd->arg, hidden_path);
1223 errno = EEXIST;
1224 return -1;
1225 }
1226
1227 if (pr_table_set(cmd->notes, "mod_xfer.store-hidden-path",
1228 hidden_path, 0) < 0) {
1229 pr_log_pri(PR_LOG_NOTICE,
1230 "notice: error setting 'mod_xfer.store-hidden-path': %s",
1231 strerror(errno));
1232 }
1233
1234 /* Only use the O_EXCL open(2) flag if the path is NOT on an NFS-mounted
1235 * filesystem (see Bug#3874).
1236 */
1237 if (found_slash == FALSE) {
1238 parent_dir = "./";
1239
1240 } else {
1241 parent_dir = pstrndup(cmd->tmp_pool, path, basenamestart);
1242 }
1243
1244 if (pr_fs_is_nfs(parent_dir) == TRUE) {
1245 if (pr_table_add(cmd->notes, "mod_xfer.store-hidden-nfs",
1246 pstrdup(cmd->pool, "1"), 0) < 0) {
1247 pr_log_pri(PR_LOG_NOTICE,
1248 "notice: error adding 'mod_xfer.store-hidden-nfs' note: %s",
1249 strerror(errno));
1250 }
1251 }
1252
1253 session.xfer.xfer_type = STOR_HIDDEN;
1254 return 0;
1255 }
1256
xfer_post_prot(cmd_rec * cmd)1257 MODRET xfer_post_prot(cmd_rec *cmd) {
1258 CHECK_CMD_ARGS(cmd, 2);
1259
1260 if (strncmp(cmd->argv[1], "C", 2) != 0) {
1261 have_rfc2228_data = TRUE;
1262
1263 } else {
1264 have_rfc2228_data = FALSE;
1265 }
1266
1267 return PR_DECLINED(cmd);
1268 }
1269
xfer_post_mode(cmd_rec * cmd)1270 MODRET xfer_post_mode(cmd_rec *cmd) {
1271 CHECK_CMD_ARGS(cmd, 2);
1272
1273 if (strncmp(cmd->argv[1], "Z", 2) == 0) {
1274 have_zmode = TRUE;
1275
1276 } else {
1277 have_zmode = FALSE;
1278 }
1279
1280 return PR_DECLINED(cmd);
1281 }
1282
1283 /* This is a PRE_CMD handler that checks security, etc, and places the full
1284 * filename to receive in cmd->notes, under the key 'mod_xfer.store-path'.
1285 * Note that we CANNOT use cmd->tmp_pool for this, as tmp_pool only lasts for
1286 * the duration of this function.
1287 */
xfer_pre_stor(cmd_rec * cmd)1288 MODRET xfer_pre_stor(cmd_rec *cmd) {
1289 char *decoded_path, *path;
1290 mode_t fmode;
1291 unsigned char *allow_overwrite = NULL, *allow_restart = NULL;
1292 config_rec *c;
1293 int res;
1294
1295 if (cmd->argc < 2) {
1296 pr_response_add_err(R_500, _("'%s' not understood"),
1297 pr_cmd_get_displayable_str(cmd, NULL));
1298
1299 pr_cmd_set_errno(cmd, EINVAL);
1300 errno = EINVAL;
1301 return PR_ERROR(cmd);
1302 }
1303
1304 decoded_path = pr_fs_decode_path2(cmd->tmp_pool, cmd->arg,
1305 FSIO_DECODE_FL_TELL_ERRORS);
1306 if (decoded_path == NULL) {
1307 int xerrno = errno;
1308
1309 pr_log_debug(DEBUG8, "'%s' failed to decode properly: %s", cmd->arg,
1310 strerror(xerrno));
1311 pr_response_add_err(R_550, _("%s: Illegal character sequence in filename"),
1312 cmd->arg);
1313
1314 pr_cmd_set_errno(cmd, xerrno);
1315 errno = xerrno;
1316 return PR_ERROR(cmd);
1317 }
1318
1319 pr_fs_clear_cache2(decoded_path);
1320 path = dir_best_path(cmd->tmp_pool, decoded_path);
1321
1322 if (path == NULL ||
1323 !dir_check(cmd->tmp_pool, cmd, cmd->group, path, NULL)) {
1324 int xerrno = errno;
1325
1326 pr_log_debug(DEBUG8, "%s %s denied by <Limit> configuration",
1327 (char *) cmd->argv[0], cmd->arg);
1328 pr_response_add_err(R_550, "%s: %s", cmd->arg, strerror(xerrno));
1329
1330 pr_cmd_set_errno(cmd, xerrno);
1331 errno = xerrno;
1332 return PR_ERROR(cmd);
1333 }
1334
1335 res = pr_filter_allow_path(CURRENT_CONF, path);
1336 switch (res) {
1337 case 0:
1338 break;
1339
1340 case PR_FILTER_ERR_FAILS_ALLOW_FILTER:
1341 pr_log_pri(PR_LOG_NOTICE, "'%s %s' denied by PathAllowFilter",
1342 (char *) cmd->argv[0], path);
1343 pr_response_add_err(R_550, _("%s: Forbidden filename"), cmd->arg);
1344
1345 pr_cmd_set_errno(cmd, EPERM);
1346 errno = EPERM;
1347 return PR_ERROR(cmd);
1348
1349 case PR_FILTER_ERR_FAILS_DENY_FILTER:
1350 pr_log_pri(PR_LOG_NOTICE, "'%s %s' denied by PathDenyFilter",
1351 (char *) cmd->argv[0], path);
1352 pr_response_add_err(R_550, _("%s: Forbidden filename"), cmd->arg);
1353
1354 pr_cmd_set_errno(cmd, EPERM);
1355 errno = EPERM;
1356 return PR_ERROR(cmd);
1357 }
1358
1359 if (xfer_check_limit(cmd) < 0) {
1360 pr_response_add_err(R_451, _("%s: Too many transfers"), cmd->arg);
1361
1362 pr_cmd_set_errno(cmd, EPERM);
1363 errno = EPERM;
1364 return PR_ERROR(cmd);
1365 }
1366
1367 fmode = file_mode2(cmd->tmp_pool, path);
1368
1369 allow_overwrite = get_param_ptr(CURRENT_CONF, "AllowOverwrite", FALSE);
1370
1371 if (fmode && (session.xfer.xfer_type != STOR_APPEND) &&
1372 (!allow_overwrite || *allow_overwrite == FALSE)) {
1373 pr_log_debug(DEBUG6, "AllowOverwrite denied permission for %s", cmd->arg);
1374 pr_response_add_err(R_550, _("%s: Overwrite permission denied"), cmd->arg);
1375
1376 pr_cmd_set_errno(cmd, EACCES);
1377 errno = EACCES;
1378 return PR_ERROR(cmd);
1379 }
1380
1381 if (fmode &&
1382 !S_ISREG(fmode) &&
1383 !S_ISFIFO(fmode)) {
1384
1385 /* Make an exception for the non-regular /dev/null file. This will allow
1386 * network link testing by uploading as much data as necessary directly
1387 * to /dev/null.
1388 *
1389 * On Linux, allow another exception for /dev/full; this is useful for
1390 * tests which want to simulate running out-of-space scenarios.
1391 */
1392 if (strcasecmp(path, "/dev/null") != 0
1393 #ifdef LINUX
1394 && strcasecmp(path, "/dev/full") != 0
1395 #endif
1396 ) {
1397 pr_response_add_err(R_550, _("%s: Not a regular file"), cmd->arg);
1398
1399 /* Deliberately use EISDIR for anything non-file (e.g. directories). */
1400 pr_cmd_set_errno(cmd, EISDIR);
1401 errno = EISDIR;
1402 return PR_ERROR(cmd);
1403 }
1404 }
1405
1406 /* If restarting, check permissions on this directory, if
1407 * AllowStoreRestart is set, permit it
1408 */
1409 allow_restart = get_param_ptr(CURRENT_CONF, "AllowStoreRestart", FALSE);
1410
1411 if (fmode &&
1412 ((session.restart_pos > 0 || session.range_len > 0) ||
1413 (session.xfer.xfer_type == STOR_APPEND)) &&
1414 (!allow_restart || *allow_restart == FALSE)) {
1415
1416 pr_response_add_err(R_451, _("%s: Append/Restart not permitted, try again"),
1417 cmd->arg);
1418 session.restart_pos = 0L;
1419 session.xfer.xfer_type = STOR_DEFAULT;
1420
1421 pr_cmd_set_errno(cmd, EPERM);
1422 errno = EPERM;
1423 return PR_ERROR(cmd);
1424 }
1425
1426 /* Reject APPE preceded by RANG. */
1427 if (session.xfer.xfer_type == STOR_APPEND &&
1428 session.range_len > 0) {
1429 pr_response_add_err(R_550, _("APPE incompatible with RANG"));
1430 pr_cmd_set_errno(cmd, EPERM);
1431 errno = EPERM;
1432 return PR_ERROR(cmd);
1433 }
1434
1435 /* If the file exists, add a note indicating that it is being modified. */
1436 if (fmode) {
1437 /* Clear any existing key in the notes. */
1438 (void) pr_table_remove(cmd->notes, "mod_xfer.file-modified", NULL);
1439
1440 if (pr_table_add(cmd->notes, "mod_xfer.file-modified",
1441 pstrdup(cmd->pool, "true"), 0) < 0) {
1442 if (errno != EEXIST) {
1443 pr_log_pri(PR_LOG_NOTICE,
1444 "notice: error adding 'mod_xfer.file-modified' note: %s",
1445 strerror(errno));
1446 }
1447 }
1448 }
1449
1450 /* Otherwise everything is good */
1451 if (pr_table_add(cmd->notes, "mod_xfer.store-path",
1452 pstrdup(cmd->pool, path), 0) < 0) {
1453 if (errno != EEXIST) {
1454 pr_log_pri(PR_LOG_NOTICE,
1455 "notice: error adding 'mod_xfer.store-path': %s", strerror(errno));
1456 }
1457 }
1458
1459 c = find_config(CURRENT_CONF, CONF_PARAM, "HiddenStores", FALSE);
1460 if (c != NULL &&
1461 *((int *) c->argv[0]) == TRUE) {
1462
1463 /* If we're using HiddenStores, then RANG/REST won't work. */
1464 if (session.restart_pos > 0 ||
1465 session.range_len > 0) {
1466 int used_rest = TRUE;
1467
1468 if (session.range_len > 0) {
1469 used_rest = FALSE;
1470 }
1471
1472 pr_log_debug(DEBUG9, "HiddenStore in effect, refusing %s upload",
1473 used_rest ? "restarted" : "range");
1474 pr_response_add_err(R_501,
1475 _("%s not compatible with server configuration"),
1476 used_rest ? C_REST : C_RANG);
1477
1478 pr_cmd_set_errno(cmd, EPERM);
1479 errno = EPERM;
1480 return PR_ERROR(cmd);
1481 }
1482
1483 /* For Bug#3598, we rejected any APPE command when HiddenStores are in
1484 * effect (for good reasons).
1485 *
1486 * However, for Bug#4144, we're relaxing that policy. Instead of rejecting
1487 * the APPE command, we accept that command, but we disable the HiddenStores
1488 * functionality.
1489 */
1490 if (session.xfer.xfer_type != STOR_APPEND) {
1491 const char *prefix, *suffix;
1492
1493 prefix = c->argv[1];
1494 suffix = c->argv[2];
1495
1496 /* Substitute the %P variable for the PID, if present. */
1497 if (strstr(prefix, "%P") != NULL) {
1498 char pid_buf[32];
1499
1500 memset(pid_buf, '\0', sizeof(pid_buf));
1501 pr_snprintf(pid_buf, sizeof(pid_buf)-1, "%lu",
1502 (unsigned long) session.pid);
1503 prefix = sreplace(cmd->pool, prefix, "%P", pid_buf, NULL);
1504 }
1505
1506 if (strstr(suffix, "%P") != NULL) {
1507 char pid_buf[32];
1508
1509 memset(pid_buf, '\0', sizeof(pid_buf));
1510 pr_snprintf(pid_buf, sizeof(pid_buf)-1, "%lu",
1511 (unsigned long) session.pid);
1512 suffix = sreplace(cmd->pool, suffix, "%P", pid_buf, NULL);
1513 }
1514
1515 if (get_hidden_store_path(cmd, path, prefix, suffix) < 0) {
1516 int xerrno = errno;
1517
1518 pr_cmd_set_errno(cmd, xerrno);
1519 errno = xerrno;
1520 return PR_ERROR(cmd);
1521 }
1522
1523 } else {
1524 pr_log_debug(DEBUG9,
1525 "HiddenStores in effect for APPE, ignoring HiddenStores");
1526 }
1527 }
1528
1529 return PR_HANDLED(cmd);
1530 }
1531
1532 /* xfer_pre_stou() is a PRE_CMD handler that changes the uploaded filename
1533 * to a unique one, after making the requisite security and authorization
1534 * checks.
1535 */
xfer_pre_stou(cmd_rec * cmd)1536 MODRET xfer_pre_stou(cmd_rec *cmd) {
1537 config_rec *c = NULL;
1538 char *prefix = "ftp", *filename = NULL;
1539 int stou_fd;
1540 mode_t mode;
1541 unsigned char *allow_overwrite = NULL;
1542
1543 session.xfer.xfer_type = STOR_DEFAULT;
1544
1545 /* Some FTP clients are "broken" in that they will send a filename
1546 * along with STOU. Technically this violates RFC959, but for now, just
1547 * ignore that filename. Stupid client implementors.
1548 */
1549
1550 if (cmd->argc > 2) {
1551 pr_response_add_err(R_500, _("'%s' not understood"),
1552 pr_cmd_get_displayable_str(cmd, NULL));
1553
1554 pr_cmd_set_errno(cmd, EINVAL);
1555 errno = EINVAL;
1556 return PR_ERROR(cmd);
1557 }
1558
1559 if (xfer_check_limit(cmd) < 0) {
1560 pr_response_add_err(R_451, _("%s: Too many transfers"), cmd->arg);
1561
1562 pr_cmd_set_errno(cmd, EPERM);
1563 errno = EPERM;
1564 return PR_ERROR(cmd);
1565 }
1566
1567 /* Watch for STOU preceded by REST, which makes no sense. Similarly
1568 * for STOU preceded by RANG.
1569 */
1570 if (session.restart_pos > 0 ||
1571 session.range_len > 0) {
1572
1573 if (session.restart_pos > 0) {
1574 pr_response_add_err(R_550, _("STOU incompatible with REST"));
1575
1576 } else {
1577 pr_response_add_err(R_550, _("STOU incompatible with RANG"));
1578 }
1579
1580 pr_cmd_set_errno(cmd, EPERM);
1581 errno = EPERM;
1582 return PR_ERROR(cmd);
1583 }
1584
1585 /* Generate the filename to be stored, depending on the configured
1586 * unique filename prefix.
1587 */
1588 c = find_config(CURRENT_CONF, CONF_PARAM, "StoreUniquePrefix", FALSE);
1589 if (c != NULL) {
1590 prefix = c->argv[0];
1591 }
1592
1593 /* Now, construct the unique filename using the cmd_rec's pool, the
1594 * prefix, and mkstemp().
1595 */
1596 filename = pstrcat(cmd->pool, prefix, "XXXXXX", NULL);
1597
1598 stou_fd = mkstemp(filename);
1599 if (stou_fd < 0) {
1600 int xerrno = errno;
1601
1602 pr_log_pri(PR_LOG_WARNING, "error: unable to use mkstemp(): %s",
1603 strerror(xerrno));
1604
1605 /* If we can't guarantee a unique filename, refuse the command. */
1606 pr_response_add_err(R_450, _("%s: unable to generate unique filename"),
1607 (char *) cmd->argv[0]);
1608
1609 pr_cmd_set_errno(cmd, xerrno);
1610 errno = xerrno;
1611 return PR_ERROR(cmd);
1612 }
1613
1614 cmd->arg = filename;
1615
1616 /* Close the unique file. This introduces a small race condition
1617 * between the time this function returns, and the STOU CMD handler
1618 * opens the unique file, but this may have to do, as closing that
1619 * race would involve some major restructuring.
1620 */
1621 (void) close(stou_fd);
1622
1623 filename = dir_best_path(cmd->tmp_pool, cmd->arg);
1624
1625 if (filename == NULL ||
1626 !dir_check(cmd->tmp_pool, cmd, cmd->group, filename, NULL)) {
1627 int xerrno = errno;
1628
1629 /* Do not forget to delete the file created by mkstemp(3) if there is
1630 * an error.
1631 */
1632 (void) pr_fsio_unlink(cmd->arg);
1633
1634 pr_response_add_err(R_550, "%s: %s", cmd->arg, strerror(xerrno));
1635
1636 pr_cmd_set_errno(cmd, xerrno);
1637 errno = xerrno;
1638 return PR_ERROR(cmd);
1639 }
1640
1641 mode = file_mode2(cmd->tmp_pool, filename);
1642
1643 /* Note: this case should never happen: how one can be appending to
1644 * a supposedly unique filename? Should probably be removed...
1645 */
1646 allow_overwrite = get_param_ptr(CURRENT_CONF, "AllowOverwrite", FALSE);
1647
1648 if (mode && session.xfer.xfer_type != STOR_APPEND &&
1649 (!allow_overwrite || *allow_overwrite == FALSE)) {
1650 pr_log_debug(DEBUG6, "AllowOverwrite denied permission for %s", cmd->arg);
1651 pr_response_add_err(R_550, _("%s: Overwrite permission denied"), cmd->arg);
1652
1653 pr_cmd_set_errno(cmd, EACCES);
1654 errno = EACCES;
1655 return PR_ERROR(cmd);
1656 }
1657
1658 /* Not likely to _not_ be a regular file, but just to be certain... */
1659 if (mode &&
1660 !S_ISREG(mode)) {
1661 (void) pr_fsio_unlink(cmd->arg);
1662 pr_response_add_err(R_550, _("%s: Not a regular file"), cmd->arg);
1663
1664 /* Deliberately use EISDIR for anything non-file (e.g. directories). */
1665 pr_cmd_set_errno(cmd, EISDIR);
1666 errno = EISDIR;
1667 return PR_ERROR(cmd);
1668 }
1669
1670 /* Otherwise everything is good */
1671 if (pr_table_add(cmd->notes, "mod_xfer.store-path",
1672 pstrdup(cmd->pool, filename), 0) < 0) {
1673 if (errno != EEXIST) {
1674 pr_log_pri(PR_LOG_NOTICE,
1675 "notice: error adding 'mod_xfer.store-path': %s", strerror(errno));
1676 }
1677 }
1678
1679 session.xfer.xfer_type = STOR_UNIQUE;
1680 return PR_HANDLED(cmd);
1681 }
1682
xfer_post_stor(cmd_rec * cmd)1683 MODRET xfer_post_stor(cmd_rec *cmd) {
1684 const char *path;
1685
1686 path = pr_table_get(cmd->notes, "mod_xfer.store-path", NULL);
1687 if (path != NULL) {
1688 struct stat st;
1689
1690 if (pr_fsio_stat(path, &st) == 0) {
1691 off_t *file_size;
1692
1693 file_size = palloc(cmd->pool, sizeof(off_t));
1694 *file_size = st.st_size;
1695 (void) pr_table_add(cmd->notes, "mod_xfer.file-size", file_size,
1696 sizeof(off_t));
1697 }
1698 }
1699
1700 return PR_DECLINED(cmd);
1701 }
1702
1703 /* xfer_post_stou() is a POST_CMD handler that changes the mode of the
1704 * STOU file from 0600, which is what mkstemp() makes it, to 0666 (modulo
1705 * Umask), the default for files uploaded via STOR. This is to prevent users
1706 * from being surprised.
1707 */
xfer_post_stou(cmd_rec * cmd)1708 MODRET xfer_post_stou(cmd_rec *cmd) {
1709 mode_t mask, perms, *umask_setting;
1710 struct stat st;
1711
1712 /* mkstemp(3) creates a file with 0600 perms; we need to adjust this
1713 * for the Umask (Bug#4223).
1714 */
1715 umask_setting = get_param_ptr(CURRENT_CONF, "Umask", FALSE);
1716 if (umask_setting != NULL) {
1717 mask = *umask_setting;
1718
1719 } else {
1720 mask = (mode_t) 0022;
1721 }
1722
1723 perms = (0666 & ~mask);
1724
1725 if (pr_fsio_chmod(cmd->arg, perms) < 0) {
1726 /* Not much to do but log the error. */
1727 pr_log_pri(PR_LOG_NOTICE, "error: unable to chmod '%s' to %04o: %s",
1728 cmd->arg, perms, strerror(errno));
1729 }
1730
1731 if (pr_fsio_stat(cmd->arg, &st) == 0) {
1732 off_t *file_size;
1733
1734 file_size = palloc(cmd->pool, sizeof(off_t));
1735 *file_size = st.st_size;
1736 (void) pr_table_add(cmd->notes, "mod_xfer.file-size", file_size,
1737 sizeof(off_t));
1738 }
1739
1740 return PR_DECLINED(cmd);
1741 }
1742
1743 /* xfer_pre_appe() is the PRE_CMD handler for the APPE command, which
1744 * simply sets xfer_type to STOR_APPEND and calls xfer_pre_stor().
1745 */
xfer_pre_appe(cmd_rec * cmd)1746 MODRET xfer_pre_appe(cmd_rec *cmd) {
1747 session.xfer.xfer_type = STOR_DEFAULT;
1748
1749 if (xfer_check_limit(cmd) < 0) {
1750 pr_response_add_err(R_451, _("%s: Too many transfers"), cmd->arg);
1751
1752 pr_cmd_set_errno(cmd, EPERM);
1753 errno = EPERM;
1754 return PR_ERROR(cmd);
1755 }
1756
1757 session.xfer.xfer_type = STOR_APPEND;
1758 return xfer_pre_stor(cmd);
1759 }
1760
xfer_stor(cmd_rec * cmd)1761 MODRET xfer_stor(cmd_rec *cmd) {
1762 const char *path;
1763 char *lbuf;
1764 int bufsz, len, xerrno = 0, res;
1765 off_t nbytes_stored, nbytes_max_store = 0;
1766 unsigned char have_limit = FALSE;
1767 struct stat st;
1768 off_t start_offset = 0, upload_len = 0;
1769 off_t curr_offset, curr_pos = 0;
1770 pr_error_t *err = NULL;
1771
1772 memset(&st, 0, sizeof(st));
1773
1774 /* Prepare for any potential throttling. */
1775 pr_throttle_init(cmd);
1776
1777 session.xfer.path = pr_table_get(cmd->notes, "mod_xfer.store-path", NULL);
1778 session.xfer.path_hidden = pr_table_get(cmd->notes,
1779 "mod_xfer.store-hidden-path", NULL);
1780
1781 path = session.xfer.path;
1782
1783 /* Make sure the proper current working directory is set in the FSIO
1784 * layer, so that the proper FS can be used for the open().
1785 */
1786 pr_fs_setcwd(pr_fs_getcwd());
1787
1788 if (session.xfer.xfer_type == STOR_HIDDEN) {
1789 const void *nfs;
1790 int oflags;
1791
1792 oflags = O_WRONLY;
1793
1794 if (session.restart_pos == 0) {
1795 oflags |= O_CREAT;
1796 }
1797
1798 nfs = pr_table_get(cmd->notes, "mod_xfer.store-hidden-nfs", NULL);
1799 if (nfs == NULL) {
1800 pr_trace_msg("fsio", 9,
1801 "HiddenStores path '%s' is NOT on NFS, using O_EXCL open(2) flags",
1802 session.xfer.path_hidden);
1803 oflags |= O_EXCL;
1804
1805 } else {
1806 pr_trace_msg("fsio", 9,
1807 "HiddenStores path '%s' is on NFS, NOT using O_EXCL open(2) flags",
1808 session.xfer.path_hidden);
1809 }
1810
1811 stor_fh = pr_fsio_open_with_error(cmd->pool, session.xfer.path_hidden,
1812 oflags, &err);
1813 xerrno = errno;
1814
1815 if (stor_fh == NULL) {
1816 pr_error_set_where(err, &xfer_module, __FILE__, __LINE__ - 5);
1817 pr_error_set_why(err, pstrcat(cmd->pool, "open HiddenStores file '",
1818 session.xfer.path_hidden, "'", NULL));
1819
1820 (void) pr_trace_msg("fileperms", 1, "%s, user '%s' (UID %s, GID %s): "
1821 "error opening '%s': %s", (char *) cmd->argv[0], session.user,
1822 pr_uid2str(cmd->tmp_pool, session.uid),
1823 pr_gid2str(cmd->tmp_pool, session.gid), session.xfer.path_hidden,
1824 strerror(xerrno));
1825 }
1826
1827 } else if (session.xfer.xfer_type == STOR_APPEND) {
1828 stor_fh = pr_fsio_open_with_error(cmd->pool, session.xfer.path,
1829 O_CREAT|O_WRONLY, &err);
1830 xerrno = errno;
1831
1832 if (stor_fh != NULL) {
1833 if (pr_fsio_lseek(stor_fh, 0, SEEK_END) == (off_t) -1) {
1834 pr_log_debug(DEBUG4, "unable to seek to end of '%s' for appending: %s",
1835 cmd->arg, strerror(errno));
1836 (void) pr_fsio_close(stor_fh);
1837 stor_fh = NULL;
1838 }
1839
1840 } else {
1841 xerrno = errno;
1842
1843 pr_error_set_where(err, &xfer_module, __FILE__, __LINE__ - 15);
1844 pr_error_set_why(err, pstrcat(cmd->pool, "append to file '",
1845 session.xfer.path, "'", NULL));
1846
1847 (void) pr_trace_msg("fileperms", 1, "%s, user '%s' (UID %s, GID %s): "
1848 "error opening '%s': %s", (char *) cmd->argv[0], session.user,
1849 pr_uid2str(cmd->tmp_pool, session.uid),
1850 pr_gid2str(cmd->tmp_pool, session.gid), session.xfer.path,
1851 strerror(xerrno));
1852 }
1853
1854 } else {
1855 int open_flags = O_WRONLY|O_CREAT;
1856
1857 if (session.range_len == 0 &&
1858 session.restart_pos == 0) {
1859 /* If we are not resuming an upload or handling a byte range transfer,
1860 * then we should truncate the file to receive the new data.
1861 */
1862 open_flags |= O_TRUNC;
1863 }
1864
1865 /* Normal session */
1866 stor_fh = pr_fsio_open_with_error(cmd->pool, path, open_flags, &err);
1867 xerrno = errno;
1868
1869 if (stor_fh == NULL) {
1870 pr_error_set_where(err, &xfer_module, __FILE__, __LINE__ - 4);
1871 pr_error_set_why(err, pstrcat(cmd->pool, "upload file '", path, "'",
1872 NULL));
1873
1874 (void) pr_trace_msg("fileperms", 1, "%s, user '%s' (UID %s, GID %s): "
1875 "error opening '%s': %s", (char *) cmd->argv[0], session.user,
1876 pr_uid2str(cmd->tmp_pool, session.uid),
1877 pr_gid2str(cmd->tmp_pool, session.gid), path, strerror(xerrno));
1878 }
1879 }
1880
1881 if (session.restart_pos > 0) {
1882 start_offset = session.restart_pos;
1883
1884 } else if (session.range_start > 0) {
1885 start_offset = session.range_start;
1886 }
1887
1888 if (stor_fh != NULL &&
1889 start_offset > 0) {
1890 xerrno = 0;
1891
1892 pr_fs_clear_cache2(path);
1893 if (pr_fsio_lseek(stor_fh, start_offset, SEEK_SET) == -1) {
1894 pr_log_debug(DEBUG4, "unable to seek to position %" PR_LU " of '%s': %s",
1895 (pr_off_t) start_offset, cmd->arg, strerror(errno));
1896 xerrno = errno;
1897
1898 } else if (pr_fsio_stat(path, &st) < 0) {
1899 pr_log_debug(DEBUG4, "unable to stat '%s': %s", cmd->arg,
1900 strerror(errno));
1901 xerrno = errno;
1902 }
1903
1904 if (xerrno) {
1905 (void) pr_fsio_close(stor_fh);
1906 errno = xerrno;
1907 stor_fh = NULL;
1908 }
1909
1910 /* Make sure that the requested offset is valid (within the size of the
1911 * file being resumed).
1912 */
1913 if (stor_fh != NULL &&
1914 start_offset > st.st_size) {
1915 int used_rest = TRUE;
1916
1917 if (session.range_start > 0) {
1918 used_rest = FALSE;
1919 }
1920
1921 pr_response_add_err(R_554, _("%s: invalid %s argument"),
1922 used_rest ? C_REST : C_RANG, cmd->arg);
1923 (void) pr_fsio_close(stor_fh);
1924 stor_fh = NULL;
1925
1926 pr_cmd_set_errno(cmd, EINVAL);
1927 errno = EINVAL;
1928 return PR_ERROR(cmd);
1929 }
1930
1931 curr_pos = start_offset;
1932
1933 if (session.restart_pos > 0) {
1934 session.restart_pos = 0L;
1935
1936 } else if (session.range_start > 0) {
1937 session.range_start = 0;
1938 }
1939 }
1940
1941 if (stor_fh == NULL) {
1942 if (err != NULL) {
1943 pr_log_debug(DEBUG4, "%s", pr_error_strerror(err, 0));
1944 pr_error_destroy(err);
1945 err = NULL;
1946
1947 } else {
1948 pr_log_debug(DEBUG4, "unable to open '%s' for writing: %s", cmd->arg,
1949 strerror(xerrno));
1950 }
1951
1952 pr_response_add_err(R_550, "%s: %s", cmd->arg, strerror(xerrno));
1953
1954 pr_cmd_set_errno(cmd, xerrno);
1955 errno = xerrno;
1956 return PR_ERROR(cmd);
1957 }
1958
1959 /* Advise the platform that we will be only writing this file. Note that a
1960 * preceding REST command does not mean we need to use a different offset
1961 * value here; we can/should still tell the platform that the entire file
1962 * should be treated this way.
1963 */
1964 pr_fs_fadvise(PR_FH_FD(stor_fh), 0, 0, PR_FS_FADVISE_DONTNEED);
1965
1966 /* Stash the offset at which we're writing to this file. */
1967 curr_offset = pr_fsio_lseek(stor_fh, (off_t) 0, SEEK_CUR);
1968 if (curr_offset != (off_t) -1) {
1969 off_t *file_offset;
1970
1971 file_offset = palloc(cmd->pool, sizeof(off_t));
1972 *file_offset = (off_t) curr_offset;
1973 (void) pr_table_add(cmd->notes, "mod_xfer.file-offset", file_offset,
1974 sizeof(off_t));
1975 }
1976
1977 /* Get the latest stats on the file. If the file already existed, we
1978 * want to know its current size.
1979 */
1980 (void) pr_fsio_fstat(stor_fh, &st);
1981
1982 /* Block any timers for this section, where we want to prepare the
1983 * data connection, then need to reprovision the session.xfer struct,
1984 * and do NOT want timers (which may want/need that session.xfer data)
1985 * to fire until after the reprovisioning (Bug#4168).
1986 */
1987 pr_alarms_block();
1988
1989 /* Perform the actual transfer now */
1990 pr_data_init(cmd->arg, PR_NETIO_IO_RD);
1991
1992 /* Note that we have to re-populate the session.xfer variables here,
1993 * AFTER the pr_data_init() call. pr_data_init() ensures that there is
1994 * no leftover information in session.xfer, as from aborted tranfers.
1995 */
1996 session.xfer.path = pr_table_get(cmd->notes, "mod_xfer.store-path", NULL);
1997 session.xfer.path_hidden = pr_table_get(cmd->notes,
1998 "mod_xfer.store-hidden-path", NULL);
1999 session.xfer.file_size = curr_pos;
2000
2001 pr_alarms_unblock();
2002
2003 /* First, make sure the uploaded file has the requested ownership. */
2004 stor_chown(cmd->tmp_pool);
2005
2006 if (session.range_len > 0) {
2007 upload_len = session.range_len;
2008 }
2009
2010 if (pr_data_open(cmd->arg, NULL, PR_NETIO_IO_RD, upload_len) < 0) {
2011 xerrno = errno;
2012
2013 stor_abort(cmd->pool);
2014 pr_data_abort(0, TRUE);
2015
2016 pr_cmd_set_errno(cmd, xerrno);
2017 errno = xerrno;
2018 return PR_ERROR(cmd);
2019 }
2020
2021 /* Initialize the number of bytes stored */
2022 nbytes_stored = 0;
2023
2024 /* Retrieve the number of bytes to store, maximum, if present.
2025 * This check is needed during the pr_data_xfer() loop, below, because
2026 * the size of the file being uploaded isn't known in advance
2027 */
2028 nbytes_max_store = find_max_nbytes("MaxStoreFileSize");
2029 if (nbytes_max_store == 0UL) {
2030 have_limit = FALSE;
2031
2032 } else {
2033 have_limit = TRUE;
2034 }
2035
2036 bufsz = pr_config_get_server_xfer_bufsz(PR_NETIO_IO_RD);
2037 lbuf = (char *) palloc(cmd->tmp_pool, bufsz);
2038 pr_trace_msg("data", 8, "allocated upload buffer of %lu bytes",
2039 (unsigned long) bufsz);
2040
2041 while ((len = pr_data_xfer(lbuf, bufsz)) > 0) {
2042 pr_signals_handle();
2043
2044 if (XFER_ABORTED) {
2045 break;
2046 }
2047
2048 nbytes_stored += len;
2049
2050 /* If MaxStoreFileSize is configured, double-check the number of bytes
2051 * uploaded so far against the configured limit. Also make sure that
2052 * we take into account the size of the file, i.e. if it already existed.
2053 */
2054 if (have_limit &&
2055 (nbytes_stored + st.st_size > nbytes_max_store)) {
2056 pr_log_pri(PR_LOG_NOTICE, "MaxStoreFileSize (%" PR_LU " bytes) reached: "
2057 "aborting transfer of '%s'", (pr_off_t) nbytes_max_store, path);
2058
2059 /* Abort the transfer. */
2060 stor_abort(cmd->pool);
2061
2062 /* Set errno to EFBIG (or the most appropriate alternative). */
2063 #if defined(EFBIG)
2064 xerrno = EFBIG;
2065 #elif defined(EDQUOT)
2066 xerrno = EDQUOT;
2067 #else
2068 xerrno = EPERM;
2069 #endif
2070
2071 pr_data_abort(xerrno, FALSE);
2072 pr_cmd_set_errno(cmd, xerrno);
2073 errno = xerrno;
2074 return PR_ERROR(cmd);
2075 }
2076
2077 /* XXX Need to handle short writes better here. It is possible that
2078 * the underlying filesystem (e.g. a network-mounted filesystem) could
2079 * be doing short writes, and we ideally should be more resilient/graceful
2080 * in the face of such things.
2081 */
2082 res = pr_fsio_write_with_error(cmd->pool, stor_fh, lbuf, len, &err);
2083 xerrno = errno;
2084
2085 while (res < 0 &&
2086 xerrno == EINTR) {
2087 /* Interrupted by signal; handle it, and try again. */
2088 errno = EINTR;
2089 pr_signals_handle();
2090
2091 res = pr_fsio_write_with_error(cmd->pool, stor_fh, lbuf, len, &err);
2092 xerrno = errno;
2093 }
2094
2095 if (res != len) {
2096 xerrno = EIO;
2097
2098 if (res < 0) {
2099 xerrno = errno;
2100
2101 pr_error_set_where(err, &xfer_module, __FILE__, __LINE__ - 9);
2102 pr_error_set_why(err, pstrcat(cmd->pool, "writing '", stor_fh->fh_path,
2103 "'", NULL));
2104 }
2105
2106 (void) pr_trace_msg("fileperms", 1, "%s, user '%s' (UID %s, GID %s): "
2107 "error writing to '%s': %s", (char *) cmd->argv[0], session.user,
2108 pr_uid2str(cmd->tmp_pool, session.uid),
2109 pr_gid2str(cmd->tmp_pool, session.gid), stor_fh->fh_path,
2110 strerror(xerrno));
2111
2112 if (err != NULL) {
2113 pr_log_debug(DEBUG9, "%s", pr_error_strerror(err, 0));
2114 pr_error_destroy(err);
2115 err = NULL;
2116 }
2117
2118 stor_abort(cmd->pool);
2119 pr_data_abort(xerrno, FALSE);
2120
2121 pr_cmd_set_errno(cmd, xerrno);
2122 errno = xerrno;
2123 return PR_ERROR(cmd);
2124 }
2125
2126 /* If no throttling is configured, this does nothing. */
2127 pr_throttle_pause(nbytes_stored, FALSE);
2128
2129 if (session.range_len > 0) {
2130 if (nbytes_stored == upload_len) {
2131 break;
2132 }
2133
2134 if (nbytes_stored > upload_len) {
2135 xerrno = EPERM;
2136
2137 pr_log_pri(PR_LOG_NOTICE, "Transfer range length (%" PR_LU
2138 " %s) exceeded; aborting transfer of '%s'", (pr_off_t) upload_len,
2139 upload_len != 1 ? "bytes" : "byte", path);
2140
2141 /* Abort the transfer. */
2142 stor_abort(cmd->pool);
2143
2144 pr_data_abort(xerrno, FALSE);
2145 pr_cmd_set_errno(cmd, xerrno);
2146 errno = xerrno;
2147 return PR_ERROR(cmd);
2148 }
2149 }
2150 }
2151
2152 if (XFER_ABORTED) {
2153 stor_abort(cmd->pool);
2154 pr_data_abort(0, FALSE);
2155
2156 pr_cmd_set_errno(cmd, EIO);
2157 errno = EIO;
2158 return PR_ERROR(cmd);
2159 }
2160
2161 if (len < 0) {
2162 /* Default abort errno, in case session.d et al has already gone away */
2163 xerrno = ECONNABORTED;
2164
2165 stor_abort(cmd->pool);
2166
2167 if (session.d != NULL &&
2168 session.d->instrm != NULL) {
2169 xerrno = PR_NETIO_ERRNO(session.d->instrm);
2170 }
2171
2172 pr_data_abort(xerrno, FALSE);
2173 pr_cmd_set_errno(cmd, xerrno);
2174 errno = xerrno;
2175 return PR_ERROR(cmd);
2176 }
2177
2178 /* Did we receive all of the expected bytes in a range? */
2179 if (session.range_len > 0 &&
2180 nbytes_stored < upload_len) {
2181 xerrno = EPERM;
2182
2183 pr_log_pri(PR_LOG_NOTICE, "Transfer range length (%" PR_LU
2184 " %s) not provided; aborting transfer of '%s'", (pr_off_t) upload_len,
2185 upload_len != 1 ? "bytes" : "byte", path);
2186
2187 /* Abort the transfer. */
2188 stor_abort(cmd->pool);
2189
2190 pr_data_abort(xerrno, FALSE);
2191 pr_cmd_set_errno(cmd, xerrno);
2192 errno = xerrno;
2193 return PR_ERROR(cmd);
2194 }
2195
2196 /* If no throttling is configured, this does nothing. */
2197 pr_throttle_pause(nbytes_stored, TRUE);
2198
2199 if (stor_complete(cmd->pool) < 0) {
2200 xerrno = errno;
2201
2202 _log_transfer('i', 'i');
2203
2204 /* Check errno for EDQOUT (or the most appropriate alternative).
2205 * (I hate the fact that FTP has a special response code just for
2206 * this, and that clients actually expect it. Special cases are
2207 * stupid.)
2208 */
2209 #if defined(EDQUOT)
2210 if (xerrno == EDQUOT) {
2211 pr_response_add_err(R_552, "%s: %s", cmd->arg, strerror(xerrno));
2212
2213 pr_cmd_set_errno(cmd, xerrno);
2214 errno = xerrno;
2215 return PR_ERROR(cmd);
2216 }
2217 #elif defined(EFBIG)
2218 if (xerrno == EFBIG) {
2219 pr_response_add_err(R_552, "%s: %s", cmd->arg, strerror(xerrno));
2220
2221 pr_cmd_set_errno(cmd, xerrno);
2222 errno = xerrno;
2223 return PR_ERROR(cmd);
2224 }
2225 #endif
2226
2227 pr_response_add_err(R_550, "%s: %s", cmd->arg, strerror(xerrno));
2228
2229 pr_cmd_set_errno(cmd, xerrno);
2230 errno = xerrno;
2231 return PR_ERROR(cmd);
2232 }
2233
2234 if (session.xfer.path &&
2235 session.xfer.path_hidden) {
2236 if (pr_fsio_rename(session.xfer.path_hidden, session.xfer.path) < 0) {
2237 xerrno = errno;
2238
2239 /* This should only fail on a race condition with a chmod/chown or if
2240 * STOR_APPEND is on and the permissions are squirrely. The poor user
2241 * will have to re-upload, but we've got more important problems to worry
2242 * about and this failure should be fairly rare.
2243 */
2244 pr_log_pri(PR_LOG_WARNING, "Rename of %s to %s failed: %s.",
2245 session.xfer.path_hidden, session.xfer.path, strerror(xerrno));
2246
2247 pr_response_add_err(R_550, _("%s: Rename of hidden file %s failed: %s"),
2248 session.xfer.path, session.xfer.path_hidden, strerror(xerrno));
2249
2250 if (pr_fsio_unlink(session.xfer.path_hidden) < 0) {
2251 if (errno != ENOENT) {
2252 pr_log_debug(DEBUG0, "failed to delete HiddenStores file '%s': %s",
2253 session.xfer.path_hidden, strerror(errno));
2254 }
2255 }
2256
2257 pr_cmd_set_errno(cmd, xerrno);
2258 errno = xerrno;
2259 return PR_ERROR(cmd);
2260 }
2261
2262 /* One way or another, we've dealt with the HiddenStores file. */
2263 session.xfer.path_hidden = NULL;
2264 }
2265
2266 xfer_displayfile();
2267 pr_data_close2();
2268 pr_response_add(R_226, _("Transfer complete"));
2269
2270 return PR_HANDLED(cmd);
2271 }
2272
2273 /* Should this become part of the String API? */
parse_offset(char * str,off_t * num)2274 static int parse_offset(char *str, off_t *num) {
2275 char *ptr, *tmp = NULL;
2276
2277 /* Don't allow negative numbers. strtoul()/strtoull() will silently
2278 * handle them.
2279 */
2280 ptr = str;
2281 if (*ptr == '-') {
2282 errno = EINVAL;
2283 return -1;
2284 }
2285
2286 #ifdef HAVE_STRTOULL
2287 *num = strtoull(ptr, &tmp, 10);
2288 #else
2289 *num = strtoul(ptr, &tmp, 10);
2290 #endif /* HAVE_STRTOULL */
2291
2292 if (tmp && *tmp) {
2293 errno = EINVAL;
2294 return -1;
2295 }
2296
2297 return 0;
2298 }
2299
xfer_rest(cmd_rec * cmd)2300 MODRET xfer_rest(cmd_rec *cmd) {
2301 int res;
2302 off_t pos = 0;
2303
2304 if (cmd->argc != 2) {
2305 pr_response_add_err(R_500, _("'%s' not understood"),
2306 pr_cmd_get_displayable_str(cmd, NULL));
2307
2308 pr_cmd_set_errno(cmd, EINVAL);
2309 errno = EINVAL;
2310 return PR_ERROR(cmd);
2311 }
2312
2313 res = parse_offset(cmd->argv[1], &pos);
2314 if (res < 0) {
2315 int xerrno = errno;
2316
2317 pr_response_add_err(R_501,
2318 _("REST requires a value greater than or equal to 0"));
2319
2320 pr_cmd_set_errno(cmd, xerrno);
2321 errno = xerrno;
2322 return PR_ERROR(cmd);
2323 }
2324
2325 /* Refuse the command if we're in ASCII mode, and the restart position
2326 * is anything other than zero.
2327 *
2328 * Ideally, we would refuse the REST command when in ASCII mode regardless
2329 * of position. However, some (IMHO, stupid) clients "test" the FTP
2330 * server by sending "REST 0" to see if the server supports REST, without
2331 * regard to the transfer type. This, then, is a hack to handle such
2332 * clients.
2333 */
2334 if ((session.sf_flags & SF_ASCII) &&
2335 pos != 0 &&
2336 !(xfer_opts & PR_XFER_OPT_IGNORE_ASCII)) {
2337 pr_log_debug(DEBUG5, "%s not allowed in ASCII mode", (char *) cmd->argv[0]);
2338 pr_response_add_err(R_501,
2339 _("%s: Resuming transfers not allowed in ASCII mode"),
2340 (char *) cmd->argv[0]);
2341
2342 pr_cmd_set_errno(cmd, EPERM);
2343 errno = EPERM;
2344 return PR_ERROR(cmd);
2345 }
2346
2347 session.restart_pos = pos;
2348
2349 /* We can honor REST, or RANG, but not both at the same time. */
2350 session.range_start = session.range_len = 0;
2351
2352 pr_response_add(R_350, _("Restarting at %" PR_LU
2353 ". Send STORE or RETRIEVE to initiate transfer"), (pr_off_t) pos);
2354 return PR_HANDLED(cmd);
2355 }
2356
xfer_rang(cmd_rec * cmd)2357 MODRET xfer_rang(cmd_rec *cmd) {
2358 int res;
2359 off_t range_start, range_end;
2360
2361 if (cmd->argc != 3) {
2362 pr_response_add_err(R_500, _("'%s' not understood"),
2363 pr_cmd_get_displayable_str(cmd, NULL));
2364
2365 pr_cmd_set_errno(cmd, EINVAL);
2366 errno = EINVAL;
2367 return PR_ERROR(cmd);
2368 }
2369
2370 if (!dir_check(cmd->tmp_pool, cmd, cmd->group, session.cwd, NULL)) {
2371 int xerrno = EPERM;
2372
2373 pr_log_debug(DEBUG8, "RANG denied by <Limit> configuration");
2374 pr_response_add_err(R_552, "%s: %s", (char *) cmd->argv[0],
2375 strerror(xerrno));
2376
2377 pr_cmd_set_errno(cmd, xerrno);
2378 errno = xerrno;
2379 return PR_ERROR(cmd);
2380 }
2381
2382 res = parse_offset(cmd->argv[1], &range_start);
2383 if (res < 0) {
2384 int xerrno = errno;
2385
2386 pr_response_add_err(R_501,
2387 _("RANG requires a value greater than or equal to 0"));
2388
2389 pr_cmd_set_errno(cmd, xerrno);
2390 errno = xerrno;
2391 return PR_ERROR(cmd);
2392 }
2393
2394 res = parse_offset(cmd->argv[2], &range_end);
2395 if (res < 0) {
2396 int xerrno = errno;
2397
2398 pr_response_add_err(R_501,
2399 _("RANG requires a value greater than or equal to 0"));
2400
2401 pr_cmd_set_errno(cmd, xerrno);
2402 errno = xerrno;
2403 return PR_ERROR(cmd);
2404 }
2405
2406 if (range_start > range_end) {
2407 /* Per Draft, such ranges will automatically reset the range. */
2408 session.range_start = session.range_len = 0;
2409
2410 /* Iff start = 1 AND end = 0, then this is the acceptable way to reset
2411 * the range. Otherwise, it is an error.
2412 */
2413 if (range_start == 1 &&
2414 range_end == 0) {
2415 pr_response_add(R_350, _("Reset byte transfer range"));
2416 return PR_HANDLED(cmd);
2417 }
2418
2419 pr_log_debug(DEBUG9, "rejecting RANG: start %" PR_LU " > end %" PR_LU,
2420 (pr_off_t) range_start, (pr_off_t) range_end);
2421 pr_response_add_err(R_501, _("RANG start must be less than end"));
2422
2423 pr_cmd_set_errno(cmd, EINVAL);
2424 errno = EINVAL;
2425 return PR_ERROR(cmd);
2426 }
2427
2428 /* Per Draft, refuse (with 551) if we are not in IMAGE type, STREAM mode. */
2429 if (session.sf_flags & SF_ASCII) {
2430 pr_log_debug(DEBUG5, "%s not allowed in ASCII mode", (char *) cmd->argv[0]);
2431 pr_response_add_err(R_551,
2432 _("%s: Transfer ranges not allowed in ASCII mode"),
2433 (char *) cmd->argv[0]);
2434
2435 pr_cmd_set_errno(cmd, EPERM);
2436 errno = EPERM;
2437 return PR_ERROR(cmd);
2438 }
2439
2440 /* Per Draft, the values given are positions, inclusive. Thus
2441 * "RANG 0 1" would be a transfer range of TWO bytes.
2442 *
2443 * For consistency, we store offset+len, rather than start/end positions.
2444 */
2445 session.range_start = range_start;
2446 session.range_len = (range_end - range_start + 1);
2447
2448 /* We can honor RANG, or REST, but not both at the same time. */
2449 session.restart_pos = 0;
2450
2451 pr_response_add(R_350, _("Transferring byte range of %" PR_LU
2452 " %s starting from %" PR_LU), (pr_off_t) session.range_len,
2453 session.range_len != 1 ? "bytes" : "byte", (pr_off_t) range_start);
2454 return PR_HANDLED(cmd);
2455 }
2456
2457 /* This is a PRE_CMD handler that checks security, etc, and places the full
2458 * filename to send in cmd->notes (note that we CANNOT use cmd->tmp_pool
2459 * for this, as tmp_pool only lasts for the duration of this function).
2460 */
xfer_pre_retr(cmd_rec * cmd)2461 MODRET xfer_pre_retr(cmd_rec *cmd) {
2462 char *decoded_path, *dir = NULL;
2463 mode_t fmode;
2464 unsigned char *allow_restart = NULL;
2465 config_rec *c;
2466
2467 xfer_logged_sendfile_decline_msg = FALSE;
2468
2469 if (cmd->argc < 2) {
2470 pr_response_add_err(R_500, _("'%s' not understood"),
2471 pr_cmd_get_displayable_str(cmd, NULL));
2472
2473 pr_cmd_set_errno(cmd, EINVAL);
2474 errno = EINVAL;
2475 return PR_ERROR(cmd);
2476 }
2477
2478 decoded_path = pr_fs_decode_path2(cmd->tmp_pool, cmd->arg,
2479 FSIO_DECODE_FL_TELL_ERRORS);
2480 if (decoded_path == NULL) {
2481 int xerrno = errno;
2482
2483 pr_log_debug(DEBUG8, "'%s' failed to decode properly: %s", cmd->arg,
2484 strerror(xerrno));
2485 pr_response_add_err(R_550, _("%s: Illegal character sequence in filename"),
2486 cmd->arg);
2487
2488 pr_cmd_set_errno(cmd, xerrno);
2489 errno = xerrno;
2490 return PR_ERROR(cmd);
2491 }
2492
2493 pr_fs_clear_cache2(decoded_path);
2494 dir = dir_realpath(cmd->tmp_pool, decoded_path);
2495 if (dir == NULL ||
2496 !dir_check(cmd->tmp_pool, cmd, cmd->group, dir, NULL)) {
2497 int xerrno = errno;
2498
2499 pr_response_add_err(R_550, "%s: %s", cmd->arg, strerror(xerrno));
2500
2501 pr_cmd_set_errno(cmd, xerrno);
2502 errno = xerrno;
2503 return PR_ERROR(cmd);
2504 }
2505
2506 /* Check for UseSendfile. */
2507 use_sendfile = TRUE;
2508 use_sendfile_len = 0;
2509 use_sendfile_pct = -1.0;
2510
2511 c = find_config(CURRENT_CONF, CONF_PARAM, "UseSendfile", FALSE);
2512 if (c != NULL) {
2513 use_sendfile = *((unsigned char *) c->argv[0]);
2514 use_sendfile_len = *((off_t *) c->argv[1]);
2515 use_sendfile_pct = *((float *) c->argv[2]);
2516 }
2517
2518 if (xfer_check_limit(cmd) < 0) {
2519 pr_response_add_err(R_451, _("%s: Too many transfers"), cmd->arg);
2520
2521 pr_cmd_set_errno(cmd, EPERM);
2522 errno = EPERM;
2523 return PR_ERROR(cmd);
2524 }
2525
2526 fmode = file_mode2(cmd->tmp_pool, dir);
2527 if (fmode == 0) {
2528 int xerrno = errno;
2529
2530 pr_response_add_err(R_550, "%s: %s", cmd->arg, strerror(xerrno));
2531
2532 pr_cmd_set_errno(cmd, xerrno);
2533 errno = xerrno;
2534 return PR_ERROR(cmd);
2535 }
2536
2537 if (!S_ISREG(fmode)
2538 #ifdef S_ISFIFO
2539 && !S_ISFIFO(fmode)
2540 #endif
2541 ) {
2542 pr_response_add_err(R_550, _("%s: Not a regular file"), cmd->arg);
2543
2544 /* Deliberately use EISDIR for anything non-file (e.g. directories). */
2545 pr_cmd_set_errno(cmd, EISDIR);
2546 errno = EISDIR;
2547 return PR_ERROR(cmd);
2548 }
2549
2550 /* If restart is on, check to see if AllowRestartRetrieve is off, in
2551 * which case we disallow the transfer and clear restart_pos.
2552 */
2553 allow_restart = get_param_ptr(CURRENT_CONF, "AllowRetrieveRestart", FALSE);
2554
2555 if ((session.restart_pos > 0 || session.range_len > 0) &&
2556 (allow_restart && *allow_restart == FALSE)) {
2557 pr_response_add_err(R_451, _("%s: Restart not permitted, try again"),
2558 cmd->arg);
2559 session.restart_pos = 0L;
2560
2561 pr_cmd_set_errno(cmd, EPERM);
2562 errno = EPERM;
2563 return PR_ERROR(cmd);
2564 }
2565
2566 /* Otherwise everything is good */
2567 if (pr_table_add(cmd->notes, "mod_xfer.retr-path",
2568 pstrdup(cmd->pool, dir), 0) < 0) {
2569 if (errno != EEXIST) {
2570 pr_log_pri(PR_LOG_NOTICE, "notice: error adding 'mod_xfer.retr-path': %s",
2571 strerror(errno));
2572 }
2573 }
2574
2575 return PR_HANDLED(cmd);
2576 }
2577
xfer_post_retr(cmd_rec * cmd)2578 MODRET xfer_post_retr(cmd_rec *cmd) {
2579 const char *path;
2580
2581 path = pr_table_get(cmd->notes, "mod_xfer.retr-path", NULL);
2582 if (path != NULL) {
2583 struct stat st;
2584
2585 if (pr_fsio_stat(path, &st) == 0) {
2586 off_t *file_size;
2587
2588 file_size = palloc(cmd->pool, sizeof(off_t));
2589 *file_size = st.st_size;
2590 (void) pr_table_add(cmd->notes, "mod_xfer.file-size", file_size,
2591 sizeof(off_t));
2592 }
2593 }
2594
2595 return PR_DECLINED(cmd);
2596 }
2597
xfer_retr(cmd_rec * cmd)2598 MODRET xfer_retr(cmd_rec *cmd) {
2599 int xerrno = 0;
2600 const char *dir = NULL;
2601 char *lbuf;
2602 struct stat st;
2603 off_t nbytes_max_retrieve = 0;
2604 unsigned char have_limit = FALSE;
2605 long bufsz, len = 0;
2606 off_t start_offset = 0, download_len = 0;
2607 off_t curr_offset, curr_pos = 0, nbytes_sent = 0, cnt_steps = 0, cnt_next = 0;
2608 pr_error_t *err = NULL;
2609
2610 /* Prepare for any potential throttling. */
2611 pr_throttle_init(cmd);
2612
2613 dir = pr_table_get(cmd->notes, "mod_xfer.retr-path", NULL);
2614
2615 retr_fh = pr_fsio_open_with_error(cmd->pool, dir, O_RDONLY, &err);
2616 xerrno = errno;
2617
2618 if (retr_fh == NULL) {
2619 pr_error_set_where(err, &xfer_module, __FILE__, __LINE__ - 4);
2620 pr_error_set_why(err, pstrcat(cmd->pool, "download file '", dir, "'",
2621 NULL));
2622
2623 (void) pr_trace_msg("fileperms", 1, "%s, user '%s' (UID %s, GID %s): "
2624 "error opening '%s': %s", (char *) cmd->argv[0], session.user,
2625 pr_uid2str(cmd->tmp_pool, session.uid),
2626 pr_gid2str(cmd->tmp_pool, session.gid), dir, strerror(xerrno));
2627
2628 if (err != NULL) {
2629 pr_log_debug(DEBUG9, "%s", pr_error_strerror(err, 0));
2630 pr_error_destroy(err);
2631 err = NULL;
2632 }
2633
2634 pr_response_add_err(R_550, "%s: %s", cmd->arg, strerror(xerrno));
2635
2636 pr_cmd_set_errno(cmd, xerrno);
2637 errno = xerrno;
2638 return PR_ERROR(cmd);
2639 }
2640
2641 if (pr_fsio_fstat(retr_fh, &st) < 0) {
2642 /* Error stat'ing the file. */
2643 xerrno = errno;
2644
2645 pr_fsio_close(retr_fh);
2646 retr_fh = NULL;
2647 pr_response_add_err(R_550, "%s: %s", cmd->arg, strerror(xerrno));
2648
2649 pr_cmd_set_errno(cmd, xerrno);
2650 errno = xerrno;
2651 return PR_ERROR(cmd);
2652 }
2653
2654 /* Advise the platform that we will be only reading this file
2655 * sequentially. Note that a preceding REST command does not mean we
2656 * need to use a different offset value here; we can/should still
2657 * tell the platform that the entire file should be treated this way.
2658 */
2659 pr_fs_fadvise(PR_FH_FD(retr_fh), 0, 0, PR_FS_FADVISE_SEQUENTIAL);
2660
2661 if (session.restart_pos > 0) {
2662 start_offset = session.restart_pos;
2663
2664 } else if (session.range_start > 0) {
2665 start_offset = session.range_start;
2666 }
2667
2668 if (start_offset > 0) {
2669 char *offset_cmd;
2670
2671 offset_cmd = C_REST;
2672 if (session.range_start > 0) {
2673 offset_cmd = C_RANG;
2674 }
2675
2676 /* Make sure that the requested offset is valid (within the size of the
2677 * file being resumed).
2678 */
2679
2680 if (start_offset > st.st_size) {
2681 pr_trace_msg(trace_channel, 4,
2682 "%s offset %" PR_LU " exceeds file size (%" PR_LU " bytes)",
2683 offset_cmd, (pr_off_t) start_offset, (pr_off_t) st.st_size);
2684 pr_response_add_err(R_554, _("%s: invalid %s argument"), offset_cmd,
2685 cmd->arg);
2686 pr_fsio_close(retr_fh);
2687 retr_fh = NULL;
2688
2689 pr_cmd_set_errno(cmd, EINVAL);
2690 errno = EINVAL;
2691 return PR_ERROR(cmd);
2692 }
2693
2694 /* The RANG Draft says, on this topic:
2695 *
2696 * The server-PI SHOULD transfer 0 octets with RETR if the specified
2697 * start point or start point and end point are larger than the actual
2698 * file size.
2699 *
2700 * However, I vehemently disagree. Sending zero bytes in such a case
2701 * would be treated as a successful download by the client, not informing
2702 * the user of the erroneous conditions. Thus, IMHO, violating the
2703 * principle of least surprise; the user might end up asking "Why did
2704 * my download succeed, but I have zero bytes?". That is a terrible user
2705 * experience. So instead, we return an error in this case.
2706 */
2707
2708 if ((start_offset + session.range_len) > st.st_size) {
2709 pr_trace_msg(trace_channel, 4,
2710 "%s offset %" PR_LU " exceeds file size (%" PR_LU " bytes)",
2711 offset_cmd, (pr_off_t) (start_offset + session.range_len),
2712 (pr_off_t) st.st_size);
2713 pr_response_add_err(R_554, _("%s: invalid RANG argument"), cmd->arg);
2714 pr_fsio_close(retr_fh);
2715 retr_fh = NULL;
2716
2717 pr_cmd_set_errno(cmd, EINVAL);
2718 errno = EINVAL;
2719 return PR_ERROR(cmd);
2720 }
2721
2722 if (pr_fsio_lseek(retr_fh, start_offset, SEEK_SET) == (off_t) -1) {
2723 xerrno = errno;
2724 pr_fsio_close(retr_fh);
2725 errno = xerrno;
2726 retr_fh = NULL;
2727
2728 (void) pr_trace_msg("fileperms", 1, "%s, user '%s' (UID %s, GID %s): "
2729 "error seeking to byte %" PR_LU " of '%s': %s", (char *) cmd->argv[0],
2730 session.user, pr_uid2str(cmd->tmp_pool, session.uid),
2731 pr_gid2str(cmd->tmp_pool, session.gid), (pr_off_t) start_offset,
2732 dir, strerror(xerrno));
2733
2734 pr_log_debug(DEBUG0, "error seeking to offset %" PR_LU
2735 " for file %s: %s", (pr_off_t) start_offset, dir, strerror(xerrno));
2736 pr_response_add_err(R_554, _("%s: invalid %s argument"), offset_cmd,
2737 cmd->arg);
2738
2739 pr_cmd_set_errno(cmd, EINVAL);
2740 errno = EINVAL;
2741 return PR_ERROR(cmd);
2742 }
2743
2744 curr_pos = start_offset;
2745
2746 if (session.restart_pos > 0) {
2747 session.restart_pos = 0L;
2748
2749 } else if (session.range_start > 0) {
2750 session.range_start = 0;
2751 }
2752 }
2753
2754 /* Stash the offset at which we're writing from this file. */
2755 curr_offset = pr_fsio_lseek(retr_fh, (off_t) 0, SEEK_CUR);
2756 if (curr_offset != (off_t) -1) {
2757 off_t *file_offset;
2758
2759 file_offset = palloc(cmd->pool, sizeof(off_t));
2760 *file_offset = (off_t) curr_offset;
2761 (void) pr_table_add(cmd->notes, "mod_xfer.file-offset", file_offset,
2762 sizeof(off_t));
2763 }
2764
2765 /* Block any timers for this section, where we want to prepare the
2766 * data connection, then need to reprovision the session.xfer struct,
2767 * and do NOT want timers (which may want/need that session.xfer data)
2768 * to fire until after the reprovisioning (Bug#4168).
2769 */
2770 pr_alarms_block();
2771
2772 /* Send the data */
2773 pr_data_init(cmd->arg, PR_NETIO_IO_WR);
2774
2775 session.xfer.path = dir;
2776 session.xfer.file_size = st.st_size;
2777
2778 pr_alarms_unblock();
2779
2780 cnt_steps = session.xfer.file_size / 100;
2781 if (cnt_steps == 0) {
2782 cnt_steps = 1;
2783 }
2784
2785 if (session.range_len > 0) {
2786 if (curr_pos + session.range_len > st.st_size) {
2787 /* If the RANG end point is past the end of our file, ignore it and
2788 * treat this as the remainder of the file, from the starting offset.
2789 */
2790 download_len = st.st_size - curr_pos;
2791
2792 } else {
2793 download_len = session.range_len;
2794 }
2795
2796 } else {
2797 download_len = st.st_size - curr_pos;
2798 }
2799
2800 if (pr_data_open(cmd->arg, NULL, PR_NETIO_IO_WR, download_len) < 0) {
2801 xerrno = errno;
2802
2803 retr_abort(cmd->pool);
2804 pr_data_abort(0, TRUE);
2805
2806 pr_cmd_set_errno(cmd, xerrno);
2807 errno = xerrno;
2808 return PR_ERROR(cmd);
2809 }
2810
2811 /* Retrieve the number of bytes to retrieve, maximum, if present */
2812 nbytes_max_retrieve = find_max_nbytes("MaxRetrieveFileSize");
2813 if (nbytes_max_retrieve == 0UL) {
2814 have_limit = FALSE;
2815
2816 } else {
2817 have_limit = TRUE;
2818 }
2819
2820 /* Check the MaxRetrieveFileSize. If it is zero, or if the size
2821 * of the file being retrieved is greater than the MaxRetrieveFileSize,
2822 * then signal an error and abort the transfer now.
2823 */
2824 if (have_limit &&
2825 ((nbytes_max_retrieve == 0) || (st.st_size > nbytes_max_retrieve))) {
2826
2827 pr_log_pri(PR_LOG_NOTICE, "MaxRetrieveFileSize (%" PR_LU " %s) reached: "
2828 "aborting transfer of '%s'", (pr_off_t) nbytes_max_retrieve,
2829 nbytes_max_retrieve != 1 ? "bytes" : "byte", dir);
2830
2831 /* Abort the transfer. */
2832 retr_abort(cmd->pool);
2833
2834 /* Set errno to EPERM ("Operation not permitted") */
2835 pr_data_abort(EPERM, FALSE);
2836
2837 pr_cmd_set_errno(cmd, EPERM);
2838 errno = EPERM;
2839 return PR_ERROR(cmd);
2840 }
2841
2842 bufsz = pr_config_get_server_xfer_bufsz(PR_NETIO_IO_WR);
2843 lbuf = (char *) palloc(cmd->tmp_pool, bufsz);
2844 pr_trace_msg("data", 8, "allocated download buffer of %lu bytes",
2845 (unsigned long) bufsz);
2846
2847 pr_scoreboard_entry_update(session.pid,
2848 PR_SCORE_XFER_SIZE, download_len,
2849 PR_SCORE_XFER_DONE, (off_t) 0,
2850 NULL);
2851
2852 if (session.range_len > 0) {
2853 if (bufsz > session.range_len) {
2854 bufsz = session.range_len;
2855 }
2856 }
2857
2858 while (nbytes_sent != download_len) {
2859 pr_signals_handle();
2860
2861 if (XFER_ABORTED) {
2862 break;
2863 }
2864
2865 len = transmit_data(cmd->pool, curr_offset, &curr_pos, lbuf, bufsz);
2866 if (len == 0) {
2867 break;
2868 }
2869
2870 if (len < 0) {
2871 /* Make sure that the errno value, needed for the pr_data_abort() call,
2872 * is preserved; errno itself might be overwritten in retr_abort().
2873 */
2874 int already_aborted = FALSE;
2875
2876 xerrno = errno;
2877 retr_abort(cmd->pool);
2878
2879 /* Do we need to abort the data transfer here? It's possible that
2880 * the transfer has already been aborted, e.g. via the TCP OOB marker
2881 * and/or the ABOR command. And if that is the case, then calling
2882 * pr_data_abort() here will only lead to a spurious response code
2883 * (see Bug#4252).
2884 *
2885 * However, there are OTHER error conditions which would lead to this
2886 * code path. So we need to resort to some heuristics to differentiate
2887 * between these cases. The errno value checks match those in the
2888 * pr_data_xfer() function, after the control channel has been polled
2889 * for commands such as ABOR.
2890 */
2891
2892 if (session.d == NULL &&
2893 #if defined(ECONNABORTED)
2894 xerrno == ECONNABORTED &&
2895 #elif defined(ENOTCONN)
2896 xerrno == ENOTCONN &&
2897 #else
2898 xerrno == EIO &&
2899 #endif
2900 session.xfer.xfer_type == STOR_DEFAULT) {
2901
2902 /* If the ABOR command has been sent, then pr_data_reset() and
2903 * pr_data_cleanup() will have been called; the latter resets the
2904 * xfer_type value to DEFAULT.
2905 */
2906 already_aborted = TRUE;
2907 }
2908
2909 if (already_aborted == FALSE) {
2910 pr_data_abort(xerrno, FALSE);
2911 }
2912
2913 pr_cmd_set_errno(cmd, xerrno);
2914 errno = xerrno;
2915 return PR_ERROR(cmd);
2916 }
2917
2918 nbytes_sent += len;
2919 curr_offset += len;
2920
2921 if ((nbytes_sent / cnt_steps) != cnt_next) {
2922 cnt_next = nbytes_sent / cnt_steps;
2923
2924 pr_scoreboard_entry_update(session.pid,
2925 PR_SCORE_XFER_DONE, nbytes_sent,
2926 NULL);
2927 }
2928
2929 /* If no throttling is configured, this simply updates the scoreboard.
2930 * In this case, we want to use session.xfer.total_bytes, rather than
2931 * nbytes_sent, as the latter incorporates a REST position and the
2932 * former does not. (When handling STOR, this is not an issue: different
2933 * end-of-loop conditions).
2934 */
2935 pr_throttle_pause(session.xfer.total_bytes, FALSE);
2936 }
2937
2938 if (XFER_ABORTED) {
2939 retr_abort(cmd->pool);
2940 pr_data_abort(0, FALSE);
2941
2942 pr_cmd_set_errno(cmd, EIO);
2943 errno = EIO;
2944 return PR_ERROR(cmd);
2945
2946 } else {
2947
2948 /* If no throttling is configured, this simply updates the scoreboard.
2949 * In this case, we want to use session.xfer.total_bytes, rather than
2950 * nbytes_sent, as the latter incorporates a REST position and the
2951 * former does not. (When handling STOR, this is not an issue: different
2952 * end-of-loop conditions).
2953 */
2954 pr_throttle_pause(session.xfer.total_bytes, TRUE);
2955
2956 retr_complete(cmd->pool);
2957 xfer_displayfile();
2958 pr_data_close2();
2959 pr_response_add(R_226, _("Transfer complete"));
2960 }
2961
2962 return PR_HANDLED(cmd);
2963 }
2964
xfer_abor(cmd_rec * cmd)2965 MODRET xfer_abor(cmd_rec *cmd) {
2966 if (cmd->argc != 1) {
2967 pr_response_add_err(R_500, _("'%s' not understood"),
2968 pr_cmd_get_displayable_str(cmd, NULL));
2969
2970 pr_cmd_set_errno(cmd, EINVAL);
2971 errno = EINVAL;
2972 return PR_ERROR(cmd);
2973 }
2974
2975 if (session.xfer.direction == PR_NETIO_IO_RD) {
2976 stor_abort(cmd->pool);
2977
2978 } else if (session.xfer.direction == PR_NETIO_IO_WR) {
2979 retr_abort(cmd->pool);
2980 }
2981
2982 pr_data_abort(0, FALSE);
2983
2984 pr_response_add(R_226, _("Abort successful"));
2985 return PR_HANDLED(cmd);
2986 }
2987
xfer_log_abor(cmd_rec * cmd)2988 MODRET xfer_log_abor(cmd_rec *cmd) {
2989
2990 /* Clean up the data connection info in the session structure. */
2991 pr_data_reset();
2992 pr_data_cleanup();
2993
2994 return PR_DECLINED(cmd);
2995 }
2996
xfer_type(cmd_rec * cmd)2997 MODRET xfer_type(cmd_rec *cmd) {
2998 char *type;
2999
3000 if (cmd->argc < 2 ||
3001 cmd->argc > 3) {
3002 pr_response_add_err(R_500, _("'%s' not understood"),
3003 pr_cmd_get_displayable_str(cmd, NULL));
3004
3005 pr_cmd_set_errno(cmd, EINVAL);
3006 errno = EINVAL;
3007 return PR_ERROR(cmd);
3008 }
3009
3010 type = pstrdup(cmd->tmp_pool, cmd->argv[1]);
3011 type[0] = toupper(type[0]);
3012
3013 if (strncmp(type, "A", 2) == 0 ||
3014 (cmd->argc == 3 &&
3015 strncmp(type, "L", 2) == 0 &&
3016 strncmp(cmd->argv[2], "7", 2) == 0)) {
3017
3018 /* TYPE A(SCII) or TYPE L 7. */
3019 session.sf_flags |= SF_ASCII;
3020
3021 } else if (strncmp(type, "I", 2) == 0 ||
3022 (cmd->argc == 3 &&
3023 strncmp(type, "L", 2) == 0 &&
3024 strncmp(cmd->argv[2], "8", 2) == 0)) {
3025
3026 /* TYPE I(MAGE) or TYPE L 8. */
3027 session.sf_flags &= (SF_ALL^(SF_ASCII|SF_ASCII_OVERRIDE));
3028
3029 } else {
3030 pr_response_add_err(R_504, _("%s not implemented for '%s' parameter"),
3031 (char *) cmd->argv[0], (char *) cmd->argv[1]);
3032
3033 pr_cmd_set_errno(cmd, ENOSYS);
3034 errno = ENOSYS;
3035 return PR_ERROR(cmd);
3036 }
3037
3038 /* Note that the client may NOT be authenticated at this point in time.
3039 * If that is the case, set a flag so that the POST_CMD PASS handler does
3040 * not overwrite the TYPE command's setting.
3041 *
3042 * Alternatively, we COULD bar/reject any TYPE commands before authentication.
3043 * However, I think that doing so would interfere with many existing clients
3044 * which assume that they can send TYPE before authenticating.
3045 */
3046 if (session.auth_mech == NULL) {
3047 have_type = TRUE;
3048 }
3049
3050 pr_response_add(R_200, _("Type set to %s"), (char *) cmd->argv[1]);
3051 return PR_HANDLED(cmd);
3052 }
3053
xfer_stru(cmd_rec * cmd)3054 MODRET xfer_stru(cmd_rec *cmd) {
3055 char *stru;
3056
3057 if (cmd->argc != 2) {
3058 pr_response_add_err(R_501, _("'%s' not understood"),
3059 pr_cmd_get_displayable_str(cmd, NULL));
3060
3061 pr_cmd_set_errno(cmd, EINVAL);
3062 errno = EINVAL;
3063 return PR_ERROR(cmd);
3064 }
3065
3066 stru = cmd->argv[1];
3067 stru[0] = toupper(stru[0]);
3068
3069 switch ((int) stru[0]) {
3070 case 'F':
3071 /* Should 202 be returned instead??? */
3072 pr_response_add(R_200, _("Structure set to F"));
3073 return PR_HANDLED(cmd);
3074 break;
3075
3076 case 'R':
3077 /* Accept R but with no operational difference from F???
3078 * R is required in minimum implementations by RFC-959, 5.1.
3079 * RFC-1123, 4.1.2.13, amends this to only apply to servers whose file
3080 * systems support record structures, but also suggests that such a
3081 * server "may still accept files with STRU R, recording the byte stream
3082 * literally." Another configurable choice, perhaps?
3083 *
3084 * NOTE: wu-ftpd does not so accept STRU R.
3085 */
3086
3087 /* FALLTHROUGH */
3088
3089 case 'P':
3090 /* RFC-1123 recommends against implementing P. */
3091 pr_response_add_err(R_504, _("'%s' unsupported structure type"),
3092 pr_cmd_get_displayable_str(cmd, NULL));
3093
3094 pr_cmd_set_errno(cmd, ENOSYS);
3095 errno = ENOSYS;
3096 return PR_ERROR(cmd);
3097
3098 default:
3099 pr_response_add_err(R_501, _("'%s' unrecognized structure type"),
3100 pr_cmd_get_displayable_str(cmd, NULL));
3101
3102 pr_cmd_set_errno(cmd, EINVAL);
3103 errno = EINVAL;
3104 return PR_ERROR(cmd);
3105 }
3106 }
3107
xfer_mode(cmd_rec * cmd)3108 MODRET xfer_mode(cmd_rec *cmd) {
3109 char *mode;
3110
3111 if (cmd->argc != 2) {
3112 pr_response_add_err(R_501, _("'%s' not understood"),
3113 pr_cmd_get_displayable_str(cmd, NULL));
3114
3115 pr_cmd_set_errno(cmd, EINVAL);
3116 errno = EINVAL;
3117 return PR_ERROR(cmd);
3118 }
3119
3120 mode = cmd->argv[1];
3121 mode[0] = toupper(mode[0]);
3122
3123 switch ((int) mode[0]) {
3124 case 'S':
3125 /* Should 202 be returned instead??? */
3126 pr_response_add(R_200, _("Mode set to S"));
3127 return PR_HANDLED(cmd);
3128
3129 case 'B':
3130 /* FALLTHROUGH */
3131
3132 case 'C':
3133 pr_response_add_err(R_504, _("'%s' unsupported transfer mode"),
3134 pr_cmd_get_displayable_str(cmd, NULL));
3135
3136 pr_cmd_set_errno(cmd, ENOSYS);
3137 errno = ENOSYS;
3138 return PR_ERROR(cmd);
3139 }
3140
3141 pr_response_add_err(R_501, _("'%s' unrecognized transfer mode"),
3142 pr_cmd_get_displayable_str(cmd, NULL));
3143
3144 pr_cmd_set_errno(cmd, EINVAL);
3145 errno = EINVAL;
3146 return PR_ERROR(cmd);
3147 }
3148
xfer_allo(cmd_rec * cmd)3149 MODRET xfer_allo(cmd_rec *cmd) {
3150 off_t requested_sz;
3151 char *tmp = NULL;
3152
3153 /* Even though we only handle the "ALLO <size>" command, we should not
3154 * barf on the unlikely (but RFC-compliant) "ALLO <size> R <size>" commands.
3155 * See RFC 959, Section 4.1.3.
3156 */
3157 if (cmd->argc != 2 &&
3158 cmd->argc != 4) {
3159 pr_response_add_err(R_504, _("'%s' not understood"),
3160 pr_cmd_get_displayable_str(cmd, NULL));
3161
3162 pr_cmd_set_errno(cmd, EINVAL);
3163 errno = EINVAL;
3164 return PR_ERROR(cmd);
3165 }
3166
3167 #ifdef HAVE_STRTOULL
3168 requested_sz = strtoull(cmd->argv[1], &tmp, 10);
3169 #else
3170 requested_sz = strtoul(cmd->argv[1], &tmp, 10);
3171 #endif /* !HAVE_STRTOULL */
3172
3173 if (tmp && *tmp) {
3174 pr_response_add_err(R_504, _("%s: Invalid ALLO argument"), cmd->arg);
3175
3176 pr_cmd_set_errno(cmd, EINVAL);
3177 errno = EINVAL;
3178 return PR_ERROR(cmd);
3179 }
3180
3181 if (xfer_opts & PR_XFER_OPT_HANDLE_ALLO) {
3182 const char *path;
3183 off_t avail_kb;
3184 int res;
3185
3186 path = pr_fs_getcwd();
3187
3188 res = pr_fs_getsize2((char *) path, &avail_kb);
3189 if (res < 0) {
3190 /* If we can't check the filesystem stats for any reason, let the request
3191 * proceed anyway.
3192 */
3193 pr_log_debug(DEBUG7,
3194 "error getting available size for filesystem containing '%s': %s",
3195 path, strerror(errno));
3196 pr_response_add(R_202, _("No storage allocation necessary"));
3197
3198 } else {
3199 off_t requested_kb;
3200
3201 /* The requested size is in bytes; the size returned from
3202 * pr_fs_getsize2() is in KB.
3203 */
3204 requested_kb = requested_sz / 1024;
3205
3206 if (requested_kb > avail_kb) {
3207 pr_log_debug(DEBUG5, "%s requested %" PR_LU " KB, only %" PR_LU
3208 " KB available on '%s'", (char *) cmd->argv[0],
3209 (pr_off_t) requested_kb, (pr_off_t) avail_kb, path);
3210 pr_response_add_err(R_552, "%s: %s", cmd->arg, strerror(ENOSPC));
3211
3212 pr_cmd_set_errno(cmd, ENOSPC);
3213 errno = ENOSPC;
3214 return PR_ERROR(cmd);
3215 }
3216
3217 pr_log_debug(DEBUG9, "%s requested %" PR_LU " KB, %" PR_LU
3218 " KB available on '%s'", (char *) cmd->argv[0], (pr_off_t) requested_kb,
3219 (pr_off_t) avail_kb, path);
3220 pr_response_add(R_200, _("%s command successful"), (char *) cmd->argv[0]);
3221 }
3222
3223 } else {
3224 pr_response_add(R_202, _("No storage allocation necessary"));
3225 }
3226
3227 return PR_HANDLED(cmd);
3228 }
3229
xfer_smnt(cmd_rec * cmd)3230 MODRET xfer_smnt(cmd_rec *cmd) {
3231 pr_response_add(R_502, _("SMNT command not implemented"));
3232 return PR_HANDLED(cmd);
3233 }
3234
xfer_err_cleanup(cmd_rec * cmd)3235 MODRET xfer_err_cleanup(cmd_rec *cmd) {
3236
3237 /* If a hidden store was aborted, remove it. */
3238 if (session.xfer.xfer_type == STOR_HIDDEN) {
3239 unsigned char *delete_stores = NULL;
3240
3241 delete_stores = get_param_ptr(CURRENT_CONF, "DeleteAbortedStores", FALSE);
3242 if (delete_stores == NULL ||
3243 *delete_stores == TRUE) {
3244 if (session.xfer.path_hidden) {
3245 pr_log_debug(DEBUG5, "removing aborted HiddenStores file '%s'",
3246 session.xfer.path_hidden);
3247 if (pr_fsio_unlink(session.xfer.path_hidden) < 0) {
3248 if (errno != ENOENT) {
3249 pr_log_debug(DEBUG0, "error deleting HiddenStores file '%s': %s",
3250 session.xfer.path_hidden, strerror(errno));
3251 }
3252 }
3253 }
3254 }
3255 }
3256
3257 pr_data_clear_xfer_pool();
3258
3259 memset(&session.xfer, '\0', sizeof(session.xfer));
3260
3261 /* Don't forget to clear any possible RANG/REST parameters as well. */
3262 session.range_start = session.range_len = 0;
3263 session.restart_pos = 0;
3264
3265 return PR_DECLINED(cmd);
3266 }
3267
xfer_log_stor(cmd_rec * cmd)3268 MODRET xfer_log_stor(cmd_rec *cmd) {
3269 _log_transfer('i', 'c');
3270
3271 /* Increment the file counters. */
3272 session.total_files_in++;
3273 session.total_files_xfer++;
3274
3275 pr_data_cleanup();
3276
3277 /* Don't forget to clear any possible RANG/REST parameters as well. */
3278 session.range_start = session.range_len = 0;
3279 session.restart_pos = 0;
3280
3281 return PR_DECLINED(cmd);
3282 }
3283
xfer_log_retr(cmd_rec * cmd)3284 MODRET xfer_log_retr(cmd_rec *cmd) {
3285 _log_transfer('o', 'c');
3286
3287 /* Increment the file counters. */
3288 session.total_files_out++;
3289 session.total_files_xfer++;
3290
3291 pr_data_cleanup();
3292
3293 /* Don't forget to clear any possible RANG/REST parameters as well. */
3294 session.range_start = session.range_len = 0;
3295 session.restart_pos = 0;
3296
3297 return PR_DECLINED(cmd);
3298 }
3299
noxfer_timeout_cb(CALLBACK_FRAME)3300 static int noxfer_timeout_cb(CALLBACK_FRAME) {
3301 int timeout;
3302 const char *proto;
3303
3304 timeout = pr_data_get_timeout(PR_DATA_TIMEOUT_NO_TRANSFER);
3305
3306 if (session.sf_flags & SF_XFER) {
3307 pr_trace_msg("timer", 4,
3308 "TimeoutNoTransfer (%d %s) reached, but data transfer in progress, "
3309 "ignoring", timeout, timeout != 1 ? "seconds" : "second");
3310
3311 /* Transfer in progress, ignore this timeout */
3312 return 1;
3313 }
3314
3315 pr_event_generate("core.timeout-no-transfer", NULL);
3316 pr_response_send_async(R_421,
3317 _("No transfer timeout (%d seconds): closing control connection"), timeout);
3318
3319 pr_timer_remove(PR_TIMER_IDLE, ANY_MODULE);
3320 pr_timer_remove(PR_TIMER_LOGIN, ANY_MODULE);
3321
3322 /* If this timeout is encountered and we are expecting a passive transfer,
3323 * add some logging that suggests things to check and possibly fix
3324 * (e.g. network/firewall rules).
3325 */
3326 if (session.sf_flags & SF_PASSIVE) {
3327 pr_log_pri(PR_LOG_NOTICE,
3328 "Passive data transfer failed, possibly due to network issues");
3329 pr_log_pri(PR_LOG_NOTICE,
3330 "Check your PassivePorts and MasqueradeAddress settings,");
3331 pr_log_pri(PR_LOG_NOTICE,
3332 "and any router, NAT, and firewall rules in the network path.");
3333 }
3334
3335 proto = pr_session_get_protocol(PR_SESS_PROTO_FL_LOGOUT);
3336
3337 pr_log_pri(PR_LOG_NOTICE, "%s no transfer timeout, disconnected", proto);
3338 pr_session_disconnect(&xfer_module, PR_SESS_DISCONNECT_TIMEOUT,
3339 "TimeoutNoTransfer");
3340
3341 return 0;
3342 }
3343
xfer_post_pass(cmd_rec * cmd)3344 MODRET xfer_post_pass(cmd_rec *cmd) {
3345 config_rec *c;
3346
3347 /* Default transfer mode is ASCII, per RFC 959, Section 3.1.1.1. Unless
3348 * the client has already sent a TYPE command.
3349 */
3350 if (have_type == FALSE) {
3351 session.sf_flags |= SF_ASCII;
3352 c = find_config(main_server->conf, CONF_PARAM, "DefaultTransferMode",
3353 FALSE);
3354 if (c != NULL) {
3355 char *default_transfer_mode;
3356
3357 default_transfer_mode = c->argv[0];
3358 if (strcasecmp(default_transfer_mode, "binary") == 0) {
3359 session.sf_flags &= (SF_ALL^SF_ASCII);
3360 }
3361 }
3362 }
3363
3364 c = find_config(TOPLEVEL_CONF, CONF_PARAM, "TimeoutNoTransfer", FALSE);
3365 if (c != NULL) {
3366 int timeout = *((int *) c->argv[0]);
3367 pr_data_set_timeout(PR_DATA_TIMEOUT_NO_TRANSFER, timeout);
3368
3369 /* Setup timer */
3370 if (timeout > 0) {
3371 pr_timer_add(timeout, PR_TIMER_NOXFER, &xfer_module, noxfer_timeout_cb,
3372 "TimeoutNoTransfer");
3373 }
3374 }
3375
3376 c = find_config(TOPLEVEL_CONF, CONF_PARAM, "TimeoutStalled", FALSE);
3377 if (c != NULL) {
3378 int timeout = *((int *) c->argv[0]);
3379 pr_data_set_timeout(PR_DATA_TIMEOUT_STALLED, timeout);
3380
3381 /* Note: timers for handling TimeoutStalled timeouts are handled in the
3382 * data transfer routines, not here.
3383 */
3384 }
3385
3386 c = find_config(main_server->conf, CONF_PARAM, "TransferOptions", FALSE);
3387 while (c != NULL) {
3388 unsigned long opts = 0;
3389
3390 pr_signals_handle();
3391
3392 opts = *((unsigned long *) c->argv[0]);
3393 xfer_opts |= opts;
3394
3395 c = find_config_next(c, c->next, CONF_PARAM, "TransferOptions", FALSE);
3396 }
3397
3398 if (xfer_opts & PR_XFER_OPT_IGNORE_ASCII) {
3399 pr_log_debug(DEBUG8, "Ignoring ASCII translation for this session");
3400 pr_data_ignore_ascii(TRUE);
3401 }
3402
3403 /* If we are chrooted, then skip actually processing the ALLO command
3404 * (Bug#3996).
3405 */
3406 if (session.chroot_path != NULL) {
3407 xfer_opts &= ~PR_XFER_OPT_HANDLE_ALLO;
3408 }
3409
3410 return PR_DECLINED(cmd);
3411 }
3412
3413 /* Configuration handlers
3414 */
3415
set_allowoverwrite(cmd_rec * cmd)3416 MODRET set_allowoverwrite(cmd_rec *cmd) {
3417 int bool = -1;
3418 config_rec *c = NULL;
3419
3420 CHECK_ARGS(cmd, 1);
3421 CHECK_CONF(cmd, CONF_ROOT|CONF_VIRTUAL|CONF_GLOBAL|CONF_ANON|
3422 CONF_DIR|CONF_DYNDIR);
3423
3424 bool = get_boolean(cmd, 1);
3425 if (bool == -1)
3426 CONF_ERROR(cmd, "expected boolean parameter");
3427
3428 c = add_config_param(cmd->argv[0], 1, NULL);
3429 c->argv[0] = pcalloc(c->pool, sizeof(unsigned char));
3430 *((unsigned char *) c->argv[0]) = (unsigned char) bool;
3431 c->flags |= CF_MERGEDOWN;
3432
3433 return PR_HANDLED(cmd);
3434 }
3435
set_allowrestart(cmd_rec * cmd)3436 MODRET set_allowrestart(cmd_rec *cmd) {
3437 int bool = -1;
3438 config_rec *c = NULL;
3439
3440 CHECK_ARGS(cmd, 1);
3441 CHECK_CONF(cmd, CONF_ROOT|CONF_VIRTUAL|CONF_GLOBAL|CONF_ANON|
3442 CONF_DIR|CONF_DYNDIR);
3443
3444 bool = get_boolean(cmd, 1);
3445 if (bool == -1)
3446 CONF_ERROR(cmd, "expected boolean parameter");
3447
3448 c = add_config_param(cmd->argv[0], 1, NULL);
3449 c->argv[0] = pcalloc(c->pool, sizeof(unsigned char));
3450 *((unsigned char *) c->argv[0]) = bool;
3451 c->flags |= CF_MERGEDOWN;
3452
3453 return PR_HANDLED(cmd);
3454 }
3455
3456 /* usage: DefaultTransferMode ascii|binary */
set_defaulttransfermode(cmd_rec * cmd)3457 MODRET set_defaulttransfermode(cmd_rec *cmd) {
3458 char *default_mode;
3459
3460 CHECK_ARGS(cmd, 1);
3461 CHECK_CONF(cmd, CONF_ROOT|CONF_VIRTUAL|CONF_GLOBAL);
3462
3463 default_mode = cmd->argv[1];
3464 if (strcasecmp(default_mode, "ascii") != 0 &&
3465 strcasecmp(default_mode, "binary") != 0) {
3466 CONF_ERROR(cmd, "parameter must be 'ascii' or 'binary'");
3467 }
3468
3469 add_config_param_str(cmd->argv[0], 1, default_mode);
3470 return PR_HANDLED(cmd);
3471 }
3472
set_deleteabortedstores(cmd_rec * cmd)3473 MODRET set_deleteabortedstores(cmd_rec *cmd) {
3474 int bool = -1;
3475 config_rec *c = NULL;
3476
3477 CHECK_ARGS(cmd, 1);
3478 CHECK_CONF(cmd, CONF_ROOT|CONF_VIRTUAL|CONF_GLOBAL|CONF_ANON|
3479 CONF_DIR|CONF_DYNDIR);
3480
3481 bool = get_boolean(cmd, 1);
3482 if (bool == -1)
3483 CONF_ERROR(cmd, "expected Boolean parameter");
3484
3485 c = add_config_param(cmd->argv[0], 1, NULL);
3486 c->argv[0] = pcalloc(c->pool, sizeof(unsigned char));
3487 *((unsigned char *) c->argv[0]) = bool;
3488 c->flags |= CF_MERGEDOWN;
3489
3490 return PR_HANDLED(cmd);
3491 }
3492
3493 /* usage: DisplayFileTransfer path */
set_displayfiletransfer(cmd_rec * cmd)3494 MODRET set_displayfiletransfer(cmd_rec *cmd) {
3495 CHECK_ARGS(cmd, 1);
3496 CHECK_CONF(cmd, CONF_ROOT|CONF_VIRTUAL|CONF_GLOBAL);
3497
3498 (void) add_config_param_str(cmd->argv[0], 1, cmd->argv[1]);
3499 return PR_HANDLED(cmd);
3500 }
3501
set_hiddenstores(cmd_rec * cmd)3502 MODRET set_hiddenstores(cmd_rec *cmd) {
3503 int enabled = -1, add_periods = TRUE;
3504 config_rec *c = NULL;
3505 char *prefix = NULL;
3506
3507 CHECK_ARGS(cmd, 1);
3508 CHECK_CONF(cmd, CONF_ROOT|CONF_VIRTUAL|CONF_GLOBAL|CONF_ANON|CONF_DIR);
3509
3510 c = add_config_param(cmd->argv[0], 3, NULL, NULL, NULL);
3511
3512 /* Handle the case where the admin may, for some reason, want a custom
3513 * prefix which could also be construed to be a Boolean value by
3514 * get_boolean(): if the value begins AND ends with a period, then treat
3515 * it as a custom prefix.
3516 */
3517 prefix = cmd->argv[1];
3518 if (prefix[0] == '.' &&
3519 prefix[strlen(prefix)-1] == '.') {
3520 add_periods = FALSE;
3521 enabled = -1;
3522
3523 } else {
3524 enabled = get_boolean(cmd, 1);
3525 }
3526
3527 /* If a suffix has been configured as well, assume that we do NOT
3528 * automatically want periods.
3529 */
3530 if (cmd->argc == 3) {
3531 add_periods = FALSE;
3532 }
3533
3534 if (enabled == -1) {
3535 /* If the parameter is not a Boolean parameter, assume that the
3536 * admin is configuring a specific prefix to use instead of the
3537 * default ".in.".
3538 */
3539
3540 c->argv[0] = pcalloc(c->pool, sizeof(int));
3541 *((int *) c->argv[0]) = TRUE;
3542
3543 if (add_periods) {
3544 /* Automatically add the leading and trailing periods. */
3545 c->argv[1] = pstrcat(c->pool, ".", cmd->argv[1], ".", NULL);
3546
3547 } else {
3548 c->argv[1] = pstrdup(c->pool, cmd->argv[1]);
3549 }
3550
3551 if (cmd->argc == 3) {
3552 c->argv[2] = pstrdup(c->pool, cmd->argv[2]);
3553
3554 } else {
3555 c->argv[2] = pstrdup(c->pool, ".");
3556 }
3557
3558 } else {
3559 c->argv[0] = pcalloc(c->pool, sizeof(int));
3560 *((int *) c->argv[0]) = enabled;
3561
3562 if (enabled) {
3563 /* The default HiddenStore prefix */
3564 c->argv[1] = pstrdup(c->pool, ".in.");
3565
3566 /* The default HiddenStores suffix. */
3567 c->argv[2] = pstrdup(c->pool, ".");
3568 }
3569 }
3570
3571 c->flags |= CF_MERGEDOWN;
3572 return PR_HANDLED(cmd);
3573 }
3574
set_maxfilesize(cmd_rec * cmd)3575 MODRET set_maxfilesize(cmd_rec *cmd) {
3576 config_rec *c = NULL;
3577 off_t nbytes;
3578 unsigned int precedence = 0;
3579
3580 int ctxt = (cmd->config && cmd->config->config_type != CONF_PARAM ?
3581 cmd->config->config_type : cmd->server->config_type ?
3582 cmd->server->config_type : CONF_ROOT);
3583
3584 if (cmd->argc-1 == 1) {
3585 if (strncmp(cmd->argv[1], "*", 2) != 0) {
3586 CONF_ERROR(cmd, "incorrect number of parameters");
3587 }
3588
3589 } else if (cmd->argc-1 != 2 && cmd->argc-1 != 4) {
3590 CONF_ERROR(cmd, "incorrect number of parameters");
3591 }
3592
3593 CHECK_CONF(cmd, CONF_ROOT|CONF_ANON|CONF_VIRTUAL|CONF_GLOBAL|CONF_DIR|
3594 CONF_DYNDIR);
3595
3596 /* Set the precedence for this config_rec based on its configuration
3597 * context.
3598 */
3599 if (ctxt & CONF_GLOBAL) {
3600 precedence = 1;
3601
3602 /* These will never appear simultaneously */
3603 } else if ((ctxt & CONF_ROOT) ||
3604 (ctxt & CONF_VIRTUAL)) {
3605 precedence = 2;
3606
3607 } else if (ctxt & CONF_ANON) {
3608 precedence = 3;
3609
3610 } else if (ctxt & CONF_DIR) {
3611 precedence = 4;
3612
3613 } else if (ctxt & CONF_DYNDIR) {
3614 precedence = 5;
3615 }
3616
3617 /* If the directive was used with four arguments, it means the optional
3618 * classifiers and expression were used. Make sure the classifier is a valid
3619 * one.
3620 */
3621 if (cmd->argc-1 == 4) {
3622 if (strncmp(cmd->argv[3], "user", 5) == 0 ||
3623 strncmp(cmd->argv[3], "group", 6) == 0 ||
3624 strncmp(cmd->argv[3], "class", 6) == 0) {
3625
3626 /* no-op */
3627
3628 } else {
3629 CONF_ERROR(cmd, pstrcat(cmd->tmp_pool, "unknown classifier used: '",
3630 cmd->argv[3], "'", NULL));
3631 }
3632 }
3633
3634 if (cmd->argc-1 == 1) {
3635
3636 /* Do nothing here -- the "*" (the only parameter allowed if there is
3637 * only a single parameter given) signifies an unlimited size, which is
3638 * what the server provides by default.
3639 */
3640 nbytes = 0UL;
3641
3642 } else {
3643
3644 /* Pass the cmd_rec off to see what number of bytes was
3645 * requested/configured.
3646 */
3647 if (pr_str_get_nbytes(cmd->argv[1], cmd->argv[2], &nbytes) < 0) {
3648 CONF_ERROR(cmd, pstrcat(cmd->tmp_pool, "unable to parse: ",
3649 cmd->argv[1], " ", cmd->argv[2], ": ", strerror(errno), NULL));
3650 }
3651
3652 if (nbytes == 0) {
3653 CONF_ERROR(cmd, "size must be greater than zero");
3654 }
3655 }
3656
3657 if (cmd->argc-1 == 1 ||
3658 cmd->argc-1 == 2) {
3659 c = add_config_param(cmd->argv[0], 2, NULL, NULL);
3660 c->argv[0] = pcalloc(c->pool, sizeof(off_t));
3661 *((off_t *) c->argv[0]) = nbytes;
3662 c->argv[1] = pcalloc(c->pool, sizeof(unsigned int));
3663 *((unsigned int *) c->argv[1]) = precedence;
3664
3665 } else {
3666 array_header *acl = NULL;
3667 unsigned int argc;
3668 void **argv;
3669
3670 argc = cmd->argc - 4;
3671 argv = cmd->argv + 3;
3672
3673 acl = pr_expr_create(cmd->tmp_pool, &argc, (char **) argv);
3674
3675 c = add_config_param(cmd->argv[0], 0);
3676 c->argc = argc + 3;
3677 c->argv = pcalloc(c->pool, ((argc + 4) * sizeof(void *)));
3678
3679 argv = c->argv;
3680
3681 /* Copy in the configured bytes */
3682 *argv = pcalloc(c->pool, sizeof(unsigned long));
3683 *((unsigned long *) *argv++) = nbytes;
3684
3685 /* Copy in the precedence */
3686 *argv = pcalloc(c->pool, sizeof(unsigned int));
3687 *((unsigned int *) *argv++) = precedence;
3688
3689 /* Copy in the classifier. */
3690 *argv++ = pstrdup(c->pool, cmd->argv[3]);
3691
3692 if (argc && acl) {
3693 while (argc--) {
3694 *argv++ = pstrdup(c->pool, *((char **) acl->elts));
3695 acl->elts = ((char **) acl->elts) + 1;
3696 }
3697 }
3698
3699 /* Don't forget the terminating NULL */
3700 *argv = NULL;
3701 }
3702
3703 c->flags |= CF_MERGEDOWN_MULTI;
3704
3705 return PR_HANDLED(cmd);
3706 }
3707
3708 /* usage: MaxTransfersPerHost cmdlist count [msg] */
set_maxtransfersperhost(cmd_rec * cmd)3709 MODRET set_maxtransfersperhost(cmd_rec *cmd) {
3710 config_rec *c = NULL;
3711 int count = 0;
3712
3713 if (cmd->argc-1 < 2 ||
3714 cmd->argc-1 > 3)
3715 CONF_ERROR(cmd, "bad number of parameters");
3716
3717 CHECK_CONF(cmd, CONF_ROOT|CONF_VIRTUAL|CONF_GLOBAL|CONF_ANON|
3718 CONF_DIR|CONF_DYNDIR);
3719
3720 count = atoi(cmd->argv[2]);
3721 if (count < 1)
3722 CONF_ERROR(cmd, pstrcat(cmd->tmp_pool, "count must be greater than zero: '",
3723 cmd->argv[2], "'", NULL));
3724
3725 c = add_config_param(cmd->argv[0], 3, NULL, NULL, NULL);
3726
3727 /* Parse the command list. */
3728 if (xfer_parse_cmdlist(cmd->argv[0], c, cmd->argv[1]) < 0)
3729 CONF_ERROR(cmd, "error with command list");
3730
3731 c->argv[1] = pcalloc(c->pool, sizeof(unsigned int));
3732 *((unsigned int *) c->argv[1]) = count;
3733
3734 if (cmd->argc-1 == 3)
3735 c->argv[2] = pstrdup(c->pool, cmd->argv[3]);
3736
3737 c->flags |= CF_MERGEDOWN_MULTI;
3738
3739 return PR_HANDLED(cmd);
3740 }
3741
3742 /* usage: MaxTransfersPerUser cmdlist count [msg] */
set_maxtransfersperuser(cmd_rec * cmd)3743 MODRET set_maxtransfersperuser(cmd_rec *cmd) {
3744 config_rec *c = NULL;
3745 int count = 0;
3746
3747 if (cmd->argc-1 < 2 ||
3748 cmd->argc-1 > 3)
3749 CONF_ERROR(cmd, "bad number of parameters");
3750
3751 CHECK_CONF(cmd, CONF_ROOT|CONF_VIRTUAL|CONF_GLOBAL|CONF_ANON|
3752 CONF_DIR|CONF_DYNDIR);
3753
3754 count = atoi(cmd->argv[2]);
3755 if (count < 1)
3756 CONF_ERROR(cmd, pstrcat(cmd->tmp_pool, "count must be greater than zero: '",
3757 cmd->argv[2], "'", NULL));
3758
3759 c = add_config_param(cmd->argv[0], 3, NULL, NULL, NULL);
3760
3761 /* Parse the command list. */
3762 if (xfer_parse_cmdlist(cmd->argv[0], c, cmd->argv[1]) < 0)
3763 CONF_ERROR(cmd, "error with command list");
3764
3765 c->argv[1] = pcalloc(c->pool, sizeof(unsigned int));
3766 *((unsigned int *) c->argv[1]) = count;
3767
3768 if (cmd->argc-1 == 3)
3769 c->argv[2] = pstrdup(c->pool, cmd->argv[3]);
3770
3771 c->flags |= CF_MERGEDOWN_MULTI;
3772
3773 return PR_HANDLED(cmd);
3774 }
3775
set_storeuniqueprefix(cmd_rec * cmd)3776 MODRET set_storeuniqueprefix(cmd_rec *cmd) {
3777 config_rec *c = NULL;
3778
3779 CHECK_ARGS(cmd, 1);
3780 CHECK_CONF(cmd, CONF_ROOT|CONF_VIRTUAL|CONF_GLOBAL|CONF_ANON|
3781 CONF_DIR|CONF_DYNDIR);
3782
3783 /* make sure there are no slashes in the prefix */
3784 if (strchr(cmd->argv[1], '/') != NULL)
3785 CONF_ERROR(cmd, pstrcat(cmd->tmp_pool, "no slashes allowed in prefix: '",
3786 cmd->argv[1], "'", NULL));
3787
3788 c = add_config_param_str(cmd->argv[0], 1, (void *) cmd->argv[1]);
3789 c->flags |= CF_MERGEDOWN;
3790
3791 return PR_HANDLED(cmd);
3792 }
3793
set_timeoutnoxfer(cmd_rec * cmd)3794 MODRET set_timeoutnoxfer(cmd_rec *cmd) {
3795 int timeout = -1;
3796 config_rec *c = NULL;
3797
3798 CHECK_ARGS(cmd, 1);
3799 CHECK_CONF(cmd, CONF_ROOT|CONF_VIRTUAL|CONF_GLOBAL|CONF_ANON);
3800
3801 if (pr_str_get_duration(cmd->argv[1], &timeout) < 0) {
3802 CONF_ERROR(cmd, pstrcat(cmd->tmp_pool, "error parsing timeout value '",
3803 cmd->argv[1], "': ", strerror(errno), NULL));
3804 }
3805
3806 c = add_config_param(cmd->argv[0], 1, NULL);
3807 c->argv[0] = pcalloc(c->pool, sizeof(int));
3808 *((int *) c->argv[0]) = timeout;
3809 c->flags |= CF_MERGEDOWN;
3810
3811 return PR_HANDLED(cmd);
3812 }
3813
set_timeoutstalled(cmd_rec * cmd)3814 MODRET set_timeoutstalled(cmd_rec *cmd) {
3815 int timeout = -1;
3816 config_rec *c = NULL;
3817
3818 CHECK_ARGS(cmd, 1);
3819 CHECK_CONF(cmd, CONF_ROOT|CONF_VIRTUAL|CONF_GLOBAL|CONF_ANON);
3820
3821 if (pr_str_get_duration(cmd->argv[1], &timeout) < 0) {
3822 CONF_ERROR(cmd, pstrcat(cmd->tmp_pool, "error parsing timeout value '",
3823 cmd->argv[1], "': ", strerror(errno), NULL));
3824 }
3825
3826 c = add_config_param(cmd->argv[0], 1, NULL);
3827 c->argv[0] = pcalloc(c->pool, sizeof(int));
3828 *((int *) c->argv[0]) = timeout;
3829 c->flags |= CF_MERGEDOWN;
3830
3831 return PR_HANDLED(cmd);
3832 }
3833
3834 /* usage: TransferOptions opt1 opt2 ... */
set_transferoptions(cmd_rec * cmd)3835 MODRET set_transferoptions(cmd_rec *cmd) {
3836 config_rec *c = NULL;
3837 register unsigned int i = 0;
3838 unsigned long opts = 0UL;
3839
3840 if (cmd->argc-1 == 0) {
3841 CONF_ERROR(cmd, "wrong number of parameters");
3842 }
3843
3844 CHECK_CONF(cmd, CONF_ROOT|CONF_VIRTUAL|CONF_GLOBAL);
3845
3846 c = add_config_param(cmd->argv[0], 1, NULL);
3847
3848 for (i = 1; i < cmd->argc; i++) {
3849 if (strcasecmp(cmd->argv[i], "IgnoreASCII") == 0) {
3850 opts |= PR_XFER_OPT_IGNORE_ASCII;
3851
3852 } else {
3853 CONF_ERROR(cmd, pstrcat(cmd->tmp_pool, ": unknown TransferOption '",
3854 cmd->argv[i], "'", NULL));
3855 }
3856 }
3857
3858 c->argv[0] = pcalloc(c->pool, sizeof(unsigned long));
3859 *((unsigned long *) c->argv[0]) = opts;
3860
3861 return PR_HANDLED(cmd);
3862 }
3863
3864 /* usage: TransferRate cmds kbps[:free-bytes] ["user"|"group"|"class"
3865 * expression]
3866 */
set_transferrate(cmd_rec * cmd)3867 MODRET set_transferrate(cmd_rec *cmd) {
3868 config_rec *c = NULL;
3869 char *tmp = NULL, *endp = NULL;
3870 long double rate = 0.0;
3871 off_t freebytes = 0;
3872 unsigned int precedence = 0;
3873
3874 int ctxt = (cmd->config && cmd->config->config_type != CONF_PARAM ?
3875 cmd->config->config_type : cmd->server->config_type ?
3876 cmd->server->config_type : CONF_ROOT);
3877
3878 /* Must have two or four parameters */
3879 if (cmd->argc-1 != 2 && cmd->argc-1 != 4)
3880 CONF_ERROR(cmd, "wrong number of parameters");
3881
3882 CHECK_CONF(cmd, CONF_ROOT|CONF_VIRTUAL|CONF_GLOBAL|CONF_ANON|
3883 CONF_DIR|CONF_DYNDIR);
3884
3885 /* Set the precedence for this config_rec based on its configuration
3886 * context.
3887 */
3888 if (ctxt & CONF_GLOBAL)
3889 precedence = 1;
3890
3891 /* These will never appear simultaneously */
3892 else if (ctxt & CONF_ROOT || ctxt & CONF_VIRTUAL)
3893 precedence = 2;
3894
3895 else if (ctxt & CONF_ANON)
3896 precedence = 3;
3897
3898 else if (ctxt & CONF_DIR)
3899 precedence = 4;
3900
3901 /* Note: by tweaking this value to be lower than the precedence for
3902 * <Directory> appearances of this directive, I can effectively cause
3903 * any .ftpaccess appearances not to override...
3904 */
3905 else if (ctxt & CONF_DYNDIR)
3906 precedence = 5;
3907
3908 /* Check for a valid classifier. */
3909 if (cmd->argc-1 > 2) {
3910 if (strncmp(cmd->argv[3], "user", 5) == 0 ||
3911 strncmp(cmd->argv[3], "group", 6) == 0 ||
3912 strncmp(cmd->argv[3], "class", 6) == 0) {
3913 /* do nothing */
3914 ;
3915
3916 } else {
3917 CONF_ERROR(cmd, pstrcat(cmd->tmp_pool, "unknown classifier requested: '",
3918 cmd->argv[3], "'", NULL));
3919 }
3920 }
3921
3922 if ((tmp = strchr(cmd->argv[2], ':')) != NULL)
3923 *tmp = '\0';
3924
3925 /* Parse the 'kbps' part. Ideally, we'd be using strtold(3) rather than
3926 * strtod(3) here, but FreeBSD doesn't have strtold(3). Yay. Portability.
3927 */
3928 rate = (long double) strtod(cmd->argv[2], &endp);
3929
3930 if (rate < 0.0)
3931 CONF_ERROR(cmd, "rate must be greater than zero");
3932
3933 if (endp && *endp)
3934 CONF_ERROR(cmd, pstrcat(cmd->tmp_pool, "invalid number: '",
3935 cmd->argv[2], "'", NULL));
3936
3937 /* Parse any 'free-bytes' part */
3938 if (tmp) {
3939 cmd->argv[2] = ++tmp;
3940
3941 freebytes = strtoul(cmd->argv[2], &endp, 10);
3942 if (endp && *endp) {
3943 CONF_ERROR(cmd, pstrcat(cmd->tmp_pool, "invalid number: '",
3944 cmd->argv[2], "'", NULL));
3945 }
3946 }
3947
3948 /* Construct the config_rec */
3949 if (cmd->argc-1 == 2) {
3950 c = add_config_param(cmd->argv[0], 4, NULL, NULL, NULL, NULL);
3951
3952 /* Parse the command list. */
3953 if (xfer_parse_cmdlist(cmd->argv[0], c, cmd->argv[1]) < 0)
3954 CONF_ERROR(cmd, "error with command list");
3955
3956 c->argv[1] = pcalloc(c->pool, sizeof(long double));
3957 *((long double *) c->argv[1]) = rate;
3958 c->argv[2] = pcalloc(c->pool, sizeof(off_t));
3959 *((off_t *) c->argv[2]) = freebytes;
3960 c->argv[3] = pcalloc(c->pool, sizeof(unsigned int));
3961 *((unsigned int *) c->argv[3]) = precedence;
3962
3963 } else {
3964 array_header *acl = NULL;
3965 unsigned int argc;
3966 void **argv;
3967
3968 argc = cmd->argc - 4;
3969 argv = cmd->argv + 3;
3970
3971 acl = pr_expr_create(cmd->tmp_pool, &argc, (char **) argv);
3972 c = add_config_param(cmd->argv[0], 0);
3973
3974 /* Parse the command list.
3975 *
3976 * The five additional slots are for: cmd-list, bps, free-bytes,
3977 * precedence, user/group/class.
3978 */
3979 c->argc = argc + 5;
3980
3981 c->argv = pcalloc(c->pool, ((c->argc + 1) * sizeof(void *)));
3982 argv = c->argv;
3983
3984 if (xfer_parse_cmdlist(cmd->argv[0], c, cmd->argv[1]) < 0) {
3985 CONF_ERROR(cmd, "error with command list");
3986 }
3987
3988 /* Note: the command list is at index 0, hence this increment. */
3989 argv++;
3990
3991 *argv = pcalloc(c->pool, sizeof(long double));
3992 *((long double *) *argv++) = rate;
3993 *argv = pcalloc(c->pool, sizeof(off_t));
3994 *((unsigned long *) *argv++) = freebytes;
3995 *argv = pcalloc(c->pool, sizeof(unsigned int));
3996 *((unsigned int *) *argv++) = precedence;
3997
3998 *argv++ = pstrdup(c->pool, cmd->argv[3]);
3999
4000 if (argc && acl) {
4001 while (argc--) {
4002 *argv++ = pstrdup(c->pool, *((char **) acl->elts));
4003 acl->elts = ((char **) acl->elts) + 1;
4004 }
4005 }
4006
4007 /* don't forget the terminating NULL */
4008 *argv = NULL;
4009 }
4010
4011 c->flags |= CF_MERGEDOWN_MULTI;
4012 return PR_HANDLED(cmd);
4013 }
4014
4015 /* usage: UseSendfile on|off|"len units"|percentage"%" */
set_usesendfile(cmd_rec * cmd)4016 MODRET set_usesendfile(cmd_rec *cmd) {
4017 int bool = -1;
4018 off_t sendfile_len = 0;
4019 float sendfile_pct = -1.0;
4020 config_rec *c;
4021
4022 CHECK_CONF(cmd, CONF_ROOT|CONF_VIRTUAL|CONF_GLOBAL|CONF_ANON|CONF_DIR|CONF_DYNDIR);
4023
4024 if (cmd->argc-1 == 1) {
4025 /* Is the given parameter a boolean, or a percentage? Try parsing it a
4026 * boolean first.
4027 */
4028 bool = get_boolean(cmd, 1);
4029 if (bool == -1) {
4030 char *arg;
4031 size_t arglen;
4032
4033 /* See if the given parameter is a percentage. */
4034 arg = cmd->argv[1];
4035 arglen = strlen(arg);
4036 if (arglen > 1 &&
4037 arg[arglen-1] == '%') {
4038 char *ptr = NULL;
4039
4040 arg[arglen-1] = '\0';
4041
4042 #ifdef HAVE_STRTOF
4043 sendfile_pct = strtof(arg, &ptr);
4044 #elif HAVE_STRTOD
4045 sendfile_pct = strtod(arg, &ptr);
4046 #else
4047 sendfile_pct = atof(arg);
4048 #endif /* !HAVE_STRTOF and !HAVE_STRTOD */
4049
4050 if (ptr && *ptr) {
4051 CONF_ERROR(cmd, pstrcat(cmd->tmp_pool, "bad percentage value '",
4052 arg, "%'", NULL));
4053 }
4054
4055 sendfile_pct /= 100.0;
4056 bool = TRUE;
4057
4058 } else {
4059 CONF_ERROR(cmd, "expected Boolean parameter");
4060 }
4061 }
4062
4063 } else if (cmd->argc-1 == 2) {
4064 off_t nbytes;
4065
4066 if (pr_str_get_nbytes(cmd->argv[1], cmd->argv[2], &nbytes) < 0) {
4067 CONF_ERROR(cmd, pstrcat(cmd->tmp_pool, "unable to parse: ",
4068 cmd->argv[1], " ", cmd->argv[2], ": ", strerror(errno), NULL));
4069 }
4070
4071 sendfile_len = nbytes;
4072 bool = TRUE;
4073
4074 } else {
4075 CONF_ERROR(cmd, "wrong number of parameters");
4076 }
4077
4078 c = add_config_param(cmd->argv[0], 3, NULL, NULL, NULL);
4079 c->argv[0] = pcalloc(c->pool, sizeof(unsigned char));
4080 *((unsigned char *) c->argv[0]) = bool;
4081 c->argv[1] = pcalloc(c->pool, sizeof(off_t));
4082 *((off_t *) c->argv[1]) = sendfile_len;
4083 c->argv[2] = pcalloc(c->pool, sizeof(float));
4084 *((float *) c->argv[2]) = sendfile_pct;
4085
4086 c->flags |= CF_MERGEDOWN;
4087 return PR_HANDLED(cmd);
4088 }
4089
4090 /* Event handlers
4091 */
4092
xfer_exit_ev(const void * event_data,void * user_data)4093 static void xfer_exit_ev(const void *event_data, void *user_data) {
4094
4095 if (stor_fh != NULL) {
4096 /* An upload is occurring... */
4097 pr_trace_msg(trace_channel, 6, "session exiting, aborting upload");
4098 stor_abort(session.pool);
4099
4100 } else if (retr_fh != NULL) {
4101 /* A download is occurring... */
4102 pr_trace_msg(trace_channel, 6, "session exiting, aborting download");
4103 retr_abort(session.pool);
4104 }
4105
4106 if (session.sf_flags & SF_XFER) {
4107 cmd_rec *cmd;
4108
4109 pr_data_abort(0, FALSE);
4110
4111 cmd = session.curr_cmd_rec;
4112 if (cmd == NULL) {
4113 cmd = pr_cmd_alloc(session.pool, 2, session.curr_cmd, session.xfer.path);
4114 }
4115
4116 (void) pr_cmd_dispatch_phase(cmd, POST_CMD_ERR, 0);
4117 (void) pr_cmd_dispatch_phase(cmd, LOG_CMD_ERR, 0);
4118 }
4119
4120 return;
4121 }
4122
xfer_sess_reinit_ev(const void * event_data,void * user_data)4123 static void xfer_sess_reinit_ev(const void *event_data, void *user_data) {
4124 int res;
4125
4126 /* A HOST command changed the main_server pointer, reinitialize ourselves. */
4127
4128 pr_event_unregister(&xfer_module, "core.exit", xfer_exit_ev);
4129 pr_event_unregister(&xfer_module, "core.session-reinit", xfer_sess_reinit_ev);
4130 pr_event_unregister(&xfer_module, "core.signal.USR2", xfer_sigusr2_ev);
4131 pr_event_unregister(&xfer_module, "core.timeout-stalled",
4132 xfer_timeout_stalled_ev);
4133
4134 if (displayfilexfer_fh != NULL) {
4135 (void) pr_fsio_close(displayfilexfer_fh);
4136 displayfilexfer_fh = NULL;
4137 }
4138
4139 res = xfer_sess_init();
4140 if (res < 0) {
4141 pr_session_disconnect(&xfer_module,
4142 PR_SESS_DISCONNECT_SESSION_INIT_FAILED, NULL);
4143 }
4144 }
4145
xfer_sigusr2_ev(const void * event_data,void * user_data)4146 static void xfer_sigusr2_ev(const void *event_data, void *user_data) {
4147
4148 if (pr_module_exists("mod_shaper.c")) {
4149 /* Only do this if we're currently involved in a data transfer.
4150 * This is a hack put in to support mod_shaper's antics.
4151 */
4152 if (session.curr_cmd_id == PR_CMD_APPE_ID ||
4153 session.curr_cmd_id == PR_CMD_RETR_ID ||
4154 session.curr_cmd_id == PR_CMD_STOR_ID ||
4155 session.curr_cmd_id == PR_CMD_STOU_ID) {
4156 pool *tmp_pool;
4157 cmd_rec *cmd;
4158
4159 tmp_pool = make_sub_pool(session.pool);
4160 pr_pool_tag(tmp_pool, "Data Transfer SIGUSR2 pool");
4161
4162 cmd = pr_cmd_alloc(tmp_pool, 1, session.curr_cmd);
4163
4164 /* Rescan the config tree for TransferRates, picking up any possible
4165 * changes.
4166 */
4167 pr_log_debug(DEBUG2, "rechecking TransferRates");
4168 pr_throttle_init(cmd);
4169
4170 destroy_pool(tmp_pool);
4171 }
4172 }
4173
4174 return;
4175 }
4176
xfer_timedout(const char * reason)4177 static void xfer_timedout(const char *reason) {
4178 if (stor_fh != NULL) {
4179 pr_trace_msg(trace_channel, 6, "%s, aborting upload", reason);
4180 stor_abort(session.pool);
4181
4182 } else if (retr_fh != NULL) {
4183 pr_trace_msg(trace_channel, 6, "%s, aborting download", reason);
4184 retr_abort(session.pool);
4185 }
4186 }
4187
xfer_timeout_session_ev(const void * event_data,void * user_data)4188 static void xfer_timeout_session_ev(const void *event_data, void *user_data) {
4189 xfer_timedout("session timeout");
4190 }
4191
xfer_timeout_stalled_ev(const void * event_data,void * user_data)4192 static void xfer_timeout_stalled_ev(const void *event_data, void *user_data) {
4193 /* In this event handler, the "else" case, for a stalled transfer, will
4194 * be handled by the 'core.exit' event handler above. For in that
4195 * scenario, a data transfer WILL have actually been in progress,
4196 * whereas in the !SF_XFER case, the client requested a transfer, but
4197 * never actually opened the data connection.
4198 */
4199
4200 if (!(session.sf_flags & SF_XFER)) {
4201 xfer_timedout("transfer stalled");
4202 }
4203 }
4204
4205 /* Initialization routines
4206 */
4207
xfer_init(void)4208 static int xfer_init(void) {
4209
4210 /* Add the commands handled by this module to the HELP list. */
4211 pr_help_add(C_TYPE, _("<sp> type-code (A, I, L 7, L 8)"), TRUE);
4212 pr_help_add(C_STRU, _("is not implemented (always F)"), TRUE);
4213 pr_help_add(C_MODE, _("is not implemented (always S)"), TRUE);
4214 pr_help_add(C_RETR, _("<sp> pathname"), TRUE);
4215 pr_help_add(C_STOR, _("<sp> pathname"), TRUE);
4216 pr_help_add(C_STOU, _("(store unique filename)"), TRUE);
4217 pr_help_add(C_APPE, _("<sp> pathname"), TRUE);
4218 pr_help_add(C_REST, _("<sp> byte-count"), TRUE);
4219 pr_help_add(C_ABOR, _("(abort current operation)"), TRUE);
4220 pr_help_add(C_RANG, _("<sp> start-point <sp> end-point"), TRUE);
4221
4222 /* Add the additional features implemented by this module into the
4223 * list, to be displayed in response to a FEAT command.
4224 */
4225 pr_feat_add(C_RANG " STREAM");
4226
4227 return 0;
4228 }
4229
xfer_sess_init(void)4230 static int xfer_sess_init(void) {
4231 char *displayfilexfer = NULL;
4232
4233 /* Exit handlers for HiddenStores cleanup */
4234 pr_event_register(&xfer_module, "core.exit", xfer_exit_ev, NULL);
4235 pr_event_register(&xfer_module, "core.session-reinit", xfer_sess_reinit_ev,
4236 NULL);
4237 pr_event_register(&xfer_module, "core.signal.USR2", xfer_sigusr2_ev,
4238 NULL);
4239 pr_event_register(&xfer_module, "core.timeout-session",
4240 xfer_timeout_session_ev, NULL);
4241 pr_event_register(&xfer_module, "core.timeout-stalled",
4242 xfer_timeout_stalled_ev, NULL);
4243
4244 have_type = FALSE;
4245
4246 /* Look for a DisplayFileTransfer file which has an absolute path. If we
4247 * find one, open a filehandle, such that that file can be displayed
4248 * even if the session is chrooted. DisplayFileTransfer files with
4249 * relative paths will be handled after chroot, preserving the old
4250 * behavior.
4251 */
4252 displayfilexfer = get_param_ptr(main_server->conf, "DisplayFileTransfer",
4253 FALSE);
4254 if (displayfilexfer &&
4255 *displayfilexfer == '/') {
4256 struct stat st;
4257
4258 displayfilexfer_fh = pr_fsio_open(displayfilexfer, O_RDONLY);
4259 if (displayfilexfer_fh == NULL) {
4260 pr_log_debug(DEBUG6, "unable to open DisplayFileTransfer file '%s': %s",
4261 displayfilexfer, strerror(errno));
4262
4263 } else {
4264 if (pr_fsio_fstat(displayfilexfer_fh, &st) < 0) {
4265 pr_log_debug(DEBUG6, "unable to stat DisplayFileTransfer file '%s': %s",
4266 displayfilexfer, strerror(errno));
4267 pr_fsio_close(displayfilexfer_fh);
4268 displayfilexfer_fh = NULL;
4269
4270 } else {
4271 if (S_ISDIR(st.st_mode)) {
4272 errno = EISDIR;
4273
4274 pr_log_debug(DEBUG6,
4275 "unable to use DisplayFileTransfer file '%s': %s",
4276 displayfilexfer, strerror(errno));
4277 pr_fsio_close(displayfilexfer_fh);
4278 displayfilexfer_fh = NULL;
4279 }
4280 }
4281 }
4282 }
4283
4284 /* IF the RFC2228 mechanism is "TLS" at this point in time, then set the flag
4285 * to disable use of sendfile; the client is probably an FTPS client using
4286 * implicit SSL (Bug#4073).
4287 */
4288 if (session.rfc2228_mech != NULL &&
4289 strncmp(session.rfc2228_mech, "TLS", 4) == 0) {
4290 have_rfc2228_data = TRUE;
4291 }
4292
4293 return 0;
4294 }
4295
4296 /* Module API tables
4297 */
4298
4299 static conftable xfer_conftab[] = {
4300 { "AllowOverwrite", set_allowoverwrite, NULL },
4301 { "AllowRetrieveRestart", set_allowrestart, NULL },
4302 { "AllowStoreRestart", set_allowrestart, NULL },
4303 { "DefaultTransferMode", set_defaulttransfermode, NULL },
4304 { "DeleteAbortedStores", set_deleteabortedstores, NULL },
4305 { "DisplayFileTransfer", set_displayfiletransfer, NULL },
4306 { "HiddenStores", set_hiddenstores, NULL },
4307 { "MaxRetrieveFileSize", set_maxfilesize, NULL },
4308 { "MaxStoreFileSize", set_maxfilesize, NULL },
4309 { "MaxTransfersPerHost", set_maxtransfersperhost, NULL },
4310 { "MaxTransfersPerUser", set_maxtransfersperuser, NULL },
4311 { "StoreUniquePrefix", set_storeuniqueprefix, NULL },
4312 { "TimeoutNoTransfer", set_timeoutnoxfer, NULL },
4313 { "TimeoutStalled", set_timeoutstalled, NULL },
4314 { "TransferOptions", set_transferoptions, NULL },
4315 { "TransferRate", set_transferrate, NULL },
4316 { "UseSendfile", set_usesendfile, NULL },
4317
4318 { NULL }
4319 };
4320
4321 static cmdtable xfer_cmdtab[] = {
4322 { CMD, C_TYPE, G_NONE, xfer_type, FALSE, FALSE, CL_MISC },
4323 { CMD, C_STRU, G_NONE, xfer_stru, TRUE, FALSE, CL_MISC },
4324 { CMD, C_MODE, G_NONE, xfer_mode, TRUE, FALSE, CL_MISC },
4325 { POST_CMD,C_MODE, G_NONE, xfer_post_mode,FALSE, FALSE },
4326 { CMD, C_ALLO, G_NONE, xfer_allo, TRUE, FALSE, CL_MISC },
4327 { CMD, C_SMNT, G_NONE, xfer_smnt, TRUE, FALSE, CL_MISC },
4328 { PRE_CMD, C_RETR, G_READ, xfer_pre_retr, TRUE, FALSE },
4329 { CMD, C_RETR, G_READ, xfer_retr, TRUE, FALSE, CL_READ },
4330 { POST_CMD,C_RETR, G_NONE, xfer_post_retr,FALSE, FALSE },
4331 { LOG_CMD, C_RETR, G_NONE, xfer_log_retr, FALSE, FALSE },
4332 { LOG_CMD_ERR, C_RETR,G_NONE, xfer_err_cleanup, FALSE, FALSE },
4333 { PRE_CMD, C_STOR, G_WRITE, xfer_pre_stor, TRUE, FALSE },
4334 { CMD, C_STOR, G_WRITE, xfer_stor, TRUE, FALSE, CL_WRITE },
4335 { POST_CMD,C_STOR, G_NONE, xfer_post_stor,FALSE, FALSE },
4336 { LOG_CMD, C_STOR, G_NONE, xfer_log_stor, FALSE, FALSE },
4337 { LOG_CMD_ERR, C_STOR,G_NONE, xfer_err_cleanup, FALSE, FALSE },
4338 { PRE_CMD, C_STOU, G_WRITE, xfer_pre_stou, TRUE, FALSE },
4339 { CMD, C_STOU, G_WRITE, xfer_stor, TRUE, FALSE, CL_WRITE },
4340 { POST_CMD,C_STOU, G_WRITE, xfer_post_stou,FALSE, FALSE },
4341 { LOG_CMD, C_STOU, G_NONE, xfer_log_stor, FALSE, FALSE },
4342 { LOG_CMD_ERR, C_STOU,G_NONE, xfer_err_cleanup, FALSE, FALSE },
4343 { PRE_CMD, C_APPE, G_WRITE, xfer_pre_appe, TRUE, FALSE },
4344 { CMD, C_APPE, G_WRITE, xfer_stor, TRUE, FALSE, CL_WRITE },
4345 { POST_CMD,C_APPE, G_NONE, xfer_post_stor,FALSE, FALSE },
4346 { LOG_CMD, C_APPE, G_NONE, xfer_log_stor, FALSE, FALSE },
4347 { LOG_CMD_ERR, C_APPE,G_NONE, xfer_err_cleanup, FALSE, FALSE },
4348 { CMD, C_ABOR, G_NONE, xfer_abor, TRUE, TRUE, CL_MISC },
4349 { LOG_CMD, C_ABOR, G_NONE, xfer_log_abor, TRUE, TRUE, CL_MISC },
4350 { CMD, C_REST, G_NONE, xfer_rest, TRUE, FALSE, CL_MISC },
4351 { CMD, C_RANG, G_NONE, xfer_rang, TRUE, FALSE, CL_MISC },
4352 { POST_CMD,C_PROT, G_NONE, xfer_post_prot, FALSE, FALSE },
4353 { POST_CMD,C_PASS, G_NONE, xfer_post_pass, FALSE, FALSE },
4354 { 0, NULL }
4355 };
4356
4357 module xfer_module = {
4358 NULL, NULL,
4359
4360 /* Module API version */
4361 0x20,
4362
4363 /* Module name */
4364 "xfer",
4365
4366 /* Module configuration directive table */
4367 xfer_conftab,
4368
4369 /* Module command handler table */
4370 xfer_cmdtab,
4371
4372 /* Module authentication handler table */
4373 NULL,
4374
4375 /* Module initialization function */
4376 xfer_init,
4377
4378 /* Session initialization function */
4379 xfer_sess_init
4380 };
4381