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