Coverage Report

Created: 2026-08-15 07:23

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/src/proftpd/modules/mod_core.c
Line
Count
Source
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-2026 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, see <https://www.gnu.org/licenses/>.
19
 *
20
 * As a special exemption, Public Flood Software/MacGyver aka Habeeb J. Dihu
21
 * and other respective copyright holders give permission to link this program
22
 * with OpenSSL, and distribute the resulting executable, without including
23
 * the source code for OpenSSL in the source distribution.
24
 */
25
26
/* Core FTPD module */
27
28
#include "conf.h"
29
#include "privs.h"
30
#include "error.h"
31
32
#include <ctype.h>
33
34
extern module *loaded_modules;
35
extern module site_module;
36
extern xaset_t *server_list;
37
38
/* From src/main.c */
39
extern unsigned long max_connects;
40
extern unsigned int max_connect_interval;
41
42
/* From modules/mod_site.c */
43
extern modret_t *site_dispatch(cmd_rec*);
44
45
/* For bytes-retrieving directives */
46
#define PR_BYTES_BAD_UNITS  -1
47
#define PR_BYTES_BAD_FORMAT -2
48
49
/* Maximum number of parameters for OPTS commands (see Bug#3870). */
50
0
#define PR_OPTS_MAX_PARAM_COUNT   8
51
52
module core_module;
53
char AddressCollisionCheck = TRUE;
54
55
static int core_scrub_timer_id = -1;
56
static pr_fh_t *displayquit_fh = NULL;
57
58
#ifdef PR_USE_TRACE
59
static const char *trace_log = NULL;
60
#endif /* PR_USE_TRACE */
61
62
/* Necessary prototypes. */
63
static void core_chroot_ev(const void *, void *);
64
static void core_exit_ev(const void *, void *);
65
static int core_sess_init(void);
66
static void reset_server_auth_order(void);
67
68
/* These are for handling any configured MaxCommandRate. */
69
static unsigned long core_cmd_count = 0UL;
70
static unsigned long core_max_cmds = 0UL;
71
static unsigned int core_max_cmd_interval = 1;
72
static time_t core_max_cmd_ts = 0;
73
74
0
static unsigned long core_exceeded_cmd_rate(cmd_rec *cmd) {
75
0
  unsigned long res = 0;
76
0
  long over = 0;
77
0
  time_t now;
78
79
0
  if (core_max_cmds == 0) {
80
0
    return 0;
81
0
  }
82
83
0
  core_cmd_count++;
84
85
0
  over = core_cmd_count - core_max_cmds;
86
0
  if (over > 0) {
87
    /* Determine the delay, in ms.
88
     *
89
     * The value for this delay must be a value which will cause the command
90
     * rate to not be exceeded.  For example, if the config is:
91
     *
92
     *  MaxCommandRate 200 1
93
     *
94
     * it means a maximum of 200 commands a sec.  This works out to a command
95
     * every 5 ms, maximum:
96
     *
97
     *  1 sec * 1000 ms / 200 cmds per sec = 5 ms
98
     *
99
     * That 5 ms, then, would be the delay to use. For each command over the
100
     * maximum number of commands per interval, we add this delay factor.
101
     * This means that the more over the limit the session is, the longer the
102
     * delay.
103
     */
104
105
0
    res = (unsigned long) (((core_max_cmd_interval * 1000) / core_max_cmds) * over);
106
0
  }
107
108
0
  now = time(NULL);
109
0
  if (core_max_cmd_ts > 0) {
110
0
    if ((now - core_max_cmd_ts) > core_max_cmd_interval) {
111
      /* If it's been longer than the MaxCommandRate interval, reset the
112
       * command counter.
113
       */
114
0
      core_cmd_count = 0;
115
0
      core_max_cmd_ts = now;
116
0
    }
117
118
0
  } else {
119
0
    core_max_cmd_ts = now;
120
0
  }
121
122
0
  return res;
123
0
}
124
125
0
static int core_idle_timeout_cb(CALLBACK_FRAME) {
126
0
  int timeout;
127
128
0
  timeout = pr_data_get_timeout(PR_DATA_TIMEOUT_IDLE);
129
130
  /* We don't want to quit in the middle of a transfer */
131
0
  if (session.sf_flags & SF_XFER) {
132
0
    pr_trace_msg("timer", 4,
133
0
      "TimeoutIdle (%d %s) reached, but data transfer in progress, ignoring",
134
0
      timeout, timeout != 1 ? "seconds" : "second");
135
136
    /* Restart the timer. */
137
0
    return 1;
138
0
  }
139
140
0
  pr_event_generate("core.timeout-idle", NULL);
141
142
0
  pr_response_send_async(R_421,
143
0
    _("Idle timeout (%d seconds): closing control connection"), timeout);
144
145
0
  pr_timer_remove(PR_TIMER_LOGIN, ANY_MODULE);
146
0
  pr_timer_remove(PR_TIMER_NOXFER, ANY_MODULE);
147
148
0
  pr_log_pri(PR_LOG_INFO, "%s", "Client session idle timeout, disconnected");
149
0
  pr_session_disconnect(&core_module, PR_SESS_DISCONNECT_TIMEOUT,
150
0
    "TimeoutIdle");
151
0
  return 0;
152
0
}
153
154
/* If the environment variable being set/unset is locale-related, then we need
155
 * to call setlocale(3) again.
156
 *
157
 * Note: We deliberately set LC_NUMERIC to "C", regardless of configuration.
158
 * Failure to do so will cause problems with formatting of e.g. floats in
159
 * SQL query strings.
160
 */
161
0
static void core_handle_locale_env(const char *env_name) {
162
#if defined(PR_USE_NLS) && defined(HAVE_LOCALE_H)
163
  register unsigned int i;
164
  const char *locale_envs[] = {
165
    "LC_ALL",
166
    "LC_COLLATE",
167
    "LC_CTYPE",
168
    "LC_MESSAGES",
169
    "LC_MONETARY",
170
    "LC_NUMERIC",
171
    "LC_TIME",
172
    "LANG",
173
    NULL
174
  };
175
176
  for (i = 0; locale_envs[i] != NULL; i++) {
177
    if (strcmp(env_name, locale_envs[i]) == 0) {
178
      if (setlocale(LC_ALL, "") != NULL) {
179
        setlocale(LC_NUMERIC, "C");
180
      }
181
    }
182
  }
183
#endif /* PR_USE_NLS and HAVE_LOCALE_H */
184
0
}
185
186
0
static int core_scrub_scoreboard_cb(CALLBACK_FRAME) {
187
  /* Always return 1 when leaving this function, to make sure the timer
188
   * gets called again.
189
   */
190
0
  pr_scoreboard_scrub();
191
192
0
  return 1;
193
0
}
194
195
0
MODRET start_ifdefine(cmd_rec *cmd) {
196
0
  unsigned int ifdefine_ctx_count = 1;
197
0
  unsigned char not_define = FALSE, defined = FALSE;
198
0
  char buf[PR_TUNABLE_BUFFER_SIZE] = {'\0'}, *config_line = NULL, *ptr;
199
200
0
  CHECK_ARGS(cmd, 1);
201
202
0
  ptr = cmd->argv[1];
203
0
  if (*ptr == '!') {
204
0
    not_define = TRUE;
205
0
    ptr++;
206
0
  }
207
208
0
  defined = pr_define_exists(ptr);
209
210
  /* Return now if we don't need to consume the <IfDefine> section
211
   * configuration lines.
212
   */
213
0
  if ((!not_define && defined) ||
214
0
      (not_define && !defined)) {
215
0
    pr_log_debug(DEBUG3, "%s: using '%s%s' section at line %u",
216
0
      (char *) cmd->argv[0], not_define ? "!" : "", (char *) cmd->argv[1],
217
0
      pr_parser_get_lineno());
218
0
    return PR_HANDLED(cmd);
219
0
  }
220
221
0
  pr_log_debug(DEBUG3, "%s: skipping '%s%s' section at line %u",
222
0
    (char *) cmd->argv[0], not_define ? "!" : "", (char *) cmd->argv[1],
223
0
    pr_parser_get_lineno());
224
225
  /* Rather than communicating with parse_config_file() via some global
226
   * variable/flag the need to skip configuration lines, if the requested
227
   * module condition is not TRUE, read in the lines here (effectively
228
   * preventing them from being parsed) up to and including the closing
229
   * directive.
230
   */
231
0
  while (ifdefine_ctx_count && (config_line = pr_parser_read_line(buf,
232
0
      sizeof(buf))) != NULL) {
233
0
    pr_signals_handle();
234
235
0
    if (strncasecmp(config_line, "<IfDefine", 9) == 0) {
236
0
      ifdefine_ctx_count++;
237
238
0
    } else if (strcasecmp(config_line, "</IfDefine>") == 0) {
239
0
      ifdefine_ctx_count--;
240
0
    }
241
0
  }
242
243
  /* If there are still unclosed <IfDefine> sections, signal an error.
244
   */
245
0
  if (ifdefine_ctx_count) {
246
0
    CONF_ERROR(cmd, "unclosed <IfDefine> context");
247
0
  }
248
249
0
  return PR_HANDLED(cmd);
250
0
}
251
252
/* As with Apache, there is no way of cleanly checking whether an
253
 * <IfDefine> section is properly closed.  Extra </IfDefine> directives
254
 * will be silently ignored.
255
 */
256
0
MODRET end_ifdefine(cmd_rec *cmd) {
257
0
  return PR_HANDLED(cmd);
258
0
}
259
260
0
MODRET start_ifmodule(cmd_rec *cmd) {
261
0
  unsigned int ifmodule_ctx_count = 1;
262
0
  unsigned char not_module = FALSE, found_module = FALSE;
263
0
  char buf[PR_TUNABLE_BUFFER_SIZE] = {'\0'}, *config_line = NULL, *ptr;
264
265
0
  CHECK_ARGS(cmd, 1);
266
267
0
  ptr = cmd->argv[1];
268
0
  if (*ptr == '!') {
269
0
    not_module = TRUE;
270
0
    ptr++;
271
0
  }
272
273
0
  found_module = pr_module_exists(ptr);
274
275
  /* Return now if we don't need to consume the <IfModule> section
276
   * configuration lines.
277
   */
278
0
  if ((!not_module && found_module) ||
279
0
      (not_module && !found_module)) {
280
0
    pr_log_debug(DEBUG3, "%s: using '%s%s' section at line %u",
281
0
      (char *) cmd->argv[0], not_module ? "!" : "", (char *) cmd->argv[1],
282
0
      pr_parser_get_lineno());
283
0
    return PR_HANDLED(cmd);
284
0
  }
285
286
0
  pr_log_debug(DEBUG3, "%s: skipping '%s%s' section at line %u",
287
0
    (char *) cmd->argv[0], not_module ? "!" : "", (char *) cmd->argv[1],
288
0
    pr_parser_get_lineno());
289
290
  /* Rather than communicating with parse_config_file() via some global
291
   * variable/flag the need to skip configuration lines, if the requested
292
   * module condition is not TRUE, read in the lines here (effectively
293
   * preventing them from being parsed) up to and including the closing
294
   * directive.
295
   */
296
0
  while (ifmodule_ctx_count && (config_line = pr_parser_read_line(buf,
297
0
      sizeof(buf))) != NULL) {
298
0
    char *bufp;
299
300
0
    pr_signals_handle();
301
302
    /* Advance past any leading whitespace. */
303
0
    for (bufp = config_line; *bufp && PR_ISSPACE(*bufp); bufp++) {
304
0
    }
305
306
0
    if (strncasecmp(bufp, "<IfModule", 9) == 0) {
307
0
      ifmodule_ctx_count++;
308
309
0
    } else if (strcasecmp(bufp, "</IfModule>") == 0) {
310
0
      ifmodule_ctx_count--;
311
0
    }
312
0
  }
313
314
  /* If there are still unclosed <IfModule> sections, signal an error. */
315
0
  if (ifmodule_ctx_count) {
316
0
    CONF_ERROR(cmd, "unclosed <IfModule> context");
317
0
  }
318
319
0
  return PR_HANDLED(cmd);
320
0
}
321
322
/* As with Apache, there is no way of cleanly checking whether an
323
 * <IfModule> section is properly closed.  Extra </IfModule> directives
324
 * will be silently ignored.
325
 */
326
0
MODRET end_ifmodule(cmd_rec *cmd) {
327
0
  if (cmd->argc != 1) {
328
0
    CONF_ERROR(cmd, pstrcat(cmd->tmp_pool, "wrong number of parameters: ",
329
0
      cmd->arg, NULL));
330
0
  }
331
332
0
  return PR_HANDLED(cmd);
333
0
}
334
335
/* Syntax: Define parameter
336
 *
337
 * Configuration file equivalent of the -D command-line option for
338
 * specifying an <IfDefine> value.
339
 *
340
 * It is suggested the RLimitMemory (a good idea to use anyway) be
341
 * used if this directive is present, to prevent Defines was being
342
 * used by a malicious local user in a .ftpaccess file.
343
 */
344
0
MODRET set_define(cmd_rec *cmd) {
345
346
  /* Make sure there's at least one parameter; any others are ignored */
347
0
  CHECK_ARGS(cmd, 1);
348
349
  /* This directive can occur in any context, so no need for the
350
   * CHECK_CONF macro.
351
   */
352
353
0
  pr_define_add(cmd->argv[1], FALSE);
354
0
  return PR_HANDLED(cmd);
355
0
}
356
357
/* usage: Include path|pattern */
358
0
MODRET set_include(cmd_rec *cmd) {
359
0
  int allowed_ctxs, parent_ctx, res, xerrno;
360
361
0
  CHECK_ARGS(cmd, 1);
362
363
  /* If we are not currently in a .ftpaccess context, then we allow Include
364
   * in a <Limit> section.  Otherwise, a .ftpaccess file could contain a
365
   * <Limit>, and that <Limit> could include e.g. itself, leading to a loop.
366
   */
367
368
0
  allowed_ctxs = CONF_ROOT|CONF_VIRTUAL|CONF_ANON|CONF_GLOBAL|CONF_DIR;
369
370
0
  parent_ctx = CONF_ROOT;
371
0
  if (cmd->config != NULL &&
372
0
      cmd->config->parent != NULL) {
373
0
    parent_ctx = cmd->config->parent->config_type;
374
0
  }
375
376
0
  if (parent_ctx != CONF_DYNDIR) {
377
0
    allowed_ctxs |= CONF_LIMIT;
378
0
  }
379
380
0
  CHECK_CONF(cmd, allowed_ctxs);
381
382
  /* Make sure the given path is a valid path. */
383
384
0
  PRIVS_ROOT
385
0
  res = pr_fs_valid_path(cmd->argv[1]);
386
0
  PRIVS_RELINQUISH
387
388
0
  if (res < 0) {
389
0
    CONF_ERROR(cmd, pstrcat(cmd->tmp_pool,
390
0
      "unable to use path for configuration file '", cmd->argv[1], "'", NULL));
391
0
  }
392
393
0
  PRIVS_ROOT
394
0
  res = parse_config_path(cmd->tmp_pool, cmd->argv[1]);
395
0
  xerrno = errno;
396
0
  PRIVS_RELINQUISH
397
398
0
  if (res < 0) {
399
0
    if (xerrno != EINVAL) {
400
0
      pr_log_pri(PR_LOG_WARNING, "warning: unable to include '%s': %s",
401
0
        (char *) cmd->argv[1], strerror(xerrno));
402
403
0
    } else {
404
0
      CONF_ERROR(cmd, pstrcat(cmd->tmp_pool, "error including '",
405
0
        (char *) cmd->argv[1], "': ", strerror(xerrno), NULL));
406
0
    }
407
0
  }
408
409
0
  return PR_HANDLED(cmd);
410
0
}
411
412
/* usage: IncludeOptions opt1 ... */
413
0
MODRET set_includeoptions(cmd_rec *cmd) {
414
0
  register unsigned int i;
415
0
  unsigned long opts = 0UL;
416
417
0
  if (cmd->argc-1 == 0) {
418
0
    CONF_ERROR(cmd, "wrong number of parameters");
419
0
  }
420
421
0
  CHECK_CONF(cmd, CONF_ROOT);
422
423
0
  for (i = 1; i < cmd->argc; i++) {
424
0
    if (strcmp(cmd->argv[i], "AllowSymlinks") == 0) {
425
0
      opts |= PR_PARSER_INCLUDE_OPT_ALLOW_SYMLINKS;
426
427
0
    } else if (strcmp(cmd->argv[i], "IgnoreTempFiles") == 0) {
428
0
      opts |= PR_PARSER_INCLUDE_OPT_IGNORE_TMP_FILES;
429
430
0
    } else if (strcmp(cmd->argv[i], "IgnoreWildcards") == 0) {
431
0
      opts |= PR_PARSER_INCLUDE_OPT_IGNORE_WILDCARDS;
432
433
0
    } else {
434
0
      CONF_ERROR(cmd, pstrcat(cmd->tmp_pool, "unknown IncludeOption '",
435
0
        cmd->argv[i], "'", NULL));
436
0
    }
437
0
  }
438
439
0
  (void) pr_parser_set_include_opts(opts);
440
0
  return PR_HANDLED(cmd);
441
0
}
442
443
0
MODRET set_debuglevel(cmd_rec *cmd) {
444
0
  config_rec *c = NULL;
445
0
  int debuglevel = -1;
446
0
  char *endp = NULL;
447
448
0
  CHECK_ARGS(cmd, 1);
449
0
  CHECK_CONF(cmd, CONF_ROOT|CONF_VIRTUAL|CONF_GLOBAL);
450
451
  /* Make sure the parameter is a valid number. */
452
0
  debuglevel = strtol(cmd->argv[1], &endp, 10);
453
454
0
  if (endp && *endp)
455
0
    CONF_ERROR(cmd, "not a valid number");
456
457
  /* Make sure the number is within the valid debug level range. */
458
0
  if (debuglevel < 0 || debuglevel > 10)
459
0
    CONF_ERROR(cmd, "invalid debug level configured");
460
461
0
  c = add_config_param(cmd->argv[0], 1, NULL);
462
0
  c->argv[0] = pcalloc(c->pool, sizeof(int));
463
0
  *((int *) c->argv[0]) = debuglevel;
464
465
0
  return PR_HANDLED(cmd);
466
0
}
467
468
0
MODRET set_defaultaddress(cmd_rec *cmd) {
469
0
  const char *name, *main_ipstr;
470
0
  const pr_netaddr_t *main_addr = NULL;
471
0
  array_header *addrs = NULL;
472
0
  unsigned int addr_flags = PR_NETADDR_GET_ADDR_FL_INCL_DEVICE;
473
474
0
  if (cmd->argc-1 < 1) {
475
0
    CONF_ERROR(cmd, "wrong number of parameters");
476
0
  }
477
478
0
  CHECK_CONF(cmd, CONF_ROOT);
479
480
0
  name = cmd->argv[1];
481
0
  main_addr = pr_netaddr_get_addr2(main_server->pool, name, &addrs, addr_flags);
482
0
  if (main_addr == NULL) {
483
0
    CONF_ERROR(cmd, pstrcat(cmd->tmp_pool, "unable to resolve '", name, "'",
484
0
      NULL));
485
0
  }
486
487
  /* If the given name is a DNS name, automatically add a ServerAlias
488
   * directive.
489
   */
490
0
  if (pr_netaddr_is_v4(name) == FALSE &&
491
0
      pr_netaddr_is_v6(name) == FALSE) {
492
0
    add_config_param_str("ServerAlias", 1, name);
493
0
  }
494
495
0
  main_server->ServerAddress = main_ipstr = pr_netaddr_get_ipstr(main_addr);
496
0
  main_server->addr = main_addr;
497
498
0
  if (addrs != NULL) {
499
0
    register unsigned int i;
500
0
    pr_netaddr_t **elts = addrs->elts;
501
502
    /* For every additional address, implicitly add a bind record. */
503
0
    for (i = 0; i < addrs->nelts; i++) {
504
0
      const char *ipstr;
505
506
0
      ipstr = pr_netaddr_get_ipstr(elts[i]);
507
508
      /* Skip duplicate addresses. */
509
0
      if (strcmp(main_ipstr, ipstr) == 0) {
510
0
        continue;
511
0
      }
512
513
0
#if defined(PR_USE_IPV6)
514
0
      if (pr_netaddr_use_ipv6()) {
515
0
        char *ipbuf;
516
517
0
        ipbuf = pcalloc(cmd->tmp_pool, INET6_ADDRSTRLEN + 1);
518
0
        if (pr_netaddr_get_family(elts[i]) == AF_INET) {
519
          /* Create the bind record using the IPv4-mapped IPv6 version of
520
           * this address.
521
           */
522
0
          pr_snprintf(ipbuf, INET6_ADDRSTRLEN, "::ffff:%s", ipstr);
523
0
          ipstr = ipbuf;
524
0
        }
525
0
      }
526
0
#endif /* PR_USE_IPV6 */
527
528
0
      add_config_param_str("_bind_", 1, ipstr);
529
0
    }
530
0
  }
531
532
  /* Handle multiple addresses in a DefaultAddress directive.  We do
533
   * this by adding bind directives to the server_rec created for the
534
   * first address.
535
   */
536
0
  if (cmd->argc-1 > 1) {
537
0
    register unsigned int i;
538
0
    char *addrs_str = (char *) pr_netaddr_get_ipstr(main_addr);
539
540
0
    for (i = 2; i < cmd->argc; i++) {
541
0
      const char *addr_ipstr;
542
0
      const pr_netaddr_t *addr;
543
0
      addrs = NULL;
544
545
0
      addr = pr_netaddr_get_addr2(cmd->tmp_pool, cmd->argv[i], &addrs,
546
0
        addr_flags|PR_NETADDR_GET_ADDR_FL_EXCL_CACHE);
547
0
      if (addr == NULL) {
548
0
        CONF_ERROR(cmd, pstrcat(cmd->tmp_pool, "error resolving '",
549
0
          cmd->argv[i], "': ", strerror(errno), NULL));
550
0
      }
551
552
0
      addr_ipstr = pr_netaddr_get_ipstr(addr);
553
0
      add_config_param_str("_bind_", 1, addr_ipstr);
554
555
      /* If the given name is a DNS name, automatically add a ServerAlias
556
       * directive.
557
       */
558
0
      if (pr_netaddr_is_v4(cmd->argv[i]) == FALSE &&
559
0
          pr_netaddr_is_v6(cmd->argv[i]) == FALSE) {
560
0
        add_config_param_str("ServerAlias", 1, cmd->argv[i]);
561
0
      }
562
563
0
      addrs_str = pstrcat(cmd->tmp_pool, addrs_str, ", ", addr_ipstr, NULL);
564
565
0
      if (addrs != NULL) {
566
0
        register unsigned int j;
567
0
        pr_netaddr_t **elts = addrs->elts;
568
569
        /* For every additional address, implicitly add a bind record. */
570
0
        for (j = 0; j < addrs->nelts; j++) {
571
0
          const char *ipstr;
572
573
0
          ipstr = pr_netaddr_get_ipstr(elts[j]);
574
575
          /* Skip duplicate addresses. */
576
0
          if (strcmp(addr_ipstr, ipstr) == 0) {
577
0
            continue;
578
0
          }
579
580
0
          add_config_param_str("_bind_", 1, ipstr);
581
0
        }
582
0
      }
583
0
    }
584
585
0
    pr_log_debug(DEBUG3, "setting default addresses to %s", addrs_str);
586
587
0
  } else {
588
0
    pr_log_debug(DEBUG3, "setting default address to %s", main_ipstr);
589
0
  }
590
591
0
  return PR_HANDLED(cmd);
592
0
}
593
594
0
MODRET set_servername(cmd_rec *cmd) {
595
0
  server_rec *s = cmd->server;
596
597
0
  CHECK_ARGS(cmd, 1);
598
0
  CHECK_CONF(cmd, CONF_ROOT|CONF_VIRTUAL);
599
600
0
  s->ServerName = pstrdup(s->pool, cmd->argv[1]);
601
0
  return PR_HANDLED(cmd);
602
0
}
603
604
0
MODRET set_servertype(cmd_rec *cmd) {
605
0
  CHECK_ARGS(cmd, 1);
606
0
  CHECK_CONF(cmd, CONF_ROOT);
607
608
0
  if (strcasecmp(cmd->argv[1], "inetd") == 0) {
609
0
    ServerType = SERVER_INETD;
610
611
0
  } else if (strcasecmp(cmd->argv[1], "standalone") == 0) {
612
0
    ServerType = SERVER_STANDALONE;
613
614
0
  } else {
615
0
    CONF_ERROR(cmd,"type must be either 'inetd' or 'standalone'");
616
0
  }
617
618
0
  return PR_HANDLED(cmd);
619
0
}
620
621
0
MODRET set_setenv(cmd_rec *cmd) {
622
0
  int ctxt_type;
623
624
0
  CHECK_ARGS(cmd, 2);
625
0
  CHECK_CONF(cmd, CONF_ROOT|CONF_VIRTUAL|CONF_GLOBAL);
626
627
0
  add_config_param_str(cmd->argv[0], 2, cmd->argv[1], cmd->argv[2]);
628
629
  /* In addition, if this is the "server config" context, set the
630
   * environment variable now.  If there was a <Daemon> context, that would
631
   * be a more appropriate place for configuring parse-time environ
632
   * variables.
633
   */
634
0
  ctxt_type = (cmd->config && cmd->config->config_type != CONF_PARAM ?
635
0
     cmd->config->config_type : cmd->server->config_type ?
636
0
     cmd->server->config_type : CONF_ROOT);
637
638
0
  if (ctxt_type == CONF_ROOT) {
639
0
    if (pr_env_set(cmd->server->pool, cmd->argv[1], cmd->argv[2]) < 0) {
640
0
      pr_log_debug(DEBUG1, "%s: unable to set environment variable '%s': %s",
641
0
        (char *) cmd->argv[0], (char *) cmd->argv[1], strerror(errno));
642
643
0
    } else {
644
0
      core_handle_locale_env(cmd->argv[1]);
645
0
    }
646
0
  }
647
648
0
  return PR_HANDLED(cmd);
649
0
}
650
651
0
MODRET add_transferlog(cmd_rec *cmd) {
652
0
  config_rec *c = NULL;
653
654
0
  CHECK_ARGS(cmd, 1);
655
0
  CHECK_CONF(cmd, CONF_ROOT|CONF_VIRTUAL|CONF_GLOBAL|CONF_ANON);
656
657
0
  c = add_config_param_str(cmd->argv[0], 1, cmd->argv[1]);
658
0
  c->flags |= CF_MERGEDOWN;
659
660
0
  return PR_HANDLED(cmd);
661
0
}
662
663
0
MODRET set_serveradmin(cmd_rec *cmd) {
664
0
  server_rec *s = cmd->server;
665
666
0
  CHECK_ARGS(cmd, 1);
667
0
  CHECK_CONF(cmd, CONF_ROOT|CONF_VIRTUAL);
668
669
0
  s->ServerAdmin = pstrdup(s->pool, cmd->argv[1]);
670
0
  return PR_HANDLED(cmd);
671
0
}
672
673
/* usage: UseIPv6 on|off */
674
0
MODRET set_useipv6(cmd_rec *cmd) {
675
0
  int use_ipv6 = -1;
676
677
0
  CHECK_ARGS(cmd, 1);
678
0
  CHECK_CONF(cmd, CONF_ROOT);
679
680
0
  use_ipv6 = get_boolean(cmd, 1);
681
0
  if (use_ipv6 == -1) {
682
0
    CONF_ERROR(cmd, "expected Boolean parameter");
683
0
  }
684
685
0
  if (use_ipv6 == FALSE) {
686
0
    pr_log_debug(DEBUG2, "disabling runtime support for IPv6 connections");
687
0
    pr_netaddr_disable_ipv6();
688
689
0
  } else {
690
0
#if defined(PR_USE_IPV6)
691
0
    pr_netaddr_enable_ipv6();
692
# else
693
  CONF_ERROR(cmd,
694
    "Use of 'UseIPv6 on' configurations requires IPv6 support (--enable-ipv6)");
695
#endif /* PR_USE_IPV6 */
696
0
  }
697
698
0
  return PR_HANDLED(cmd);
699
0
}
700
701
0
MODRET set_usereversedns(cmd_rec *cmd) {
702
0
  int use_reverse_dns = -1;
703
704
0
  CHECK_ARGS(cmd, 1);
705
0
  CHECK_CONF(cmd, CONF_ROOT);
706
707
0
  use_reverse_dns = get_boolean(cmd, 1);
708
0
  if (use_reverse_dns == -1) {
709
0
    CONF_ERROR(cmd, "expected Boolean parameter");
710
0
  }
711
712
0
  ServerUseReverseDNS = use_reverse_dns;
713
0
  pr_netaddr_set_reverse_dns(use_reverse_dns);
714
715
0
  return PR_HANDLED(cmd);
716
0
}
717
718
0
MODRET set_satisfy(cmd_rec *cmd) {
719
0
  int satisfy = -1;
720
721
0
  CHECK_ARGS(cmd, 1);
722
0
  CHECK_CONF(cmd, CONF_CLASS);
723
724
0
  if (strcasecmp(cmd->argv[1], "any") == 0) {
725
0
    satisfy = PR_CLASS_SATISFY_ANY;
726
727
0
  } else if (strcasecmp(cmd->argv[1], "all") == 0) {
728
0
    satisfy = PR_CLASS_SATISFY_ALL;
729
730
0
  } else {
731
0
    CONF_ERROR(cmd, pstrcat(cmd->tmp_pool, "invalid parameter: '",
732
0
      cmd->argv[1], "'", NULL));
733
0
  }
734
735
0
  if (pr_class_set_satisfy(satisfy) < 0) {
736
0
    CONF_ERROR(cmd, pstrcat(cmd->tmp_pool, "error setting Satisfy: ",
737
0
      strerror(errno), NULL));
738
0
  }
739
740
0
  return PR_HANDLED(cmd);
741
0
}
742
743
/* usage: ScoreboardFile path */
744
0
MODRET set_scoreboardfile(cmd_rec *cmd) {
745
0
  CHECK_ARGS(cmd, 1);
746
0
  CHECK_CONF(cmd, CONF_ROOT);
747
748
0
  if (pr_set_scoreboard(cmd->argv[1]) < 0) {
749
0
    CONF_ERROR(cmd, pstrcat(cmd->tmp_pool, "unable to use '", cmd->argv[1],
750
0
      "': ", strerror(errno), NULL));
751
0
  }
752
753
0
  return PR_HANDLED(cmd);
754
0
}
755
756
/* usage: ScoreboardMutex path */
757
0
MODRET set_scoreboardmutex(cmd_rec *cmd) {
758
0
  CHECK_ARGS(cmd, 1);
759
0
  CHECK_CONF(cmd, CONF_ROOT);
760
761
0
  if (pr_set_scoreboard_mutex(cmd->argv[1]) < 0) {
762
0
    CONF_ERROR(cmd, pstrcat(cmd->tmp_pool, "unable to use '", cmd->argv[1],
763
0
      "': ", strerror(errno), NULL));
764
0
  }
765
766
0
  return PR_HANDLED(cmd);
767
0
}
768
769
/* usage: ScoreboardOptions opt1 ... */
770
0
MODRET set_scoreboardoptions(cmd_rec *cmd) {
771
0
  register unsigned int i = 0;
772
0
  config_rec *c = NULL;
773
0
  unsigned long opts = 0UL;
774
775
0
  if (cmd->argc-1 == 0) {
776
0
    CONF_ERROR(cmd, "wrong number of parameters");
777
0
  }
778
779
0
  CHECK_CONF(cmd, CONF_ROOT|CONF_VIRTUAL|CONF_GLOBAL);
780
781
0
  c = add_config_param(cmd->argv[0], 1, NULL);
782
783
0
  for (i = 1; i < cmd->argc; i++) {
784
0
    if (strcasecmp(cmd->argv[i], "AllowMissingEntry") == 0) {
785
0
      opts |= PR_SCOREBOARD_OPT_ALLOW_MISSING_ENTRY;
786
787
0
    } else {
788
0
      CONF_ERROR(cmd, pstrcat(cmd->tmp_pool, ": unknown ScoreboardOption '",
789
0
        cmd->argv[i], "'", NULL));
790
0
    }
791
0
  }
792
793
0
  c->argv[0] = pcalloc(c->pool, sizeof(unsigned long));
794
0
  *((unsigned long *) c->argv[0]) = opts;
795
796
0
  return PR_HANDLED(cmd);
797
0
}
798
799
/* usage: ScoreboardScrub "on"|"off"|secs */
800
0
MODRET set_scoreboardscrub(cmd_rec *cmd) {
801
0
  int do_scrub = -1, nsecs = 0;
802
0
  config_rec *c;
803
804
0
  CHECK_ARGS(cmd, 1);
805
0
  CHECK_CONF(cmd, CONF_ROOT);
806
807
0
  do_scrub = get_boolean(cmd, 1);
808
0
  if (do_scrub == -1) {
809
    /* If this is the case, try handling the parameter as the number of
810
     * seconds, as the scrub frequency.
811
     */
812
0
    nsecs = atoi(cmd->argv[1]);
813
0
    if (nsecs <= 0) {
814
0
      CONF_ERROR(cmd, "number must be greater than zero");
815
0
    }
816
0
  }
817
818
0
  if (nsecs > 0) {
819
0
    c = add_config_param(cmd->argv[0], 2, NULL, NULL);
820
0
    c->argv[0] = pcalloc(c->pool, sizeof(int));
821
0
    *((int *) c->argv[0]) = TRUE;
822
0
    c->argv[1] = pcalloc(c->pool, sizeof(int));
823
0
    *((int *) c->argv[1]) = nsecs;
824
825
0
  } else {
826
0
    c = add_config_param(cmd->argv[0], 1, NULL);
827
0
    c->argv[0] = pcalloc(c->pool, sizeof(int));
828
0
    *((int *) c->argv[0]) = do_scrub;
829
0
  }
830
831
0
  return PR_HANDLED(cmd);
832
0
}
833
834
0
MODRET set_port(cmd_rec *cmd) {
835
0
  config_rec *c;
836
0
  int ctx, port;
837
838
0
  CHECK_ARGS(cmd, 1);
839
0
  CHECK_CONF(cmd, CONF_ROOT|CONF_VIRTUAL|CONF_GLOBAL);
840
841
0
  port = atoi(cmd->argv[1]);
842
0
  if (port < 0 ||
843
0
      port > 65535) {
844
0
    CONF_ERROR(cmd, "value must be between 0 and 65535");
845
0
  }
846
847
0
  c = add_config_param(cmd->argv[0], 1, NULL);
848
0
  c->argv[0] = pcalloc(c->pool, sizeof(int));
849
0
  *((int *) c->argv[0]) = port;
850
851
  /* For backward compatibility, if we are in the "server config" or the
852
   * <VirtualHost> context, set the port number on the vhost now, too.
853
   */
854
0
  ctx = (cmd->config && cmd->config->config_type != CONF_PARAM ?
855
0
    cmd->config->config_type : cmd->server->config_type ?
856
0
    cmd->server->config_type : CONF_ROOT);
857
0
  if (ctx == CONF_ROOT ||
858
0
      ctx == CONF_VIRTUAL) {
859
0
    cmd->server->ServerPort = port;
860
0
  }
861
862
0
  return PR_HANDLED(cmd);
863
0
}
864
865
0
MODRET set_pidfile(cmd_rec *cmd) {
866
0
  CHECK_ARGS(cmd, 1);
867
0
  CHECK_CONF(cmd, CONF_ROOT);
868
869
0
  if (pr_pidfile_set(cmd->argv[1]) < 0) {
870
0
    CONF_ERROR(cmd, pstrcat(cmd->tmp_pool, "unable to set PidFile '",
871
0
      cmd->argv[1], "': ", strerror(errno), NULL));
872
0
  }
873
874
0
  return PR_HANDLED(cmd);
875
0
}
876
877
0
MODRET set_sysloglevel(cmd_rec *cmd) {
878
0
  config_rec *c = NULL;
879
0
  int level = 0;
880
881
0
  CHECK_ARGS(cmd, 1);
882
0
  CHECK_CONF(cmd, CONF_ROOT|CONF_VIRTUAL|CONF_GLOBAL);
883
884
0
  level = pr_log_str2sysloglevel(cmd->argv[1]);
885
0
  if (level < 0) {
886
0
    CONF_ERROR(cmd, "SyslogLevel requires level keyword: one of "
887
0
      "emerg/alert/crit/error/warn/notice/info/debug");
888
0
  }
889
890
0
  c = add_config_param(cmd->argv[0], 1, NULL);
891
0
  c->argv[0] = pcalloc(c->pool, sizeof(int));
892
0
  *((int *) c->argv[0]) = level;
893
894
0
  return PR_HANDLED(cmd);
895
0
}
896
897
/* usage: ServerAlias hostname [hostname ...] */
898
0
MODRET set_serveralias(cmd_rec *cmd) {
899
0
  register unsigned int i;
900
901
0
  if (cmd->argc < 2) {
902
0
    CONF_ERROR(cmd, "wrong number of parameters");
903
0
  }
904
905
0
  CHECK_CONF(cmd, CONF_ROOT|CONF_VIRTUAL);
906
907
0
  for (i = 1; i < cmd->argc; i++) {
908
0
    add_config_param_str(cmd->argv[0], 1, cmd->argv[i]);
909
0
  }
910
911
0
  return PR_HANDLED(cmd);
912
0
}
913
914
/* usage: ServerIdent off|on [name] */
915
0
MODRET set_serverident(cmd_rec *cmd) {
916
0
  int ident_on = -1;
917
0
  config_rec *c = NULL;
918
919
0
  if (cmd->argc < 2 ||
920
0
      cmd->argc > 3) {
921
0
    CONF_ERROR(cmd, "wrong number of parameters");
922
0
  }
923
924
0
  CHECK_CONF(cmd, CONF_ROOT|CONF_VIRTUAL|CONF_GLOBAL);
925
926
0
  ident_on = get_boolean(cmd, 1);
927
0
  if (ident_on == -1) {
928
0
    CONF_ERROR(cmd, "expected Boolean parameter");
929
0
  }
930
931
0
  if (ident_on == TRUE &&
932
0
      cmd->argc == 3) {
933
0
    c = add_config_param(cmd->argv[0], 2, NULL, NULL);
934
0
    c->argv[0] = pcalloc(c->pool, sizeof(unsigned char));
935
0
    *((unsigned char *) c->argv[0]) = ident_on;
936
0
    c->argv[1] = pstrdup(c->pool, cmd->argv[2]);
937
938
0
  } else {
939
0
    c = add_config_param(cmd->argv[0], 1, NULL);
940
0
    c->argv[0] = pcalloc(c->pool, sizeof(unsigned char));
941
0
    *((unsigned char *) c->argv[0]) = ident_on;
942
0
  }
943
944
0
  return PR_HANDLED(cmd);
945
0
}
946
947
0
MODRET set_defaultserver(cmd_rec *cmd) {
948
0
  int default_server = -1;
949
0
  server_rec *s = NULL;
950
0
  config_rec *c = NULL;
951
952
0
  CHECK_ARGS(cmd, 1);
953
0
  CHECK_CONF(cmd, CONF_ROOT|CONF_VIRTUAL);
954
955
0
  default_server = get_boolean(cmd, 1);
956
0
  if (default_server == -1) {
957
0
    CONF_ERROR(cmd, "expected Boolean parameter");
958
0
  }
959
960
0
  if (default_server == FALSE) {
961
0
    return PR_HANDLED(cmd);
962
0
  }
963
964
  /* DefaultServer is not allowed if already set somewhere */
965
0
  for (s = (server_rec *) server_list->xas_list; s; s = s->next) {
966
0
    if (find_config(s->conf, CONF_PARAM, cmd->argv[0], FALSE)) {
967
0
      CONF_ERROR(cmd, "DefaultServer has already been set");
968
0
    }
969
0
  }
970
971
0
  c = add_config_param(cmd->argv[0], 1, NULL);
972
0
  c->argv[0] = pcalloc(c->pool, sizeof(unsigned char));
973
0
  *((unsigned char *) c->argv[0]) = default_server;
974
975
0
  return PR_HANDLED(cmd);
976
0
}
977
978
0
MODRET set_masqueradeaddress(cmd_rec *cmd) {
979
0
  config_rec *c = NULL;
980
0
  const char *name;
981
0
  size_t namelen;
982
0
  const pr_netaddr_t *masq_addr = NULL;
983
0
  unsigned int addr_flags = PR_NETADDR_GET_ADDR_FL_INCL_DEVICE;
984
985
0
  CHECK_ARGS(cmd, 1);
986
0
  CHECK_CONF(cmd, CONF_ROOT|CONF_VIRTUAL|CONF_GLOBAL);
987
988
  /* We can only masquerade as one address, so we don't need to know if the
989
   * given name might map to multiple addresses.
990
   */
991
0
  name = cmd->argv[1];
992
0
  namelen = strlen(name);
993
0
  if (namelen == 0) {
994
    /* Guard against empty names here. */
995
0
    CONF_ERROR(cmd, "missing required name parameter");
996
0
  }
997
998
0
  masq_addr = pr_netaddr_get_addr2(cmd->server->pool, name, NULL, addr_flags);
999
0
  if (masq_addr == NULL) {
1000
    /* If the requested name cannot be resolved because it is not known AT
1001
     * THIS TIME, then do not fail to start the server.  We will simply try
1002
     * again later (Bug#4104).
1003
     */
1004
0
    if (errno != ENOENT) {
1005
0
      CONF_ERROR(cmd, pstrcat(cmd->tmp_pool, "unable to resolve '", name, "'",
1006
0
        NULL));
1007
0
    }
1008
0
  }
1009
1010
0
  c = add_config_param(cmd->argv[0], 2, (void *) masq_addr, NULL);
1011
0
  c->argv[1] = pstrdup(c->pool, cmd->argv[1]);
1012
1013
0
  return PR_HANDLED(cmd);
1014
0
}
1015
1016
0
MODRET set_maxinstances(cmd_rec *cmd) {
1017
0
  long max_instances;
1018
0
  char *endp;
1019
1020
0
  CHECK_ARGS(cmd, 1);
1021
0
  CHECK_CONF(cmd, CONF_ROOT);
1022
1023
0
  if (strcasecmp(cmd->argv[1], "none") == 0) {
1024
0
    max_instances = 0UL;
1025
1026
0
  } else {
1027
0
    max_instances = strtol(cmd->argv[1], &endp, 10);
1028
1029
0
    if ((endp && *endp) ||
1030
0
        max_instances < 1) {
1031
0
      CONF_ERROR(cmd, "argument must be 'none' or a number greater than 0");
1032
0
    }
1033
0
  }
1034
1035
0
  ServerMaxInstances = max_instances;
1036
0
  return PR_HANDLED(cmd);
1037
0
}
1038
1039
/* usage: MaxCommandRate rate [interval] */
1040
0
MODRET set_maxcommandrate(cmd_rec *cmd) {
1041
0
  config_rec *c;
1042
0
  long cmd_max = 0L;
1043
0
  unsigned int max_cmd_interval = 1;
1044
0
  char *endp = NULL;
1045
1046
0
  if (cmd->argc-1 < 1 ||
1047
0
      cmd->argc-1 > 2) {
1048
0
    CONF_ERROR(cmd, "wrong number of parameters");
1049
0
  }
1050
1051
0
  CHECK_CONF(cmd, CONF_ROOT|CONF_VIRTUAL|CONF_GLOBAL);
1052
1053
0
  cmd_max = strtol(cmd->argv[1], &endp, 10);
1054
1055
0
  if (endp && *endp) {
1056
0
    CONF_ERROR(cmd, "invalid command rate");
1057
0
  }
1058
1059
0
  if (cmd_max < 0) {
1060
0
    CONF_ERROR(cmd, "command rate must be positive");
1061
0
  }
1062
1063
  /* If the optional interval parameter is given, parse it. */
1064
0
  if (cmd->argc-1 == 2) {
1065
0
    max_cmd_interval = atoi(cmd->argv[2]);
1066
1067
0
    if (max_cmd_interval < 1) {
1068
0
      CONF_ERROR(cmd, pstrcat(cmd->tmp_pool,
1069
0
        "interval must be greater than zero", NULL));
1070
0
    }
1071
0
  }
1072
1073
0
  c = add_config_param(cmd->argv[0], 2, NULL, NULL);
1074
0
  c->argv[0] = palloc(c->pool, sizeof(unsigned long));
1075
0
  *((unsigned long *) c->argv[0]) = cmd_max;
1076
0
  c->argv[1] = palloc(c->pool, sizeof(unsigned int));
1077
0
  *((unsigned int *) c->argv[1]) = max_cmd_interval;
1078
1079
0
  return PR_HANDLED(cmd);
1080
0
}
1081
1082
1083
/* usage: MaxConnectionRate rate [interval] */
1084
0
MODRET set_maxconnrate(cmd_rec *cmd) {
1085
0
  long conn_max = 0L;
1086
0
  char *endp = NULL;
1087
1088
0
  if (cmd->argc-1 < 1 ||
1089
0
      cmd->argc-1 > 2) {
1090
0
    CONF_ERROR(cmd, "wrong number of parameters");
1091
0
  }
1092
0
  CHECK_CONF(cmd, CONF_ROOT);
1093
1094
0
  conn_max = strtol(cmd->argv[1], &endp, 10);
1095
1096
0
  if (endp && *endp) {
1097
0
    CONF_ERROR(cmd, "invalid connection rate");
1098
0
  }
1099
1100
0
  if (conn_max < 0) {
1101
0
    CONF_ERROR(cmd, "connection rate must be positive");
1102
0
  }
1103
1104
0
  max_connects = conn_max;
1105
1106
  /* If the optional interval parameter is given, parse it. */
1107
0
  if (cmd->argc-1 == 2) {
1108
0
    max_connect_interval = atoi(cmd->argv[2]);
1109
1110
0
    if (max_connect_interval < 1) {
1111
0
      CONF_ERROR(cmd, pstrcat(cmd->tmp_pool,
1112
0
        "interval must be greater than zero", NULL));
1113
0
    }
1114
0
  }
1115
1116
0
  return PR_HANDLED(cmd);
1117
0
}
1118
1119
0
MODRET set_timeoutidle(cmd_rec *cmd) {
1120
0
  int timeout = -1;
1121
0
  config_rec *c = NULL;
1122
1123
0
  CHECK_ARGS(cmd, 1);
1124
0
  CHECK_CONF(cmd, CONF_ROOT|CONF_VIRTUAL|CONF_GLOBAL|CONF_ANON);
1125
1126
0
  if (pr_str_get_duration(cmd->argv[1], &timeout) < 0) {
1127
0
    CONF_ERROR(cmd, pstrcat(cmd->tmp_pool, "error parsing timeout value '",
1128
0
      cmd->argv[1], "': ", strerror(errno), NULL));
1129
0
  }
1130
1131
0
  c = add_config_param(cmd->argv[0], 1, NULL);
1132
0
  c->argv[0] = pcalloc(c->pool, sizeof(int));
1133
0
  *((int *) c->argv[0]) = timeout;
1134
0
  c->flags |= CF_MERGEDOWN;
1135
1136
0
  return PR_HANDLED(cmd);
1137
0
}
1138
1139
0
MODRET set_timeoutlinger(cmd_rec *cmd) {
1140
0
  int timeout = -1;
1141
0
  config_rec *c = NULL;
1142
1143
0
  CHECK_ARGS(cmd, 1);
1144
0
  CHECK_CONF(cmd, CONF_ROOT|CONF_VIRTUAL|CONF_GLOBAL);
1145
1146
0
  if (pr_str_get_duration(cmd->argv[1], &timeout) < 0) {
1147
0
    CONF_ERROR(cmd, pstrcat(cmd->tmp_pool, "error parsing timeout value '",
1148
0
      cmd->argv[1], "': ", strerror(errno), NULL));
1149
0
  }
1150
1151
0
  c = add_config_param(cmd->argv[0], 1, NULL);
1152
0
  c->argv[0] = pcalloc(c->pool, sizeof(int));
1153
0
  *((int *) c->argv[0]) = timeout;
1154
1155
0
  return PR_HANDLED(cmd);
1156
0
}
1157
1158
0
MODRET set_socketbindtight(cmd_rec *cmd) {
1159
0
  int bind_tight = -1;
1160
0
  CHECK_ARGS(cmd, 1);
1161
0
  CHECK_CONF(cmd, CONF_ROOT);
1162
1163
0
  bind_tight = get_boolean(cmd, 1);
1164
0
  if (bind_tight == -1) {
1165
0
    CONF_ERROR(cmd, "expected Boolean parameter");
1166
0
  }
1167
1168
0
  SocketBindTight = bind_tight;
1169
0
  return PR_HANDLED(cmd);
1170
0
}
1171
1172
/* NOTE: at some point in the future, SocketBindTight should be folded
1173
 * into this SocketOptions directive handler.
1174
 */
1175
0
MODRET set_socketoptions(cmd_rec *cmd) {
1176
0
  register unsigned int i = 0;
1177
1178
  /* Make sure we have the right number of parameters. */
1179
0
  if ((cmd->argc - 1) % 2 != 0) {
1180
0
   CONF_ERROR(cmd, "bad number of parameters");
1181
0
  }
1182
1183
0
  CHECK_CONF(cmd, CONF_ROOT|CONF_VIRTUAL);
1184
1185
0
  for (i = 1; i < cmd->argc; i++) {
1186
0
    int value = 0;
1187
1188
0
    if (strcasecmp(cmd->argv[i], "maxseg") == 0) {
1189
0
      value = atoi(cmd->argv[++i]);
1190
1191
      /* As per the tcp(7) man page, sizes larger than the interface MTU
1192
       * will be ignored, and will have no effect.
1193
       */
1194
1195
0
      if (value < 0) {
1196
0
        CONF_ERROR(cmd, "maxseg size must be greater than 0");
1197
0
      }
1198
1199
0
      cmd->server->tcp_mss_len = value;
1200
1201
0
    } else if (strcasecmp(cmd->argv[i], "rcvbuf") == 0) {
1202
0
      value = atoi(cmd->argv[++i]);
1203
1204
0
      if (value < 1024) {
1205
0
        CONF_ERROR(cmd, "rcvbuf size must be greater than or equal to 1024");
1206
0
      }
1207
1208
0
      cmd->server->tcp_rcvbuf_len = value;
1209
0
      cmd->server->tcp_rcvbuf_override = TRUE;
1210
1211
0
    } else if (strcasecmp(cmd->argv[i], "sndbuf") == 0) {
1212
0
      value = atoi(cmd->argv[++i]);
1213
1214
0
      if (value < 1024) {
1215
0
        CONF_ERROR(cmd, "sndbuf size must be greater than or equal to 1024");
1216
0
      }
1217
1218
0
      cmd->server->tcp_sndbuf_len = value;
1219
0
      cmd->server->tcp_sndbuf_override = TRUE;
1220
1221
    /* SocketOption keepalive off
1222
     * SocketOption keepalive on
1223
     * SocketOption keepalive 7200:9:75
1224
     */
1225
0
    } else if (strcasecmp(cmd->argv[i], "keepalive") == 0) {
1226
0
      int b;
1227
1228
0
      b = get_boolean(cmd, i+1);
1229
0
      if (b == -1) {
1230
0
#if defined(TCP_KEEPIDLE) || defined(TCP_KEEPCNT) || defined(TCP_KEEPINTVL)
1231
0
        char *keepalive_spec, *ptr, *ptr2;
1232
0
        int idle, count, intvl;
1233
1234
        /* Parse the given keepalive-spec */
1235
0
        keepalive_spec = cmd->argv[i+1];
1236
1237
0
        ptr = strchr(keepalive_spec, ':');
1238
0
        if (ptr == NULL) {
1239
0
          CONF_ERROR(cmd, pstrcat(cmd->tmp_pool,
1240
0
            "badly formatted TCP keepalive spec '", cmd->argv[i+1], "'", NULL));
1241
0
        }
1242
1243
0
        *ptr = '\0';
1244
0
        idle = atoi(keepalive_spec);
1245
1246
0
        keepalive_spec = ptr + 1;
1247
0
        ptr2 = strchr(keepalive_spec, ':');
1248
0
        if (ptr2 == NULL) {
1249
0
          *ptr = ':';
1250
0
          CONF_ERROR(cmd, pstrcat(cmd->tmp_pool,
1251
0
            "badly formatted TCP keepalive spec '", cmd->argv[i+1], "'", NULL));
1252
0
        }
1253
1254
0
        *ptr2 = '\0';
1255
0
        count = atoi(keepalive_spec);
1256
1257
0
        keepalive_spec = ptr2 + 1;
1258
0
        intvl = atoi(keepalive_spec);
1259
1260
0
        if (idle < 1) {
1261
0
          char val_str[33];
1262
1263
0
          memset(val_str, '\0', sizeof(val_str));
1264
0
          pr_snprintf(val_str, sizeof(val_str)-1, "%d", idle);
1265
1266
0
          CONF_ERROR(cmd, pstrcat(cmd->tmp_pool,
1267
0
            "badly formatted TCP keepalive spec: idle time '", val_str,
1268
0
            "' cannot be less than 1", NULL));
1269
0
        }
1270
1271
0
        if (count < 1) {
1272
0
          char val_str[33];
1273
1274
0
          memset(val_str, '\0', sizeof(val_str));
1275
0
          pr_snprintf(val_str, sizeof(val_str)-1, "%d", count);
1276
1277
0
          CONF_ERROR(cmd, pstrcat(cmd->tmp_pool,
1278
0
            "badly formatted TCP keepalive spec: count '", val_str,
1279
0
            "' cannot be less than 1", NULL));
1280
0
        }
1281
1282
0
        if (intvl < 1) {
1283
0
          char val_str[33];
1284
1285
0
          memset(val_str, '\0', sizeof(val_str));
1286
0
          pr_snprintf(val_str, sizeof(val_str)-1, "%d", intvl);
1287
1288
0
          CONF_ERROR(cmd, pstrcat(cmd->tmp_pool,
1289
0
            "badly formatted TCP keepalive spec: interval time '", val_str,
1290
0
            "' cannot be less than 1", NULL));
1291
0
        }
1292
1293
0
        cmd->server->tcp_keepalive->keepalive_enabled = TRUE;
1294
0
        cmd->server->tcp_keepalive->keepalive_idle = idle;
1295
0
        cmd->server->tcp_keepalive->keepalive_count = count;
1296
0
        cmd->server->tcp_keepalive->keepalive_intvl = intvl;
1297
#else
1298
        cmd->server->tcp_keepalive->keepalive_enabled = TRUE;
1299
        pr_log_debug(DEBUG0,
1300
          "%s: platform does not support fine-grained TCP keepalive control, "
1301
          "using \"keepalive on\"", (char *) cmd->argv[0]);
1302
#endif /* No TCP_KEEPIDLE, TCP_KEEPCNT, or TCP_KEEPINTVL */
1303
1304
0
      } else {
1305
0
        cmd->server->tcp_keepalive->keepalive_enabled = b;
1306
0
      }
1307
1308
      /* Don't forget to increment the iterator. */
1309
0
      i++;
1310
1311
0
    } else if (strcasecmp(cmd->argv[i], "reuseport") == 0) {
1312
0
      int b;
1313
1314
0
      b = get_boolean(cmd, i + 1);
1315
0
      if (b == -1) {
1316
0
        CONF_ERROR(cmd, "reuseport must have Boolean parameter");
1317
0
      }
1318
1319
0
      cmd->server->tcp_reuse_port = b;
1320
1321
      /* Don't forget to increment the iterator. */
1322
0
      i++;
1323
1324
0
    } else {
1325
0
      CONF_ERROR(cmd, pstrcat(cmd->tmp_pool, ": unknown socket option: '",
1326
0
        cmd->argv[i], "'", NULL));
1327
0
    }
1328
0
  }
1329
1330
0
  return PR_HANDLED(cmd);
1331
0
}
1332
1333
0
MODRET set_multilinerfc2228(cmd_rec *cmd) {
1334
0
  pr_log_pri(PR_LOG_WARNING,
1335
0
    "the %s directive has been deprecated, and will be removed in a future "
1336
0
    "release.  Please remove it from your config file.", (char *) cmd->argv[0]);
1337
0
  return PR_HANDLED(cmd);
1338
0
}
1339
1340
0
MODRET set_tcpbacklog(cmd_rec *cmd) {
1341
0
  int backlog;
1342
1343
0
  CHECK_ARGS(cmd, 1);
1344
0
  CHECK_CONF(cmd, CONF_ROOT);
1345
1346
0
  backlog = atoi(cmd->argv[1]);
1347
1348
0
  if (backlog < 1) {
1349
0
    CONF_ERROR(cmd, "parameter must be greater than zero");
1350
0
  }
1351
1352
0
  tcpBackLog = backlog;
1353
0
  return PR_HANDLED(cmd);
1354
0
}
1355
1356
0
MODRET set_tcpnodelay(cmd_rec *cmd) {
1357
0
  int ctrl_use_nodelay = -1, data_use_nodelay = -1;
1358
0
  config_rec *c = NULL;
1359
1360
0
  CHECK_CONF(cmd, CONF_ROOT|CONF_VIRTUAL|CONF_GLOBAL);
1361
1362
0
  if (cmd->argc == 2) {
1363
0
    ctrl_use_nodelay = get_boolean(cmd, 1);
1364
0
    if (ctrl_use_nodelay == -1) {
1365
0
      CONF_ERROR(cmd, "expected Boolean parameter");
1366
0
    }
1367
1368
    /* The legacy behavior, for the single parameter configuration, means
1369
     * using Nagle for data transfers.
1370
     */
1371
0
    data_use_nodelay = FALSE;
1372
1373
0
  } else if (cmd->argc == 3) {
1374
0
    if (strcasecmp(cmd->argv[1], "ctrl") == 0 ||
1375
0
        strcasecmp(cmd->argv[1], "control") == 0) {
1376
0
      ctrl_use_nodelay = get_boolean(cmd, 2);
1377
0
      if (ctrl_use_nodelay == -1) {
1378
0
        CONF_ERROR(cmd, "expected Boolean parameter");
1379
0
      }
1380
1381
0
      data_use_nodelay = TRUE;
1382
1383
0
    } else if (strcasecmp(cmd->argv[1], "data") == 0) {
1384
0
      data_use_nodelay = get_boolean(cmd, 2);
1385
0
      if (data_use_nodelay == -1) {
1386
0
        CONF_ERROR(cmd, "expected Boolean parameter");
1387
0
      }
1388
1389
0
      ctrl_use_nodelay = TRUE;
1390
1391
0
    } else {
1392
0
      CONF_ERROR(cmd, pstrcat(cmd->tmp_pool, "unsupported parameter: ",
1393
0
        cmd->argv[1], NULL));
1394
0
    }
1395
1396
0
  } else if (cmd->argc == 5) {
1397
0
    if (strcasecmp(cmd->argv[1], "ctrl") == 0 ||
1398
0
        strcasecmp(cmd->argv[1], "control") == 0) {
1399
0
      ctrl_use_nodelay = get_boolean(cmd, 2);
1400
0
      if (ctrl_use_nodelay == -1) {
1401
0
        CONF_ERROR(cmd, "expected Boolean parameter");
1402
0
      }
1403
1404
0
    } else if (strcasecmp(cmd->argv[1], "data") == 0) {
1405
0
      data_use_nodelay = get_boolean(cmd, 2);
1406
0
      if (data_use_nodelay == -1) {
1407
0
        CONF_ERROR(cmd, "expected Boolean parameter");
1408
0
      }
1409
1410
0
    } else {
1411
0
      CONF_ERROR(cmd, pstrcat(cmd->tmp_pool, "unsupported parameter: ",
1412
0
        cmd->argv[1], NULL));
1413
0
    }
1414
1415
0
    if (strcasecmp(cmd->argv[3], "ctrl") == 0 ||
1416
0
        strcasecmp(cmd->argv[3], "control") == 0) {
1417
0
      ctrl_use_nodelay = get_boolean(cmd, 4);
1418
0
      if (ctrl_use_nodelay == -1) {
1419
0
        CONF_ERROR(cmd, "expected Boolean parameter");
1420
0
      }
1421
1422
0
    } else if (strcasecmp(cmd->argv[3], "data") == 0) {
1423
0
      data_use_nodelay = get_boolean(cmd, 4);
1424
0
      if (data_use_nodelay == -1) {
1425
0
        CONF_ERROR(cmd, "expected Boolean parameter");
1426
0
      }
1427
1428
0
    } else {
1429
0
      CONF_ERROR(cmd, pstrcat(cmd->tmp_pool, "unsupported parameter: ",
1430
0
        cmd->argv[3], NULL));
1431
0
    }
1432
1433
0
  } else {
1434
0
    CONF_ERROR(cmd, "wrong number of parameters");
1435
0
  }
1436
1437
0
  c = add_config_param(cmd->argv[0], 2, NULL, NULL);
1438
0
  c->argv[0] = pcalloc(c->pool, sizeof(int));
1439
0
  *((int *) c->argv[0]) = ctrl_use_nodelay;
1440
0
  c->argv[1] = pcalloc(c->pool, sizeof(int));
1441
0
  *((int *) c->argv[1]) = data_use_nodelay;
1442
1443
0
  return PR_HANDLED(cmd);
1444
0
}
1445
1446
0
MODRET set_user(cmd_rec *cmd) {
1447
0
  struct passwd *pw = NULL;
1448
1449
0
  CHECK_ARGS(cmd, 1);
1450
0
  CHECK_CONF(cmd, CONF_ROOT|CONF_VIRTUAL|CONF_GLOBAL|CONF_ANON);
1451
1452
  /* 1.1.7, no longer force user/group lookup inside <Anonymous>
1453
   * it's now deferred until authentication occurs.
1454
   */
1455
1456
0
  if (!cmd->config || cmd->config->config_type != CONF_ANON) {
1457
0
    pw = pr_auth_getpwnam(cmd->tmp_pool, cmd->argv[1]);
1458
0
    if (pw == NULL) {
1459
0
      CONF_ERROR(cmd, pstrcat(cmd->tmp_pool, "Unknown user '",
1460
0
        cmd->argv[1], "'", NULL));
1461
0
    }
1462
0
  }
1463
1464
0
  if (pw) {
1465
0
    config_rec *c = add_config_param("UserID", 1, NULL);
1466
0
    c->argv[0] = pcalloc(c->pool, sizeof(uid_t));
1467
0
    *((uid_t *) c->argv[0]) = pw->pw_uid;
1468
0
  }
1469
1470
0
  add_config_param_str("UserName", 1, cmd->argv[1]);
1471
0
  return PR_HANDLED(cmd);
1472
0
}
1473
1474
0
MODRET add_from(cmd_rec *cmd) {
1475
0
  int cargc;
1476
0
  void **cargv;
1477
1478
0
  CHECK_CONF(cmd, CONF_CLASS);
1479
1480
0
  cargc = cmd->argc-1;
1481
0
  cargv = cmd->argv;
1482
1483
0
  while (cargc && *(cargv + 1)) {
1484
0
    char *from;
1485
1486
0
    from = *(((char **) cargv) + 1);
1487
1488
0
    if (strcasecmp(from, "all") == 0 ||
1489
0
        strcasecmp(from, "none") == 0) {
1490
0
      pr_netacl_t *acl = pr_netacl_create(cmd->tmp_pool, from);
1491
0
      if (acl == NULL) {
1492
0
        CONF_ERROR(cmd, pstrcat(cmd->tmp_pool, "bad ACL definition '", from,
1493
0
          "': ", strerror(errno), NULL));
1494
0
      }
1495
1496
0
      pr_trace_msg("netacl", 9, "'%s' parsed into netacl '%s'", from,
1497
0
        pr_netacl_get_str(cmd->tmp_pool, acl));
1498
1499
0
      if (pr_class_add_acl(acl) < 0) {
1500
0
        CONF_ERROR(cmd, pstrcat(cmd->tmp_pool, "error adding rule '", from,
1501
0
          "': ", strerror(errno), NULL));
1502
0
      }
1503
1504
0
      cargc = 0;
1505
0
    }
1506
1507
0
    break;
1508
0
  }
1509
1510
  /* Parse each parameter into a netacl. */
1511
0
  while (cargc-- && *(++cargv)) {
1512
0
    char *ent = NULL, *str;
1513
1514
0
    str = pstrdup(cmd->tmp_pool, *((char **) cargv));
1515
1516
0
    while ((ent = pr_str_get_token(&str, ",")) != NULL) {
1517
0
      if (*ent) {
1518
0
        pr_netacl_t *acl;
1519
1520
0
        pr_signals_handle();
1521
1522
0
        if (strcasecmp(ent, "all") == 0 ||
1523
0
            strcasecmp(ent, "none") == 0) {
1524
0
           cargc = 0;
1525
0
           break;
1526
0
         }
1527
1528
0
        acl = pr_netacl_create(cmd->tmp_pool, ent);
1529
0
        if (acl == NULL) {
1530
0
          CONF_ERROR(cmd, pstrcat(cmd->tmp_pool, "bad ACL definition '",
1531
0
            ent, "': ", strerror(errno), NULL));
1532
0
        }
1533
1534
0
        pr_trace_msg("netacl", 9, "'%s' parsed into netacl '%s'", ent,
1535
0
          pr_netacl_get_str(cmd->tmp_pool, acl));
1536
1537
0
        if (pr_class_add_acl(acl) < 0) {
1538
0
          CONF_ERROR(cmd, pstrcat(cmd->tmp_pool, "error adding rule '", ent,
1539
0
            "': ", strerror(errno), NULL));
1540
0
        }
1541
0
      }
1542
0
    }
1543
0
  }
1544
1545
0
  return PR_HANDLED(cmd);
1546
0
}
1547
1548
/* usage: FSCachePolicy on|off|size {count} [maxAge {age}] */
1549
0
MODRET set_fscachepolicy(cmd_rec *cmd) {
1550
0
  register unsigned int i;
1551
0
  config_rec *c;
1552
1553
0
  if (cmd->argc != 2 &&
1554
0
      cmd->argc != 5) {
1555
0
    CONF_ERROR(cmd, "wrong number of parameters");
1556
0
  }
1557
1558
0
  if (cmd->argc == 2) {
1559
0
    int engine;
1560
1561
0
    engine = get_boolean(cmd, 1);
1562
0
    if (engine == -1) {
1563
0
      CONF_ERROR(cmd, "expected Boolean parameter");
1564
0
    }
1565
1566
0
    c = add_config_param(cmd->argv[0], 3, NULL, NULL, NULL);
1567
0
    c->argv[0] = palloc(c->pool, sizeof(int));
1568
0
    *((int *) c->argv[0]) = engine;
1569
0
    c->argv[1] = palloc(c->pool, sizeof(unsigned int));
1570
0
    *((unsigned int *) c->argv[1]) = PR_TUNABLE_FS_STATCACHE_SIZE;
1571
0
    c->argv[2] = palloc(c->pool, sizeof(unsigned int));
1572
0
    *((unsigned int *) c->argv[2]) = PR_TUNABLE_FS_STATCACHE_MAX_AGE;
1573
1574
0
    return PR_HANDLED(cmd);
1575
0
  }
1576
1577
0
  c = add_config_param_str(cmd->argv[0], 3, NULL, NULL, NULL);
1578
0
  c->argv[0] = palloc(c->pool, sizeof(int));
1579
0
  *((int *) c->argv[0]) = TRUE;
1580
0
  c->argv[1] = palloc(c->pool, sizeof(unsigned int));
1581
0
  *((unsigned int *) c->argv[1]) = PR_TUNABLE_FS_STATCACHE_SIZE;
1582
0
  c->argv[2] = palloc(c->pool, sizeof(unsigned int));
1583
0
  *((unsigned int *) c->argv[2]) = PR_TUNABLE_FS_STATCACHE_MAX_AGE;
1584
1585
0
  for (i = 1; i < cmd->argc; i++) {
1586
0
    if (strncasecmp(cmd->argv[i], "size", 5) == 0) {
1587
0
      int size;
1588
1589
0
      i++;
1590
0
      size = atoi(cmd->argv[i]);
1591
0
      if (size < 1) {
1592
0
        CONF_ERROR(cmd, "size parameter must be greater than 1");
1593
0
      }
1594
1595
0
      *((unsigned int *) c->argv[1]) = size;
1596
1597
0
    } else if (strncasecmp(cmd->argv[i], "maxAge", 7) == 0) {
1598
0
      int max_age;
1599
1600
0
      i++;
1601
0
      max_age = atoi(cmd->argv[i]);
1602
0
      if (max_age < 1) {
1603
0
        CONF_ERROR(cmd, "maxAge parameter must be greater than 1");
1604
0
      }
1605
1606
0
      *((unsigned int *) c->argv[2]) = max_age;
1607
1608
0
    } else {
1609
0
      CONF_ERROR(cmd, pstrcat(cmd->tmp_pool, "unknown FSCachePolicy: ",
1610
0
        cmd->argv[i], NULL));
1611
0
    }
1612
0
  }
1613
1614
0
  return PR_HANDLED(cmd);
1615
0
}
1616
1617
/* usage: FSOptions opt1 opt2 ... */
1618
0
MODRET set_fsoptions(cmd_rec *cmd) {
1619
0
  register unsigned int i;
1620
0
  config_rec *c;
1621
1622
0
  unsigned long opts = 0UL;
1623
1624
0
  if (cmd->argc-1 == 0) {
1625
0
    CONF_ERROR(cmd, "wrong number of parameters");
1626
0
  }
1627
1628
0
  CHECK_CONF(cmd, CONF_ROOT|CONF_VIRTUAL|CONF_GLOBAL);
1629
1630
0
  c = add_config_param(cmd->argv[0], 1, NULL);
1631
1632
0
  for (i = 1; i < cmd->argc; i++) {
1633
0
    if (strcmp(cmd->argv[i], "IgnoreExtendedAttributes") == 0) {
1634
0
      opts |= PR_FSIO_OPT_IGNORE_XATTR;
1635
1636
0
    } else {
1637
0
      CONF_ERROR(cmd, pstrcat(cmd->tmp_pool, ": unknown FSOption '",
1638
0
        cmd->argv[i], "'", NULL));
1639
0
    }
1640
0
  }
1641
1642
0
  c->argv[0] = pcalloc(c->pool, sizeof(unsigned long));
1643
0
  *((unsigned long *) c->argv[0]) = opts;
1644
1645
0
  return PR_HANDLED(cmd);
1646
0
}
1647
1648
0
MODRET set_group(cmd_rec *cmd) {
1649
0
  struct group *grp = NULL;
1650
1651
0
  CHECK_ARGS(cmd, 1);
1652
0
  CHECK_CONF(cmd, CONF_ROOT|CONF_VIRTUAL|CONF_GLOBAL|CONF_ANON);
1653
1654
0
  if (!cmd->config || cmd->config->config_type != CONF_ANON) {
1655
0
    grp = pr_auth_getgrnam(cmd->tmp_pool, cmd->argv[1]);
1656
0
    if (grp == NULL) {
1657
0
      CONF_ERROR(cmd, pstrcat(cmd->tmp_pool, "Unknown group '",
1658
0
        cmd->argv[1], "'", NULL));
1659
0
    }
1660
0
  }
1661
1662
0
  if (grp) {
1663
0
    config_rec *c = add_config_param("GroupID", 1, NULL);
1664
0
    c->argv[0] = pcalloc(c->pool, sizeof(gid_t));
1665
0
    *((gid_t *) c->argv[0]) = grp->gr_gid;
1666
0
  }
1667
1668
0
  add_config_param_str("GroupName", 1, cmd->argv[1]);
1669
0
  return PR_HANDLED(cmd);
1670
0
}
1671
1672
/* usage: Trace ["session"] channel1:level1 ... */
1673
0
MODRET set_trace(cmd_rec *cmd) {
1674
0
#ifdef PR_USE_TRACE
1675
0
  register unsigned int i;
1676
0
  int per_session = FALSE, ctx = 0;
1677
0
  unsigned int idx = 1;
1678
1679
0
  if (cmd->argc-1 < 1) {
1680
0
    CONF_ERROR(cmd, "wrong number of parameters");
1681
0
  }
1682
0
  CHECK_CONF(cmd, CONF_ROOT|CONF_VIRTUAL|CONF_GLOBAL);
1683
1684
0
  ctx = (cmd->config && cmd->config->config_type != CONF_PARAM ?
1685
0
     cmd->config->config_type : cmd->server->config_type ?
1686
0
     cmd->server->config_type : CONF_ROOT);
1687
1688
  /* Look for the optional "session" keyword, which will indicate that these
1689
   * Trace settings are to be applied to a session process only.
1690
   */
1691
0
  if (strcasecmp(cmd->argv[1], "session") == 0) {
1692
    /* If this is the only parameter, it's a config error. */
1693
0
    if (cmd->argc == 2) {
1694
0
      CONF_ERROR(cmd, "wrong number of parameters");
1695
0
    }
1696
1697
0
    per_session = TRUE;
1698
0
    idx = 2;
1699
0
  }
1700
1701
  /* If this directive appears in a <VirtualHost> or <Global> context,
1702
   * automatically handle it as if the "session" keyword has been used.
1703
   */
1704
1705
0
  if ((ctx & CONF_VIRTUAL) ||
1706
0
      (ctx & CONF_GLOBAL)) {
1707
0
    per_session = TRUE;
1708
0
  }
1709
1710
0
  if (per_session == FALSE) {
1711
0
    for (i = idx; i < cmd->argc; i++) {
1712
0
      char *channel, *ptr;
1713
0
      int min_level, max_level, res;
1714
1715
0
      ptr = strchr(cmd->argv[i], ':');
1716
0
      if (ptr == NULL) {
1717
0
        CONF_ERROR(cmd, pstrcat(cmd->tmp_pool, "badly formatted parameter: '",
1718
0
          cmd->argv[i], "'", NULL));
1719
0
      }
1720
1721
0
      channel = cmd->argv[i];
1722
0
      *ptr = '\0';
1723
1724
0
      res = pr_trace_parse_levels(ptr + 1, &min_level, &max_level);
1725
0
      if (res < 0) {
1726
0
        CONF_ERROR(cmd, pstrcat(cmd->tmp_pool, "error parsing level \"",
1727
0
          ptr + 1, "\" for channel '", channel, "': ", strerror(errno), NULL));
1728
0
      }
1729
1730
0
      if (pr_trace_set_levels(channel, min_level, max_level) < 0) {
1731
0
        CONF_ERROR(cmd, pstrcat(cmd->tmp_pool, "error setting level \"",
1732
0
          ptr + 1, "\" for channel '", channel, "': ", strerror(errno), NULL));
1733
0
      }
1734
1735
0
      *ptr = ':';
1736
0
    }
1737
1738
0
  } else {
1739
0
    register unsigned int j = 0;
1740
0
    config_rec *c;
1741
1742
    /* Do a syntax check of the configured trace channels/levels, and store
1743
     * them in a config rec for later handling.
1744
     */
1745
1746
0
    c = add_config_param(cmd->argv[0], 0);
1747
0
    c->argc = cmd->argc - idx;
1748
0
    c->argv = pcalloc(c->pool, ((c->argc + 1) * sizeof(void *)));
1749
1750
0
    for (i = idx; i < cmd->argc; i++) {
1751
0
      char *ptr;
1752
1753
0
      ptr = strchr(cmd->argv[i], ':');
1754
0
      if (ptr == NULL) {
1755
0
        CONF_ERROR(cmd, pstrcat(cmd->tmp_pool, "badly formatted parameter: '",
1756
0
          cmd->argv[i], "'", NULL));
1757
0
      }
1758
1759
0
      c->argv[j++] = pstrdup(c->pool, cmd->argv[i]);
1760
0
    }
1761
0
  }
1762
1763
0
  return PR_HANDLED(cmd);
1764
#else
1765
  CONF_ERROR(cmd,
1766
    "Use of the Trace directive requires trace support (--enable-trace)");
1767
#endif /* PR_USE_TRACE */
1768
0
}
1769
1770
/* usage: TraceLog path */
1771
0
MODRET set_tracelog(cmd_rec *cmd) {
1772
0
#ifdef PR_USE_TRACE
1773
0
  if (cmd->argc-1 != 1) {
1774
0
    CONF_ERROR(cmd, "wrong number of parameters");
1775
0
  }
1776
1777
0
  CHECK_CONF(cmd, CONF_ROOT|CONF_GLOBAL);
1778
1779
0
  if (pr_fs_valid_path(cmd->argv[1]) < 0) {
1780
0
    CONF_ERROR(cmd, "must be an absolute path");
1781
0
  }
1782
1783
0
  trace_log = pstrdup(cmd->server->pool, cmd->argv[1]);
1784
0
  if (pr_trace_set_file(trace_log) < 0) {
1785
0
    if (errno == EPERM) {
1786
0
      CONF_ERROR(cmd, pstrcat(cmd->tmp_pool, "error using TraceLog '",
1787
0
        trace_log, "': directory is symlink or is world-writable", NULL));
1788
0
    }
1789
1790
0
    CONF_ERROR(cmd, pstrcat(cmd->tmp_pool, "error using TraceLog '",
1791
0
      trace_log, "': ", strerror(errno), NULL));
1792
0
  }
1793
1794
0
  return PR_HANDLED(cmd);
1795
#else
1796
  CONF_ERROR(cmd,
1797
    "Use of the TraceLog directive requires trace support (--enable-trace)");
1798
#endif /* PR_USE_TRACE */
1799
0
}
1800
1801
/* usage: TraceOptions opt1 ... optN */
1802
0
MODRET set_traceoptions(cmd_rec *cmd) {
1803
0
#ifdef PR_USE_TRACE
1804
0
  register unsigned int i;
1805
0
  int ctx;
1806
0
  config_rec *c;
1807
0
  unsigned long trace_opts = PR_TRACE_OPT_DEFAULT;
1808
1809
0
  if (cmd->argc < 2) {
1810
0
    CONF_ERROR(cmd, "wrong number of parameters");
1811
0
  }
1812
1813
0
  CHECK_CONF(cmd, CONF_ROOT|CONF_VIRTUAL|CONF_GLOBAL);
1814
1815
0
  for (i = 1; i < cmd->argc; i++) {
1816
0
    char action, *opt;
1817
1818
0
    opt = cmd->argv[i];
1819
0
    action = *opt;
1820
1821
0
    if (action != '-' &&
1822
0
        action != '+') {
1823
0
      CONF_ERROR(cmd, pstrcat(cmd->tmp_pool, "bad TraceOption: '", opt, "'",
1824
0
        NULL));
1825
0
    }
1826
1827
0
    opt++;
1828
1829
0
    if (strcasecmp(opt, "ConnIPs") == 0) {
1830
0
      switch (action) {
1831
0
        case '-':
1832
0
          trace_opts &= ~PR_TRACE_OPT_LOG_CONN_IPS;
1833
0
          break;
1834
1835
0
        case '+':
1836
0
          trace_opts |= PR_TRACE_OPT_LOG_CONN_IPS;
1837
0
          break;
1838
0
      }
1839
1840
0
    } else if (strcasecmp(opt, "Timestamp") == 0) {
1841
0
      switch (action) {
1842
0
        case '-':
1843
0
          trace_opts &= ~PR_TRACE_OPT_USE_TIMESTAMP;
1844
0
          break;
1845
1846
0
        case '+':
1847
0
          trace_opts |= PR_TRACE_OPT_USE_TIMESTAMP;
1848
0
          break;
1849
0
      }
1850
1851
0
    } else if (strcasecmp(opt, "TimestampMillis") == 0) {
1852
0
      switch (action) {
1853
0
        case '-':
1854
0
          trace_opts &= ~PR_TRACE_OPT_USE_TIMESTAMP_MILLIS;
1855
0
          break;
1856
1857
0
        case '+':
1858
0
          trace_opts |= PR_TRACE_OPT_USE_TIMESTAMP_MILLIS;
1859
0
          break;
1860
0
      }
1861
1862
0
    } else {
1863
0
      CONF_ERROR(cmd, pstrcat(cmd->tmp_pool, "unknown TraceOption: '",
1864
0
        opt, "'", NULL));
1865
0
    }
1866
0
  }
1867
1868
0
  c = add_config_param(cmd->argv[0], 1, NULL);
1869
0
  c->argv[0] = palloc(c->pool, sizeof(unsigned long));
1870
0
  *((unsigned long *) c->argv[0]) = trace_opts;
1871
1872
0
  ctx = (cmd->config && cmd->config->config_type != CONF_PARAM ?
1873
0
    cmd->config->config_type : cmd->server->config_type ?
1874
0
    cmd->server->config_type : CONF_ROOT);
1875
1876
0
  if (ctx == CONF_ROOT) {
1877
    /* If we're the "server config" context, set the TraceOptions here,
1878
     * too.  This will apply these TraceOptions to the daemon process.
1879
     */
1880
0
    if (pr_trace_set_options(trace_opts) < 0) {
1881
0
      pr_log_debug(DEBUG6, "%s: error setting TraceOptions (%lu): %s",
1882
0
        (char *) cmd->argv[0], trace_opts, strerror(errno));
1883
0
    }
1884
0
  }
1885
1886
0
  return PR_HANDLED(cmd);
1887
1888
#else
1889
  CONF_ERROR(cmd,
1890
    "Use of the TraceOptions directive requires trace support (--enable-trace)");
1891
#endif /* PR_USE_TRACE */
1892
0
}
1893
1894
0
MODRET set_umask(cmd_rec *cmd) {
1895
0
  config_rec *c;
1896
0
  char *endp;
1897
0
  mode_t tmp_umask;
1898
1899
0
  CHECK_VARARGS(cmd, 1, 2);
1900
0
  CHECK_CONF(cmd, CONF_ROOT|CONF_VIRTUAL|CONF_GLOBAL|CONF_ANON|
1901
0
    CONF_DIR|CONF_DYNDIR);
1902
1903
0
  tmp_umask = (mode_t) strtol(cmd->argv[1], &endp, 8);
1904
1905
0
  if (endp && *endp) {
1906
0
    CONF_ERROR(cmd, pstrcat(cmd->tmp_pool, "'", cmd->argv[1],
1907
0
      "' is not a valid umask", NULL));
1908
0
  }
1909
1910
0
  c = add_config_param(cmd->argv[0], 1, NULL);
1911
0
  c->argv[0] = pcalloc(c->pool, sizeof(mode_t));
1912
0
  *((mode_t *) c->argv[0]) = tmp_umask;
1913
0
  c->flags |= CF_MERGEDOWN;
1914
1915
  /* Have we specified a directory umask as well?
1916
   */
1917
0
  if (CHECK_HASARGS(cmd, 2)) {
1918
1919
    /* allocate space for another mode_t.  Don't worry -- the previous
1920
     * pointer was recorded in the Umask config_rec
1921
     */
1922
0
    tmp_umask = (mode_t) strtol(cmd->argv[2], &endp, 8);
1923
1924
0
    if (endp && *endp) {
1925
0
      CONF_ERROR(cmd, pstrcat(cmd->tmp_pool, "'", cmd->argv[2],
1926
0
        "' is not a valid umask", NULL));
1927
0
    }
1928
1929
0
    c = add_config_param("DirUmask", 1, NULL);
1930
0
    c->argv[0] = pcalloc(c->pool, sizeof(mode_t));
1931
0
    *((mode_t *) c->argv[0]) = tmp_umask;
1932
0
    c->flags |= CF_MERGEDOWN;
1933
0
  }
1934
1935
0
  return PR_HANDLED(cmd);
1936
0
}
1937
1938
0
MODRET set_unsetenv(cmd_rec *cmd) {
1939
0
  int ctxt_type;
1940
1941
0
  CHECK_ARGS(cmd, 1);
1942
0
  CHECK_CONF(cmd, CONF_ROOT|CONF_VIRTUAL|CONF_GLOBAL);
1943
1944
0
  add_config_param_str(cmd->argv[0], 1, cmd->argv[1]);
1945
1946
  /* In addition, if this is the "server config" context, unset the
1947
   * environment variable now.  If there was a <Daemon> context, that would
1948
   * be a more appropriate place for configuring parse-time environment
1949
   * variables.
1950
   */
1951
0
  ctxt_type = (cmd->config && cmd->config->config_type != CONF_PARAM ?
1952
0
    cmd->config->config_type : cmd->server->config_type ?
1953
0
    cmd->server->config_type : CONF_ROOT);
1954
1955
0
  if (ctxt_type == CONF_ROOT) {
1956
0
    if (pr_env_unset(cmd->server->pool, cmd->argv[1]) < 0) {
1957
0
      pr_log_debug(DEBUG1, "%s: unable to unset environment variable '%s': %s",
1958
0
        (char *) cmd->argv[0], (char *) cmd->argv[1], strerror(errno));
1959
1960
0
    } else {
1961
0
      core_handle_locale_env(cmd->argv[1]);
1962
0
    }
1963
0
  }
1964
1965
0
  return PR_HANDLED(cmd);
1966
0
}
1967
1968
/* usage: ProcessTitles "terse"|"verbose" */
1969
0
MODRET set_processtitles(cmd_rec *cmd) {
1970
0
  CHECK_ARGS(cmd, 1);
1971
0
  CHECK_CONF(cmd, CONF_ROOT);
1972
1973
0
  if (strcasecmp(cmd->argv[1], "terse") != 0 &&
1974
0
      strcasecmp(cmd->argv[1], "verbose") != 0) {
1975
0
    CONF_ERROR(cmd, "unknown parameter");
1976
0
  }
1977
1978
0
  add_config_param_str(cmd->argv[0], 1, cmd->argv[1]);
1979
0
  return PR_HANDLED(cmd);
1980
0
}
1981
1982
/* usage: Protocols protocol1 ... protocolN */
1983
0
MODRET set_protocols(cmd_rec *cmd) {
1984
0
  register unsigned int i;
1985
0
  config_rec *c;
1986
0
  array_header *list;
1987
1988
0
  CHECK_CONF(cmd, CONF_ROOT|CONF_VIRTUAL|CONF_GLOBAL);
1989
1990
0
  if (cmd->argc < 2) {
1991
0
    CONF_ERROR(cmd, "wrong number of parameters");
1992
0
  }
1993
1994
0
  c = add_config_param(cmd->argv[0], 1, NULL);
1995
1996
0
  list = make_array(c->pool, 0, sizeof(char *));
1997
0
  for (i = 1; i < cmd->argc; i++) {
1998
0
    *((char **) push_array(list)) = pstrdup(c->pool, cmd->argv[i]);
1999
0
  }
2000
2001
0
  c->argv[0] = list;
2002
0
  c->flags |= CF_MULTI;
2003
2004
0
  return PR_HANDLED(cmd);
2005
0
}
2006
2007
/* usage: RegexOptions [Engine engine] [MatchLimit limit] [MatchLimitRecursion limit]
2008
 */
2009
0
MODRET set_regexoptions(cmd_rec *cmd) {
2010
0
  register unsigned int i;
2011
0
  config_rec *c;
2012
0
  char *engine = NULL;
2013
0
  unsigned long match_limit = 0, match_limit_recursion = 0;
2014
0
  int npairs;
2015
2016
0
  if (cmd->argc < 3) {
2017
0
    CONF_ERROR(cmd, "Wrong number of parameters");
2018
0
  }
2019
2020
  /* Make sure we have an even number of args for the key/value pairs. */
2021
0
  npairs = cmd->argc - 1;
2022
0
  if (npairs % 2 != 0) {
2023
0
    CONF_ERROR(cmd, "Wrong number of parameters");
2024
0
  }
2025
2026
0
  CHECK_CONF(cmd, CONF_ROOT|CONF_VIRTUAL|CONF_GLOBAL);
2027
2028
  /* XXX If more limits/options are supported, switch to using a table
2029
   * for storing the key/value pairs.
2030
   */
2031
2032
0
  for (i = 1; i < cmd->argc; i++) {
2033
0
    if (strcmp(cmd->argv[i], "Engine") == 0) {
2034
0
      engine = cmd->argv[i+1];
2035
2036
0
      if (strcasecmp(engine, "POSIX") != 0 &&
2037
0
          strcasecmp(engine, "PCRE") != 0 &&
2038
0
          strcasecmp(engine, "PCRE2") != 0) {
2039
0
        CONF_ERROR(cmd, pstrcat(cmd->tmp_pool, "bad Engine value: ",
2040
0
          engine, NULL));
2041
0
      }
2042
2043
      /* Don't forget to advance i past the value. */
2044
0
      i += 2;
2045
2046
0
    } else if (strcmp(cmd->argv[i], "MatchLimit") == 0) {
2047
0
      char *ptr = NULL;
2048
2049
0
      match_limit = strtoul(cmd->argv[i+1], &ptr, 10);
2050
0
      if (ptr && *ptr) {
2051
0
        CONF_ERROR(cmd, pstrcat(cmd->tmp_pool, "bad MatchLimit value: ",
2052
0
          cmd->argv[i+1], NULL));
2053
0
      }
2054
2055
      /* Don't forget to advance i past the value. */
2056
0
      i += 2;
2057
2058
0
    } else if (strcmp(cmd->argv[i], "MatchLimitRecursion") == 0) {
2059
0
      char *ptr = NULL;
2060
2061
0
      match_limit_recursion = strtoul(cmd->argv[i+1], &ptr, 10);
2062
0
      if (ptr && *ptr) {
2063
0
        CONF_ERROR(cmd, pstrcat(cmd->tmp_pool,
2064
0
          "bad MatchLimitRecursion value: ", cmd->argv[i+1], NULL));
2065
0
      }
2066
2067
      /* Don't forget to advance i past the value. */
2068
0
      i += 2;
2069
2070
0
    } else {
2071
0
      CONF_ERROR(cmd, pstrcat(cmd->tmp_pool, ": unknown RegexOptions option: '",
2072
0
        cmd->argv[i], "'", NULL));
2073
0
    }
2074
0
  }
2075
2076
0
  c = add_config_param(cmd->argv[0], 3, NULL, NULL, NULL);
2077
0
  c->argv[0] = palloc(c->pool, sizeof(unsigned long));
2078
0
  *((unsigned long *) c->argv[0]) = match_limit;
2079
0
  c->argv[1] = palloc(c->pool, sizeof(unsigned long));
2080
0
  *((unsigned long *) c->argv[1]) = match_limit_recursion;
2081
0
  if (engine != NULL) {
2082
0
    c->argv[2] = pstrdup(c->pool, engine);
2083
0
  }
2084
2085
0
  return PR_HANDLED(cmd);
2086
0
}
2087
2088
0
MODRET set_syslogfacility(cmd_rec *cmd) {
2089
0
  int i;
2090
0
  struct {
2091
0
    char *name;
2092
0
    int facility;
2093
0
  } factable[] = {
2094
0
  { "AUTH",   LOG_AUTHPRIV    },
2095
0
  { "AUTHPRIV",   LOG_AUTHPRIV    },
2096
0
#ifdef HAVE_LOG_FTP
2097
0
  { "FTP",    LOG_FTP     },
2098
0
#endif
2099
0
#ifdef HAVE_LOG_CRON
2100
0
  { "CRON",   LOG_CRON    },
2101
0
#endif
2102
0
  { "DAEMON",   LOG_DAEMON    },
2103
0
  { "KERN",   LOG_KERN    },
2104
0
  { "LOCAL0",   LOG_LOCAL0    },
2105
0
  { "LOCAL1",   LOG_LOCAL1    },
2106
0
  { "LOCAL2",   LOG_LOCAL2    },
2107
0
  { "LOCAL3",   LOG_LOCAL3    },
2108
0
  { "LOCAL4",   LOG_LOCAL4    },
2109
0
  { "LOCAL5",   LOG_LOCAL5    },
2110
0
  { "LOCAL6",   LOG_LOCAL6    },
2111
0
  { "LOCAL7",   LOG_LOCAL7    },
2112
0
  { "LPR",    LOG_LPR     },
2113
0
  { "MAIL",   LOG_MAIL    },
2114
0
  { "NEWS",   LOG_NEWS    },
2115
0
  { "USER",   LOG_USER    },
2116
0
  { "UUCP",   LOG_UUCP    },
2117
0
  { NULL,   0     } };
2118
2119
0
  CHECK_ARGS(cmd, 1);
2120
0
  CHECK_CONF(cmd, CONF_ROOT);
2121
2122
0
  for (i = 0; factable[i].name; i++) {
2123
0
    if (strcasecmp(cmd->argv[1], factable[i].name) == 0) {
2124
0
      log_closesyslog();
2125
0
      log_setfacility(factable[i].facility);
2126
2127
0
      pr_signals_block();
2128
0
      switch (log_opensyslog(NULL)) {
2129
0
        case -1:
2130
0
          pr_signals_unblock();
2131
0
          CONF_ERROR(cmd, pstrcat(cmd->tmp_pool, "unable to open syslog: ",
2132
0
            strerror(errno), NULL));
2133
0
          break;
2134
2135
0
        case PR_LOG_WRITABLE_DIR:
2136
0
          pr_signals_unblock();
2137
0
          CONF_ERROR(cmd,
2138
0
            "you are attempting to log to a world-writable directory");
2139
0
          break;
2140
2141
0
        case PR_LOG_SYMLINK:
2142
0
          pr_signals_unblock();
2143
0
          CONF_ERROR(cmd, "you are attempting to log to a symbolic link");
2144
0
          break;
2145
2146
0
        default:
2147
0
          break;
2148
0
      }
2149
0
      pr_signals_unblock();
2150
2151
0
      return PR_HANDLED(cmd);
2152
0
    }
2153
0
  }
2154
2155
0
  CONF_ERROR(cmd, "argument must be a valid syslog facility");
2156
0
}
2157
2158
0
MODRET set_timesgmt(cmd_rec *cmd) {
2159
0
  int times_gmt = -1;
2160
0
  config_rec *c = NULL;
2161
2162
0
  CHECK_ARGS(cmd, 1);
2163
0
  CHECK_CONF(cmd, CONF_ROOT|CONF_VIRTUAL|CONF_GLOBAL|CONF_ANON);
2164
2165
0
  times_gmt = get_boolean(cmd, 1);
2166
0
  if (times_gmt == -1) {
2167
0
    CONF_ERROR(cmd, "expected Boolean parameter");
2168
0
  }
2169
2170
0
  c = add_config_param(cmd->argv[0], 1, NULL);
2171
0
  c->argv[0] = pcalloc(c->pool, sizeof(unsigned char));
2172
0
  *((unsigned char *) c->argv[0]) = times_gmt;
2173
2174
0
  c->flags |= CF_MERGEDOWN;
2175
0
  return PR_HANDLED(cmd);
2176
0
}
2177
2178
0
MODRET set_regex(cmd_rec *cmd, char *param, char *type) {
2179
0
#ifdef PR_USE_REGEX
2180
0
  pr_regex_t *pre = NULL;
2181
0
  config_rec *c = NULL;
2182
0
  int regex_flags = REG_EXTENDED|REG_NOSUB, res = 0;
2183
2184
0
  if (cmd->argc-1 < 1 ||
2185
0
      cmd->argc-1 > 2) {
2186
0
    CONF_ERROR(cmd, "bad number of parameters");
2187
0
  }
2188
2189
0
  CHECK_CONF(cmd, CONF_ROOT|CONF_VIRTUAL|CONF_GLOBAL|CONF_ANON|CONF_DIR|
2190
0
    CONF_DYNDIR);
2191
2192
  /* Make sure that, if present, the flags parameter is correctly formatted. */
2193
0
  if (cmd->argc-1 == 2) {
2194
0
    int flags = 0;
2195
2196
    /* We need to parse the flags parameter here, to see if any flags which
2197
     * affect the compilation of the regex (e.g. NC) are present.
2198
     */
2199
2200
0
    flags = pr_filter_parse_flags(cmd->tmp_pool, cmd->argv[2]);
2201
0
    if (flags < 0) {
2202
0
      CONF_ERROR(cmd, pstrcat(cmd->tmp_pool,
2203
0
        "badly formatted flags parameter: '", cmd->argv[2], "'", NULL));
2204
0
    }
2205
2206
0
    if (flags == 0) {
2207
0
      CONF_ERROR(cmd, pstrcat(cmd->tmp_pool,
2208
0
        "unknown filter flags '", cmd->argv[2], "'", NULL));
2209
0
    }
2210
2211
0
    regex_flags |= flags;
2212
0
  }
2213
2214
0
  pr_log_debug(DEBUG4, "%s: compiling %s regex '%s'", (char *) cmd->argv[0],
2215
0
    type, (char *) cmd->argv[1]);
2216
0
  pre = pr_regexp_alloc(&core_module);
2217
2218
0
  res = pr_regexp_compile(pre, cmd->argv[1], regex_flags);
2219
0
  if (res != 0) {
2220
0
    char errstr[200] = {'\0'};
2221
2222
0
    pr_regexp_error(res, pre, errstr, sizeof(errstr));
2223
0
    pr_regexp_free(NULL, pre);
2224
2225
0
    CONF_ERROR(cmd, pstrcat(cmd->tmp_pool, "'", (char *) cmd->argv[1],
2226
0
      "' failed regex compilation: ", errstr, NULL));
2227
0
  }
2228
2229
0
  c = add_config_param(param, 1, pre);
2230
0
  c->flags |= CF_MERGEDOWN;
2231
0
  return PR_HANDLED(cmd);
2232
2233
#else /* no regular expression support at the moment */
2234
  CONF_ERROR(cmd, pstrcat(cmd->tmp_pool, "The ", param, " directive cannot be "
2235
    "used on this system, as you do not have POSIX compliant regex support",
2236
    NULL));
2237
#endif
2238
0
}
2239
2240
0
MODRET set_allowdenyfilter(cmd_rec *cmd) {
2241
0
#ifdef PR_USE_REGEX
2242
0
  pr_regex_t *pre = NULL;
2243
0
  config_rec *c = NULL;
2244
0
  int regex_flags = REG_EXTENDED|REG_NOSUB, res = 0;
2245
2246
0
  if (cmd->argc-1 < 1 ||
2247
0
      cmd->argc-1 > 2) {
2248
0
    CONF_ERROR(cmd, "bad number of parameters");
2249
0
  }
2250
2251
0
  CHECK_CONF(cmd, CONF_ROOT|CONF_VIRTUAL|CONF_GLOBAL|CONF_ANON|CONF_DIR|
2252
0
    CONF_DYNDIR|CONF_LIMIT);
2253
2254
  /* Make sure that, if present, the flags parameter is correctly formatted. */
2255
0
  if (cmd->argc-1 == 2) {
2256
0
    int flags = 0;
2257
2258
    /* We need to parse the flags parameter here, to see if any flags which
2259
     * affect the compilation of the regex (e.g. NC) are present.
2260
     */
2261
2262
0
    flags = pr_filter_parse_flags(cmd->tmp_pool, cmd->argv[2]);
2263
0
    if (flags < 0) {
2264
0
      CONF_ERROR(cmd, pstrcat(cmd->tmp_pool,
2265
0
        "badly formatted flags parameter: '", cmd->argv[2], "'", NULL));
2266
0
    }
2267
2268
0
    if (flags == 0) {
2269
0
      CONF_ERROR(cmd, pstrcat(cmd->tmp_pool,
2270
0
        "unknown filter flags '", cmd->argv[2], "'", NULL));
2271
0
    }
2272
2273
0
    regex_flags |= flags;
2274
0
  }
2275
2276
0
  pr_log_debug(DEBUG4, "%s: compiling regex '%s'", (char *) cmd->argv[0],
2277
0
    (char *) cmd->argv[1]);
2278
0
  pre = pr_regexp_alloc(&core_module);
2279
2280
0
  res = pr_regexp_compile(pre, cmd->argv[1], regex_flags);
2281
0
  if (res != 0) {
2282
0
    char errstr[200] = {'\0'};
2283
2284
0
    pr_regexp_error(res, pre, errstr, sizeof(errstr));
2285
0
    pr_regexp_free(NULL, pre);
2286
2287
0
    CONF_ERROR(cmd, pstrcat(cmd->tmp_pool, "'", (char *) cmd->argv[1],
2288
0
      "' failed regex compilation: ", errstr, NULL));
2289
0
  }
2290
2291
0
  c = add_config_param(cmd->argv[0], 1, pre);
2292
0
  c->flags |= CF_MERGEDOWN;
2293
0
  return PR_HANDLED(cmd);
2294
2295
#else /* no regular expression support at the moment */
2296
  CONF_ERROR(cmd, pstrcat(cmd->tmp_pool, "The ", cmd->argv[0],
2297
    " directive cannot be used on this system, as you do not have POSIX "
2298
    "compliant regex support", NULL));
2299
#endif
2300
0
}
2301
2302
0
MODRET set_passiveports(cmd_rec *cmd) {
2303
0
  int pasv_min_port, pasv_max_port;
2304
0
  config_rec *c = NULL;
2305
2306
0
  CHECK_ARGS(cmd, 2);
2307
0
  CHECK_CONF(cmd, CONF_ROOT|CONF_VIRTUAL|CONF_GLOBAL);
2308
2309
0
  pasv_min_port = atoi(cmd->argv[1]);
2310
0
  pasv_max_port = atoi(cmd->argv[2]);
2311
2312
  /* Sanity check */
2313
0
  if (pasv_min_port <= 0 ||
2314
0
      pasv_min_port > 65535) {
2315
0
    CONF_ERROR(cmd, "min port must be allowable port number");
2316
0
  }
2317
2318
0
  if (pasv_max_port <= 0 ||
2319
0
      pasv_max_port > 65535) {
2320
0
    CONF_ERROR(cmd, "max port must be allowable port number");
2321
0
  }
2322
2323
0
  if (pasv_min_port < 1024 ||
2324
0
      pasv_max_port < 1024) {
2325
0
    CONF_ERROR(cmd, "port numbers must be above 1023");
2326
0
  }
2327
2328
0
  if (pasv_max_port <= pasv_min_port) {
2329
0
    CONF_ERROR(cmd, "min port must be less than max port");
2330
0
  }
2331
2332
0
  c = add_config_param(cmd->argv[0], 2, NULL, NULL);
2333
0
  c->argv[0] = pcalloc(c->pool, sizeof(int));
2334
0
  *((int *) c->argv[0]) = pasv_min_port;
2335
0
  c->argv[1] = pcalloc(c->pool, sizeof(int));
2336
0
  *((int *) c->argv[1]) = pasv_max_port;
2337
2338
0
  return PR_HANDLED(cmd);
2339
0
}
2340
2341
0
MODRET set_pathallowfilter(cmd_rec *cmd) {
2342
0
  return set_regex(cmd, cmd->argv[0], "allow");
2343
0
}
2344
2345
0
MODRET set_pathdenyfilter(cmd_rec *cmd) {
2346
0
  return set_regex(cmd, cmd->argv[0], "deny");
2347
0
}
2348
2349
/* usage: AllowForeignAddress on|off|class */
2350
0
MODRET set_allowforeignaddress(cmd_rec *cmd) {
2351
0
  int allow_foreign_addr = -1;
2352
0
  config_rec *c = NULL;
2353
0
  char *class_name = NULL;
2354
2355
0
  CHECK_ARGS(cmd, 1);
2356
0
  CHECK_CONF(cmd, CONF_ROOT|CONF_VIRTUAL|CONF_GLOBAL|CONF_ANON);
2357
2358
0
  allow_foreign_addr = get_boolean(cmd, 1);
2359
0
  if (allow_foreign_addr == -1) {
2360
    /* Not a boolean?  Assume it's a <Class> name, then. */
2361
0
    class_name = cmd->argv[1];
2362
0
  }
2363
2364
0
  c = add_config_param(cmd->argv[0], 2, NULL, NULL);
2365
0
  c->argv[0] = pcalloc(c->pool, sizeof(int));
2366
0
  *((int *) c->argv[0]) = allow_foreign_addr;
2367
0
  c->argv[1] = pstrdup(c->pool, class_name);
2368
2369
0
  c->flags |= CF_MERGEDOWN;
2370
0
  return PR_HANDLED(cmd);
2371
0
}
2372
2373
0
MODRET set_commandbuffersize(cmd_rec *cmd) {
2374
0
  size_t size = 0;
2375
0
  off_t nbytes = 0;
2376
0
  config_rec *c = NULL;
2377
0
  const char *units = NULL;
2378
2379
0
  if (cmd->argc < 2 || cmd->argc > 3) {
2380
0
    CONF_ERROR(cmd, "wrong number of parameters")
2381
0
  }
2382
2383
0
  CHECK_CONF(cmd, CONF_ROOT|CONF_VIRTUAL|CONF_GLOBAL);
2384
2385
0
  if (cmd->argc == 3) {
2386
0
    units = cmd->argv[2];
2387
0
  }
2388
2389
0
  if (pr_str_get_nbytes(cmd->argv[1], units, &nbytes) < 0) {
2390
0
    CONF_ERROR(cmd, pstrcat(cmd->tmp_pool, "unable to parse: ",
2391
0
      cmd->argv[1], " ", units ? units : "", ": ", strerror(errno), NULL));
2392
0
  }
2393
2394
0
  if (nbytes > PR_TUNABLE_CMD_BUFFER_SIZE) {
2395
0
    char max[1024];
2396
2397
0
    pr_snprintf(max, sizeof(max)-1, "%lu", (unsigned long)
2398
0
      PR_TUNABLE_CMD_BUFFER_SIZE);
2399
0
    max[sizeof(max)-1] = '\0';
2400
2401
0
    CONF_ERROR(cmd, pstrcat(cmd->tmp_pool, "size ", cmd->argv[1],
2402
0
      units ? units : "", "exceeds max size ", max, NULL));
2403
0
  }
2404
2405
  /* Possible truncation here, but only for an absurdly large size. */
2406
0
  size = (size_t) nbytes;
2407
2408
0
  c = add_config_param(cmd->argv[0], 1, NULL);
2409
0
  c->argv[0] = pcalloc(c->pool, sizeof(size_t));
2410
0
  *((size_t *) c->argv[0]) = size;
2411
2412
0
  return PR_HANDLED(cmd);
2413
0
}
2414
2415
0
MODRET set_cdpath(cmd_rec *cmd) {
2416
0
  config_rec *c = NULL;
2417
2418
0
  CHECK_ARGS(cmd, 1);
2419
0
  CHECK_CONF(cmd, CONF_ROOT|CONF_VIRTUAL|CONF_GLOBAL|CONF_ANON);
2420
2421
0
  c = add_config_param_str(cmd->argv[0], 1, cmd->argv[1]);
2422
0
  c->flags |= CF_MERGEDOWN;
2423
2424
0
  return PR_HANDLED(cmd);
2425
0
}
2426
2427
0
MODRET add_directory(cmd_rec *cmd) {
2428
0
  config_rec *c;
2429
0
  char *dir, *rootdir = NULL;
2430
0
  int flags = 0;
2431
2432
0
  CHECK_ARGS(cmd, 1);
2433
0
  CHECK_CONF(cmd, CONF_ROOT|CONF_VIRTUAL|CONF_GLOBAL|CONF_ANON);
2434
2435
0
  dir = cmd->argv[1];
2436
2437
0
  if (*dir != '/' &&
2438
0
      *dir != '~' &&
2439
0
      (!cmd->config ||
2440
0
       cmd->config->config_type != CONF_ANON)) {
2441
0
    CONF_ERROR(cmd, "relative path not allowed in non-<Anonymous> sections");
2442
0
  }
2443
2444
  /* If in anonymous mode, and path is relative, just cat anon root
2445
   * and relative path.
2446
   *
2447
   * Note: This is no longer necessary, because we don't interpolate anonymous
2448
   * directories at run-time.
2449
   */
2450
0
  if (cmd->config &&
2451
0
      cmd->config->config_type == CONF_ANON &&
2452
0
      *dir != '/' &&
2453
0
      *dir != '~') {
2454
0
    if (strcmp(dir, "*") != 0) {
2455
0
      dir = pdircat(cmd->tmp_pool, "/", dir, NULL);
2456
0
    }
2457
0
    rootdir = cmd->config->name;
2458
2459
0
  } else {
2460
0
    if (pr_fs_valid_path(dir) < 0) {
2461
      /* Not an absolute path; mark it for deferred resolution. */
2462
0
      flags |= CF_DEFER;
2463
0
    }
2464
0
  }
2465
2466
  /* Check to see that there isn't already a config for this directory,
2467
   * but only if we're not in an <Anonymous> section.  Due to the way
2468
   * in which later <Directory> checks are done, <Directory> blocks inside
2469
   * <Anonymous> sections are handled differently than outside, probably
2470
   * overriding their outside counterparts (if necessary).  This is
2471
   * probably OK, as this overriding only takes effect for the <Anonymous>
2472
   * user.
2473
   */
2474
2475
0
  if (!check_context(cmd, CONF_ANON) &&
2476
0
      find_config(cmd->server->conf, CONF_DIR, dir, FALSE) != NULL) {
2477
0
    CONF_ERROR(cmd, pstrcat(cmd->tmp_pool,
2478
0
      "<Directory> section already configured for '", cmd->argv[1], "'", NULL));
2479
0
  }
2480
2481
  /* Check for any expandable variables, and mark this config_rec for
2482
   * deferred resolution if present
2483
   */
2484
0
  if (strstr(dir, "%u")) {
2485
0
    flags |= CF_DEFER;
2486
0
  }
2487
2488
0
  c = pr_parser_config_ctxt_open(dir);
2489
0
  c->argc = 2;
2490
0
  c->argv = pcalloc(c->pool, 3 * sizeof(void *));
2491
2492
  /* If we do NOT have rootdir, then do NOT add anything to the argv[1] slot;
2493
   * it is intended solely for that particular use case.
2494
   */
2495
0
  if (rootdir) {
2496
0
    c->argv[1] = pstrdup(c->pool, rootdir);
2497
0
  }
2498
2499
0
  c->config_type = CONF_DIR;
2500
0
  c->flags |= flags;
2501
2502
0
  if (!(c->flags & CF_DEFER)) {
2503
0
    pr_log_debug(DEBUG2, "<Directory %s>: adding section for resolved "
2504
0
      "path '%s'", (char *) cmd->argv[1], dir);
2505
2506
0
  } else {
2507
0
    pr_log_debug(DEBUG2,
2508
0
      "<Directory %s>: deferring resolution of path", (char *) cmd->argv[1]);
2509
0
  }
2510
2511
0
  return PR_HANDLED(cmd);
2512
0
}
2513
2514
0
MODRET set_hidefiles(cmd_rec *cmd) {
2515
0
#ifdef PR_USE_REGEX
2516
0
  pr_regex_t *pre = NULL;
2517
0
  config_rec *c = NULL;
2518
0
  unsigned int precedence = 0;
2519
0
  unsigned char negated = FALSE, none = FALSE;
2520
0
  char *ptr;
2521
2522
0
  int ctxt = (cmd->config && cmd->config->config_type != CONF_PARAM ?
2523
0
    cmd->config->config_type : cmd->server->config_type ?
2524
0
    cmd->server->config_type : CONF_ROOT);
2525
2526
  /* This directive must have either 1, or 3, arguments */
2527
0
  if (cmd->argc-1 != 1 &&
2528
0
      cmd->argc-1 != 3) {
2529
0
    CONF_ERROR(cmd, "wrong number of parameters");
2530
0
  }
2531
2532
0
  CHECK_CONF(cmd, CONF_DIR|CONF_DYNDIR);
2533
2534
  /* Set the precedence for this config_rec based on its configuration
2535
   * context.
2536
   */
2537
0
  if (ctxt & CONF_DIR) {
2538
0
    precedence = 1;
2539
2540
0
  } else {
2541
0
    precedence = 2;
2542
0
  }
2543
2544
  /* Check for a leading '!' prefix, signifying regex negation */
2545
0
  ptr = cmd->argv[1];
2546
0
  if (*ptr == '!') {
2547
0
    negated = TRUE;
2548
0
    ptr++;
2549
2550
0
  } else {
2551
    /* Check for a "none" argument, which is used to nullify inherited
2552
     * HideFiles configurations from parent directories.
2553
     */
2554
0
    if (strcasecmp(ptr, "none") == 0) {
2555
0
      none = TRUE;
2556
0
    }
2557
0
  }
2558
2559
0
  if (!none) {
2560
0
    int res;
2561
2562
0
    pre = pr_regexp_alloc(&core_module);
2563
2564
0
    res = pr_regexp_compile(pre, ptr, REG_EXTENDED|REG_NOSUB);
2565
0
    if (res != 0) {
2566
0
      char errstr[200] = {'\0'};
2567
2568
0
      pr_regexp_error(res, pre, errstr, sizeof(errstr));
2569
0
      pr_regexp_free(NULL, pre);
2570
2571
0
      CONF_ERROR(cmd, pstrcat(cmd->tmp_pool, "'", ptr,
2572
0
        "' failed regex compilation: ", errstr, NULL));
2573
0
    }
2574
0
  }
2575
2576
  /* If the directive was used with 3 arguments, then the optional
2577
   * classifiers, and classifier expression, were used.  Make sure that
2578
   * a valid classifier was used.
2579
   */
2580
0
  if (cmd->argc-1 == 3) {
2581
0
    if (strcasecmp(cmd->argv[2], "user") != 0 &&
2582
0
        strcasecmp(cmd->argv[2], "group") != 0 &&
2583
0
        strcasecmp(cmd->argv[2], "class") != 0) {
2584
0
      return PR_ERROR_MSG(cmd, NULL, pstrcat(cmd->tmp_pool, cmd->argv[0],
2585
0
        "unknown classifier used: '", cmd->argv[2], "'", NULL));
2586
0
    }
2587
0
  }
2588
2589
0
  if (cmd->argc-1 == 1) {
2590
0
    c = add_config_param(cmd->argv[0], 3, NULL, NULL, NULL);
2591
0
    c->argv[0] = pcalloc(c->pool, sizeof(pr_regex_t *));
2592
0
    *((pr_regex_t **) c->argv[0]) = pre;
2593
0
    c->argv[1] = pcalloc(c->pool, sizeof(unsigned char));
2594
0
    *((unsigned char *) c->argv[1]) = negated;
2595
0
    c->argv[2] = pcalloc(c->pool, sizeof(unsigned int));
2596
0
    *((unsigned int *) c->argv[2]) = precedence;
2597
2598
0
    c->flags |= CF_MERGEDOWN_MULTI;
2599
2600
0
  } else if (cmd->argc-1 == 3) {
2601
0
    array_header *acl = NULL;
2602
0
    unsigned int argc = cmd->argc - 3;
2603
0
    void **argv;
2604
2605
0
    argv = &(cmd->argv[2]);
2606
2607
0
    acl = pr_expr_create(cmd->tmp_pool, &argc, (char **) argv);
2608
0
    if (acl == NULL) {
2609
0
      CONF_ERROR(cmd, pstrcat(cmd->tmp_pool, "error creating expression: ",
2610
0
        strerror(errno), NULL));
2611
0
    }
2612
2613
0
    c = add_config_param(cmd->argv[0], 0);
2614
0
    c->argc = argc + 4;
2615
2616
    /* Add 5 to argc for the argv of the config_rec: one for the
2617
     * regexp, one for the 'negated' value, one for the precedence,
2618
     * one for the classifier, and one for the terminating NULL
2619
     */
2620
0
    c->argv = pcalloc(c->pool, ((argc + 5) * sizeof(void *)));
2621
2622
    /* Capture the config_rec's argv pointer for doing the by-hand
2623
     * population.
2624
     */
2625
0
    argv = c->argv;
2626
2627
    /* Copy in the regexp. */
2628
0
    *argv = pcalloc(c->pool, sizeof(pr_regex_t *));
2629
0
    *((pr_regex_t **) *argv++) = pre;
2630
2631
    /* Copy in the 'negated' flag */
2632
0
    *argv = pcalloc(c->pool, sizeof(unsigned char));
2633
0
    *((unsigned char *) *argv++) = negated;
2634
2635
    /* Copy in the precedence. */
2636
0
    *argv = pcalloc(c->pool, sizeof(unsigned int));
2637
0
    *((unsigned int *) *argv++) = precedence;
2638
2639
    /* Copy in the expression classifier */
2640
0
    *argv++ = pstrdup(c->pool, cmd->argv[2]);
2641
2642
    /* now, copy in the expression arguments */
2643
0
    if (argc && acl) {
2644
0
      while (argc-- > 0) {
2645
0
        *argv++ = pstrdup(c->pool, *((char **) acl->elts));
2646
0
        acl->elts = ((char **) acl->elts) + 1;
2647
0
      }
2648
0
    }
2649
2650
    /* don't forget the terminating NULL */
2651
0
    *argv = NULL;
2652
2653
0
    c->flags |= CF_MERGEDOWN_MULTI;
2654
0
  }
2655
2656
0
  return PR_HANDLED(cmd);
2657
2658
#else /* no regular expression support at the moment */
2659
  CONF_ERROR(cmd, pstrcat(cmd->tmp_pool, "The HideFiles directive cannot be "
2660
    "used on this system, as you do not have POSIX compliant regex support",
2661
    NULL));
2662
#endif
2663
0
}
2664
2665
0
MODRET set_hidenoaccess(cmd_rec *cmd) {
2666
0
  int hide_no_access = -1;
2667
0
  config_rec *c = NULL;
2668
2669
0
  CHECK_ARGS(cmd, 1);
2670
0
  CHECK_CONF(cmd, CONF_ANON|CONF_DIR);
2671
2672
0
  hide_no_access = get_boolean(cmd, 1);
2673
0
  if (hide_no_access == -1) {
2674
0
    CONF_ERROR(cmd, "expected Boolean parameter");
2675
0
  }
2676
2677
0
  c = add_config_param(cmd->argv[0], 1, NULL);
2678
0
  c->argv[0] = pcalloc(c->pool, sizeof(unsigned char));
2679
0
  *((unsigned char *) c->argv[0]) = hide_no_access;
2680
0
  c->flags |= CF_MERGEDOWN;
2681
2682
0
  return PR_HANDLED(cmd);
2683
0
}
2684
2685
0
MODRET set_hideuser(cmd_rec *cmd) {
2686
0
  config_rec *c = NULL;
2687
0
  char *user = NULL;
2688
0
  int inverted = FALSE;
2689
2690
0
  CHECK_ARGS(cmd, 1);
2691
0
  CHECK_CONF(cmd, CONF_ANON|CONF_DIR);
2692
2693
0
  user = cmd->argv[1];
2694
0
  if (*user == '!') {
2695
0
    inverted = TRUE;
2696
0
    user++;
2697
0
  }
2698
2699
0
  c = add_config_param(cmd->argv[0], 2, NULL, NULL);
2700
0
  c->argv[0] = pstrdup(c->pool, user);
2701
0
  c->argv[1] = pcalloc(c->pool, sizeof(int));
2702
0
  *((int *) c->argv[1]) = inverted;
2703
2704
0
  c->flags |= CF_MERGEDOWN;
2705
0
  return PR_HANDLED(cmd);
2706
0
}
2707
2708
0
MODRET set_hidegroup(cmd_rec *cmd) {
2709
0
  config_rec *c = NULL;
2710
0
  char *group = NULL;
2711
0
  int inverted = FALSE;
2712
2713
0
  CHECK_ARGS(cmd, 1);
2714
0
  CHECK_CONF(cmd, CONF_ANON|CONF_DIR);
2715
2716
0
  group = cmd->argv[1];
2717
0
  if (*group == '!') {
2718
0
    inverted = TRUE;
2719
0
    group++;
2720
0
  }
2721
2722
0
  c = add_config_param(cmd->argv[0], 2, NULL, NULL);
2723
0
  c->argv[0] = pstrdup(c->pool, group);
2724
0
  c->argv[1] = pcalloc(c->pool, sizeof(int));
2725
0
  *((int *) c->argv[1]) = inverted;
2726
2727
0
  c->flags |= CF_MERGEDOWN;
2728
0
  return PR_HANDLED(cmd);
2729
0
}
2730
2731
0
MODRET add_groupowner(cmd_rec *cmd) {
2732
0
  config_rec *c = NULL;
2733
2734
0
  CHECK_ARGS(cmd, 1);
2735
0
  CHECK_CONF(cmd, CONF_ANON|CONF_DIR|CONF_DYNDIR);
2736
2737
0
  c = add_config_param_str(cmd->argv[0], 1, cmd->argv[1]);
2738
0
  c->flags |= CF_MERGEDOWN;
2739
2740
0
  return PR_HANDLED(cmd);
2741
0
}
2742
2743
0
MODRET add_userowner(cmd_rec *cmd) {
2744
0
  config_rec *c = NULL;
2745
2746
0
  CHECK_ARGS(cmd, 1);
2747
0
  CHECK_CONF(cmd, CONF_ANON|CONF_DIR);
2748
2749
0
  c = add_config_param_str(cmd->argv[0], 1, cmd->argv[1]);
2750
0
  c->flags |= CF_MERGEDOWN;
2751
2752
0
  return PR_HANDLED(cmd);
2753
0
}
2754
2755
0
MODRET set_allowoverride(cmd_rec *cmd) {
2756
0
  int allow_override = -1;
2757
0
  config_rec *c = NULL;
2758
0
  unsigned int precedence = 0;
2759
2760
0
  int ctxt = (cmd->config && cmd->config->config_type != CONF_PARAM ?
2761
0
     cmd->config->config_type : cmd->server->config_type ?
2762
0
     cmd->server->config_type : CONF_ROOT);
2763
2764
  /* This directive must have either 1 argument; the 3 arguments format is
2765
   * now deprecated.
2766
   */
2767
0
  if (cmd->argc-1 == 3) {
2768
0
    CONF_ERROR(cmd, pstrcat(cmd->tmp_pool, "Please use mod_ifsession for "
2769
0
      "per-user/group/class conditional configuration", NULL));
2770
0
  }
2771
2772
0
  CHECK_ARGS(cmd, 1);
2773
0
  CHECK_CONF(cmd, CONF_ROOT|CONF_VIRTUAL|CONF_GLOBAL|CONF_ANON|CONF_DIR);
2774
2775
0
  allow_override = get_boolean(cmd, 1);
2776
0
  if (allow_override == -1) {
2777
0
    CONF_ERROR(cmd, "expected Boolean parameter");
2778
0
  }
2779
2780
  /* Set the precedence for this config_rec based on its configuration
2781
   * context.
2782
   */
2783
0
  if (ctxt & CONF_GLOBAL) {
2784
0
    precedence = 1;
2785
2786
  /* These will never appear simultaneously */
2787
0
  } else if (ctxt & CONF_ROOT || ctxt & CONF_VIRTUAL) {
2788
0
    precedence = 2;
2789
2790
0
  } else if (ctxt & CONF_ANON) {
2791
0
    precedence = 3;
2792
2793
0
  } else if (ctxt & CONF_DIR) {
2794
0
    precedence = 4;
2795
0
  }
2796
2797
0
  c = add_config_param(cmd->argv[0], 2, NULL, NULL);
2798
0
  c->argv[0] = pcalloc(c->pool, sizeof(int));
2799
0
  *((int *) c->argv[0]) = allow_override;
2800
0
  c->argv[1] = pcalloc(c->pool, sizeof(unsigned int));
2801
0
  *((unsigned int *) c->argv[1]) = precedence;
2802
0
  c->flags |= CF_MERGEDOWN_MULTI;
2803
2804
0
  return PR_HANDLED(cmd);
2805
0
}
2806
2807
0
MODRET end_directory(cmd_rec *cmd) {
2808
0
  int empty_ctxt = FALSE;
2809
2810
0
  if (cmd->argc > 1) {
2811
0
    CONF_ERROR(cmd, "wrong number of parameters");
2812
0
  }
2813
2814
0
  CHECK_CONF(cmd, CONF_DIR);
2815
2816
0
  pr_parser_config_ctxt_close(&empty_ctxt);
2817
2818
0
  if (empty_ctxt) {
2819
0
    pr_log_debug(DEBUG3, "%s: ignoring empty section", (char *) cmd->argv[0]);
2820
0
  }
2821
2822
0
  return PR_HANDLED(cmd);
2823
0
}
2824
2825
0
MODRET add_anonymous(cmd_rec *cmd) {
2826
0
  config_rec *c = NULL;
2827
0
  char *dir;
2828
2829
0
  CHECK_ARGS(cmd, 1);
2830
0
  CHECK_CONF(cmd, CONF_ROOT|CONF_VIRTUAL|CONF_GLOBAL);
2831
2832
0
  dir = cmd->argv[1];
2833
2834
0
  if (*dir != '/' && *dir != '~') {
2835
0
    CONF_ERROR(cmd, pstrcat(cmd->tmp_pool, "(", dir, ") absolute pathname "
2836
0
      "required", NULL));
2837
0
  }
2838
2839
0
  if (strchr(dir, '*')) {
2840
0
    CONF_ERROR(cmd, pstrcat(cmd->tmp_pool, "(", dir, ") wildcards not allowed "
2841
0
      "in pathname", NULL));
2842
0
  }
2843
2844
0
  if (strcmp(dir, "/") == 0) {
2845
0
    CONF_ERROR(cmd, "'/' not permitted for anonymous root directory");
2846
0
  }
2847
2848
0
  if (*(dir + strlen(dir)-1) != '/') {
2849
0
    dir = pstrcat(cmd->tmp_pool, dir, "/", NULL);
2850
0
  }
2851
2852
0
  if (dir == NULL) {
2853
0
    CONF_ERROR(cmd, pstrcat(cmd->tmp_pool, cmd->argv[1], ": ",
2854
0
      strerror(errno), NULL));
2855
0
  }
2856
2857
0
  c = pr_parser_config_ctxt_open(dir);
2858
2859
0
  c->config_type = CONF_ANON;
2860
0
  return PR_HANDLED(cmd);
2861
0
}
2862
2863
0
MODRET end_anonymous(cmd_rec *cmd) {
2864
0
  int empty_ctxt = FALSE;
2865
2866
0
  if (cmd->argc > 1) {
2867
0
    CONF_ERROR(cmd, "wrong number of parameters");
2868
0
  }
2869
2870
0
  CHECK_CONF(cmd, CONF_ANON);
2871
2872
0
  pr_parser_config_ctxt_close(&empty_ctxt);
2873
2874
0
  if (empty_ctxt) {
2875
0
    pr_log_debug(DEBUG3, "%s: ignoring empty section", (char *) cmd->argv[0]);
2876
0
  }
2877
2878
0
  return PR_HANDLED(cmd);
2879
0
}
2880
2881
0
MODRET add_class(cmd_rec *cmd) {
2882
0
  config_rec *c;
2883
2884
0
  CHECK_ARGS(cmd, 1);
2885
0
  CHECK_CONF(cmd, CONF_ROOT|CONF_GLOBAL);
2886
2887
0
  if (pr_class_open(main_server->pool, cmd->argv[1]) < 0) {
2888
0
    CONF_ERROR(cmd, pstrcat(cmd->tmp_pool, "error creating <Class ",
2889
0
      cmd->argv[1], ">: ", strerror(errno), NULL));
2890
0
  }
2891
2892
0
  c = pr_parser_config_ctxt_open("Class");
2893
0
  c->config_type = CONF_CLASS;
2894
2895
0
  return PR_HANDLED(cmd);
2896
0
}
2897
2898
0
MODRET end_class(cmd_rec *cmd) {
2899
0
  int empty_ctx = FALSE;
2900
2901
0
  if (cmd->argc > 1) {
2902
0
    CONF_ERROR(cmd, "wrong number of parameters");
2903
0
  }
2904
2905
0
  CHECK_CONF(cmd, CONF_CLASS);
2906
2907
0
  pr_parser_config_ctxt_close(&empty_ctx);
2908
0
  if (empty_ctx == TRUE) {
2909
0
    pr_log_debug(DEBUG3, "%s: ignoring empty section", (char *) cmd->argv[0]);
2910
0
  }
2911
2912
0
  if (pr_class_close() < 0) {
2913
0
    pr_log_pri(PR_LOG_WARNING, "warning: empty <Class> definition");
2914
0
  }
2915
2916
0
  return PR_HANDLED(cmd);
2917
0
}
2918
2919
0
MODRET add_global(cmd_rec *cmd) {
2920
0
  config_rec *c = NULL;
2921
2922
0
  if (cmd->argc-1 != 0) {
2923
0
    CONF_ERROR(cmd, "Too many parameters");
2924
0
  }
2925
2926
0
  CHECK_CONF(cmd, CONF_ROOT|CONF_VIRTUAL);
2927
2928
0
  c = pr_parser_config_ctxt_open(cmd->argv[0]);
2929
0
  c->config_type = CONF_GLOBAL;
2930
2931
0
  return PR_HANDLED(cmd);
2932
0
}
2933
2934
0
MODRET end_global(cmd_rec *cmd) {
2935
0
  int empty_ctxt = FALSE;
2936
2937
0
  if (cmd->argc > 1) {
2938
0
    CONF_ERROR(cmd, "wrong number of parameters");
2939
0
  }
2940
2941
0
  CHECK_CONF(cmd, CONF_GLOBAL);
2942
2943
0
  pr_parser_config_ctxt_close(&empty_ctxt);
2944
2945
0
  if (empty_ctxt) {
2946
0
    pr_log_debug(DEBUG3, "%s: ignoring empty section", (char *) cmd->argv[0]);
2947
0
  }
2948
2949
0
  return PR_HANDLED(cmd);
2950
0
}
2951
2952
0
MODRET add_limit(cmd_rec *cmd) {
2953
0
  register unsigned int i;
2954
0
  config_rec *c = NULL;
2955
0
  int cargc, have_cdup = FALSE, have_xcup = FALSE, have_mkd = FALSE,
2956
0
    have_xmkd = FALSE, have_pwd = FALSE, have_xpwd = FALSE, have_rmd = FALSE,
2957
0
    have_xrmd = FALSE;
2958
0
  void **cargv, **elts;
2959
0
  array_header *list;
2960
2961
0
  if (cmd->argc < 2) {
2962
0
    CONF_ERROR(cmd, "directive requires one or more commands");
2963
0
  }
2964
2965
0
  CHECK_CONF(cmd, CONF_ROOT|CONF_VIRTUAL|CONF_DIR|CONF_ANON|CONF_DYNDIR|CONF_GLOBAL);
2966
2967
0
  c = pr_parser_config_ctxt_open("Limit");
2968
0
  c->config_type = CONF_LIMIT;
2969
0
  cargc = cmd->argc;
2970
0
  cargv = cmd->argv;
2971
2972
0
  list = make_array(c->pool, c->argc + 1, sizeof(void *));
2973
2974
0
  while (cargc-- && *(++cargv)) {
2975
0
    char *ent = NULL, *str;
2976
2977
0
    str = pstrdup(cmd->tmp_pool, *((char **) cargv));
2978
0
    while ((ent = pr_str_get_token(&str, ",")) != NULL) {
2979
0
      pr_signals_handle();
2980
2981
0
      if (*ent) {
2982
0
        *((char **) push_array(list)) = pstrdup(c->pool, ent);
2983
0
      }
2984
0
    }
2985
0
  }
2986
2987
  /* Now iterate though the list, looking for the following commands:
2988
   *
2989
   *  CDUP/XCUP
2990
   *  MKD/XMKD
2991
   *  PWD/XPWD
2992
   *  RMD/XRMD
2993
   *
2994
   * If we see one of these without its counterpart, automatically add
2995
   * the counterpart (see Bug#3077).
2996
   */
2997
2998
0
  elts = list->elts;
2999
0
  for (i = 0; i < list->nelts; i++) {
3000
0
    if (strcasecmp(elts[i], C_CDUP) == 0) {
3001
0
      have_cdup = TRUE;
3002
3003
0
    } else if (strcasecmp(elts[i], C_XCUP) == 0) {
3004
0
      have_xcup = TRUE;
3005
3006
0
    } else if (strcasecmp(elts[i], C_MKD) == 0) {
3007
0
      have_mkd = TRUE;
3008
3009
0
    } else if (strcasecmp(elts[i], C_XMKD) == 0) {
3010
0
      have_xmkd = TRUE;
3011
3012
0
    } else if (strcasecmp(elts[i], C_PWD) == 0) {
3013
0
      have_pwd = TRUE;
3014
3015
0
    } else if (strcasecmp(elts[i], C_XPWD) == 0) {
3016
0
      have_xpwd = TRUE;
3017
3018
0
    } else if (strcasecmp(elts[i], C_RMD) == 0) {
3019
0
      have_rmd = TRUE;
3020
3021
0
    } else if (strcasecmp(elts[i], C_XRMD) == 0) {
3022
0
      have_xrmd = TRUE;
3023
0
    }
3024
0
  }
3025
3026
0
  if (have_cdup && !have_xcup) {
3027
0
    *((char **) push_array(list)) = pstrdup(c->pool, C_XCUP);
3028
0
  }
3029
3030
0
  if (!have_cdup && have_xcup) {
3031
0
    *((char **) push_array(list)) = pstrdup(c->pool, C_CDUP);
3032
0
  }
3033
3034
0
  if (have_mkd && !have_xmkd) {
3035
0
    *((char **) push_array(list)) = pstrdup(c->pool, C_XMKD);
3036
0
  }
3037
3038
0
  if (!have_mkd && have_xmkd) {
3039
0
    *((char **) push_array(list)) = pstrdup(c->pool, C_MKD);
3040
0
  }
3041
3042
0
  if (have_pwd && !have_xpwd) {
3043
0
    *((char **) push_array(list)) = pstrdup(c->pool, C_XPWD);
3044
0
  }
3045
3046
0
  if (!have_pwd && have_xpwd) {
3047
0
    *((char **) push_array(list)) = pstrdup(c->pool, C_PWD);
3048
0
  }
3049
3050
0
  if (have_rmd && !have_xrmd) {
3051
0
    *((char **) push_array(list)) = pstrdup(c->pool, C_XRMD);
3052
0
  }
3053
3054
0
  if (!have_rmd && have_xrmd) {
3055
0
    *((char **) push_array(list)) = pstrdup(c->pool, C_RMD);
3056
0
  }
3057
3058
0
  c->argc = list->nelts;
3059
0
  c->argv = list->elts;
3060
3061
0
  return PR_HANDLED(cmd);
3062
0
}
3063
3064
0
MODRET set_order(cmd_rec *cmd) {
3065
0
  int order = -1;
3066
0
  char *arg = "";
3067
0
  config_rec *c = NULL;
3068
3069
0
  if (cmd->argc != 2 &&
3070
0
      cmd->argc != 3) {
3071
0
    CONF_ERROR(cmd, "wrong number of parameters");
3072
0
  }
3073
0
  CHECK_CONF(cmd, CONF_LIMIT);
3074
3075
0
  if (cmd->argc == 2) {
3076
0
    arg = cmd->argv[1];
3077
3078
0
  } else {
3079
    /* Concatenate our parameters. */
3080
0
    arg = pstrcat(cmd->tmp_pool, arg, (char *) cmd->argv[1],
3081
0
      (char *) cmd->argv[2], NULL);
3082
0
  }
3083
3084
0
  if (strcasecmp(arg, "allow,deny") == 0) {
3085
0
    order = ORDER_ALLOWDENY;
3086
3087
0
  } else if (strcasecmp(arg, "deny,allow") == 0) {
3088
0
    order = ORDER_DENYALLOW;
3089
3090
0
  } else {
3091
0
    CONF_ERROR(cmd, pstrcat(cmd->tmp_pool, "'", arg, "': invalid argument",
3092
0
      NULL));
3093
0
  }
3094
3095
0
  c = add_config_param(cmd->argv[0], 1, NULL);
3096
0
  c->argv[0] = pcalloc(c->pool, sizeof(int));
3097
0
  *((int *) c->argv[0]) = order;
3098
3099
0
  return PR_HANDLED(cmd);
3100
0
}
3101
3102
0
MODRET set_allowdenyusergroupclass(cmd_rec *cmd) {
3103
0
  config_rec *c;
3104
0
  void **argv;
3105
0
  unsigned int argc;
3106
0
  int eval_type;
3107
0
  array_header *acl = NULL;
3108
3109
0
  CHECK_CONF(cmd, CONF_LIMIT);
3110
3111
0
  if (cmd->argc < 2) {
3112
0
    CONF_ERROR(cmd, "wrong number of parameters");
3113
0
  }
3114
3115
  /* For AllowClass/DenyClass and AllowUser/DenyUser, the default expression
3116
   * type is "or".
3117
   */
3118
0
  if (strcmp(cmd->argv[0], "AllowClass") == 0 ||
3119
0
      strcmp(cmd->argv[0], "AllowUser") == 0 ||
3120
0
      strcmp(cmd->argv[0], "DenyClass") == 0 ||
3121
0
      strcmp(cmd->argv[0], "DenyUser") == 0) {
3122
0
    eval_type = PR_EXPR_EVAL_OR;
3123
3124
  /* For AllowGroup and DenyGroup, the default expression type is "and". */
3125
0
  } else {
3126
0
    eval_type = PR_EXPR_EVAL_AND;
3127
0
  }
3128
3129
0
  if (cmd->argc > 2) {
3130
    /* Check the first parameter to see if it is an evaluation modifier:
3131
     * "and", "or", or "regex".
3132
     */
3133
0
    if (strcasecmp(cmd->argv[1], "AND") == 0) {
3134
0
      eval_type = PR_EXPR_EVAL_AND;
3135
0
      argc = cmd->argc-2;
3136
0
      argv = cmd->argv;
3137
3138
0
    } else if (strcasecmp(cmd->argv[1], "OR") == 0) {
3139
0
      eval_type = PR_EXPR_EVAL_OR;
3140
0
      argc = cmd->argc-2;
3141
0
      argv = cmd->argv+1;
3142
3143
0
    } else if (strcasecmp(cmd->argv[1], "regex") == 0) {
3144
0
#ifdef PR_USE_REGEX
3145
0
      pr_regex_t *pre;
3146
0
      int res;
3147
3148
0
      if (cmd->argc != 3) {
3149
0
        CONF_ERROR(cmd, "wrong number of parameters");
3150
0
      }
3151
3152
0
      pre = pr_regexp_alloc(&core_module);
3153
3154
0
      res = pr_regexp_compile_posix(pre, cmd->argv[2], REG_EXTENDED|REG_NOSUB);
3155
0
      if (res != 0) {
3156
0
        char errstr[200] = {'\0'};
3157
3158
0
        pr_regexp_error(res, pre, errstr, sizeof(errstr));
3159
0
        pr_regexp_free(NULL, pre);
3160
3161
0
        CONF_ERROR(cmd, pstrcat(cmd->tmp_pool, "'", (char *) cmd->argv[2],
3162
0
          "' failed regex compilation: ", errstr, NULL));
3163
0
      }
3164
3165
0
      c = add_config_param(cmd->argv[0], 2, NULL, NULL);
3166
0
      c->argv[0] = pcalloc(c->pool, sizeof(unsigned char));
3167
0
      *((unsigned char *) c->argv[0]) = PR_EXPR_EVAL_REGEX;
3168
0
      c->argv[1] = (void *) pre;
3169
0
      c->flags |= CF_MERGEDOWN_MULTI;
3170
3171
0
      return PR_HANDLED(cmd);
3172
#else
3173
      CONF_ERROR(cmd, "The 'regex' parameter cannot be used on this system, "
3174
        "as you do not have POSIX compliant regex support");
3175
#endif /* regex support */
3176
3177
0
    } else {
3178
0
      argc = cmd->argc-1;
3179
0
      argv = cmd->argv;
3180
0
    }
3181
3182
0
  } else {
3183
0
    argc = cmd->argc-1;
3184
0
    argv = cmd->argv;
3185
0
  }
3186
3187
0
  acl = pr_expr_create(cmd->tmp_pool, &argc, (char **) argv);
3188
0
  if (acl == NULL) {
3189
0
    CONF_ERROR(cmd, pstrcat(cmd->tmp_pool, "error creating expression: ",
3190
0
      strerror(errno), NULL));
3191
0
  }
3192
3193
0
  c = add_config_param(cmd->argv[0], 0);
3194
3195
0
  c->argc = acl->nelts + 1;
3196
0
  c->argv = pcalloc(c->pool, (c->argc + 1) * sizeof(void *));
3197
3198
0
  c->argv[0] = pcalloc(c->pool, sizeof(unsigned char));
3199
0
  *((unsigned char *) c->argv[0]) = eval_type;
3200
3201
0
  argv = &(c->argv[1]);
3202
3203
0
  while (acl->nelts-- > 0) {
3204
0
    pr_signals_handle();
3205
3206
0
    *argv++ = pstrdup(c->pool, *((char **) acl->elts));
3207
0
    acl->elts = ((char **) acl->elts) + 1;
3208
0
  }
3209
3210
0
  *argv = NULL;
3211
3212
0
  c->flags |= CF_MERGEDOWN_MULTI;
3213
0
  return PR_HANDLED(cmd);
3214
0
}
3215
3216
0
MODRET set_allowdeny(cmd_rec *cmd) {
3217
0
  int argc;
3218
0
  void **argv;
3219
0
  pr_netacl_t **aclargv;
3220
0
  array_header *list;
3221
0
  config_rec *c;
3222
3223
0
  CHECK_CONF(cmd, CONF_LIMIT);
3224
3225
  /* Syntax: allow [from] [all|none]|host|network[,...] */
3226
0
  list = make_array(cmd->tmp_pool, cmd->argc, sizeof(pr_netacl_t *));
3227
0
  argc = cmd->argc-1;
3228
0
  argv = cmd->argv;
3229
3230
0
  c = add_config_param(cmd->argv[0], 0);
3231
3232
  /* Skip optional "from" keyword. The '!' character is allowed in front of a
3233
   * hostmask or IP, but NOT in front of "ALL" or "NONE".
3234
   */
3235
3236
0
  while (argc && *(argv+1)) {
3237
0
    if (strcasecmp("from", *(((char **) argv) + 1)) == 0) {
3238
0
      argv++;
3239
0
      argc--;
3240
0
      continue;
3241
0
    }
3242
3243
0
    if (strcasecmp("!all", *(((char **) argv) + 1)) == 0 ||
3244
0
         strcasecmp("!none", *(((char **) argv) + 1)) == 0) {
3245
0
      CONF_ERROR(cmd, "the ! negation operator cannot be used with ALL/NONE");
3246
0
    }
3247
3248
0
    if (strcasecmp("all", *(argv+1)) == 0 ||
3249
0
        strcasecmp("none", *(argv+1)) == 0) {
3250
0
      *((pr_netacl_t **) push_array(list)) =
3251
0
        pr_netacl_create(c->pool, *(argv+1));
3252
0
      argc = 0;
3253
0
    }
3254
3255
0
    break;
3256
0
  }
3257
3258
  /* Parse any other/remaining rules. */
3259
0
  while (argc-- && *(++argv)) {
3260
0
    char *ent = NULL;
3261
0
    char *s = pstrdup(cmd->tmp_pool, *argv);
3262
3263
    /* Parse the string into comma-delimited entries */
3264
0
    while ((ent = pr_str_get_token(&s, ",")) != NULL) {
3265
0
      if (*ent) {
3266
0
        pr_netacl_t *acl;
3267
3268
0
        if (strcasecmp(ent, "all") == 0 ||
3269
0
            strcasecmp(ent, "none") == 0) {
3270
0
          list->nelts = 0;
3271
0
          argc = 0;
3272
0
          break;
3273
0
        }
3274
3275
0
        acl = pr_netacl_create(c->pool, ent);
3276
0
        if (acl == NULL) {
3277
0
          CONF_ERROR(cmd, pstrcat(cmd->tmp_pool, "bad ACL definition '",
3278
0
            ent, "': ", strerror(errno), NULL));
3279
0
        }
3280
3281
0
        pr_trace_msg("netacl", 9, "'%s' parsed into netacl '%s'", ent,
3282
0
          pr_netacl_get_str(cmd->tmp_pool, acl));
3283
3284
0
        *((pr_netacl_t **) push_array(list)) = acl;
3285
0
      }
3286
0
    }
3287
0
  }
3288
3289
0
  if (!list->nelts)
3290
0
    CONF_ERROR(cmd, pstrcat(cmd->tmp_pool, "syntax: ", cmd->argv[0],
3291
0
      " [from] [all|none]|host|network[,...]", NULL));
3292
3293
0
  c->argc = list->nelts;
3294
0
  c->argv = pcalloc(c->pool, (c->argc+1) * sizeof(pr_netacl_t *));
3295
0
  aclargv = (pr_netacl_t **) c->argv;
3296
3297
0
  while (list->nelts--) {
3298
0
    *aclargv++ = *((pr_netacl_t **) list->elts);
3299
0
    list->elts = ((pr_netacl_t **) list->elts) + 1;
3300
0
  }
3301
0
  *aclargv = NULL;
3302
3303
0
  return PR_HANDLED(cmd);
3304
0
}
3305
3306
0
MODRET set_denyall(cmd_rec *cmd) {
3307
0
  config_rec *c = NULL;
3308
3309
0
  if (cmd->argc > 1) {
3310
0
    CONF_ERROR(cmd, "wrong number of parameters");
3311
0
  }
3312
3313
0
  CHECK_CONF(cmd, CONF_LIMIT|CONF_ANON|CONF_DIR|CONF_DYNDIR);
3314
3315
0
  c = add_config_param(cmd->argv[0], 1, NULL);
3316
0
  c->argv[0] = pcalloc(c->pool, sizeof(unsigned char));
3317
0
  *((unsigned char *) c->argv[0]) = TRUE;
3318
3319
0
  return PR_HANDLED(cmd);
3320
0
}
3321
3322
0
MODRET set_allowall(cmd_rec *cmd) {
3323
0
  config_rec *c = NULL;
3324
3325
0
  if (cmd->argc > 1) {
3326
0
    CONF_ERROR(cmd, "wrong number of parameters");
3327
0
  }
3328
3329
0
  CHECK_CONF(cmd, CONF_LIMIT|CONF_ANON|CONF_DIR|CONF_DYNDIR);
3330
3331
0
  c = add_config_param(cmd->argv[0], 1, NULL);
3332
0
  c->argv[0] = pcalloc(c->pool, sizeof(unsigned char));
3333
0
  *((unsigned char *) c->argv[0]) = TRUE;
3334
3335
0
  return PR_HANDLED(cmd);
3336
0
}
3337
3338
0
MODRET set_authorder(cmd_rec *cmd) {
3339
0
  register unsigned int i = 0;
3340
0
  config_rec *c = NULL;
3341
0
  array_header *module_list = NULL;
3342
3343
0
  CHECK_ARGS(cmd, 1);
3344
0
  CHECK_CONF(cmd, CONF_ROOT|CONF_VIRTUAL|CONF_GLOBAL);
3345
3346
  /* Check to see if the directive has already been set */
3347
0
  if (find_config(cmd->server->conf, CONF_PARAM, cmd->argv[0], FALSE)) {
3348
0
    CONF_ERROR(cmd, "AuthOrder has already been configured");
3349
0
  }
3350
3351
0
  c = add_config_param(cmd->argv[0], 1, NULL);
3352
0
  module_list = make_array(c->pool, 0, sizeof(char *));
3353
3354
0
  for (i = 1; i < cmd->argc; i++) {
3355
0
    *((char **) push_array(module_list)) = pstrdup(c->pool, cmd->argv[i]);
3356
0
  }
3357
0
  c->argv[0] = (void *) module_list;
3358
3359
0
  return PR_HANDLED(cmd);
3360
0
}
3361
3362
0
MODRET end_limit(cmd_rec *cmd) {
3363
0
  int empty_ctxt = FALSE;
3364
3365
0
  if (cmd->argc > 1) {
3366
0
    CONF_ERROR(cmd, "wrong number of parameters");
3367
0
  }
3368
3369
0
  CHECK_CONF(cmd, CONF_LIMIT);
3370
3371
0
  pr_parser_config_ctxt_close(&empty_ctxt);
3372
3373
0
  if (empty_ctxt) {
3374
0
    pr_log_debug(DEBUG3, "%s: ignoring empty section", (char *) cmd->argv[0]);
3375
0
  }
3376
3377
0
  return PR_HANDLED(cmd);
3378
0
}
3379
3380
0
MODRET set_ignorehidden(cmd_rec *cmd) {
3381
0
  int ignore_hidden = -1;
3382
0
  config_rec *c = NULL;
3383
3384
0
  CHECK_ARGS(cmd, 1);
3385
0
  CHECK_CONF(cmd, CONF_LIMIT);
3386
3387
0
  ignore_hidden = get_boolean(cmd, 1);
3388
0
  if (ignore_hidden == -1) {
3389
0
    CONF_ERROR(cmd, "expected Boolean parameter");
3390
0
  }
3391
3392
0
  c = add_config_param(cmd->argv[0], 1, NULL);
3393
0
  c->argv[0] = pcalloc(c->pool, sizeof(unsigned char));
3394
0
  *((unsigned char *) c->argv[0]) = ignore_hidden;
3395
3396
0
  return PR_HANDLED(cmd);
3397
0
}
3398
3399
/* usage: DisplayChdir path [on|off] */
3400
0
MODRET set_displaychdir(cmd_rec *cmd) {
3401
0
  config_rec *c = NULL;
3402
0
  int display_once = FALSE;
3403
3404
0
  if (cmd->argc-1 < 1 ||
3405
0
      cmd->argc-1 > 2) {
3406
0
    CONF_ERROR(cmd, "wrong number of parameters");
3407
0
  }
3408
3409
0
  CHECK_CONF(cmd, CONF_ROOT|CONF_VIRTUAL|CONF_GLOBAL|CONF_ANON|CONF_DIR);
3410
3411
0
  if (cmd->argc-1 == 2) {
3412
0
    display_once = get_boolean(cmd, 2);
3413
0
    if (display_once < 0) {
3414
0
      CONF_ERROR(cmd, "expected Boolean parameter");
3415
0
    }
3416
0
  }
3417
3418
  /* Note that we allocate one extra slot, for a possible fh, as for
3419
   * absolute paths in a chrooted session (Issue #1688).
3420
   */
3421
0
  c = add_config_param(cmd->argv[0], 3, NULL, NULL, NULL);
3422
0
  c->argv[0] = pstrdup(c->pool, cmd->argv[1]);
3423
0
  c->argv[1] = pcalloc(c->pool, sizeof(int));
3424
0
  *((int *) c->argv[1]) = display_once;
3425
3426
0
  c->flags |= CF_MERGEDOWN;
3427
0
  return PR_HANDLED(cmd);
3428
0
}
3429
3430
0
MODRET set_displayconnect(cmd_rec *cmd) {
3431
0
  CHECK_ARGS(cmd, 1);
3432
0
  CHECK_CONF(cmd, CONF_ROOT|CONF_VIRTUAL|CONF_GLOBAL);
3433
3434
0
  add_config_param_str(cmd->argv[0], 1, cmd->argv[1]);
3435
0
  return PR_HANDLED(cmd);
3436
0
}
3437
3438
0
MODRET set_displayquit(cmd_rec *cmd) {
3439
0
  config_rec *c = NULL;
3440
3441
0
  CHECK_ARGS(cmd, 1);
3442
0
  CHECK_CONF(cmd, CONF_ROOT|CONF_VIRTUAL|CONF_GLOBAL|CONF_ANON);
3443
3444
0
  c = add_config_param_str(cmd->argv[0], 1, cmd->argv[1]);
3445
0
  c->flags |= CF_MERGEDOWN;
3446
3447
0
  return PR_HANDLED(cmd);
3448
0
}
3449
3450
0
MODRET add_virtualhost(cmd_rec *cmd) {
3451
0
  const char *name, *addr_ipstr;
3452
0
  server_rec *s = NULL;
3453
0
  const pr_netaddr_t *addr = NULL;
3454
0
  array_header *addrs = NULL;
3455
0
  unsigned int addr_flags = PR_NETADDR_GET_ADDR_FL_INCL_DEVICE|PR_NETADDR_GET_ADDR_FL_EXCL_CACHE;
3456
3457
0
  if (cmd->argc-1 < 1) {
3458
0
    CONF_ERROR(cmd, "wrong number of parameters");
3459
0
  }
3460
0
  CHECK_CONF(cmd, CONF_ROOT);
3461
3462
0
  name = cmd->argv[1];
3463
0
  s = pr_parser_server_ctxt_open(name);
3464
0
  if (s == NULL) {
3465
0
    CONF_ERROR(cmd, "unable to create virtual server configuration");
3466
0
  }
3467
3468
  /* It's possible for a server to have multiple IP addresses (e.g. a DNS
3469
   * name that has both A and AAAA records).  We need to handle that case
3470
   * here by looking up all of a server's addresses, and making sure there
3471
   * are server_recs for each one.
3472
   */
3473
3474
0
  addr = pr_netaddr_get_addr2(cmd->tmp_pool, name, &addrs, addr_flags);
3475
0
  if (addr == NULL) {
3476
0
    CONF_ERROR(cmd, pstrcat(cmd->tmp_pool, "error resolving '", name, "': ",
3477
0
      strerror(errno), NULL));
3478
0
  }
3479
3480
  /* If the given name is a DNS name, automatically add a ServerAlias
3481
   * directive.
3482
   */
3483
0
  if (pr_netaddr_is_v4(name) == FALSE &&
3484
0
      pr_netaddr_is_v6(name) == FALSE) {
3485
0
    add_config_param_str("ServerAlias", 1, name);
3486
0
  }
3487
3488
0
  addr_ipstr = pr_netaddr_get_ipstr(addr);
3489
3490
0
  if (addrs != NULL) {
3491
0
    register unsigned int i;
3492
0
    pr_netaddr_t **elts = addrs->elts;
3493
3494
    /* For every additional address, implicitly add a bind record. */
3495
0
    for (i = 0; i < addrs->nelts; i++) {
3496
0
      const char *ipstr;
3497
3498
0
      ipstr = pr_netaddr_get_ipstr(elts[i]);
3499
3500
      /* Skip duplicate addresses. */
3501
0
      if (strcmp(addr_ipstr, ipstr) == 0) {
3502
0
        continue;
3503
0
      }
3504
3505
0
      add_config_param_str("_bind_", 1, ipstr);
3506
0
    }
3507
0
  }
3508
3509
  /* Handle multiple addresses in a <VirtualHost> directive.  We do
3510
   * this by adding bind directives to the server_rec created for the
3511
   * first address.
3512
   */
3513
0
  if (cmd->argc-1 > 1) {
3514
0
    register unsigned int i;
3515
3516
0
    for (i = 2; i < cmd->argc; i++) {
3517
0
      addrs = NULL;
3518
3519
0
      name = cmd->argv[i];
3520
0
      addr = pr_netaddr_get_addr2(cmd->tmp_pool, name, &addrs, addr_flags);
3521
0
      if (addr == NULL) {
3522
0
        CONF_ERROR(cmd, pstrcat(cmd->tmp_pool, "error resolving '", name, "': ",
3523
0
          strerror(errno), NULL));
3524
0
      }
3525
3526
      /* If the given name is a DNS name, automatically add a ServerAlias
3527
       * directive.
3528
       */
3529
0
      if (pr_netaddr_is_v4(name) == FALSE &&
3530
0
          pr_netaddr_is_v6(name) == FALSE) {
3531
0
        add_config_param_str("ServerAlias", 1, name);
3532
0
      }
3533
3534
0
      addr_ipstr = pr_netaddr_get_ipstr(addr);
3535
0
      add_config_param_str("_bind_", 1, addr_ipstr);
3536
3537
0
      if (addrs != NULL) {
3538
0
        register unsigned int j;
3539
0
        pr_netaddr_t **elts = addrs->elts;
3540
3541
        /* For every additional address, implicitly add a bind record. */
3542
0
        for (j = 0; j < addrs->nelts; j++) {
3543
0
          const char *ipstr;
3544
3545
0
          ipstr = pr_netaddr_get_ipstr(elts[j]);
3546
3547
          /* Skip duplicate addresses. */
3548
0
          if (strcmp(addr_ipstr, ipstr) == 0) {
3549
0
            continue;
3550
0
          }
3551
3552
0
          add_config_param_str("_bind_", 1, ipstr);
3553
0
        }
3554
0
      }
3555
0
    }
3556
0
  }
3557
3558
0
  return PR_HANDLED(cmd);
3559
0
}
3560
3561
0
MODRET end_virtualhost(cmd_rec *cmd) {
3562
0
  server_rec *s = NULL, *next_s = NULL;
3563
0
  const pr_netaddr_t *addr = NULL;
3564
0
  const char *address = NULL;
3565
0
  unsigned int addr_flags = PR_NETADDR_GET_ADDR_FL_INCL_DEVICE|PR_NETADDR_GET_ADDR_FL_EXCL_CACHE;
3566
3567
0
  if (cmd->argc > 1) {
3568
0
    CONF_ERROR(cmd, "wrong number of parameters");
3569
0
  }
3570
3571
0
  CHECK_CONF(cmd, CONF_VIRTUAL);
3572
3573
0
  if (cmd->server->ServerAddress) {
3574
0
    address = cmd->server->ServerAddress;
3575
3576
0
  } else {
3577
0
    address = pr_netaddr_get_localaddr_str(cmd->tmp_pool);
3578
0
  }
3579
3580
  /* Any additional addresses associated with the configured address have
3581
   * already been handled, so we can ignore them here.
3582
   */
3583
0
  addr = pr_netaddr_get_addr2(cmd->tmp_pool, address, NULL, addr_flags);
3584
0
  if (addr == NULL) {
3585
    /* This bad server context will be removed in fixup_servers(), after
3586
     * the parsing has completed, so we need do nothing else here.
3587
     */
3588
0
    pr_log_pri(PR_LOG_WARNING,
3589
0
      "warning: unable to determine IP address of '%s'", address);
3590
0
  }
3591
3592
0
  if (AddressCollisionCheck) {
3593
    /* Check if this server's address/port combination is already being used. */
3594
0
    for (s = (server_rec *) server_list->xas_list; addr && s; s = next_s) {
3595
0
      next_s = s->next;
3596
3597
      /* Have to resort to duplicating some of fixup_servers()'s functionality
3598
       * here, to do this check The Right Way(tm).
3599
       */
3600
0
      if (s != cmd->server) {
3601
0
        const char *serv_addrstr = NULL;
3602
0
        const pr_netaddr_t *serv_addr = NULL;
3603
3604
0
        if (s->addr) {
3605
0
          serv_addr = s->addr;
3606
3607
0
        } else {
3608
0
          serv_addrstr = s->ServerAddress ? s->ServerAddress :
3609
0
            pr_netaddr_get_localaddr_str(cmd->tmp_pool);
3610
3611
0
          serv_addr = pr_netaddr_get_addr2(cmd->tmp_pool, serv_addrstr, NULL,
3612
0
            addr_flags);
3613
0
        }
3614
3615
0
        if (serv_addr == NULL) {
3616
0
          pr_log_pri(PR_LOG_WARNING,
3617
0
            "warning: unable to determine IP address of '%s'", serv_addrstr);
3618
3619
0
        } else if (pr_netaddr_cmp(addr, serv_addr) == 0 &&
3620
0
            cmd->server->ServerPort == s->ServerPort) {
3621
0
          config_rec *c;
3622
3623
          /* If this server has a ServerAlias, it means it's a named vhost and
3624
           * can be used for name-based virtual hosting.  Which, in turn, means
3625
           * that this collision is expected, even wanted.
3626
           */
3627
0
          c = find_config(cmd->server->conf, CONF_PARAM, "ServerAlias", FALSE);
3628
0
          if (c == NULL) {
3629
0
            pr_log_pri(PR_LOG_WARNING,
3630
0
              "warning: \"%s\" address/port (%s:%d) already in use by \"%s\"",
3631
0
              cmd->server->ServerName ? cmd->server->ServerName : "ProFTPD",
3632
0
              pr_netaddr_get_ipstr(addr), cmd->server->ServerPort,
3633
0
              s->ServerName ? s->ServerName : "ProFTPD");
3634
3635
0
            if (xaset_remove(server_list, (xasetmember_t *) cmd->server) == 1) {
3636
0
              destroy_pool(cmd->server->pool);
3637
0
            }
3638
0
          }
3639
0
        }
3640
3641
0
        continue;
3642
0
      }
3643
0
    }
3644
0
  }
3645
3646
0
  if (pr_parser_server_ctxt_close() == NULL) {
3647
0
    CONF_ERROR(cmd, "must have matching <VirtualHost> directive");
3648
0
  }
3649
3650
0
  return PR_HANDLED(cmd);
3651
0
}
3652
3653
#ifdef PR_USE_REGEX
3654
0
MODRET regex_filters(cmd_rec *cmd) {
3655
0
  pr_regex_t *allow_regex = NULL, *deny_regex = NULL;
3656
3657
  /* Don't apply the filter checks to passwords (arguments to the PASS
3658
   * command).
3659
   */
3660
0
  if (strcasecmp(cmd->argv[0], C_PASS) == 0) {
3661
0
    return PR_DECLINED(cmd);
3662
0
  }
3663
3664
  /* Check for an AllowFilter */
3665
0
  allow_regex = get_param_ptr(CURRENT_CONF, "AllowFilter", FALSE);
3666
0
  if (allow_regex != NULL &&
3667
0
      cmd->arg != NULL &&
3668
0
      pr_regexp_exec(allow_regex, cmd->arg, 0, NULL, 0, 0, 0) != 0) {
3669
0
    pr_log_debug(DEBUG2, "'%s %s' denied by AllowFilter", (char *) cmd->argv[0],
3670
0
      cmd->arg);
3671
0
    pr_response_add_err(R_550, _("%s: Forbidden command argument"), cmd->arg);
3672
3673
0
    pr_cmd_set_errno(cmd, EACCES);
3674
0
    errno = EACCES;
3675
0
    return PR_ERROR(cmd);
3676
0
  }
3677
3678
  /* Check for a DenyFilter */
3679
0
  deny_regex = get_param_ptr(CURRENT_CONF, "DenyFilter", FALSE);
3680
0
  if (deny_regex != NULL &&
3681
0
      cmd->arg != NULL &&
3682
0
      pr_regexp_exec(deny_regex, cmd->arg, 0, NULL, 0, 0, 0) == 0) {
3683
0
    pr_log_debug(DEBUG2, "'%s %s' denied by DenyFilter", (char *) cmd->argv[0],
3684
0
      cmd->arg);
3685
0
    pr_response_add_err(R_550, _("%s: Forbidden command argument"), cmd->arg);
3686
3687
0
    pr_cmd_set_errno(cmd, EACCES);
3688
0
    errno = EACCES;
3689
0
    return PR_ERROR(cmd);
3690
0
  }
3691
3692
0
  return PR_DECLINED(cmd);
3693
0
}
3694
#endif /* regex support */
3695
3696
0
MODRET core_pre_any(cmd_rec *cmd) {
3697
0
  unsigned long cmd_delay = 0;
3698
0
  const char *rnfr_path = NULL;
3699
3700
  /* Check for an exceeded MaxCommandRate. */
3701
0
  cmd_delay = core_exceeded_cmd_rate(cmd);
3702
0
  if (cmd_delay > 0) {
3703
0
    struct timeval tv;
3704
3705
0
    pr_event_generate("core.max-command-rate", NULL);
3706
3707
0
    pr_log_pri(PR_LOG_NOTICE,
3708
0
      "MaxCommandRate (%lu cmds/%u %s) exceeded, injecting processing delay "
3709
0
      "of %lu ms", core_max_cmds, core_max_cmd_interval,
3710
0
      core_max_cmd_interval == 1 ? "sec" : "secs", cmd_delay);
3711
3712
0
    pr_trace_msg("command", 8, "MaxCommandRate exceeded, delaying for %lu ms",
3713
0
      cmd_delay);
3714
3715
0
    tv.tv_sec = (cmd_delay / 1000);
3716
0
    tv.tv_usec = (cmd_delay - (tv.tv_sec * 1000)) * 1000;
3717
3718
0
    pr_signals_block();
3719
0
    (void) select(0, NULL, NULL, NULL, &tv);
3720
0
    pr_signals_unblock();
3721
0
  }
3722
3723
  /* Make sure that any command immediately following an RNFR command which
3724
   * is NOT the RNTO command is rejected (see Bug#3829).
3725
   *
3726
   * Make exception for the following commands:
3727
   *
3728
   *  HELP
3729
   *  NOOP
3730
   *  QUIT
3731
   *  STAT
3732
   *
3733
   *  and RFC 2228 commands.
3734
   */
3735
0
  rnfr_path = pr_table_get(session.notes, "mod_core.rnfr-path", NULL);
3736
0
  if (rnfr_path != NULL) {
3737
0
    if (pr_cmd_cmp(cmd, PR_CMD_RNTO_ID) != 0 &&
3738
0
        pr_cmd_cmp(cmd, PR_CMD_HELP_ID) != 0 &&
3739
0
        pr_cmd_cmp(cmd, PR_CMD_NOOP_ID) != 0 &&
3740
0
        pr_cmd_cmp(cmd, PR_CMD_QUIT_ID) != 0 &&
3741
0
        pr_cmd_cmp(cmd, PR_CMD_STAT_ID) != 0) {
3742
0
      int reject_cmd = TRUE;
3743
3744
      /* Perform additional checks if an RFC 2228 auth mechanism (TLS, GSSAPI)
3745
       * has been negotiated/used.
3746
       */
3747
0
      if (session.rfc2228_mech != NULL) {
3748
0
        if (pr_cmd_cmp(cmd, PR_CMD_CCC_ID) == 0 ||
3749
0
            pr_cmd_cmp(cmd, PR_CMD_CONF_ID) == 0 ||
3750
0
            pr_cmd_cmp(cmd, PR_CMD_ENC_ID) == 0 ||
3751
0
            pr_cmd_cmp(cmd, PR_CMD_MIC_ID) == 0 ||
3752
0
            pr_cmd_cmp(cmd, PR_CMD_PBSZ_ID) == 0 ||
3753
0
            pr_cmd_cmp(cmd, PR_CMD_PROT_ID) == 0) {
3754
0
          reject_cmd = FALSE;
3755
0
        }
3756
0
      }
3757
3758
0
      if (reject_cmd) {
3759
0
        pr_log_debug(DEBUG3,
3760
0
          "RNFR followed immediately by %s rather than RNTO, rejecting command",
3761
0
          (char *) cmd->argv[0]);
3762
0
        pr_response_add_err(R_501, _("Bad sequence of commands"));
3763
3764
0
        pr_cmd_set_errno(cmd, EPERM);
3765
0
        errno = EPERM;
3766
0
        return PR_ERROR(cmd);
3767
0
      }
3768
0
    }
3769
0
  }
3770
3771
0
  return PR_DECLINED(cmd);
3772
0
}
3773
3774
0
MODRET core_quit(cmd_rec *cmd) {
3775
0
  int flags = PR_DISPLAY_FL_SEND_NOW;
3776
3777
0
  if (displayquit_fh) {
3778
0
    if (pr_display_fh(displayquit_fh, NULL, R_221, flags) < 0) {
3779
0
      pr_log_debug(DEBUG6, "unable to display DisplayQuit file '%s': %s",
3780
0
        displayquit_fh->fh_path, strerror(errno));
3781
0
    }
3782
3783
0
    pr_fsio_close(displayquit_fh);
3784
0
    displayquit_fh = NULL;
3785
3786
0
  } else {
3787
0
    char *display;
3788
3789
0
    display = get_param_ptr(TOPLEVEL_CONF, "DisplayQuit", FALSE);
3790
0
    if (display) {
3791
0
      if (pr_display_file(display, NULL, R_221, flags) < 0) {
3792
0
        int xerrno = errno;
3793
3794
0
        pr_log_debug(DEBUG6, "unable to display DisplayQuit file '%s': %s",
3795
0
          display, strerror(xerrno));
3796
3797
0
        if (xerrno == ENOENT) {
3798
          /* No file found?  Send our normal fairwell, then. */
3799
0
          pr_response_send(R_221, "%s", _("Goodbye."));
3800
0
        }
3801
0
      }
3802
3803
0
    } else {
3804
0
      pr_response_send(R_221, "%s", _("Goodbye."));
3805
0
    }
3806
0
  }
3807
3808
  /* The LOG_CMD handler for QUIT is responsible for actually ending
3809
   * the session.
3810
   */
3811
3812
0
  return PR_HANDLED(cmd);
3813
0
}
3814
3815
0
MODRET core_log_quit(cmd_rec *cmd) {
3816
3817
0
#ifndef PR_DEVEL_NO_DAEMON
3818
0
  pr_session_disconnect(&core_module, PR_SESS_DISCONNECT_CLIENT_QUIT, NULL);
3819
0
#endif /* PR_DEVEL_NO_DAEMON */
3820
3821
  /* Even though pr_session_end() does not return, this is necessary to avoid
3822
   * compiler warnings.
3823
   */
3824
0
  return PR_HANDLED(cmd);
3825
0
}
3826
3827
0
MODRET core_pwd(cmd_rec *cmd) {
3828
0
  CHECK_CMD_ARGS(cmd, 1);
3829
3830
0
  if (!dir_check(cmd->tmp_pool, cmd, cmd->group, session.vwd, NULL)) {
3831
0
    int xerrno = EACCES;
3832
3833
0
    pr_log_debug(DEBUG7, "%s command denied by <Limit> configuration",
3834
0
      (char *) cmd->argv[0]);
3835
0
    pr_response_add_err(R_550, "%s: %s", (char *) cmd->argv[0],
3836
0
      strerror(xerrno));
3837
3838
0
    pr_cmd_set_errno(cmd, xerrno);
3839
0
    errno = xerrno;
3840
0
    return PR_ERROR(cmd);
3841
0
  }
3842
3843
0
  pr_response_add(R_257, _("\"%s\" is the current directory"),
3844
0
    quote_dir(cmd->tmp_pool, pr_fs_encode_path(cmd->tmp_pool, session.vwd)));
3845
3846
0
  return PR_HANDLED(cmd);
3847
0
}
3848
3849
0
MODRET core_pasv(cmd_rec *cmd) {
3850
0
  unsigned int port = 0;
3851
0
  char *addrstr = NULL, *tmp = NULL;
3852
0
  config_rec *c = NULL;
3853
0
  const pr_netaddr_t *bind_addr = NULL;
3854
0
  const char *proto;
3855
0
  int tcp_nodelay = 1;
3856
3857
0
  if (session.sf_flags & SF_EPSV_ALL) {
3858
0
    pr_response_add_err(R_500, _("Illegal PASV command, EPSV ALL in effect"));
3859
3860
0
    pr_cmd_set_errno(cmd, EPERM);
3861
0
    errno = EPERM;
3862
0
    return PR_ERROR(cmd);
3863
0
  }
3864
3865
0
  CHECK_CMD_ARGS(cmd, 1);
3866
3867
  /* Returning 501 is the best we can do.  It would be nicer if RFC959 allowed
3868
   * 550 as a possible response.
3869
   */
3870
0
  if (!dir_check(cmd->tmp_pool, cmd, cmd->group, session.cwd, NULL)) {
3871
0
    int xerrno = EPERM;
3872
3873
0
    pr_log_debug(DEBUG8, "PASV denied by <Limit> configuration");
3874
0
    pr_response_add_err(R_501, "%s: %s", (char *) cmd->argv[0],
3875
0
      strerror(xerrno));
3876
3877
0
    pr_cmd_set_errno(cmd, xerrno);
3878
0
    errno = xerrno;
3879
0
    return PR_ERROR(cmd);
3880
0
  }
3881
3882
  /* If we already have a passive listen data connection open, kill it. */
3883
0
  if (session.d != NULL) {
3884
0
    pr_inet_close(session.d->pool, session.d);
3885
0
    session.d = NULL;
3886
0
  }
3887
3888
0
  if (pr_netaddr_get_family(session.c->local_addr) == pr_netaddr_get_family(session.c->remote_addr)) {
3889
3890
0
#if defined(PR_USE_IPV6)
3891
0
    if (pr_netaddr_use_ipv6()) {
3892
      /* Make sure that the family is NOT IPv6, even though the family of the
3893
       * local and remote ends match.  The PASV command cannot be used for
3894
       * IPv6 addresses (Bug#3745).
3895
       *
3896
       * However, SOME clients and ALGs ARE able to properly handle the
3897
       * PASV response, even for an IPv6 address.  So we relax this code
3898
       * to merely warn about possible incompatibilities, rather than
3899
       * rejecting the command outright.
3900
       */
3901
0
      if (pr_netaddr_get_family(session.c->local_addr) == AF_INET6) {
3902
0
        pr_log_pri(PR_LOG_INFO,
3903
0
          "sending a PASV response for an IPv6 address '%s'; some FTP clients "
3904
0
          "may have interoperability issues with this response",
3905
0
          pr_netaddr_get_ipstr(session.c->local_addr));
3906
0
        pr_log_pri(PR_LOG_INFO, "%s", "please configure your FTP client "
3907
0
          "to use the IPv6-compatible EPSV/EPRT commands");
3908
0
      }
3909
0
    }
3910
0
#endif /* PR_USE_IPV6 */
3911
3912
0
    bind_addr = session.c->local_addr;
3913
3914
0
  } else {
3915
    /* In this scenario, the server has an IPv6 socket, but the remote client
3916
     * is an IPv4 (or IPv4-mapped IPv6) peer.
3917
     */
3918
0
    bind_addr = pr_netaddr_v6tov4(cmd->pool, session.c->local_addr);
3919
0
  }
3920
3921
0
  c = find_config(main_server->conf, CONF_PARAM, "PassivePorts", FALSE);
3922
0
  if (c != NULL) {
3923
0
    int pasv_min_port = *((int *) c->argv[0]);
3924
0
    int pasv_max_port = *((int *) c->argv[1]);
3925
3926
0
    session.d = pr_inet_create_conn_portrange(session.pool, bind_addr,
3927
0
      pasv_min_port, pasv_max_port);
3928
0
    if (session.d == NULL) {
3929
      /* If not able to open a passive port in the given range, default to
3930
       * normal behavior (using INPORT_ANY), and log the failure.  This
3931
       * indicates a too-small range configuration.
3932
       */
3933
0
      pr_log_pri(PR_LOG_WARNING,
3934
0
        "unable to find open port in PassivePorts range %d-%d: "
3935
0
        "defaulting to INPORT_ANY (consider defining a larger PassivePorts "
3936
0
        "range)", pasv_min_port, pasv_max_port);
3937
0
    }
3938
0
  }
3939
3940
  /* Open up the connection and pass it back. */
3941
0
  if (session.d == NULL) {
3942
0
    session.d = pr_inet_create_conn2(session.pool, -1, bind_addr, INPORT_ANY,
3943
0
      0);
3944
0
  }
3945
3946
0
  if (session.d == NULL) {
3947
0
    int xerrno = errno;
3948
3949
0
    pr_response_add_err(R_425,
3950
0
      _("Unable to build data connection: Internal error"));
3951
3952
0
    pr_cmd_set_errno(cmd, xerrno);
3953
0
    errno = xerrno;
3954
0
    return PR_ERROR(cmd);
3955
0
  }
3956
3957
  /* Make sure that necessary socket options are set on the socket prior
3958
   * to the call to listen(2).
3959
   */
3960
0
  c = find_config(main_server->conf, CONF_PARAM, "TCPNoDelay", FALSE);
3961
0
  if (c != NULL) {
3962
0
    int data_use_nodelay;
3963
3964
0
    data_use_nodelay = *((int *) c->argv[1]);
3965
0
    if (data_use_nodelay == FALSE) {
3966
0
      tcp_nodelay = 0;
3967
0
    }
3968
0
  }
3969
3970
0
  session.d->use_nodelay = tcp_nodelay;
3971
0
  pr_inet_set_proto_opts(session.pool, session.d, main_server->tcp_mss_len,
3972
0
    tcp_nodelay, IPTOS_THROUGHPUT, 1);
3973
0
  pr_inet_generate_socket_event("core.data-listen", main_server,
3974
0
    session.d->local_addr, session.d->listen_fd);
3975
3976
0
  (void) pr_inet_set_block(session.pool, session.d);
3977
0
  if (pr_inet_listen(session.pool, session.d, 1, 0) < 0) {
3978
0
    int xerrno = errno;
3979
3980
0
    pr_response_add_err(R_425, "%s: %s", (char *) cmd->argv[0],
3981
0
      strerror(xerrno));
3982
3983
0
    pr_cmd_set_errno(cmd, xerrno);
3984
0
    errno = xerrno;
3985
0
    return PR_ERROR(cmd);
3986
0
  }
3987
3988
0
  session.d->instrm = pr_netio_open(session.pool, PR_NETIO_STRM_DATA,
3989
0
    session.d->listen_fd, PR_NETIO_IO_RD);
3990
3991
  /* Now tell the client our address/port */
3992
0
  port = session.data_port = session.d->local_port;
3993
0
  session.sf_flags |= SF_PASSIVE;
3994
3995
0
  addrstr = (char *) pr_netaddr_get_ipstr(session.d->local_addr);
3996
3997
  /* Check for a MasqueradeAddress configuration record, and return that
3998
   * addr if appropriate.  Note that if TLSMasqueradeAddress is configured AND
3999
   * this is an FTPS session, TLSMasqueradeAddress will take precedence;
4000
   * see Bug#3862.
4001
   */
4002
0
  proto = pr_session_get_protocol(0);
4003
0
  if (strcmp(proto, "ftps") == 0) {
4004
0
    c = find_config(main_server->conf, CONF_PARAM, "TLSMasqueradeAddress",
4005
0
      FALSE);
4006
0
    if (c != NULL) {
4007
0
      addrstr = (char *) pr_netaddr_get_ipstr(c->argv[0]);
4008
4009
0
    } else {
4010
0
      c = find_config(main_server->conf, CONF_PARAM, "MasqueradeAddress",
4011
0
        FALSE);
4012
0
      if (c != NULL) {
4013
0
        if (c->argv[0] != NULL) {
4014
0
          addrstr = (char *) pr_netaddr_get_ipstr(c->argv[0]);
4015
0
        }
4016
0
      }
4017
0
    }
4018
4019
0
  } else {
4020
0
    c = find_config(main_server->conf, CONF_PARAM, "MasqueradeAddress", FALSE);
4021
0
    if (c != NULL) {
4022
0
      if (c->argv[0] != NULL) {
4023
0
        addrstr = (char *) pr_netaddr_get_ipstr(c->argv[0]);
4024
0
      }
4025
0
    }
4026
0
  }
4027
4028
  /* Fixup the address string for the PASV response. */
4029
0
  tmp = strrchr(addrstr, ':');
4030
0
  if (tmp) {
4031
0
    addrstr = tmp + 1;
4032
0
  }
4033
4034
0
  for (tmp = addrstr; *tmp; tmp++) {
4035
0
    if (*tmp == '.') {
4036
0
      *tmp = ',';
4037
0
    }
4038
0
  }
4039
4040
0
  pr_log_debug(DEBUG1, "Entering Passive Mode (%s,%u,%u).", addrstr,
4041
0
    (port >> 8) & 255, port & 255);
4042
4043
  /* Note: this response is specifically NOT localised because clients
4044
   * assume this particular text.  Nice, huh?
4045
   */
4046
0
  pr_response_add(R_227, "Entering Passive Mode (%s,%u,%u).", addrstr,
4047
0
    (port >> 8) & 255, port & 255);
4048
4049
0
  return PR_HANDLED(cmd);
4050
0
}
4051
4052
0
MODRET core_port(cmd_rec *cmd) {
4053
0
  const pr_netaddr_t *listen_addr = NULL, *port_addr = NULL;
4054
0
  char *port_info;
4055
0
#if defined(PR_USE_IPV6)
4056
0
  char buf[INET6_ADDRSTRLEN] = {'\0'};
4057
#else
4058
  char buf[INET_ADDRSTRLEN] = {'\0'};
4059
#endif /* PR_USE_IPV6 */
4060
0
  unsigned int h1, h2, h3, h4, p1, p2;
4061
0
  unsigned short port;
4062
0
  int *root_revoke = NULL, res;
4063
0
  config_rec *c;
4064
0
  const char *proto;
4065
4066
0
  if (session.sf_flags & SF_EPSV_ALL) {
4067
0
    pr_response_add_err(R_500, _("Illegal PORT command, EPSV ALL in effect"));
4068
4069
0
    pr_cmd_set_errno(cmd, EPERM);
4070
0
    errno = EPERM;
4071
0
    return PR_ERROR(cmd);
4072
0
  }
4073
4074
0
  CHECK_CMD_ARGS(cmd, 2);
4075
4076
  /* Returning 501 is the best we can do.  It would be nicer if RFC959 allowed
4077
   * 550 as a possible response.
4078
   */
4079
0
  if (!dir_check(cmd->tmp_pool, cmd, cmd->group, session.cwd, NULL)) {
4080
0
    int xerrno = EPERM;
4081
4082
0
    pr_log_debug(DEBUG8, "PORT denied by <Limit> configuration");
4083
0
    pr_response_add_err(R_501, "%s: %s", (char *) cmd->argv[0],
4084
0
      strerror(xerrno));
4085
4086
0
    pr_cmd_set_errno(cmd, xerrno);
4087
0
    errno = xerrno;
4088
0
    return PR_ERROR(cmd);
4089
0
  }
4090
4091
  /* Block active transfers (the PORT command) if RootRevoke is in effect
4092
   * and the server's port is below 1024 (binding to the data port in this
4093
   * case would require root privs, which will have been dropped).
4094
   *
4095
   * A RootRevoke value of 0 indicates 'false', 1 indicates 'true', and
4096
   * 2 indicates 'NonCompliantActiveTransfer'.  We only block active transfers
4097
   * for a RootRevoke value of 1.
4098
   */
4099
0
  root_revoke = get_param_ptr(TOPLEVEL_CONF, "RootRevoke", FALSE);
4100
0
  if (root_revoke != NULL &&
4101
0
      *root_revoke == 1 &&
4102
0
      session.c->local_port < 1024) {
4103
0
    pr_log_debug(DEBUG0, "RootRevoke in effect, unable to bind to local "
4104
0
      "port %d for active transfer", session.c->local_port-1);
4105
0
    pr_response_add_err(R_500, _("Unable to service PORT commands"));
4106
4107
0
    pr_cmd_set_errno(cmd, EPERM);
4108
0
    errno = EPERM;
4109
0
    return PR_ERROR(cmd);
4110
0
  }
4111
4112
  /* Format is h1,h2,h3,h4,p1,p2 (ASCII in network order) */
4113
0
  port_info = cmd->argv[1];
4114
0
  if (sscanf(port_info, "%u,%u,%u,%u,%u,%u", &h1, &h2, &h3, &h4, &p1,
4115
0
      &p2) != 6) {
4116
0
    pr_log_debug(DEBUG2, "PORT '%s' is not syntactically valid", port_info);
4117
0
    pr_response_add_err(R_501, _("Illegal PORT command"));
4118
4119
0
    pr_cmd_set_errno(cmd, EPERM);
4120
0
    errno = EPERM;
4121
0
    return PR_ERROR(cmd);
4122
0
  }
4123
4124
0
  if (h1 > 255 || h2 > 255 || h3 > 255 || h4 > 255 || p1 > 255 || p2 > 255 ||
4125
0
      (h1|h2|h3|h4) == 0 || (p1|p2) == 0) {
4126
0
    pr_log_debug(DEBUG2, "PORT '%s' has invalid value(s)", cmd->arg);
4127
0
    pr_response_add_err(R_501, _("Illegal PORT command"));
4128
4129
0
    pr_cmd_set_errno(cmd, EPERM);
4130
0
    errno = EPERM;
4131
0
    return PR_ERROR(cmd);
4132
0
  }
4133
0
  port = ((p1 << 8) | p2);
4134
4135
0
#if defined(PR_USE_IPV6)
4136
0
  if (pr_netaddr_use_ipv6()) {
4137
0
    if (pr_netaddr_get_family(session.c->remote_addr) == AF_INET6) {
4138
0
      pr_snprintf(buf, sizeof(buf), "::ffff:%u.%u.%u.%u", h1, h2, h3, h4);
4139
4140
0
    } else {
4141
0
      pr_snprintf(buf, sizeof(buf), "%u.%u.%u.%u", h1, h2, h3, h4);
4142
0
    }
4143
4144
0
  } else
4145
0
#endif /* PR_USE_IPV6 */
4146
0
  pr_snprintf(buf, sizeof(buf), "%u.%u.%u.%u", h1, h2, h3, h4);
4147
0
  buf[sizeof(buf)-1] = '\0';
4148
4149
0
  port_addr = pr_netaddr_get_addr2(cmd->tmp_pool, buf, NULL,
4150
0
    PR_NETADDR_GET_ADDR_FL_EXCL_CACHE);
4151
0
  if (port_addr == NULL) {
4152
0
    pr_log_debug(DEBUG1, "error getting sockaddr for '%s': %s", buf,
4153
0
      strerror(errno));
4154
0
    pr_response_add_err(R_501, _("Illegal PORT command"));
4155
4156
0
    pr_cmd_set_errno(cmd, EPERM);
4157
0
    errno = EPERM;
4158
0
    return PR_ERROR(cmd);
4159
0
  }
4160
4161
  /* If we are NOT listening on an RFC1918 address, BUT the client HAS
4162
   * sent us an RFC1918 address in its PORT command (which we know to not be
4163
   * routable), then ignore that address, and use the client's remote address.
4164
   */
4165
0
  listen_addr = session.c->local_addr;
4166
4167
0
  proto = pr_session_get_protocol(0);
4168
0
  if (strcmp(proto, "ftps") == 0) {
4169
0
    c = find_config(main_server->conf, CONF_PARAM, "TLSMasqueradeAddress",
4170
0
      FALSE);
4171
0
    if (c != NULL) {
4172
0
      listen_addr = c->argv[0];
4173
4174
0
    } else {
4175
0
      c = find_config(main_server->conf, CONF_PARAM, "MasqueradeAddress",
4176
0
        FALSE);
4177
0
      if (c != NULL) {
4178
0
        if (c->argv[0] != NULL) {
4179
0
          listen_addr = c->argv[0];
4180
0
        }
4181
0
      }
4182
0
    }
4183
4184
0
  } else {
4185
0
    c = find_config(main_server->conf, CONF_PARAM, "MasqueradeAddress", FALSE);
4186
0
    if (c != NULL) {
4187
0
      if (c->argv[0] != NULL) {
4188
0
        listen_addr = c->argv[0];
4189
0
      }
4190
0
    }
4191
0
  }
4192
4193
0
  if (pr_netaddr_is_rfc1918(listen_addr) != TRUE &&
4194
0
      pr_netaddr_is_rfc1918(session.c->remote_addr) != TRUE &&
4195
0
      pr_netaddr_is_rfc1918(port_addr) == TRUE) {
4196
0
    const char *rfc1918_ipstr;
4197
4198
0
    rfc1918_ipstr = pr_netaddr_get_ipstr(port_addr);
4199
0
    port_addr = pr_netaddr_dup(cmd->tmp_pool, session.c->remote_addr);
4200
0
    pr_log_debug(DEBUG1, "client sent RFC1918 address '%s' in PORT command, "
4201
0
      "ignoring it and using '%s'", rfc1918_ipstr,
4202
0
      pr_netaddr_get_ipstr(port_addr));
4203
0
  }
4204
4205
0
  pr_netaddr_set_family(&session.data_addr, pr_netaddr_get_family(port_addr));
4206
0
  pr_netaddr_set_port(&session.data_addr, htons(port));
4207
4208
  /* Make sure that the address specified matches the address from which
4209
   * the control connection is coming.
4210
   */
4211
4212
0
  c = find_config(TOPLEVEL_CONF, CONF_PARAM, "AllowForeignAddress", FALSE);
4213
0
  res = pr_inet_allowforeignaddress(cmd->tmp_pool, port_addr,
4214
0
    session.c->remote_addr, c);
4215
0
  if (res != 1) {
4216
0
    const pr_netaddr_t *remote_addr = session.c->remote_addr;
4217
4218
0
#if defined(PR_USE_IPV6)
4219
0
    if (pr_netaddr_use_ipv6()) {
4220
      /* We can only compare the PORT-given address against the remote client
4221
       * address if the remote client address is an IPv4-mapped IPv6 address.
4222
       */
4223
0
      if (pr_netaddr_get_family(remote_addr) == AF_INET6 &&
4224
0
          pr_netaddr_is_v4mappedv6(remote_addr) != TRUE) {
4225
0
        pr_log_pri(PR_LOG_WARNING,
4226
0
          "Refused PORT %s (IPv4/IPv6 address mismatch)", cmd->arg);
4227
0
        pr_response_add_err(R_500, _("Illegal PORT command"));
4228
4229
0
        pr_cmd_set_errno(cmd, EPERM);
4230
0
        errno = EPERM;
4231
0
        return PR_ERROR(cmd);
4232
0
      }
4233
0
    }
4234
0
#endif /* PR_USE_IPV6 */
4235
4236
0
    pr_log_pri(PR_LOG_WARNING, "Refused PORT %s (address mismatch)",
4237
0
      cmd->arg);
4238
0
    pr_response_add_err(R_500, _("Illegal PORT command"));
4239
4240
0
    pr_cmd_set_errno(cmd, EPERM);
4241
0
    errno = EPERM;
4242
0
    return PR_ERROR(cmd);
4243
0
  }
4244
4245
  /* Additionally, make sure that the port number used is a "high numbered"
4246
   * port, to avoid bounce attacks.  For remote Windows machines, the
4247
   * port numbers mean little.  However, there are also quite a few Unix
4248
   * machines out there for whom the port number matters...
4249
   */
4250
4251
0
  if (port < 1024) {
4252
0
    pr_log_pri(PR_LOG_WARNING,
4253
0
      "Refused PORT %s (port %d below 1024, possible bounce attack)", cmd->arg,
4254
0
      port);
4255
0
    pr_response_add_err(R_500, _("Illegal PORT command"));
4256
4257
0
    pr_cmd_set_errno(cmd, EPERM);
4258
0
    errno = EPERM;
4259
0
    return PR_ERROR(cmd);
4260
0
  }
4261
4262
0
  memcpy(&session.data_addr, port_addr, sizeof(session.data_addr));
4263
0
  session.data_port = port;
4264
0
  session.sf_flags &= (SF_ALL^SF_PASSIVE);
4265
4266
  /* If we already have a data connection open, kill it. */
4267
0
  if (session.d != NULL) {
4268
0
    pr_inet_close(session.d->pool, session.d);
4269
0
    session.d = NULL;
4270
0
  }
4271
4272
0
  session.sf_flags |= SF_PORT;
4273
0
  pr_response_add(R_200, _("PORT command successful"));
4274
4275
0
  return PR_HANDLED(cmd);
4276
0
}
4277
4278
0
MODRET core_eprt(cmd_rec *cmd) {
4279
0
  const pr_netaddr_t *listen_addr = NULL;
4280
0
  pr_netaddr_t na;
4281
0
  int family = 0, res;
4282
0
  unsigned short port = 0;
4283
0
  int *root_revoke = NULL;
4284
0
  char delim = '\0', *argstr = pstrdup(cmd->tmp_pool, cmd->argv[1]);
4285
0
  char *tmp = NULL;
4286
0
  config_rec *c;
4287
0
  const char *proto;
4288
4289
0
  if (session.sf_flags & SF_EPSV_ALL) {
4290
0
    pr_response_add_err(R_500, _("Illegal EPRT command, EPSV ALL in effect"));
4291
4292
0
    pr_cmd_set_errno(cmd, EPERM);
4293
0
    errno = EPERM;
4294
0
    return PR_ERROR(cmd);
4295
0
  }
4296
4297
0
  CHECK_CMD_ARGS(cmd, 2);
4298
4299
  /* Returning 501 is the best we can do.  It would be nicer if RFC959 allowed
4300
   * 550 as a possible response.
4301
   */
4302
0
  if (!dir_check(cmd->tmp_pool, cmd, cmd->group, session.cwd, NULL)) {
4303
0
    int xerrno = EPERM;
4304
4305
0
    pr_log_debug(DEBUG8, "EPRT denied by <Limit> configuration");
4306
0
    pr_response_add_err(R_501, "%s: %s", (char *) cmd->argv[0],
4307
0
      strerror(xerrno));
4308
4309
0
    pr_cmd_set_errno(cmd, xerrno);
4310
0
    errno = xerrno;
4311
0
    return PR_ERROR(cmd);
4312
0
  }
4313
4314
  /* Initialize the netaddr. */
4315
0
  pr_netaddr_clear(&na);
4316
4317
  /* Block active transfers (the EPRT command) if RootRevoke is in effect
4318
   * and the server's port is below 1024 (binding to the data port in this
4319
   * case would require root privs, which will have been dropped.
4320
   *
4321
   * A RootRevoke value of 0 indicates 'false', 1 indicates 'true', and
4322
   * 2 indicates 'NonCompliantActiveTransfer'.  We only block active transfers
4323
   * for a RootRevoke value of 1.
4324
   */
4325
0
  root_revoke = get_param_ptr(TOPLEVEL_CONF, "RootRevoke", FALSE);
4326
0
  if (root_revoke != NULL &&
4327
0
      *root_revoke == 1 &&
4328
0
      session.c->local_port < 1024) {
4329
0
    pr_log_debug(DEBUG0, "RootRevoke in effect, unable to bind to local "
4330
0
      "port %d for active transfer", session.c->local_port-1);
4331
0
    pr_response_add_err(R_500, _("Unable to service EPRT commands"));
4332
4333
0
    pr_cmd_set_errno(cmd, EPERM);
4334
0
    errno = EPERM;
4335
0
    return PR_ERROR(cmd);
4336
0
  }
4337
4338
  /* Format is <d>proto<d>ip address<d>port<d> (ASCII in network order),
4339
   * where <d> is an arbitrary delimiter character.
4340
   */
4341
0
  delim = *argstr++;
4342
4343
  /* atoi() will happily any trailing non-numeric characters, so feeding
4344
   * the parameter string won't hurt.
4345
   */
4346
0
  family = atoi(argstr);
4347
4348
0
  switch (family) {
4349
0
    case 1:
4350
0
      break;
4351
4352
0
#if defined(PR_USE_IPV6)
4353
0
    case 2:
4354
0
      if (pr_netaddr_use_ipv6()) {
4355
0
        break;
4356
0
      }
4357
0
#endif /* PR_USE_IPV6 */
4358
4359
0
    default:
4360
0
#if defined(PR_USE_IPV6)
4361
0
      if (pr_netaddr_use_ipv6()) {
4362
0
        pr_response_add_err(R_522,
4363
0
          _("Network protocol not supported, use (1,2)"));
4364
4365
0
      } else {
4366
0
        pr_response_add_err(R_522,
4367
0
          _("Network protocol not supported, use (1)"));
4368
0
      }
4369
#else
4370
      pr_response_add_err(R_522, _("Network protocol not supported, use (1)"));
4371
#endif /* PR_USE_IPV6 */
4372
4373
0
      pr_cmd_set_errno(cmd, EINVAL);
4374
0
      errno = EINVAL;
4375
0
      return PR_ERROR(cmd);
4376
0
  }
4377
4378
  /* Now, skip past those numeric characters that atoi() used. */
4379
0
  while (PR_ISDIGIT(*argstr)) {
4380
0
    argstr++;
4381
0
  }
4382
4383
  /* If the next character is not the delimiter, it's a badly formatted
4384
   * parameter.
4385
   */
4386
0
  if (*argstr == delim) {
4387
0
    argstr++;
4388
4389
0
  } else {
4390
0
    pr_response_add_err(R_501, _("Illegal EPRT command"));
4391
4392
0
    pr_cmd_set_errno(cmd, EPERM);
4393
0
    errno = EPERM;
4394
0
    return PR_ERROR(cmd);
4395
0
  }
4396
4397
0
  tmp = strchr(argstr, delim);
4398
0
  if (tmp == NULL) {
4399
0
    pr_log_debug(DEBUG3, "badly formatted EPRT argument: '%s'",
4400
0
      (char *) cmd->argv[1]);
4401
0
    pr_response_add_err(R_501, _("Illegal EPRT command"));
4402
4403
0
    pr_cmd_set_errno(cmd, EPERM);
4404
0
    errno = EPERM;
4405
0
    return PR_ERROR(cmd);
4406
0
  }
4407
4408
  /* Twiddle the string so that just the address portion will be processed
4409
   * by pr_inet_pton().
4410
   */
4411
0
  *tmp = '\0';
4412
4413
  /* Use pr_inet_pton() to translate the address string into the address
4414
   * value.
4415
   */
4416
0
  switch (family) {
4417
0
    case 1: {
4418
0
      struct sockaddr *sa = NULL;
4419
4420
0
      pr_netaddr_set_family(&na, AF_INET);
4421
0
      sa = pr_netaddr_get_sockaddr(&na);
4422
0
      if (sa != NULL) {
4423
0
        sa->sa_family = AF_INET;
4424
0
      }
4425
4426
0
      if (pr_inet_pton(AF_INET, argstr, pr_netaddr_get_inaddr(&na)) <= 0) {
4427
0
        pr_log_debug(DEBUG2, "error converting IPv4 address '%s': %s",
4428
0
          argstr, strerror(errno));
4429
0
        pr_response_add_err(R_501, _("Illegal EPRT command"));
4430
4431
0
        pr_cmd_set_errno(cmd, EPERM);
4432
0
        errno = EPERM;
4433
0
        return PR_ERROR(cmd);
4434
0
      }
4435
0
      break;
4436
0
    }
4437
4438
0
    case 2: {
4439
0
      struct sockaddr *sa = NULL;
4440
4441
0
      pr_netaddr_set_family(&na, AF_INET6);
4442
0
      sa = pr_netaddr_get_sockaddr(&na);
4443
0
      if (sa != NULL) {
4444
0
        sa->sa_family = AF_INET6;
4445
0
      }
4446
4447
0
      if (pr_inet_pton(AF_INET6, argstr, pr_netaddr_get_inaddr(&na)) <= 0) {
4448
0
        pr_log_debug(DEBUG2, "error converting IPv6 address '%s': %s",
4449
0
          argstr, strerror(errno));
4450
0
        pr_response_add_err(R_501, _("Illegal EPRT command"));
4451
4452
0
        pr_cmd_set_errno(cmd, EPERM);
4453
0
        errno = EPERM;
4454
0
        return PR_ERROR(cmd);
4455
0
      }
4456
0
      break;
4457
0
    }
4458
0
  }
4459
4460
  /* Advance past the address portion of the argument. */
4461
0
  argstr = ++tmp;
4462
4463
0
  port = atoi(argstr);
4464
4465
0
  while (PR_ISDIGIT(*argstr)) {
4466
0
    argstr++;
4467
0
  }
4468
4469
  /* If the next character is not the delimiter, it's a badly formatted
4470
   * parameter.
4471
   */
4472
0
  if (*argstr != delim) {
4473
0
    pr_log_debug(DEBUG3, "badly formatted EPRT argument: '%s'",
4474
0
      (char *) cmd->argv[1]);
4475
0
    pr_response_add_err(R_501, _("Illegal EPRT command"));
4476
4477
0
    pr_cmd_set_errno(cmd, EPERM);
4478
0
    errno = EPERM;
4479
0
    return PR_ERROR(cmd);
4480
0
  }
4481
4482
  /* If we are NOT listening on an RFC1918 address, BUT the client HAS
4483
   * sent us an RFC1918 address in its PORT command (which we know to not be
4484
   * routable), then ignore that address, and use the client's remote address.
4485
   */
4486
0
  listen_addr = session.c->local_addr;
4487
4488
0
  proto = pr_session_get_protocol(0);
4489
0
  if (strcmp(proto, "ftps") == 0) {
4490
0
    c = find_config(main_server->conf, CONF_PARAM, "TLSMasqueradeAddress",
4491
0
      FALSE);
4492
0
    if (c != NULL) {
4493
0
      listen_addr = c->argv[0];
4494
4495
0
    } else {
4496
0
      c = find_config(main_server->conf, CONF_PARAM, "MasqueradeAddress",
4497
0
        FALSE);
4498
0
      if (c != NULL) {
4499
0
        if (c->argv[0] != NULL) {
4500
0
          listen_addr = c->argv[0];
4501
0
        }
4502
0
      }
4503
0
    }
4504
4505
0
  } else {
4506
0
    c = find_config(main_server->conf, CONF_PARAM, "MasqueradeAddress", FALSE);
4507
0
    if (c != NULL) {
4508
0
      if (c->argv[0] != NULL) {
4509
0
        listen_addr = c->argv[0];
4510
0
      }
4511
0
    }
4512
0
  }
4513
4514
0
  if (pr_netaddr_is_rfc1918(listen_addr) != TRUE &&
4515
0
      pr_netaddr_is_rfc1918(session.c->remote_addr) != TRUE &&
4516
0
      pr_netaddr_is_rfc1918(&na) == TRUE) {
4517
0
    const char *rfc1918_ipstr;
4518
4519
0
    rfc1918_ipstr = pr_netaddr_get_ipstr(&na);
4520
4521
0
    pr_netaddr_clear(&na);
4522
0
    pr_netaddr_set_family(&na, pr_netaddr_get_family(session.c->remote_addr));
4523
0
    pr_netaddr_set_sockaddr(&na,
4524
0
      pr_netaddr_get_sockaddr(session.c->remote_addr));
4525
4526
0
    pr_log_debug(DEBUG1, "client sent RFC1918 address '%s' in EPRT command, "
4527
0
      "ignoring it and using '%s'", rfc1918_ipstr, pr_netaddr_get_ipstr(&na));
4528
0
  }
4529
4530
  /* Make sure that the address specified matches the address from which
4531
   * the control connection is coming.
4532
   */
4533
4534
0
  c = find_config(TOPLEVEL_CONF, CONF_PARAM, "AllowForeignAddress", FALSE);
4535
0
  res = pr_inet_allowforeignaddress(cmd->tmp_pool, &na,
4536
0
    session.c->remote_addr, c);
4537
0
  if (res != 1 || !port) {
4538
0
    pr_log_pri(PR_LOG_WARNING, "Refused EPRT %s (address mismatch)",
4539
0
      cmd->arg);
4540
0
    pr_response_add_err(R_500, _("Illegal EPRT command"));
4541
4542
0
    pr_cmd_set_errno(cmd, EPERM);
4543
0
    errno = EPERM;
4544
0
    return PR_ERROR(cmd);
4545
0
  }
4546
4547
  /* Additionally, make sure that the port number used is a "high numbered"
4548
   * port, to avoid bounce attacks.  For remote Windows machines, the
4549
   * port numbers mean little.  However, there are also quite a few Unix
4550
   * machines out there for whom the port number matters...
4551
   */
4552
4553
0
  if (port < 1024) {
4554
0
    pr_log_pri(PR_LOG_WARNING,
4555
0
      "Refused EPRT %s (port %d below 1024, possible bounce attack)", cmd->arg,
4556
0
      port);
4557
0
    pr_response_add_err(R_500, _("Illegal EPRT command"));
4558
4559
0
    pr_cmd_set_errno(cmd, EPERM);
4560
0
    errno = EPERM;
4561
0
    return PR_ERROR(cmd);
4562
0
  }
4563
4564
  /* Make sure we're using network byte order. */
4565
0
  pr_netaddr_set_port(&na, htons(port));
4566
4567
0
  switch (family) {
4568
0
    case 1:
4569
0
      pr_netaddr_set_family(&session.data_addr, AF_INET);
4570
0
      break;
4571
4572
0
    case 2:
4573
0
      pr_netaddr_set_family(&session.data_addr, AF_INET6);
4574
0
      break;
4575
0
  }
4576
4577
0
  pr_netaddr_set_sockaddr(&session.data_addr, pr_netaddr_get_sockaddr(&na));
4578
0
  pr_netaddr_set_port(&session.data_addr, pr_netaddr_get_port(&na));
4579
0
  session.data_port = port;
4580
0
  session.sf_flags &= (SF_ALL^SF_PASSIVE);
4581
4582
  /* If we already have a data connection open, kill it. */
4583
0
  if (session.d != NULL) {
4584
0
    pr_inet_close(session.d->pool, session.d);
4585
0
    session.d = NULL;
4586
0
  }
4587
4588
0
  session.sf_flags |= SF_PORT;
4589
0
  pr_response_add(R_200, _("EPRT command successful"));
4590
4591
0
  return PR_HANDLED(cmd);
4592
0
}
4593
4594
0
MODRET core_epsv(cmd_rec *cmd) {
4595
0
  char *addrstr = "";
4596
0
  char *endp = NULL, *arg = NULL;
4597
0
  int family = 0;
4598
0
  int epsv_min_port = 1024, epsv_max_port = 65535, tcp_nodelay = 1;
4599
0
  config_rec *c = NULL;
4600
0
  const pr_netaddr_t *bind_addr;
4601
4602
0
  CHECK_CMD_MIN_ARGS(cmd, 1);
4603
4604
  /* Returning 501 is the best we can do.  It would be nicer if RFC959 allowed
4605
   * 550 as a possible response.
4606
   */
4607
0
  if (!dir_check(cmd->tmp_pool, cmd, cmd->group, session.cwd, NULL)) {
4608
0
    int xerrno = EPERM;
4609
4610
0
    pr_log_debug(DEBUG8, "EPSV denied by <Limit> configuration");
4611
0
    pr_response_add_err(R_501, "%s: %s", (char *) cmd->argv[0],
4612
0
      strerror(xerrno));
4613
4614
0
    pr_cmd_set_errno(cmd, xerrno);
4615
0
    errno = xerrno;
4616
0
    return PR_ERROR(cmd);
4617
0
  }
4618
4619
0
  if (cmd->argc-1 == 1) {
4620
0
    arg = pstrdup(cmd->tmp_pool, cmd->argv[1]);
4621
0
  }
4622
4623
0
  if (arg && strcasecmp(arg, "all") == 0) {
4624
0
    session.sf_flags |= SF_EPSV_ALL;
4625
0
    pr_response_add(R_200, _("EPSV ALL command successful"));
4626
4627
0
    pr_cmd_set_errno(cmd, EPERM);
4628
0
    errno = EPERM;
4629
0
    return PR_HANDLED(cmd);
4630
0
  }
4631
4632
  /* If the optional parameter was given, determine the address family from
4633
   * that.  If not, determine the family from the control connection address
4634
   * family.
4635
   */
4636
0
  if (arg) {
4637
0
    family = strtol(arg, &endp, 10);
4638
4639
0
    if (endp && *endp) {
4640
0
      pr_response_add_err(R_501, _("%s: unknown network protocol"),
4641
0
        (char *) cmd->argv[0]);
4642
4643
0
      pr_cmd_set_errno(cmd, EINVAL);
4644
0
      errno = EINVAL;
4645
0
      return PR_ERROR(cmd);
4646
0
    }
4647
4648
0
  } else {
4649
0
    switch (pr_netaddr_get_family(session.c->local_addr)) {
4650
0
      case AF_INET:
4651
0
        family = 1;
4652
0
        break;
4653
4654
0
#if defined(PR_USE_IPV6)
4655
0
      case AF_INET6:
4656
0
        if (pr_netaddr_use_ipv6()) {
4657
0
          family = 2;
4658
0
          break;
4659
0
        }
4660
0
#endif /* PR_USE_IPV6 */
4661
4662
0
      default:
4663
0
        family = 0;
4664
0
        break;
4665
0
    }
4666
0
  }
4667
4668
0
  switch (family) {
4669
0
    case 1:
4670
0
      break;
4671
4672
0
#if defined(PR_USE_IPV6)
4673
0
    case 2:
4674
0
      if (pr_netaddr_use_ipv6()) {
4675
0
        break;
4676
0
      }
4677
0
#endif /* PR_USE_IPV6 */
4678
4679
0
    default:
4680
0
#if defined(PR_USE_IPV6)
4681
0
      if (pr_netaddr_use_ipv6()) {
4682
0
        pr_response_add_err(R_522,
4683
0
          _("Network protocol not supported, use (1,2)"));
4684
4685
0
      } else {
4686
0
        pr_response_add_err(R_522,
4687
0
          _("Network protocol not supported, use (1)"));
4688
0
      }
4689
#else
4690
      pr_response_add_err(R_522, _("Network protocol not supported, use (1)"));
4691
#endif /* PR_USE_IPV6 */
4692
4693
0
      pr_cmd_set_errno(cmd, EINVAL);
4694
0
      errno = EINVAL;
4695
0
      return PR_ERROR(cmd);
4696
0
  }
4697
4698
  /* If we already have a passive listen data connection open, kill it. */
4699
0
  if (session.d != NULL) {
4700
0
    pr_inet_close(session.d->pool, session.d);
4701
0
    session.d = NULL;
4702
0
  }
4703
4704
0
  if (pr_netaddr_get_family(session.c->local_addr) == pr_netaddr_get_family(session.c->remote_addr)) {
4705
0
    bind_addr = session.c->local_addr;
4706
4707
0
  } else {
4708
    /* In this scenario, the server has an IPv6 socket, but the remote client
4709
     * is an IPv4 (or IPv4-mapped IPv6) peer.
4710
     */
4711
0
    bind_addr = pr_netaddr_v6tov4(cmd->pool, session.c->local_addr);
4712
0
  }
4713
4714
0
  c = find_config(main_server->conf, CONF_PARAM, "PassivePorts", FALSE);
4715
0
  if (c != NULL) {
4716
0
    epsv_min_port = *((int *) c->argv[0]);
4717
0
    epsv_max_port = *((int *) c->argv[1]);
4718
0
  }
4719
4720
  /* We always use the portrange variant of inet_create_conn() here,
4721
   * since it seems that some Unix kernels have issues when choosing a
4722
   * random port number for IPv6 sockets (see Bug #2900).  By using the
4723
   * portrange variant, proftpd, and not the kernel, will be the one
4724
   * choosing the port number.  We really only need to do this for EPSV
4725
   * and not PASV since only the EPSV command can be used for IPv6
4726
   * connections; using PASV means IPv4 connections, and Unix kernels
4727
   * have more predictable behavior for choosing random IPv4 socket ports.
4728
   */
4729
4730
0
  session.d = pr_inet_create_conn_portrange(session.pool, bind_addr,
4731
0
    epsv_min_port, epsv_max_port);
4732
0
  if (session.d == NULL) {
4733
    /* If not able to open a passive port in the given range, default to
4734
     * normal behavior (using INPORT_ANY), and log the failure.  This
4735
     * indicates a too-small range configuration.
4736
     */
4737
0
    pr_log_pri(PR_LOG_WARNING, "unable to find open port in PassivePorts "
4738
0
      "range %d-%d: defaulting to INPORT_ANY (consider defining a larger "
4739
0
      "PassivePorts range)", epsv_min_port, epsv_max_port);
4740
4741
0
    session.d = pr_inet_create_conn2(session.pool, -1, bind_addr, INPORT_ANY,
4742
0
      0);
4743
0
  }
4744
4745
0
  if (session.d == NULL) {
4746
0
    int xerrno = errno;
4747
4748
0
    pr_response_add_err(R_425,
4749
0
      _("Unable to build data connection: Internal error"));
4750
4751
0
    pr_cmd_set_errno(cmd, xerrno);
4752
0
    errno = xerrno;
4753
0
    return PR_ERROR(cmd);
4754
0
  }
4755
4756
  /* Make sure that necessary socket options are set on the socket prior
4757
   * to the call to listen(2).
4758
   */
4759
4760
0
  c = find_config(main_server->conf, CONF_PARAM, "TCPNoDelay", FALSE);
4761
0
  if (c != NULL) {
4762
0
    int data_use_nodelay;
4763
4764
0
    data_use_nodelay = *((int *) c->argv[1]);
4765
0
    if (data_use_nodelay == FALSE) {
4766
0
      tcp_nodelay = 0;
4767
0
    }
4768
0
  }
4769
4770
0
  session.d->use_nodelay = tcp_nodelay;
4771
0
  pr_inet_set_proto_opts(session.pool, session.d, main_server->tcp_mss_len,
4772
0
    tcp_nodelay, IPTOS_THROUGHPUT, 1);
4773
0
  pr_inet_generate_socket_event("core.data-listen", main_server,
4774
0
    session.d->local_addr, session.d->listen_fd);
4775
4776
0
  pr_inet_set_block(session.pool, session.d);
4777
0
  if (pr_inet_listen(session.pool, session.d, 1, 0) < 0) {
4778
0
    int xerrno = errno;
4779
4780
0
    pr_response_add_err(R_425, "%s: %s", (char *) cmd->argv[0],
4781
0
      strerror(xerrno));
4782
4783
0
    pr_cmd_set_errno(cmd, xerrno);
4784
0
    errno = xerrno;
4785
0
    return PR_ERROR(cmd);
4786
0
  }
4787
4788
0
  session.d->instrm = pr_netio_open(session.pool, PR_NETIO_STRM_DATA,
4789
0
    session.d->listen_fd, PR_NETIO_IO_RD);
4790
4791
  /* Now tell the client our address/port. */
4792
0
  session.data_port = session.d->local_port;
4793
0
  session.sf_flags |= SF_PASSIVE;
4794
4795
  /* Note: what about masquerading IPv6 addresses?  It seems that RFC2428,
4796
   * which defines the EPSV command, does not explicitly handle the
4797
   * case where the server may wish to return a network address in its
4798
   * EPSV response.  The assumption is that in an IPv6 environment, there
4799
   * will be no need for NAT, and hence no need for masquerading.  This
4800
   * may be true in an ideal world, but I think it more likely that current
4801
   * clients will simply use EPSV, rather than PASV, in existing IPv4 networks.
4802
   *
4803
   * Note that this also means that EPSV cannot be use for site-to-site (FXP)
4804
   * transfers; the EPSV response is not allowed to contain a different
4805
   * network address.
4806
   *
4807
   * Disable the honoring of MasqueradeAddress for EPSV until this can
4808
   * be officially determined (Bug#2369).  See also Bug#3862.
4809
   */
4810
#if 0
4811
  c = find_config(main_server->conf, CONF_PARAM, "MasqueradeAddress", FALSE);
4812
  if (c != NULL) {
4813
   addrstr = (char *) pr_netaddr_get_ipstr(c->argv[0]);
4814
  }
4815
#endif
4816
4817
0
  pr_log_debug(DEBUG1, "Entering Extended Passive Mode (||%s|%u|)",
4818
0
    addrstr, (unsigned int) session.data_port);
4819
0
  pr_response_add(R_229, "Entering Extended Passive Mode (||%s|%u|)",
4820
0
    addrstr, (unsigned int) session.data_port);
4821
4822
0
  return PR_HANDLED(cmd);
4823
0
}
4824
4825
0
MODRET core_help(cmd_rec *cmd) {
4826
0
  if (!dir_check(cmd->tmp_pool, cmd, cmd->group, session.vwd, NULL)) {
4827
0
    int xerrno = EACCES;
4828
4829
0
    pr_log_debug(DEBUG7, "%s command denied by <Limit> configuration",
4830
0
      (char *) cmd->argv[0]);
4831
4832
    /* Returning 501 is the best we can do.  It would be nicer if RFC959 allowed
4833
     * 550 as a possible response.
4834
     */
4835
0
    pr_response_add_err(R_501, "%s: %s", (char *) cmd->argv[0],
4836
0
      strerror(xerrno));
4837
4838
0
    pr_cmd_set_errno(cmd, xerrno);
4839
0
    errno = xerrno;
4840
0
    return PR_ERROR(cmd);
4841
0
  }
4842
4843
0
  if (cmd->argc == 1) {
4844
0
    pr_help_add_response(cmd, NULL);
4845
4846
0
  } else {
4847
0
    char *cp;
4848
4849
0
    for (cp = cmd->argv[1]; *cp; cp++) {
4850
0
      if (PR_ISALPHA((int) *cp)) {
4851
0
        *cp = toupper((int) *cp);
4852
0
      }
4853
0
    }
4854
4855
0
    if (strcasecmp(cmd->argv[1], C_SITE) == 0) {
4856
0
      return pr_module_call(&site_module, site_dispatch, cmd);
4857
0
    }
4858
4859
0
    if (pr_help_add_response(cmd, cmd->argv[1]) == 0) {
4860
0
      return PR_HANDLED(cmd);
4861
0
    }
4862
4863
0
    pr_response_add_err(R_502, _("Unknown command '%s'"),
4864
0
      (char *) cmd->argv[1]);
4865
4866
0
    pr_cmd_set_errno(cmd, EINVAL);
4867
0
    errno = EINVAL;
4868
0
    return PR_ERROR(cmd);
4869
0
  }
4870
4871
0
  return PR_HANDLED(cmd);
4872
0
}
4873
4874
0
MODRET core_host(cmd_rec *cmd) {
4875
0
  const char *local_ipstr;
4876
0
  char *host;
4877
0
  size_t hostlen;
4878
0
  server_rec *named_server;
4879
0
  int found_ipv6 = FALSE;
4880
4881
0
  if (cmd->argc != 2) {
4882
0
    pr_response_add_err(R_500, _("'%s' not understood"), (char *) cmd->argv[0]);
4883
4884
0
    pr_cmd_set_errno(cmd, EINVAL);
4885
0
    errno = EINVAL;
4886
0
    return PR_ERROR(cmd);
4887
0
  }
4888
4889
0
  if (session.user != NULL) {
4890
0
    pr_log_debug(DEBUG0,
4891
0
      "HOST '%s' command received after login, refusing HOST command",
4892
0
      (char *) cmd->argv[1]);
4893
4894
    /* Per HOST spec, HOST after successful USER/PASS is not allowed. */
4895
0
    pr_response_add_err(R_503, _("Bad sequence of commands"));
4896
4897
0
    pr_cmd_set_errno(cmd, EPERM);
4898
0
    errno = EPERM;
4899
0
    return PR_ERROR(cmd);
4900
0
  }
4901
4902
0
  if (!dir_check(cmd->tmp_pool, cmd, cmd->group, session.cwd, NULL)) {
4903
0
    int xerrno = EACCES;
4904
4905
0
    pr_log_debug(DEBUG7, "%s command denied by <Limit> configuration",
4906
0
      (char *) cmd->argv[0]);
4907
0
    pr_response_add_err(R_504, "%s: %s", (char *) cmd->argv[1],
4908
0
      strerror(xerrno));
4909
4910
0
    pr_cmd_set_errno(cmd, xerrno);
4911
0
    errno = xerrno;
4912
0
    return PR_ERROR(cmd);
4913
0
  }
4914
4915
  /* Should there be a limit on the number of HOST commands that a client
4916
   * can send?
4917
   *
4918
   * In practice, this will be limited by the TimeoutLogin time interval;
4919
   * a client can send as many HOST commands as it wishes, as long as it
4920
   * successfully authenticates in that time.
4921
   */
4922
4923
  /* If the user has already authenticated or negotiated a RFC2228 mechanism,
4924
   * then the HOST command is too late.
4925
   */
4926
0
  if (session.rfc2228_mech != NULL &&
4927
0
      pr_table_get(session.notes, "mod_tls.sni", NULL) == NULL) {
4928
0
    pr_log_debug(DEBUG0, "HOST '%s' command received after client has "
4929
0
      "requested RFC2228 protection (%s), refusing HOST command",
4930
0
      (char *) cmd->argv[1], session.rfc2228_mech);
4931
4932
0
    pr_response_add_err(R_503, _("Bad sequence of commands"));
4933
4934
0
    pr_cmd_set_errno(cmd, EPERM);
4935
0
    errno = EPERM;
4936
0
    return PR_ERROR(cmd);
4937
0
  }
4938
4939
0
  host = cmd->argv[1];
4940
0
  hostlen = strlen(host);
4941
4942
0
  if (host[0] == '[') {
4943
    /* Check for any literal IPv6 hostnames. Per HOST spec, these IPv6
4944
     * addresses MUST be enclosed within square brackets.
4945
     */
4946
4947
0
    if (pr_netaddr_use_ipv6()) {
4948
0
      if (host[hostlen-1] != ']') {
4949
0
        pr_response_add_err(R_501, _("%s: Invalid IPv6 address provided"),
4950
0
          host);
4951
4952
0
        pr_cmd_set_errno(cmd, EINVAL);
4953
0
        errno = EINVAL;
4954
0
        return PR_ERROR(cmd);
4955
0
      }
4956
4957
0
      host = pstrndup(cmd->tmp_pool, host + 1, hostlen - 2);
4958
0
      hostlen = hostlen - 2;
4959
4960
0
      if (pr_netaddr_is_v6(host) != TRUE) {
4961
0
        pr_log_debug(DEBUG0,
4962
0
          "Client-sent hostname '%s' is not a valid IPv6 address, "
4963
0
          "refusing HOST command", host);
4964
4965
0
        pr_response_add_err(R_501, _("%s: Invalid IPv6 address provided"),
4966
0
          (char *) cmd->argv[1]);
4967
4968
0
        pr_cmd_set_errno(cmd, EINVAL);
4969
0
        errno = EINVAL;
4970
0
        return PR_ERROR(cmd);
4971
0
      }
4972
4973
0
      found_ipv6 = TRUE;
4974
4975
0
    } else {
4976
0
      pr_response_add_err(R_501, _("%s: Invalid hostname provided"),
4977
0
        host);
4978
4979
0
      pr_cmd_set_errno(cmd, EINVAL);
4980
0
      errno = EINVAL;
4981
0
      return PR_ERROR(cmd);
4982
0
    }
4983
0
  }
4984
4985
0
  local_ipstr = pr_netaddr_get_ipstr(session.c->local_addr);
4986
4987
0
  if (pr_netaddr_is_v4(host) == TRUE) {
4988
0
    if (pr_netaddr_is_v4(local_ipstr) == TRUE) {
4989
0
      if (strncmp(host, local_ipstr, hostlen) != 0) {
4990
        /* The client connected to an IP address, but requested a different IP
4991
         * address in its HOST command.  That won't work.
4992
         */
4993
0
        pr_log_debug(DEBUG0, "HOST '%s' requested, but client connected to "
4994
0
          "IPv4 address '%s', refusing HOST command", host, local_ipstr);
4995
0
        pr_response_add_err(R_504, _("%s: Unknown hostname provided"),
4996
0
          (char *) cmd->argv[1]);
4997
4998
0
        pr_cmd_set_errno(cmd, EPERM);
4999
0
        errno = EPERM;
5000
0
        return PR_ERROR(cmd);
5001
0
      }
5002
0
    }
5003
5004
0
    (void) pr_table_remove(session.notes, "mod_core.host", NULL);
5005
0
    if (pr_table_add_dup(session.notes, "mod_core.host", host, 0) < 0) {
5006
0
      pr_trace_msg("command", 3,
5007
0
        "error stashing 'mod_core.host' in session.notes: %s", strerror(errno));
5008
0
    }
5009
5010
    /* No need to send the banner information again, since we didn't actually
5011
     * change the virtual host used by the client.
5012
     */
5013
0
    pr_response_add(R_220, _("HOST command successful"));
5014
0
    return PR_HANDLED(cmd);
5015
0
  }
5016
5017
0
  if (pr_netaddr_is_v6(host) == TRUE) {
5018
0
    if (pr_netaddr_is_v6(local_ipstr) == TRUE) {
5019
5020
0
      if (found_ipv6 == FALSE) {
5021
        /* The client sent us an IPv6 address WITHOUT the '[...]' notation,
5022
         * which is a syntax error.
5023
         */
5024
0
        pr_log_debug(DEBUG0, "Client-sent hostname '%s' is an IPv6 address, "
5025
0
          "but did not have required [] notation, refusing HOST command",
5026
0
          host);
5027
5028
0
        pr_response_add_err(R_501, _("%s: Invalid IPv6 address provided"),
5029
0
          host);
5030
5031
0
        pr_cmd_set_errno(cmd, EINVAL);
5032
0
        errno = EINVAL;
5033
0
        return PR_ERROR(cmd);
5034
0
      }
5035
5036
0
      if (strncmp(host, local_ipstr, hostlen) != 0) {
5037
        /* The client connected to an IP address, but requested a different IP
5038
         * address in its HOST command.  That won't work.
5039
         */
5040
0
        pr_log_debug(DEBUG0, "HOST '%s' requested, but client connected to "
5041
0
          "IPv6 address '%s', refusing HOST command", host, local_ipstr);
5042
0
        pr_response_add_err(R_504, _("%s: Unknown hostname provided"),
5043
0
          (char *) cmd->argv[1]);
5044
5045
0
        pr_cmd_set_errno(cmd, EPERM);
5046
0
        errno = EPERM;
5047
0
        return PR_ERROR(cmd);
5048
0
      }
5049
0
    }
5050
5051
0
    (void) pr_table_remove(session.notes, "mod_core.host", NULL);
5052
0
    if (pr_table_add_dup(session.notes, "mod_core.host", host, 0) < 0) {
5053
0
      pr_trace_msg("command", 3,
5054
0
        "error stashing 'mod_core.host' in session.notes: %s", strerror(errno));
5055
0
    }
5056
5057
    /* No need to send the banner information again, since we didn't actually
5058
     * change the virtual host used by the client.
5059
     */
5060
0
    pr_response_add(R_220, _("HOST command successful"));
5061
0
    return PR_HANDLED(cmd);
5062
0
  }
5063
5064
  /* If we reach this point, the hostname is probably a DNS name.  See if we
5065
   * have a matching namebind based on the current IP address/port.
5066
   */
5067
5068
0
  if (strchr(host, ':') != NULL) {
5069
    /* Hostnames cannot contain colon characters. */
5070
0
    pr_response_add_err(R_501, _("%s: Invalid hostname provided"), host);
5071
5072
0
    pr_cmd_set_errno(cmd, EINVAL);
5073
0
    errno = EINVAL;
5074
0
    return PR_ERROR(cmd);
5075
0
  }
5076
5077
0
  named_server = pr_namebind_get_server(host, main_server->addr,
5078
0
    session.c->local_port);
5079
0
  if (named_server == NULL) {
5080
0
    pr_log_debug(DEBUG0, "Unknown host '%s' requested on %s#%d, "
5081
0
      "refusing HOST command", host, local_ipstr, main_server->ServerPort);
5082
5083
0
    pr_response_add_err(R_504, _("%s: Unknown hostname provided"),
5084
0
      (char *) cmd->argv[1]);
5085
5086
0
    pr_cmd_set_errno(cmd, ENOENT);
5087
0
    errno = ENOENT;
5088
0
    return PR_ERROR(cmd);
5089
0
  }
5090
5091
0
  if (session.rfc2228_mech != NULL &&
5092
0
      strcmp(session.rfc2228_mech, "TLS") == 0) {
5093
0
    const char *sni = NULL;
5094
5095
    /* If the TLS client used the SNI extension, ensure that the SNI name
5096
     * matches the HOST name, per RFC 7151, Section 3.2.2.  Otherwise, we
5097
     * reject the HOST command.
5098
     */
5099
0
    sni = pr_table_get(session.notes, "mod_tls.sni", NULL);
5100
0
    if (sni != NULL) {
5101
0
      if (strcasecmp(sni, host) != 0) {
5102
0
        pr_log_debug(DEBUG0, "HOST '%s' requested, but client connected via "
5103
0
          "TLS to SNI '%s', refusing HOST command", host, sni);
5104
0
        pr_response_add_err(R_504, _("%s: Unknown hostname provided"),
5105
0
          (char *) cmd->argv[1]);
5106
5107
0
        pr_cmd_set_errno(cmd, EPERM);
5108
0
        errno = EPERM;
5109
0
        return PR_ERROR(cmd);
5110
0
      }
5111
0
    }
5112
0
  }
5113
5114
0
  (void) pr_table_remove(session.notes, "mod_core.host", NULL);
5115
0
  if (pr_table_add_dup(session.notes, "mod_core.host", host, 0) < 0) {
5116
0
    pr_trace_msg("command", 3,
5117
0
      "error stashing 'mod_core.host' in session.notes: %s", strerror(errno));
5118
0
  }
5119
5120
0
  if (named_server != main_server) {
5121
    /* Set a session flag indicating that the main_server pointer changed. */
5122
0
    pr_log_debug(DEBUG0,
5123
0
      "Changing to server '%s' (ServerAlias %s) due to HOST command",
5124
0
      named_server->ServerName, host);
5125
0
    session.prev_server = main_server;
5126
0
    main_server = named_server;
5127
5128
0
    pr_event_generate("core.session-reinit", named_server);
5129
0
  }
5130
5131
  /* XXX Ultimately, if HOST is successful, we change the main_server pointer
5132
   * to point to the named server_rec.
5133
   *
5134
   * Check *every single sess_init* function, since there are MANY things
5135
   * which currently happen at sess_init, based on the main_server pointer,
5136
   * that will need to be re-done in a HOST POST_CMD handler.  This includes
5137
   * AuthOrder, timeouts, etc etc.  (Unfortunately, POST_CMD handlers cannot
5138
   * fail the given command; for modules which then need to end the
5139
   * connection, they'll need to use pr_session_disconnect().)
5140
   *
5141
   * Modules implementing post_host handlers:
5142
   *   mod_core
5143
   *
5144
   * Modules implementing 'sess-reinit' event handlers:
5145
   *   mod_auth
5146
   *   mod_auth_file
5147
   *   mod_auth_unix
5148
   *   mod_ban
5149
   *   mod_cap
5150
   *   mod_copy
5151
   *   mod_deflate
5152
   *   mod_delay
5153
   *   mod_dnsbl
5154
   *   mod_exec
5155
   *   mod_facts
5156
   *   mod_ident
5157
   *   mod_ldap
5158
   *   mod_log
5159
   *   mod_log_forensic
5160
   *   mod_memcache
5161
   *   mod_qos
5162
   *   mod_quotatab
5163
   *   mod_radius
5164
   *   mod_rewrite
5165
   *   mod_site_misc
5166
   *   mod_sql
5167
   *   mod_sql_passwd
5168
   *   mod_tls
5169
   *   mod_wrap
5170
   *   mod_wrap2
5171
   *   mod_xfer
5172
   *
5173
   * Modules that MIGHT need a session-reinit listener:
5174
   *   mod_ratio
5175
   *   mod_snmp
5176
   *
5177
   * Modules that DO NOT NEED a session-reinit listener:
5178
   *   mod_auth_pam
5179
   *   mod_ctrls_admin
5180
   *   mod_dynmasq
5181
   *   mod_ifsession
5182
   *   mod_ifversion
5183
   *   mod_load
5184
   *   mod_readme
5185
   *   mod_sftp (HOST command is FTP only)
5186
   *   mod_sftp_pam
5187
   *   mod_sftp_sql
5188
   *   mod_shaper
5189
   *   mod_sql_mysql
5190
   *   mod_sql_postgres
5191
   *   mod_sql_odbc
5192
   *   mod_sql_sqlite
5193
   *   mod_tls_fscache
5194
   *   mod_tls_memcache
5195
   *   mod_tls_shmcache
5196
   *   mod_unique_id
5197
   */
5198
5199
  /* XXX Will this function need to use pr_response_add(), rather than
5200
   * pr_response_send(), in order to accommodate the delaying of sending the
5201
   * response until after POST_CMD/LOG_CMD handlers have run (and thus allowing
5202
   * module e.g. mod_tls to send an error response in the POST_CMD handler,
5203
   * and close the connection)?
5204
   */
5205
5206
0
  pr_session_send_banner(main_server, 0);
5207
0
  return PR_HANDLED(cmd);
5208
0
}
5209
5210
0
MODRET core_post_host(cmd_rec *cmd) {
5211
5212
  /* If the HOST command changed the main_server pointer, reinitialize
5213
   * ourselves.
5214
   */
5215
0
  if (session.prev_server != NULL) {
5216
0
    int res;
5217
0
    config_rec *c;
5218
5219
    /* Reset the FS options */
5220
0
    (void) pr_fsio_set_options(0UL);
5221
5222
    /* Remove the TimeoutIdle timer. */
5223
0
    (void) pr_timer_remove(PR_TIMER_IDLE, ANY_MODULE);
5224
5225
    /* Restore the original TimeoutLinger value. */
5226
0
    pr_data_set_linger(PR_TUNABLE_TIMEOUTLINGER);
5227
5228
    /* Restore original DebugLevel, but only if set via directive, not
5229
     * via the command-line.
5230
     */
5231
0
    c = find_config(session.prev_server->conf, CONF_PARAM, "DebugLevel", FALSE);
5232
0
    if (c != NULL) {
5233
0
      pr_log_setdebuglevel(DEBUG0);
5234
0
    }
5235
5236
    /* Restore the original RegexOptions values. */
5237
0
    pr_regexp_set_engine(NULL);
5238
0
    pr_regexp_set_limits(0, 0);
5239
5240
    /* Remove any configured SetEnvs. */
5241
0
    c = find_config(session.prev_server->conf, CONF_PARAM, "SetEnv", FALSE);
5242
0
    while (c != NULL) {
5243
0
      pr_signals_handle();
5244
5245
0
      if (pr_env_unset(session.pool, c->argv[0]) < 0) {
5246
0
        pr_log_debug(DEBUG0, "unable to unset environment variable '%s': %s",
5247
0
          (char *) c->argv[0], strerror(errno));
5248
0
      }
5249
5250
0
      c = find_config_next(c, c->next, CONF_PARAM, "SetEnv", FALSE);
5251
0
    }
5252
5253
    /* Restore original AuthOrder. */
5254
0
    reset_server_auth_order();
5255
5256
0
#if defined(PR_USE_TRACE)
5257
    /* XXX Restore original Trace settings. */
5258
5259
    /* Restore original TraceOptions settings. */
5260
0
    (void) pr_trace_set_options(PR_TRACE_OPT_DEFAULT);
5261
0
#endif /* PR_USE_TRACE */
5262
5263
    /* Remove the variables set via pr_var_set(). */
5264
0
    (void) pr_var_delete("%{bytes_xfer}");
5265
0
    (void) pr_var_delete("%{total_bytes_in}");
5266
0
    (void) pr_var_delete("%{total_bytes_out}");
5267
0
    (void) pr_var_delete("%{total_bytes_xfer}");
5268
0
    (void) pr_var_delete("%{total_files_in}");
5269
0
    (void) pr_var_delete("%{total_files_out}");
5270
0
    (void) pr_var_delete("%{total_files_xfer}");
5271
5272
    /* Reset the DisplayQuit file. */
5273
0
    if (displayquit_fh != NULL) {
5274
0
      pr_fsio_close(displayquit_fh);
5275
0
      displayquit_fh = NULL;
5276
0
    }
5277
5278
    /* Restore the original ProcessTitles setting. */
5279
0
    pr_proctitle_set_static_str(NULL);
5280
5281
    /* Unregister any event listeners. */
5282
0
    pr_event_unregister(&core_module, "core.chroot", core_chroot_ev);
5283
5284
0
    res = core_sess_init();
5285
0
    if (res < 0) {
5286
0
      pr_session_disconnect(&core_module,
5287
0
        PR_SESS_DISCONNECT_SESSION_INIT_FAILED, NULL);
5288
0
    }
5289
0
  }
5290
5291
0
  return PR_DECLINED(cmd);
5292
0
}
5293
5294
0
MODRET core_clnt(cmd_rec *cmd) {
5295
0
  (void) pr_table_add_dup(session.notes,
5296
0
    pstrdup(session.pool, "clnt"), cmd->arg, 0);
5297
0
  pr_response_add(R_200, _("OK"));
5298
0
  return PR_HANDLED(cmd);
5299
0
}
5300
5301
0
static pr_table_t *parse_client_facts(pool *p, char *text) {
5302
0
  array_header *facts;
5303
0
  pr_table_t *facts_tab = NULL;
5304
5305
0
  facts = pr_str_text_to_array(p, text, ';');
5306
0
  if (facts != NULL) {
5307
0
    register unsigned int i;
5308
5309
0
    facts_tab = pr_table_alloc(p, 0);
5310
5311
0
    for (i = 0; i < facts->nelts; i++) {
5312
0
      char *fact, *ptr;
5313
0
      size_t fact_len;
5314
5315
0
      fact = ((char **) (facts->elts))[i];
5316
0
      fact_len = strlen(fact);
5317
5318
0
      if (fact_len == 0) {
5319
0
        continue;
5320
0
      }
5321
5322
0
      ptr = strchr(fact, '=');
5323
0
      if (ptr == NULL) {
5324
        /* Ignore invalid facts. */
5325
0
        pr_log_debug(DEBUG0, "ignoring invalid client-sent CSID fact '%s'",
5326
0
          fact);
5327
0
        continue;
5328
0
      }
5329
5330
      /* Skip any leading whitespace. */
5331
0
      while (PR_ISSPACE(*fact)) {
5332
0
        pr_signals_handle();
5333
0
        fact++;
5334
0
      }
5335
5336
0
      if (strncasecmp(fact, "Name", ptr - fact) == 0) {
5337
0
        pr_log_debug(DEBUG9, "client sent CSID Name fact: '%s'", ptr+1);
5338
0
        (void) pr_table_add_dup(facts_tab, pstrdup(p, "Name"), ptr+1, 0);
5339
0
        continue;
5340
0
      }
5341
5342
0
      if (strncasecmp(fact, "Version", ptr - fact) == 0) {
5343
0
        pr_log_debug(DEBUG9, "client sent CSID Version fact: '%s'", ptr+1);
5344
0
        (void) pr_table_add_dup(facts_tab, pstrdup(p, "Version"), ptr+1, 0);
5345
0
        continue;
5346
0
      }
5347
5348
0
      if (strncasecmp(fact, "Vendor", ptr - fact) == 0) {
5349
0
        pr_log_debug(DEBUG9, "client sent CSID Vendor fact: '%s'", ptr+1);
5350
0
        (void) pr_table_add_dup(facts_tab, pstrdup(p, "Vendor"), ptr+1, 0);
5351
0
        continue;
5352
0
      }
5353
5354
      /* Ignore unknown/unsupported facts. */
5355
0
      pr_log_debug(DEBUG0, "ignoring unknown client-sent CSID fact '%s'", fact);
5356
0
    }
5357
0
  }
5358
5359
0
  return facts_tab;
5360
0
}
5361
5362
0
MODRET core_csid(cmd_rec *cmd) {
5363
0
  config_rec *c;
5364
0
  int use_server_ident = TRUE;
5365
0
  pr_table_t *client_facts = NULL;
5366
0
  char server_text[PR_TUNABLE_BUFFER_SIZE];
5367
5368
  /* Parse the client-provided facts.  Sadly, per IETF Draft, if there are
5369
   * issues parsing the client-provided text, or if the client does not provide
5370
   * the "required" facts, they are to be silently ignored, but not rejected.
5371
   */
5372
0
  client_facts = parse_client_facts(cmd->tmp_pool, cmd->arg);
5373
0
  if (client_facts != NULL &&
5374
0
      pr_table_count(client_facts) > 0) {
5375
0
    const char *key;
5376
0
    const void *val;
5377
0
    size_t valsz;
5378
5379
0
    key = "Name";
5380
0
    val = pr_table_get(client_facts, key, &valsz);
5381
0
    if (val != NULL) {
5382
0
      (void) pr_table_add_dup(session.notes,
5383
0
        pstrdup(session.pool, "csid.name"), val, valsz);
5384
0
    }
5385
5386
0
    key = "Version";
5387
0
    val = pr_table_get(client_facts, key, &valsz);
5388
0
    if (val != NULL) {
5389
0
      (void) pr_table_add_dup(session.notes,
5390
0
        pstrdup(session.pool, "csid.version"), val, valsz);
5391
0
    }
5392
5393
0
    key = "Vendor";
5394
0
    val = pr_table_get(client_facts, key, &valsz);
5395
0
    if (val != NULL) {
5396
0
      (void) pr_table_add_dup(session.notes,
5397
0
        pstrdup(session.pool, "csid.vendor"), val, valsz);
5398
0
    }
5399
0
  }
5400
5401
0
  c = find_config(main_server->conf, CONF_PARAM, "ServerIdent", FALSE);
5402
0
  if (c != NULL) {
5403
0
    use_server_ident = *((unsigned char *) c->argv[0]);
5404
5405
    /* What if the admin has configured an explicit string via
5406
     * "ServerIdent on <text>"?
5407
     *
5408
     * Perhaps we ignore that custom text, on the assumption that the actual
5409
     * server info here is being provided to a successfully authenticated (and
5410
     * thus allowed) client, rather than to some random client on the Internet.
5411
     */
5412
0
  }
5413
5414
0
  memset(server_text, '\0', sizeof(server_text));
5415
5416
0
  if (use_server_ident == TRUE) {
5417
0
    (void) pr_snprintf(server_text, sizeof(server_text),
5418
0
      "name=ProFTPD;version=%s;casesensitive=1;", PROFTPD_VERSION_TEXT);
5419
5420
0
  } else {
5421
0
    (void) pr_snprintf(server_text, sizeof(server_text), "%s",
5422
0
      "casesensitive=1;");
5423
0
  }
5424
5425
0
  pr_response_add(R_200, "%s", server_text);
5426
0
  return PR_HANDLED(cmd);
5427
0
}
5428
5429
0
MODRET core_syst(cmd_rec *cmd) {
5430
0
  pr_response_add(R_215, "UNIX Type: L8");
5431
0
  return PR_HANDLED(cmd);
5432
0
}
5433
5434
0
int core_chgrp(cmd_rec *cmd, const char *path, uid_t uid, gid_t gid) {
5435
0
  char *cmd_name;
5436
5437
0
  cmd_name = cmd->argv[0];
5438
0
  pr_cmd_set_name(cmd, "SITE_CHGRP");
5439
0
  if (!dir_check(cmd->tmp_pool, cmd, G_WRITE, path, NULL)) {
5440
0
    pr_log_debug(DEBUG7, "SITE CHGRP command denied by <Limit> config");
5441
0
    pr_cmd_set_name(cmd, cmd_name);
5442
5443
0
    errno = EACCES;
5444
0
    return -1;
5445
0
  }
5446
0
  pr_cmd_set_name(cmd, cmd_name);
5447
5448
0
  return pr_fsio_lchown(path, uid, gid);
5449
0
}
5450
5451
0
int core_chmod(cmd_rec *cmd, const char *path, mode_t mode) {
5452
0
  char *cmd_name;
5453
5454
0
  cmd_name = cmd->argv[0];
5455
0
  pr_cmd_set_name(cmd, "SITE_CHMOD");
5456
0
  if (!dir_check(cmd->tmp_pool, cmd, G_WRITE, path, NULL)) {
5457
0
    pr_log_debug(DEBUG7, "SITE CHMOD command denied by <Limit> config");
5458
0
    pr_cmd_set_name(cmd, cmd_name);
5459
5460
0
    errno = EACCES;
5461
0
    return -1;
5462
0
  }
5463
0
  pr_cmd_set_name(cmd, cmd_name);
5464
5465
0
  return pr_fsio_chmod(path, mode);
5466
0
}
5467
5468
0
MODRET core_chdir(cmd_rec *cmd, char *ndir) {
5469
0
  char *dir, *orig_dir, *cdir;
5470
0
  int xerrno = 0;
5471
0
  config_rec *c = NULL, *cdpath;
5472
0
  unsigned char show_symlinks = TRUE, *ptr = NULL;
5473
0
  struct stat st;
5474
5475
0
  orig_dir = ndir;
5476
5477
0
  ptr = get_param_ptr(TOPLEVEL_CONF, "ShowSymlinks", FALSE);
5478
0
  if (ptr != NULL) {
5479
0
    show_symlinks = *ptr;
5480
0
  }
5481
5482
0
  if (show_symlinks) {
5483
0
    int use_cdpath = FALSE;
5484
5485
0
    dir = dir_realpath(cmd->tmp_pool, ndir);
5486
0
    if (dir == NULL) {
5487
0
      use_cdpath = TRUE;
5488
0
    }
5489
5490
0
    if (!use_cdpath) {
5491
0
      int allowed_access = TRUE;
5492
5493
0
      allowed_access = dir_check_full(cmd->tmp_pool, cmd, cmd->group, dir,
5494
0
        NULL);
5495
0
      if (!allowed_access) {
5496
0
        use_cdpath = TRUE;
5497
0
      }
5498
0
    }
5499
5500
0
    if (use_cdpath == FALSE &&
5501
0
        pr_fsio_chdir(dir, 0) < 0) {
5502
0
      xerrno = errno;
5503
0
      use_cdpath = TRUE;
5504
0
    }
5505
5506
0
    if (use_cdpath) {
5507
0
      for (cdpath = find_config(main_server->conf, CONF_PARAM, "CDPath", TRUE);
5508
0
          cdpath != NULL;
5509
0
          cdpath = find_config_next(cdpath, cdpath->next, CONF_PARAM, "CDPath", TRUE)) {
5510
0
        cdir = palloc(cmd->tmp_pool, strlen(cdpath->argv[0]) + strlen(ndir) + 2);
5511
0
        pr_snprintf(cdir, strlen(cdpath->argv[0]) + strlen(ndir) + 2,
5512
0
                 "%s%s%s", (char *) cdpath->argv[0],
5513
0
                 ((char *) cdpath->argv[0])[strlen(cdpath->argv[0]) - 1] == '/' ? "" : "/",
5514
0
                 ndir);
5515
0
        dir = dir_realpath(cmd->tmp_pool, cdir);
5516
5517
0
        if (dir &&
5518
0
            dir_check_full(cmd->tmp_pool, cmd, cmd->group, dir, NULL) &&
5519
0
            pr_fsio_chdir(dir, 0) == 0) {
5520
0
          break;
5521
0
        }
5522
0
      }
5523
5524
0
      if (cdpath == FALSE) {
5525
0
        if (xerrno == 0) {
5526
0
          xerrno = errno;
5527
0
        }
5528
5529
0
        pr_response_add_err(R_550, "%s: %s", orig_dir, strerror(xerrno));
5530
5531
0
        pr_cmd_set_errno(cmd, xerrno);
5532
0
        errno = xerrno;
5533
0
        return PR_ERROR(cmd);
5534
0
      }
5535
0
    }
5536
5537
0
  } else {
5538
0
    int use_cdpath = FALSE;
5539
5540
    /* Virtualize the chdir */
5541
0
    ndir = dir_canonical_vpath(cmd->tmp_pool, ndir);
5542
0
    dir = dir_realpath(cmd->tmp_pool, ndir);
5543
5544
0
    if (dir == NULL) {
5545
0
      use_cdpath = TRUE;
5546
0
    }
5547
5548
0
    if (use_cdpath == FALSE) {
5549
0
      int allowed_access = TRUE;
5550
5551
0
      allowed_access = dir_check_full(cmd->tmp_pool, cmd, cmd->group, dir,
5552
0
        NULL);
5553
0
      if (allowed_access == FALSE) {
5554
0
        use_cdpath = TRUE;
5555
0
      }
5556
0
    }
5557
5558
0
    if (use_cdpath == FALSE &&
5559
0
        pr_fsio_chdir_canon(ndir, 1) < 0) {
5560
0
      use_cdpath = TRUE;
5561
0
    }
5562
5563
0
    if (use_cdpath == TRUE) {
5564
0
      for (cdpath = find_config(main_server->conf, CONF_PARAM, "CDPath", TRUE);
5565
0
          cdpath != NULL;
5566
0
          cdpath = find_config_next(cdpath, cdpath->next, CONF_PARAM, "CDPath", TRUE)) {
5567
0
        cdir = palloc(cmd->tmp_pool, strlen(cdpath->argv[0]) + strlen(ndir) + 2);
5568
0
        pr_snprintf(cdir, strlen(cdpath->argv[0]) + strlen(ndir) + 2,
5569
0
                 "%s%s%s", (char *) cdpath->argv[0],
5570
0
                ((char *)cdpath->argv[0])[strlen(cdpath->argv[0]) - 1] == '/' ? "" : "/",
5571
0
                ndir);
5572
0
        ndir = dir_canonical_vpath(cmd->tmp_pool, cdir);
5573
0
        dir = dir_realpath(cmd->tmp_pool, ndir);
5574
5575
0
        if (dir &&
5576
0
            dir_check_full(cmd->tmp_pool, cmd, cmd->group, dir, NULL) &&
5577
0
            pr_fsio_chdir_canon(ndir, 1) != -1) {
5578
0
          break;
5579
0
        }
5580
0
      }
5581
5582
0
      if (cdpath == NULL) {
5583
0
        if (xerrno == 0) {
5584
0
          xerrno = errno;
5585
0
        }
5586
5587
0
        pr_response_add_err(R_550, "%s: %s", orig_dir, strerror(xerrno));
5588
5589
0
        pr_cmd_set_errno(cmd, xerrno);
5590
0
        errno = xerrno;
5591
0
        return PR_ERROR(cmd);
5592
0
      }
5593
0
    }
5594
0
  }
5595
5596
0
  sstrncpy(session.cwd, pr_fs_getcwd(), sizeof(session.cwd));
5597
0
  sstrncpy(session.vwd, pr_fs_getvwd(), sizeof(session.vwd));
5598
5599
0
  pr_scoreboard_entry_update(session.pid,
5600
0
    PR_SCORE_CWD, session.cwd,
5601
0
    NULL);
5602
5603
0
  if (session.dir_config != NULL) {
5604
0
    c = find_config(session.dir_config->subset, CONF_PARAM, "DisplayChdir",
5605
0
      FALSE);
5606
0
  }
5607
5608
0
  if (c == NULL &&
5609
0
      session.anon_config != NULL) {
5610
0
    c = find_config(session.anon_config->subset, CONF_PARAM, "DisplayChdir",
5611
0
      FALSE);
5612
0
  }
5613
5614
0
  if (c == NULL) {
5615
0
    c = find_config(cmd->server->conf, CONF_PARAM, "DisplayChdir", FALSE);
5616
0
  }
5617
5618
0
  if (c != NULL) {
5619
0
    time_t prev = 0;
5620
0
    int display_once = FALSE, display_now = FALSE, res = -1;
5621
0
    char *display_file = NULL;
5622
0
    pr_fh_t *display_fh = NULL;
5623
5624
0
    display_file = c->argv[0];
5625
0
    display_once = *((int *) c->argv[1]);
5626
0
    display_fh = c->argv[2];
5627
5628
0
    if (display_once == TRUE) {
5629
      /* XXX Get rid of this CONF_USERDATA instance; it's the only
5630
       * occurrence of it in the source.  Use the session.notes table instead.
5631
       */
5632
0
      c = find_config(cmd->server->conf, CONF_USERDATA, session.cwd, FALSE);
5633
0
      if (c == NULL) {
5634
0
        time(&prev);
5635
0
        c = pr_config_add_set(&cmd->server->conf, session.cwd, 0);
5636
0
        c->config_type = CONF_USERDATA;
5637
0
        c->argc = 1;
5638
0
        c->argv = pcalloc(c->pool, sizeof(void **) * 2);
5639
0
        c->argv[0] = palloc(c->pool, sizeof(time_t));
5640
0
        *((time_t *) c->argv[0]) = prev;
5641
0
        prev = (time_t) 0L;
5642
5643
0
      } else {
5644
0
        prev = *((time_t *) c->argv[0]);
5645
5646
        /* Update the timestamp stored for this directory. */
5647
0
        *((time_t *) c->argv[0]) = time(NULL);
5648
0
      }
5649
0
    }
5650
5651
0
    if (display_fh != NULL) {
5652
0
      res = pr_fsio_fstat(display_fh, &st);
5653
0
      if (res < 0) {
5654
0
        pr_log_debug(DEBUG3, "DisplayChdir: error checking '%s': %s",
5655
0
          display_fh->fh_path, strerror(errno));
5656
0
      }
5657
5658
0
    } else {
5659
0
      res = pr_fsio_stat(display_file, &st);
5660
0
      if (res < 0) {
5661
0
        pr_log_debug(DEBUG3, "DisplayChdir: error checking '%s': %s",
5662
0
          display_file, strerror(errno));
5663
0
      }
5664
0
    }
5665
5666
0
    if (res == 0 &&
5667
0
        !S_ISDIR(st.st_mode) &&
5668
0
        (display_once ? st.st_mtime > prev : TRUE)) {
5669
0
      display_now = TRUE;
5670
0
    }
5671
5672
0
    if (display_now == TRUE) {
5673
0
      if (display_fh != NULL) {
5674
0
        if (pr_display_fh(display_fh, session.cwd, R_250, 0) < 0) {
5675
0
          pr_log_debug(DEBUG3, "DisplayChdir: error displaying '%s': %s",
5676
0
            display_fh->fh_path, strerror(errno));
5677
0
        }
5678
5679
0
      } else {
5680
0
        if (pr_display_file(display_file, session.cwd, R_250, 0) < 0) {
5681
0
          pr_log_debug(DEBUG3, "DisplayChdir: error displaying '%s': %s",
5682
0
           display_file, strerror(errno));
5683
0
        }
5684
0
      }
5685
0
    }
5686
0
  }
5687
5688
0
  pr_response_add(R_250, _("%s command successful"), (char *) cmd->argv[0]);
5689
0
  return PR_HANDLED(cmd);
5690
0
}
5691
5692
0
MODRET core_rmd(cmd_rec *cmd) {
5693
0
  int res;
5694
0
  char *decoded_path, *dir;
5695
0
  pr_error_t *err = NULL;
5696
5697
0
  CHECK_CMD_MIN_ARGS(cmd, 2);
5698
5699
0
  decoded_path = pr_fs_decode_path2(cmd->tmp_pool, cmd->arg,
5700
0
    FSIO_DECODE_FL_TELL_ERRORS);
5701
0
  if (decoded_path == NULL) {
5702
0
    int xerrno = errno;
5703
5704
0
    pr_log_debug(DEBUG8, "'%s' failed to decode properly: %s", cmd->arg,
5705
0
      strerror(xerrno));
5706
0
    pr_response_add_err(R_550, _("%s: Illegal character sequence in filename"),
5707
0
      cmd->arg);
5708
5709
0
    pr_cmd_set_errno(cmd, xerrno);
5710
0
    errno = xerrno;
5711
0
    return PR_ERROR(cmd);
5712
0
  }
5713
5714
0
  dir = decoded_path;
5715
5716
0
  res = pr_filter_allow_path(CURRENT_CONF, dir);
5717
0
  switch (res) {
5718
0
    case 0:
5719
0
      break;
5720
5721
0
    case PR_FILTER_ERR_FAILS_ALLOW_FILTER:
5722
0
      pr_log_pri(PR_LOG_NOTICE, "'%s %s' denied by PathAllowFilter",
5723
0
        (char *) cmd->argv[0], dir);
5724
0
      pr_response_add_err(R_550, _("%s: Forbidden filename"), cmd->arg);
5725
5726
0
      pr_cmd_set_errno(cmd, EPERM);
5727
0
      errno = EPERM;
5728
0
      return PR_ERROR(cmd);
5729
5730
0
    case PR_FILTER_ERR_FAILS_DENY_FILTER:
5731
0
      pr_log_pri(PR_LOG_NOTICE, "'%s %s' denied by PathDenyFilter",
5732
0
        (char *) cmd->argv[0], dir);
5733
0
      pr_response_add_err(R_550, _("%s: Forbidden filename"), cmd->arg);
5734
5735
0
      pr_cmd_set_errno(cmd, EPERM);
5736
0
      errno = EPERM;
5737
0
      return PR_ERROR(cmd);
5738
0
  }
5739
5740
0
  dir = dir_canonical_path(cmd->tmp_pool, dir);
5741
0
  if (dir == NULL) {
5742
0
    int xerrno = EINVAL;
5743
5744
0
    pr_response_add_err(R_550, "%s: %s", cmd->arg, strerror(xerrno));
5745
5746
0
    pr_cmd_set_errno(cmd, xerrno);
5747
0
    errno = xerrno;
5748
0
    return PR_ERROR(cmd);
5749
0
  }
5750
5751
0
  if (!dir_check_canon(cmd->tmp_pool, cmd, cmd->group, dir, NULL)) {
5752
0
    int xerrno = EACCES;
5753
5754
0
    pr_log_debug(DEBUG8, "%s command denied by <Limit> config",
5755
0
      (char *) cmd->argv[0]);
5756
0
    pr_response_add_err(R_550, "%s: %s", cmd->arg, strerror(xerrno));
5757
5758
0
    pr_cmd_set_errno(cmd, xerrno);
5759
0
    errno = xerrno;
5760
0
    return PR_ERROR(cmd);
5761
0
  }
5762
5763
0
  res = pr_fsio_rmdir_with_error(cmd->pool, dir, &err);
5764
0
  if (res < 0) {
5765
0
    int xerrno = errno;
5766
5767
0
    pr_error_set_where(err, &core_module, __FILE__, __LINE__ - 4);
5768
0
    pr_error_set_why(err, pstrcat(cmd->pool, "remove directory '", dir, "'",
5769
0
      NULL));
5770
5771
0
    (void) pr_trace_msg("fileperms", 1, "%s, user '%s' (UID %s, GID %s): "
5772
0
      "error removing directory '%s': %s", (char *) cmd->argv[0], session.user,
5773
0
      pr_uid2str(cmd->tmp_pool, session.uid),
5774
0
      pr_gid2str(cmd->tmp_pool, session.gid), dir, strerror(xerrno));
5775
5776
0
    if (err != NULL) {
5777
0
      pr_log_debug(DEBUG9, "%s", pr_error_strerror(err, 0));
5778
0
      pr_error_destroy(err);
5779
0
      err = NULL;
5780
5781
0
    } else {
5782
0
      pr_log_debug(DEBUG9, "error removing directory '%s': %s", dir,
5783
0
        strerror(xerrno));
5784
0
    }
5785
5786
0
    pr_response_add_err(R_550, "%s: %s", cmd->arg, strerror(xerrno));
5787
5788
0
    pr_cmd_set_errno(cmd, xerrno);
5789
0
    errno = xerrno;
5790
0
    return PR_ERROR(cmd);
5791
0
  }
5792
5793
0
  pr_response_add(R_250, _("%s command successful"), (char *) cmd->argv[0]);
5794
0
  return PR_HANDLED(cmd);
5795
0
}
5796
5797
0
MODRET core_mkd(cmd_rec *cmd) {
5798
0
  int res;
5799
0
  char *decoded_path, *dir;
5800
5801
0
  CHECK_CMD_MIN_ARGS(cmd, 2);
5802
5803
  /* XXX Why is there a check to prevent the creation of any directory
5804
   * name containing an asterisk?
5805
   */
5806
0
  if (strchr(cmd->arg, '*')) {
5807
0
    pr_response_add_err(R_550, _("%s: Invalid directory name"), cmd->arg);
5808
5809
0
    pr_cmd_set_errno(cmd, EINVAL);
5810
0
    errno = EINVAL;
5811
0
    return PR_ERROR(cmd);
5812
0
  }
5813
5814
0
  decoded_path = pr_fs_decode_path2(cmd->tmp_pool, cmd->arg,
5815
0
    FSIO_DECODE_FL_TELL_ERRORS);
5816
0
  if (decoded_path == NULL) {
5817
0
    int xerrno = errno;
5818
5819
0
    pr_log_debug(DEBUG8, "'%s' failed to decode properly: %s", cmd->arg,
5820
0
      strerror(xerrno));
5821
0
    pr_response_add_err(R_550, _("%s: Illegal character sequence in filename"),
5822
0
      cmd->arg);
5823
5824
0
    pr_cmd_set_errno(cmd, xerrno);
5825
0
    errno = xerrno;
5826
0
    return PR_ERROR(cmd);
5827
0
  }
5828
5829
0
  dir = decoded_path;
5830
5831
0
  res = pr_filter_allow_path(CURRENT_CONF, dir);
5832
0
  switch (res) {
5833
0
    case 0:
5834
0
      break;
5835
5836
0
    case PR_FILTER_ERR_FAILS_ALLOW_FILTER:
5837
0
      pr_log_pri(PR_LOG_NOTICE, "'%s %s' denied by PathAllowFilter",
5838
0
        (char *) cmd->argv[0], dir);
5839
0
      pr_response_add_err(R_550, _("%s: Forbidden filename"), cmd->arg);
5840
5841
0
      pr_cmd_set_errno(cmd, EPERM);
5842
0
      errno = EPERM;
5843
0
      return PR_ERROR(cmd);
5844
5845
0
    case PR_FILTER_ERR_FAILS_DENY_FILTER:
5846
0
      pr_log_pri(PR_LOG_NOTICE, "'%s %s' denied by PathDenyFilter",
5847
0
        (char *) cmd->argv[0], dir);
5848
0
      pr_response_add_err(R_550, _("%s: Forbidden filename"), cmd->arg);
5849
5850
0
      pr_cmd_set_errno(cmd, EPERM);
5851
0
      errno = EPERM;
5852
0
      return PR_ERROR(cmd);
5853
0
  }
5854
5855
0
  dir = dir_canonical_path(cmd->tmp_pool, dir);
5856
0
  if (dir == NULL) {
5857
0
    int xerrno = EINVAL;
5858
5859
0
    pr_response_add_err(R_550, "%s: %s", cmd->arg, strerror(xerrno));
5860
5861
0
    pr_cmd_set_errno(cmd, xerrno);
5862
0
    errno = xerrno;
5863
0
    return PR_ERROR(cmd);
5864
0
  }
5865
5866
0
  if (!dir_check_canon(cmd->tmp_pool, cmd, cmd->group, dir, NULL)) {
5867
0
    int xerrno = EACCES;
5868
5869
0
    pr_log_debug(DEBUG8, "%s command denied by <Limit> config",
5870
0
      (char *) cmd->argv[0]);
5871
0
    pr_response_add_err(R_550, "%s: %s", cmd->arg, strerror(xerrno));
5872
5873
0
    pr_cmd_set_errno(cmd, xerrno);
5874
0
    errno = xerrno;
5875
0
    return PR_ERROR(cmd);
5876
0
  }
5877
5878
0
  res = pr_fsio_smkdir(cmd->tmp_pool, dir, 0777, session.fsuid, session.fsgid);
5879
0
  if (res < 0 &&
5880
0
      errno != EEXIST) {
5881
0
    int xerrno = errno;
5882
5883
0
    (void) pr_trace_msg("fileperms", 1, "%s, user '%s' (UID %s, GID %s): "
5884
0
      "error making directory '%s': %s", (char *) cmd->argv[0], session.user,
5885
0
      pr_uid2str(cmd->tmp_pool, session.uid),
5886
0
      pr_gid2str(cmd->tmp_pool, session.gid), dir, strerror(xerrno));
5887
5888
0
    pr_response_add_err(R_550, "%s: %s", cmd->arg, strerror(xerrno));
5889
5890
0
    pr_cmd_set_errno(cmd, xerrno);
5891
0
    errno = xerrno;
5892
0
    return PR_ERROR(cmd);
5893
0
  }
5894
5895
0
  pr_response_add(R_257, _("\"%s\" - Directory successfully created"),
5896
0
    quote_dir(cmd->tmp_pool, dir));
5897
5898
0
  return PR_HANDLED(cmd);
5899
0
}
5900
5901
0
MODRET core_cwd(cmd_rec *cmd) {
5902
0
  char *decoded_path;
5903
0
  CHECK_CMD_MIN_ARGS(cmd, 2);
5904
5905
0
  decoded_path = pr_fs_decode_path2(cmd->tmp_pool, cmd->arg,
5906
0
    FSIO_DECODE_FL_TELL_ERRORS);
5907
0
  if (decoded_path == NULL) {
5908
0
    int xerrno = errno;
5909
5910
0
    pr_log_debug(DEBUG8, "'%s' failed to decode properly: %s", cmd->arg,
5911
0
      strerror(xerrno));
5912
0
    pr_response_add_err(R_550, _("%s: Illegal character sequence in filename"),
5913
0
      cmd->arg);
5914
5915
0
    pr_cmd_set_errno(cmd, xerrno);
5916
0
    errno = xerrno;
5917
0
    return PR_ERROR(cmd);
5918
0
  }
5919
5920
0
  return core_chdir(cmd, decoded_path);
5921
0
}
5922
5923
0
MODRET core_cdup(cmd_rec *cmd) {
5924
0
  CHECK_CMD_ARGS(cmd, 1);
5925
0
  return core_chdir(cmd, "..");
5926
0
}
5927
5928
/* Returns the modification time of a file, as per RFC3659. */
5929
0
MODRET core_mdtm(cmd_rec *cmd) {
5930
0
  char *decoded_path, *path;
5931
0
  struct stat st;
5932
0
  char buf[16];
5933
0
  struct tm *tm;
5934
5935
0
  CHECK_CMD_MIN_ARGS(cmd, 2);
5936
5937
0
  decoded_path = pr_fs_decode_path2(cmd->tmp_pool, cmd->arg,
5938
0
    FSIO_DECODE_FL_TELL_ERRORS);
5939
0
  if (decoded_path == NULL) {
5940
0
    int xerrno = errno;
5941
5942
0
    pr_log_debug(DEBUG8, "'%s' failed to decode properly: %s", cmd->arg,
5943
0
      strerror(xerrno));
5944
0
    pr_response_add_err(R_550, _("%s: Illegal character sequence in filename"),
5945
0
      cmd->arg);
5946
5947
0
    pr_cmd_set_errno(cmd, xerrno);
5948
0
    errno = xerrno;
5949
0
    return PR_ERROR(cmd);
5950
0
  }
5951
5952
0
  path = decoded_path;
5953
5954
0
  pr_fs_clear_cache2(path);
5955
0
  path = dir_realpath(cmd->tmp_pool, decoded_path);
5956
0
  if (!path ||
5957
0
      !dir_check(cmd->tmp_pool, cmd, cmd->group, path, NULL) ||
5958
0
      pr_fsio_stat(path, &st) == -1) {
5959
0
    int xerrno = errno;
5960
5961
0
    pr_response_add_err(R_550, "%s: %s", cmd->arg, strerror(xerrno));
5962
5963
0
    pr_cmd_set_errno(cmd, xerrno);
5964
0
    errno = xerrno;
5965
0
    return PR_ERROR(cmd);
5966
0
  }
5967
5968
0
  if (!S_ISREG(st.st_mode)) {
5969
0
    pr_response_add_err(R_550, _("%s: not a plain file"), cmd->arg);
5970
5971
0
    pr_cmd_set_errno(cmd, EINVAL);
5972
0
    errno = EINVAL;
5973
0
    return PR_ERROR(cmd);
5974
0
  }
5975
5976
0
  memset(buf, '\0', sizeof(buf));
5977
5978
0
  tm = pr_gmtime(cmd->tmp_pool, &st.st_mtime);
5979
0
  if (tm != NULL) {
5980
0
    pr_snprintf(buf, sizeof(buf), "%04d%02d%02d%02d%02d%02d",
5981
0
      tm->tm_year+1900, tm->tm_mon+1, tm->tm_mday, tm->tm_hour,
5982
0
      tm->tm_min, tm->tm_sec);
5983
5984
0
  } else {
5985
0
    pr_snprintf(buf, sizeof(buf), "00000000000000");
5986
0
  }
5987
5988
0
  pr_response_add(R_213, "%s", buf);
5989
0
  return PR_HANDLED(cmd);
5990
0
}
5991
5992
0
MODRET core_size(cmd_rec *cmd) {
5993
0
  char *decoded_path, *path;
5994
0
  struct stat st;
5995
5996
0
  CHECK_CMD_MIN_ARGS(cmd, 2);
5997
5998
  /* The PR_ALLOW_ASCII_MODE_SIZE macro should ONLY be defined at compile time,
5999
   * e.g. using:
6000
   *
6001
   *  $ ./configure CPPFLAGS=-DPR_ALLOW_ASCII_MODE_SIZE ...
6002
   *
6003
   * Define this macro if you want proftpd to handle a SIZE command while in
6004
   * ASCII mode.  Note, however, that ProFTPD will NOT properly calculate
6005
   * CRLF sequences EVEN if this macro is defined: ProFTPD will always return
6006
   * the number of bytes on disk for the requested file, even if the number of
6007
   * bytes transferred when that file is downloaded is different.  Thus this
6008
   * behavior will not comply with RFC 3659, Section 4.  Caveat emptor.
6009
   */
6010
0
#ifndef PR_ALLOW_ASCII_MODE_SIZE
6011
  /* Refuse the command if we're in ASCII mode. */
6012
0
  if (session.sf_flags & SF_ASCII) {
6013
0
    pr_log_debug(DEBUG5, "%s not allowed in ASCII mode", (char *) cmd->argv[0]);
6014
0
    pr_response_add_err(R_550, _("%s not allowed in ASCII mode"),
6015
0
      (char *) cmd->argv[0]);
6016
6017
0
    pr_cmd_set_errno(cmd, EPERM);
6018
0
    errno = EPERM;
6019
0
    return PR_ERROR(cmd);
6020
0
  }
6021
0
#endif /* PR_ALLOW_ASCII_MODE_SIZE */
6022
6023
0
  decoded_path = pr_fs_decode_path2(cmd->tmp_pool, cmd->arg,
6024
0
    FSIO_DECODE_FL_TELL_ERRORS);
6025
0
  if (decoded_path == NULL) {
6026
0
    int xerrno = errno;
6027
6028
0
    pr_log_debug(DEBUG8, "'%s' failed to decode properly: %s", cmd->arg,
6029
0
      strerror(xerrno));
6030
0
    pr_response_add_err(R_550, _("%s: Illegal character sequence in filename"),
6031
0
      cmd->arg);
6032
6033
0
    pr_cmd_set_errno(cmd, xerrno);
6034
0
    errno = xerrno;
6035
0
    return PR_ERROR(cmd);
6036
0
  }
6037
6038
0
  pr_fs_clear_cache2(decoded_path);
6039
0
  path = dir_realpath(cmd->tmp_pool, decoded_path);
6040
0
  if (path != NULL) {
6041
0
    pr_fs_clear_cache2(path);
6042
0
  }
6043
6044
0
  if (path == NULL ||
6045
0
      !dir_check(cmd->tmp_pool, cmd, cmd->group, path, NULL) ||
6046
0
      pr_fsio_stat(path, &st) == -1) {
6047
0
    int xerrno = errno;
6048
6049
0
    pr_response_add_err(R_550, "%s: %s", cmd->arg, strerror(xerrno));
6050
6051
0
    pr_cmd_set_errno(cmd, xerrno);
6052
0
    errno = xerrno;
6053
0
    return PR_ERROR(cmd);
6054
0
  }
6055
6056
0
  if (!S_ISREG(st.st_mode)) {
6057
0
    pr_response_add_err(R_550, _("%s: not a regular file"), cmd->arg);
6058
6059
0
    pr_cmd_set_errno(cmd, EINVAL);
6060
0
    errno = EINVAL;
6061
0
    return PR_ERROR(cmd);
6062
0
  }
6063
6064
0
  pr_response_add(R_213, "%" PR_LU, (pr_off_t) st.st_size);
6065
0
  return PR_HANDLED(cmd);
6066
0
}
6067
6068
0
MODRET core_dele(cmd_rec *cmd) {
6069
0
  int res;
6070
0
  char *decoded_path, *path, *fullpath;
6071
0
  struct stat st;
6072
0
  pr_error_t *err = NULL;
6073
6074
0
  CHECK_CMD_MIN_ARGS(cmd, 2);
6075
6076
0
  decoded_path = pr_fs_decode_path2(cmd->tmp_pool, cmd->arg,
6077
0
    FSIO_DECODE_FL_TELL_ERRORS);
6078
0
  if (decoded_path == NULL) {
6079
0
    int xerrno = errno;
6080
6081
0
    pr_log_debug(DEBUG8, "'%s' failed to decode properly: %s", cmd->arg,
6082
0
      strerror(xerrno));
6083
0
    pr_response_add_err(R_550, _("%s: Illegal character sequence in filename"),
6084
0
      cmd->arg);
6085
6086
0
    pr_cmd_set_errno(cmd, xerrno);
6087
0
    errno = xerrno;
6088
0
    return PR_ERROR(cmd);
6089
0
  }
6090
6091
0
  path = decoded_path;
6092
6093
0
  res = pr_filter_allow_path(CURRENT_CONF, path);
6094
0
  switch (res) {
6095
0
    case 0:
6096
0
      break;
6097
6098
0
    case PR_FILTER_ERR_FAILS_ALLOW_FILTER:
6099
0
      pr_log_pri(PR_LOG_NOTICE, "'%s %s' denied by PathAllowFilter",
6100
0
        (char *) cmd->argv[0], path);
6101
0
      pr_response_add_err(R_550, _("%s: Forbidden filename"), cmd->arg);
6102
6103
0
      pr_cmd_set_errno(cmd, EPERM);
6104
0
      errno = EPERM;
6105
0
      return PR_ERROR(cmd);
6106
6107
0
    case PR_FILTER_ERR_FAILS_DENY_FILTER:
6108
0
      pr_log_pri(PR_LOG_NOTICE, "'%s %s' denied by PathDenyFilter",
6109
0
        (char *) cmd->argv[0], path);
6110
0
      pr_response_add_err(R_550, _("%s: Forbidden filename"), cmd->arg);
6111
6112
0
      pr_cmd_set_errno(cmd, EPERM);
6113
0
      errno = EPERM;
6114
0
      return PR_ERROR(cmd);
6115
0
  }
6116
6117
  /* If told to delete a symlink, don't delete the file it points to!  */
6118
0
  path = dir_canonical_path(cmd->tmp_pool, path);
6119
0
  if (path == NULL) {
6120
0
    int xerrno = ENOENT;
6121
6122
0
    pr_response_add_err(R_550, "%s: %s", cmd->arg, strerror(xerrno));
6123
6124
0
    pr_cmd_set_errno(cmd, xerrno);
6125
0
    errno = xerrno;
6126
0
    return PR_ERROR(cmd);
6127
0
  }
6128
6129
0
  if (!dir_check(cmd->tmp_pool, cmd, cmd->group, path, NULL)) {
6130
0
    int xerrno = errno;
6131
6132
0
    pr_log_debug(DEBUG7, "deleting '%s' denied by <Limit> configuration", path);
6133
0
    pr_response_add_err(R_550, "%s: %s", cmd->arg, strerror(xerrno));
6134
6135
0
    pr_cmd_set_errno(cmd, xerrno);
6136
0
    errno = xerrno;
6137
0
    return PR_ERROR(cmd);
6138
0
  }
6139
6140
  /* Stat the path, before it is deleted, so that the size of the file
6141
   * being deleted can be logged.  Note that unlink() doesn't follow symlinks,
6142
   * so we need to use lstat(), not stat(), lest we log the wrong size.
6143
   */
6144
0
  memset(&st, 0, sizeof(st));
6145
0
  pr_fs_clear_cache2(path);
6146
0
  res = pr_fsio_lstat_with_error(cmd->tmp_pool, path, &st, &err);
6147
0
  if (res < 0) {
6148
0
    int xerrno = errno;
6149
6150
0
    pr_error_set_where(err, &core_module, __FILE__, __LINE__ - 4);
6151
0
    pr_error_set_why(err, pstrcat(cmd->pool, "check file '", path, "'", NULL));
6152
6153
0
    if (err != NULL) {
6154
0
      pr_log_debug(DEBUG3, "%s", pr_error_strerror(err, 0));
6155
0
      pr_error_destroy(err);
6156
0
      err = NULL;
6157
6158
0
    } else {
6159
0
      pr_log_debug(DEBUG3, "unable to lstat '%s': %s", path, strerror(xerrno));
6160
0
    }
6161
6162
0
    pr_response_add_err(R_550, "%s: %s", cmd->arg, strerror(xerrno));
6163
6164
0
    pr_cmd_set_errno(cmd, xerrno);
6165
0
    errno = xerrno;
6166
0
    return PR_ERROR(cmd);
6167
0
  }
6168
6169
0
#ifdef EISDIR
6170
  /* If the path is a directory, try to return a good error message (e.g.
6171
   * EISDIR).
6172
   */
6173
0
  if (S_ISDIR(st.st_mode)) {
6174
0
    int xerrno = EISDIR;
6175
6176
0
    (void) pr_trace_msg("fileperms", 1, "%s, user '%s' (UID %s, GID %s): "
6177
0
      "error deleting '%s': %s", (char *) cmd->argv[0], session.user,
6178
0
      pr_uid2str(cmd->tmp_pool, session.uid),
6179
0
      pr_gid2str(cmd->tmp_pool, session.gid), path, strerror(xerrno));
6180
6181
0
    pr_log_debug(DEBUG3, "error deleting '%s': %s", path, strerror(xerrno));
6182
0
    pr_response_add_err(R_550, "%s: %s", cmd->arg, strerror(xerrno));
6183
6184
0
    pr_cmd_set_errno(cmd, xerrno);
6185
0
    errno = xerrno;
6186
0
    return PR_ERROR(cmd);
6187
0
  }
6188
0
#endif /* !EISDIR */
6189
6190
0
  res = pr_fsio_unlink_with_error(cmd->pool, path, &err);
6191
0
  if (res < 0) {
6192
0
    int xerrno = errno;
6193
6194
0
    pr_error_set_where(err, &core_module, __FILE__, __LINE__ - 4);
6195
0
    pr_error_set_why(err, pstrcat(cmd->pool, "delete file '", path, "'", NULL));
6196
6197
0
    (void) pr_trace_msg("fileperms", 1, "%s, user '%s' (UID %s, GID %s): "
6198
0
      "error deleting '%s': %s", (char *) cmd->argv[0], session.user,
6199
0
      pr_uid2str(cmd->tmp_pool, session.uid),
6200
0
      pr_gid2str(cmd->tmp_pool, session.gid), path, strerror(xerrno));
6201
6202
0
    if (err != NULL) {
6203
0
      pr_log_debug(DEBUG3, "%s", pr_error_strerror(err, 0));
6204
0
      pr_error_destroy(err);
6205
0
      err = NULL;
6206
6207
0
    } else {
6208
0
      pr_log_debug(DEBUG3, "error deleting '%s': %s", path, strerror(xerrno));
6209
0
    }
6210
6211
0
    pr_response_add_err(R_550, "%s: %s", cmd->arg, strerror(xerrno));
6212
6213
0
    pr_cmd_set_errno(cmd, xerrno);
6214
0
    errno = xerrno;
6215
0
    return PR_ERROR(cmd);
6216
0
  }
6217
6218
0
  fullpath = dir_abs_path(cmd->tmp_pool, path, TRUE);
6219
6220
0
  if (session.sf_flags & SF_ANON) {
6221
0
    xferlog_write(0, session.c->remote_name, st.st_size, fullpath,
6222
0
      (session.sf_flags & SF_ASCII ? 'a' : 'b'), 'd', 'a', session.anon_user,
6223
0
      'c', "_");
6224
6225
0
  } else {
6226
0
    xferlog_write(0, session.c->remote_name, st.st_size, fullpath,
6227
0
      (session.sf_flags & SF_ASCII ? 'a' : 'b'), 'd', 'r', session.user, 'c',
6228
0
      "_");
6229
0
  }
6230
6231
0
  pr_response_add(R_250, _("%s command successful"), (char *) cmd->argv[0]);
6232
0
  return PR_HANDLED(cmd);
6233
0
}
6234
6235
static int handle_cross_mount_rename(cmd_rec *cmd, const char *src_path,
6236
0
    const char *dst_path, struct stat *dst_st) {
6237
0
  int res, xerrno;
6238
0
  struct stat src_st;
6239
6240
0
  pr_fs_clear_cache2(src_path);
6241
0
  res = pr_fsio_lstat(src_path, &src_st);
6242
0
  if (res < 0) {
6243
0
    xerrno = errno;
6244
6245
0
    pr_log_debug(DEBUG4, "Unable to stat '%s' for cross filesystem mount "
6246
0
      "point rename: %s", src_path, strerror(xerrno));
6247
6248
0
    pr_response_add_err(R_550, _("%s: %s"), cmd->arg, strerror(xerrno));
6249
0
    pr_cmd_set_errno(cmd, xerrno);
6250
0
    errno = xerrno;
6251
6252
0
    return -1;
6253
0
  }
6254
6255
0
  if (S_ISDIR(src_st.st_mode)) {
6256
0
    xerrno = EPERM;
6257
6258
0
    pr_log_debug(DEBUG4, "Cannot rename directory '%s' across a filesystem "
6259
0
      "mount", src_path);
6260
6261
0
    pr_response_add_err(R_550, _("%s: %s"), cmd->arg, strerror(xerrno));
6262
0
    pr_cmd_set_errno(cmd, xerrno);
6263
0
    errno = xerrno;
6264
6265
0
    return -1;
6266
0
  }
6267
6268
0
  if (!S_ISREG(src_st.st_mode) &&
6269
0
      !S_ISLNK(src_st.st_mode)) {
6270
0
    xerrno = EPERM;
6271
6272
0
    pr_log_debug(DEBUG4, "Cannot rename unsupported file type '%s' across "
6273
0
      "a filesystem mount", src_path);
6274
6275
0
    pr_response_add_err(R_550, _("%s: %s"), cmd->arg, strerror(xerrno));
6276
0
    pr_cmd_set_errno(cmd, xerrno);
6277
0
    errno = xerrno;
6278
6279
0
    return -1;
6280
0
  }
6281
6282
  /* If the destination exists, unlink it first.  Unless it is a directory,
6283
   * in which case we error out now; we do not copy into directories across
6284
   * mounts at the moment.
6285
   */
6286
0
  if (dst_st != NULL) {
6287
0
    if (S_ISDIR(dst_st->st_mode)) {
6288
0
      xerrno = EPERM;
6289
6290
0
      pr_log_debug(DEBUG4, "Cannot rename path '%s' across a filesystem mount "
6291
0
        "point into directory '%s'", src_path, dst_path);
6292
6293
0
      pr_response_add_err(R_550, _("%s: %s"), cmd->arg, strerror(xerrno));
6294
0
      pr_cmd_set_errno(cmd, xerrno);
6295
0
      errno = xerrno;
6296
6297
0
      return -1;
6298
0
    }
6299
6300
0
    res = pr_fsio_unlink(dst_path);
6301
0
    if (res < 0) {
6302
0
      xerrno = errno;
6303
6304
0
      pr_log_debug(DEBUG4, "Unable to delete '%s' for cross filesystem mount "
6305
0
        "point rename: %s", dst_path, strerror(xerrno));
6306
6307
0
      pr_response_add_err(R_550, _("%s: %s"), cmd->arg, strerror(xerrno));
6308
0
      pr_cmd_set_errno(cmd, xerrno);
6309
0
      errno = xerrno;
6310
6311
0
      return -1;
6312
0
    }
6313
6314
0
    dst_st = NULL;
6315
0
  }
6316
6317
  /* At this point, we know that the source path exists, and is a regular file
6318
   * or symlink.  And we know that the destination path does not exist.
6319
   */
6320
6321
0
  if (S_ISREG(src_st.st_mode)) {
6322
0
    res = pr_fs_copy_file(src_path, dst_path);
6323
0
    xerrno = errno;
6324
6325
0
  } else if (S_ISLNK(src_st.st_mode)) {
6326
0
    res = pr_fs_copy_symlink(src_path, dst_path, 0);
6327
0
    xerrno = errno;
6328
0
  }
6329
6330
0
  if (res < 0) {
6331
0
    (void) pr_trace_msg("fileperms", 1, "%s, user '%s' (UID %s, GID %s): "
6332
0
      "error copying '%s' to '%s': %s", (char *) cmd->argv[0], session.user,
6333
0
      pr_uid2str(cmd->tmp_pool, session.uid),
6334
0
      pr_gid2str(cmd->tmp_pool, session.gid), src_path, dst_path,
6335
0
      strerror(xerrno));
6336
6337
0
    pr_response_add_err(R_550, _("Rename %s: %s"), cmd->arg,
6338
0
      strerror(xerrno));
6339
0
    pr_cmd_set_errno(cmd, xerrno);
6340
0
    errno = xerrno;
6341
6342
0
    return -1;
6343
0
  }
6344
6345
  /* Once copied, unlink the original file. */
6346
0
  res = pr_fsio_unlink(src_path);
6347
0
  if (res < 0) {
6348
0
    pr_log_debug(DEBUG0, "error deleting '%s': %s", src_path, strerror(errno));
6349
0
  }
6350
6351
0
  return 0;
6352
0
}
6353
6354
0
MODRET core_rnto(cmd_rec *cmd) {
6355
0
  int res, xerrno, dst_exists = FALSE;
6356
0
  char *decoded_path, *path;
6357
0
  unsigned char *allow_overwrite = NULL;
6358
0
  struct stat st;
6359
0
  pr_error_t *err = NULL;
6360
6361
0
  CHECK_CMD_MIN_ARGS(cmd, 2);
6362
6363
0
  if (session.xfer.path == NULL) {
6364
0
    pr_data_clear_xfer_pool();
6365
6366
0
    pr_response_add_err(R_503, _("Bad sequence of commands"));
6367
6368
0
    pr_cmd_set_errno(cmd, EPERM);
6369
0
    errno = EPERM;
6370
0
    return PR_ERROR(cmd);
6371
0
  }
6372
6373
0
  decoded_path = pr_fs_decode_path2(cmd->tmp_pool, cmd->arg,
6374
0
    FSIO_DECODE_FL_TELL_ERRORS);
6375
0
  if (decoded_path == NULL) {
6376
0
    xerrno = errno;
6377
6378
0
    pr_log_debug(DEBUG8, "'%s' failed to decode properly: %s", cmd->arg,
6379
0
      strerror(xerrno));
6380
0
    pr_response_add_err(R_550, _("%s: Illegal character sequence in filename"),
6381
0
      cmd->arg);
6382
6383
0
    pr_cmd_set_errno(cmd, xerrno);
6384
0
    errno = xerrno;
6385
0
    return PR_ERROR(cmd);
6386
0
  }
6387
6388
0
  path = decoded_path;
6389
6390
0
  res = pr_filter_allow_path(CURRENT_CONF, path);
6391
0
  switch (res) {
6392
0
    case 0:
6393
0
      break;
6394
6395
0
    case PR_FILTER_ERR_FAILS_ALLOW_FILTER:
6396
0
      pr_log_pri(PR_LOG_NOTICE, "'%s %s' denied by PathAllowFilter",
6397
0
        (char *) cmd->argv[0], path);
6398
0
      pr_response_add_err(R_550, _("%s: Forbidden filename"), cmd->arg);
6399
6400
0
      pr_cmd_set_errno(cmd, EPERM);
6401
0
      errno = EPERM;
6402
0
      return PR_ERROR(cmd);
6403
6404
0
    case PR_FILTER_ERR_FAILS_DENY_FILTER:
6405
0
      pr_log_pri(PR_LOG_NOTICE, "'%s %s' denied by PathDenyFilter",
6406
0
        (char *) cmd->argv[0], path);
6407
0
      pr_response_add_err(R_550, _("%s: Forbidden filename"), cmd->arg);
6408
6409
0
      pr_cmd_set_errno(cmd, EPERM);
6410
0
      errno = EPERM;
6411
0
      return PR_ERROR(cmd);
6412
0
  }
6413
6414
0
  path = dir_canonical_path(cmd->tmp_pool, path);
6415
6416
  /* Deny the rename if AllowOverwrites are not allowed, and the destination
6417
   * rename file already exists.
6418
   */
6419
0
  allow_overwrite = get_param_ptr(CURRENT_CONF, "AllowOverwrite", FALSE);
6420
6421
0
  pr_fs_clear_cache2(path);
6422
0
  res = pr_fsio_lstat(path, &st);
6423
0
  if (res == 0) {
6424
0
    dst_exists = TRUE;
6425
6426
0
    if (allow_overwrite == NULL ||
6427
0
        *allow_overwrite == FALSE) {
6428
0
      pr_log_debug(DEBUG6, "AllowOverwrite denied permission for %s", path);
6429
0
      pr_response_add_err(R_550, _("%s: Rename permission denied"), cmd->arg);
6430
6431
0
      pr_cmd_set_errno(cmd, EACCES);
6432
0
      errno = EACCES;
6433
0
      return PR_ERROR(cmd);
6434
0
    }
6435
0
  }
6436
6437
0
  if (path == NULL ||
6438
0
      !dir_check_canon(cmd->tmp_pool, cmd, cmd->group, path, NULL)) {
6439
0
    pr_log_debug(DEBUG8, "%s command denied by <Limit> config",
6440
0
      (char *) cmd->argv[0]);
6441
0
    pr_response_add_err(R_550, _("%s: %s"), cmd->arg, strerror(EPERM));
6442
6443
0
    pr_cmd_set_errno(cmd, EPERM);
6444
0
    errno = EPERM;
6445
0
    return PR_ERROR(cmd);
6446
0
  }
6447
6448
0
  res = pr_fsio_rename_with_error(cmd->pool, session.xfer.path, path, &err);
6449
0
  if (res < 0) {
6450
0
    xerrno = errno;
6451
6452
0
    pr_error_set_where(err, &core_module, __FILE__, __LINE__ - 4);
6453
0
    pr_error_set_why(err, pstrcat(cmd->pool, "rename '", session.xfer.path,
6454
0
      "' to '", path, "'", NULL));
6455
6456
0
    if (xerrno == EISDIR) {
6457
      /* In this case, the client has requested that a directory be renamed
6458
       * across mount points.  The pr_fs_copy_file() function can't handle
6459
       * copying directories; it only knows about files.  (This could be
6460
       * fixed to work later, e.g. using code from the mod_copy module.)
6461
       *
6462
       * For now, error out now with a more informative error message to the
6463
       * client.
6464
       */
6465
6466
0
      (void) pr_trace_msg("fileperms", 1, "%s, user '%s' (UID %s, GID %s): "
6467
0
        "error copying '%s' to '%s': %s (previous error was '%s')",
6468
0
        (char *) cmd->argv[0], session.user,
6469
0
        pr_uid2str(cmd->tmp_pool, session.uid),
6470
0
        pr_gid2str(cmd->tmp_pool, session.gid), session.xfer.path, path,
6471
0
        strerror(xerrno), strerror(EXDEV));
6472
6473
0
      pr_log_debug(DEBUG4,
6474
0
        "Cannot rename directory '%s' across a filesystem mount point",
6475
0
        session.xfer.path);
6476
6477
0
      if (err != NULL) {
6478
0
        pr_error_destroy(err);
6479
0
        err = NULL;
6480
0
      }
6481
6482
      /* Use EPERM, rather than EISDIR, to get slightly more informative
6483
       * error messages.
6484
       */
6485
0
      xerrno = EPERM;
6486
6487
0
      pr_response_add_err(R_550, _("%s: %s"), cmd->arg, strerror(xerrno));
6488
6489
0
      pr_cmd_set_errno(cmd, xerrno);
6490
0
      errno = xerrno;
6491
0
      return PR_ERROR(cmd);
6492
0
    }
6493
6494
0
    if (xerrno != EXDEV) {
6495
0
      (void) pr_trace_msg("fileperms", 1, "%s, user '%s' (UID %s, GID %s): "
6496
0
        "error renaming '%s' to '%s': %s", (char *) cmd->argv[0], session.user,
6497
0
        pr_uid2str(cmd->tmp_pool, session.uid),
6498
0
        pr_gid2str(cmd->tmp_pool, session.gid), session.xfer.path, path,
6499
0
        strerror(xerrno));
6500
6501
0
      if (err != NULL) {
6502
0
        pr_log_debug(DEBUG9, "%s", pr_error_strerror(err, 0));
6503
0
        pr_error_destroy(err);
6504
0
        err = NULL;
6505
0
      }
6506
6507
0
      pr_response_add_err(R_550, _("%s: %s"), cmd->arg, strerror(xerrno));
6508
6509
0
      pr_cmd_set_errno(cmd, xerrno);
6510
0
      errno = xerrno;
6511
0
      return PR_ERROR(cmd);
6512
0
    }
6513
6514
    /* In this case, we'll need to manually copy the file from the source
6515
     * to the destination paths.
6516
     */
6517
0
    if (handle_cross_mount_rename(cmd, session.xfer.path, path,
6518
0
        dst_exists == TRUE ? &st : NULL) < 0) {
6519
0
      return PR_ERROR(cmd);
6520
0
    }
6521
0
  }
6522
6523
  /* Change the xfer path to the name of the destination file, for logging. */
6524
0
  session.xfer.path = pstrdup(session.xfer.p, path);
6525
6526
0
  pr_response_add(R_250, _("Rename successful"));
6527
0
  return PR_HANDLED(cmd);
6528
0
}
6529
6530
0
MODRET core_rnto_cleanup(cmd_rec *cmd) {
6531
0
  pr_data_clear_xfer_pool();
6532
6533
0
  pr_table_remove(session.notes, "mod_core.rnfr-path", NULL);
6534
0
  return PR_DECLINED(cmd);
6535
0
}
6536
6537
0
MODRET core_rnfr(cmd_rec *cmd) {
6538
0
  int res;
6539
0
  const char *abs_path;
6540
0
  char *decoded_path, *path;
6541
6542
0
  CHECK_CMD_MIN_ARGS(cmd, 2);
6543
6544
0
  decoded_path = pr_fs_decode_path2(cmd->tmp_pool, cmd->arg,
6545
0
    FSIO_DECODE_FL_TELL_ERRORS);
6546
0
  if (decoded_path == NULL) {
6547
0
    int xerrno = errno;
6548
6549
0
    pr_log_debug(DEBUG8, "'%s' failed to decode properly: %s", cmd->arg,
6550
0
      strerror(xerrno));
6551
0
    pr_response_add_err(R_550, _("%s: Illegal character sequence in filename"),
6552
0
      cmd->arg);
6553
6554
0
    pr_cmd_set_errno(cmd, xerrno);
6555
0
    errno = xerrno;
6556
0
    return PR_ERROR(cmd);
6557
0
  }
6558
6559
0
  path = decoded_path;
6560
6561
0
  res = pr_filter_allow_path(CURRENT_CONF, path);
6562
0
  switch (res) {
6563
0
    case 0:
6564
0
      break;
6565
6566
0
    case PR_FILTER_ERR_FAILS_ALLOW_FILTER:
6567
0
      pr_log_pri(PR_LOG_NOTICE, "'%s %s' denied by PathAllowFilter",
6568
0
        (char *) cmd->argv[0], path);
6569
0
      pr_response_add_err(R_550, _("%s: Forbidden filename"), cmd->arg);
6570
6571
0
      pr_cmd_set_errno(cmd, EPERM);
6572
0
      errno = EPERM;
6573
0
      return PR_ERROR(cmd);
6574
6575
0
    case PR_FILTER_ERR_FAILS_DENY_FILTER:
6576
0
      pr_log_pri(PR_LOG_NOTICE, "'%s %s' denied by PathDenyFilter",
6577
0
        (char *) cmd->argv[0], path);
6578
0
      pr_response_add_err(R_550, _("%s: Forbidden filename"), cmd->arg);
6579
6580
0
      pr_cmd_set_errno(cmd, EPERM);
6581
0
      errno = EPERM;
6582
0
      return PR_ERROR(cmd);
6583
0
  }
6584
6585
0
  abs_path = dir_abs_path(cmd->tmp_pool, path, FALSE);
6586
6587
  /* Allow renaming a symlink, even a dangling one. */
6588
0
  path = dir_canonical_path(cmd->tmp_pool, path);
6589
0
  if (abs_path == NULL) {
6590
0
    abs_path = path;
6591
0
  }
6592
6593
0
  if (path == NULL ||
6594
0
      !dir_check(cmd->tmp_pool, cmd, cmd->group, path, NULL) ||
6595
0
      !exists2(cmd->tmp_pool, path)) {
6596
0
    int xerrno = errno;
6597
6598
0
    pr_response_add_err(R_550, _("%s: %s"), cmd->arg, strerror(xerrno));
6599
6600
0
    pr_cmd_set_errno(cmd, xerrno);
6601
0
    errno = xerrno;
6602
0
    return PR_ERROR(cmd);
6603
0
  }
6604
6605
  /* We store the path in session.xfer.path */
6606
6607
0
  pr_data_clear_xfer_pool();
6608
0
  session.xfer.p = make_sub_pool(session.pool);
6609
0
  pr_pool_tag(session.xfer.p, "session xfer pool");
6610
6611
0
  session.xfer.path = pstrdup(session.xfer.p, path);
6612
6613
  /* Make sure we store the absolute path for LogFormat %w (Issue #1808). */
6614
0
  abs_path = pr_fsio_realpath(session.xfer.p, abs_path);
6615
6616
0
  pr_table_remove(session.notes, "mod_core.rnfr-path", NULL);
6617
0
  pr_table_add(session.notes, "mod_core.rnfr-path",
6618
0
    pstrdup(session.xfer.p, abs_path), 0);
6619
6620
0
  pr_response_add(R_350,
6621
0
    _("File or directory exists, ready for destination name"));
6622
0
  return PR_HANDLED(cmd);
6623
0
}
6624
6625
0
MODRET core_noop(cmd_rec *cmd) {
6626
0
  if (!dir_check(cmd->tmp_pool, cmd, cmd->group, session.vwd, NULL)) {
6627
0
    int xerrno = EPERM;
6628
6629
0
    pr_log_debug(DEBUG7, "%s command denied by <Limit> configuration",
6630
0
      (char *) cmd->argv[0]);
6631
0
    pr_response_add_err(R_550, "%s", strerror(xerrno));
6632
6633
0
    pr_cmd_set_errno(cmd, xerrno);
6634
0
    errno = xerrno;
6635
0
    return PR_ERROR(cmd);
6636
0
  }
6637
6638
0
  pr_response_add(R_200, _("NOOP command successful"));
6639
0
  return PR_HANDLED(cmd);
6640
0
}
6641
6642
0
static int feat_cmp(const void *a, const void *b) {
6643
0
  return strcasecmp(*((const char **) a), *((const char **) b));
6644
0
}
6645
6646
0
MODRET core_feat(cmd_rec *cmd) {
6647
0
  register unsigned int i;
6648
0
  const char *feat = NULL;
6649
0
  array_header *feats = NULL;
6650
6651
0
  CHECK_CMD_ARGS(cmd, 1);
6652
6653
0
  if (!dir_check(cmd->tmp_pool, cmd, cmd->group, session.vwd, NULL)) {
6654
0
    int xerrno = EPERM;
6655
6656
0
    pr_log_debug(DEBUG3, "%s command denied by <Limit> configuration",
6657
0
      (char *) cmd->argv[0]);
6658
0
    pr_response_add_err(R_550, "%s: %s", (char *) cmd->argv[0],
6659
0
      strerror(xerrno));
6660
6661
0
    pr_cmd_set_errno(cmd, xerrno);
6662
0
    errno = xerrno;
6663
0
    return PR_ERROR(cmd);
6664
0
  }
6665
6666
0
  feat = pr_feat_get();
6667
0
  if (feat == NULL) {
6668
0
    pr_response_add(R_211, _("No features supported"));
6669
0
    return PR_HANDLED(cmd);
6670
0
  }
6671
6672
0
  feats = make_array(cmd->tmp_pool, 0, sizeof(char **));
6673
6674
0
  while (feat != NULL) {
6675
0
    pr_signals_handle();
6676
0
    *((char **) push_array(feats)) = pstrdup(cmd->tmp_pool, feat);
6677
0
    feat = pr_feat_get_next();
6678
0
  }
6679
6680
  /* Sort the features, for a prettier output. */
6681
0
  qsort(feats->elts, feats->nelts, sizeof(char *), feat_cmp);
6682
6683
0
  pr_response_add(R_211, "%s", _("Features:"));
6684
0
  for (i = 0; i < feats->nelts; i++) {
6685
0
    pr_response_add(R_DUP, "%s", ((const char **) feats->elts)[i]);
6686
0
  }
6687
0
  pr_response_add(R_DUP, _("End"));
6688
6689
0
  return PR_HANDLED(cmd);
6690
0
}
6691
6692
0
MODRET core_opts(cmd_rec *cmd) {
6693
0
  register unsigned int i;
6694
0
  int res;
6695
0
  char *arg = "";
6696
0
  cmd_rec *subcmd;
6697
6698
0
  CHECK_CMD_MIN_ARGS(cmd, 2);
6699
6700
  /* Impose a maximum number of allowed arguments, to prevent malicious
6701
   * clients from trying to do Bad Things(tm).  See Bug#3870.
6702
   */
6703
0
  if ((cmd->argc-1) > PR_OPTS_MAX_PARAM_COUNT) {
6704
0
    int xerrno = EINVAL;
6705
6706
0
    pr_log_debug(DEBUG2,
6707
0
      "OPTS command with too many parameters (%d), rejecting", cmd->argc-1);
6708
0
    pr_response_add_err(R_550, "%s: %s", (char *) cmd->argv[0],
6709
0
      strerror(xerrno));
6710
6711
0
    pr_cmd_set_errno(cmd, xerrno);
6712
0
    errno = xerrno;
6713
0
    return PR_ERROR(cmd);
6714
0
  }
6715
6716
0
  subcmd = pr_cmd_alloc(cmd->tmp_pool, cmd->argc-1, NULL);
6717
0
  subcmd->argv[0] = pstrcat(cmd->tmp_pool, "OPTS_", cmd->argv[1], NULL);
6718
0
  subcmd->group = cmd->group;
6719
6720
0
  if (!dir_check(cmd->tmp_pool, subcmd, subcmd->group, session.vwd, NULL)) {
6721
0
    int xerrno = EACCES;
6722
6723
0
    pr_log_debug(DEBUG7, "OPTS %s denied by <Limit> configuration",
6724
0
      (char *) cmd->argv[1]);
6725
0
    pr_response_add_err(R_550, "%s %s: %s", (char *) cmd->argv[0],
6726
0
      (char *) cmd->argv[1], strerror(xerrno));
6727
6728
0
    pr_cmd_set_errno(cmd, xerrno);
6729
0
    errno = xerrno;
6730
0
    return PR_ERROR(cmd);
6731
0
  }
6732
6733
0
  for (i = 2; i < cmd->argc; i++) {
6734
0
    subcmd->argv[i-1] = cmd->argv[i];
6735
6736
0
    arg = pstrcat(cmd->tmp_pool, arg, *arg ? " " : "", cmd->argv[i], NULL);
6737
0
  }
6738
6739
0
  subcmd->arg = arg;
6740
6741
0
  res = pr_cmd_dispatch(subcmd);
6742
0
  if (res < 0) {
6743
0
    return PR_ERROR(cmd);
6744
0
  }
6745
6746
0
  return PR_HANDLED(cmd);
6747
0
}
6748
6749
0
MODRET core_post_pass(cmd_rec *cmd) {
6750
0
  config_rec *c;
6751
6752
0
  c = find_config(TOPLEVEL_CONF, CONF_PARAM, "TimeoutIdle", FALSE);
6753
0
  if (c != NULL) {
6754
0
    int prev_timeout, timeout;
6755
6756
0
    prev_timeout = pr_data_get_timeout(PR_DATA_TIMEOUT_IDLE);
6757
0
    timeout = *((int *) c->argv[0]);
6758
6759
0
    if (timeout != prev_timeout) {
6760
0
      pr_data_set_timeout(PR_DATA_TIMEOUT_IDLE, timeout);
6761
6762
      /* Remove the old timer, and add a new one with the changed
6763
       * timeout value.
6764
       */
6765
0
      pr_timer_remove(PR_TIMER_IDLE, &core_module);
6766
6767
0
      if (timeout > 0) {
6768
0
        pr_timer_add(timeout, PR_TIMER_IDLE, &core_module, core_idle_timeout_cb,
6769
0
          "TimeoutIdle");
6770
0
      }
6771
0
    }
6772
0
  }
6773
6774
0
#ifdef PR_USE_TRACE
6775
  /* Handle any user/group-specific Trace settings. */
6776
0
  c = find_config(main_server->conf, CONF_PARAM, "Trace", FALSE);
6777
0
  if (c != NULL) {
6778
0
    register unsigned int i;
6779
6780
0
    for (i = 0; i < c->argc; i++) {
6781
0
      char *channel, *ptr;
6782
0
      int min_level, max_level, res;
6783
6784
0
      pr_signals_handle();
6785
6786
0
      channel = c->argv[i];
6787
0
      ptr = strchr(channel, ':');
6788
0
      if (ptr == NULL) {
6789
0
        pr_log_debug(DEBUG6, "skipping badly formatted '%s' setting",
6790
0
          channel);
6791
0
        continue;
6792
0
      }
6793
6794
0
      *ptr = '\0';
6795
6796
0
      res = pr_trace_parse_levels(ptr + 1, &min_level, &max_level);
6797
0
      if (res == 0) {
6798
0
        res = pr_trace_set_levels(channel, min_level, max_level);
6799
0
        *ptr = ':';
6800
6801
0
        if (res < 0) {
6802
0
          pr_log_debug(DEBUG6, "%s: error setting levels %d-%d for "
6803
0
            "channel '%s': %s", c->name, min_level, max_level, channel,
6804
0
            strerror(errno));
6805
0
        }
6806
6807
0
      } else {
6808
0
        pr_log_debug(DEBUG6, "%s: error parsing level '%s' for channel '%s': "
6809
0
          "%s", c->name, ptr + 1, channel, strerror(errno));
6810
0
      }
6811
0
    }
6812
0
  }
6813
6814
  /* Handle any user/group-specific TraceOptions settings. */
6815
0
  c = find_config(main_server->conf, CONF_PARAM, "TraceOptions", FALSE);
6816
0
  if (c != NULL) {
6817
0
    unsigned long trace_opts;
6818
6819
0
    trace_opts = *((unsigned long *) c->argv[0]);
6820
0
    if (pr_trace_set_options(trace_opts) < 0) {
6821
0
      pr_log_debug(DEBUG6, "%s: error setting TraceOptions (%lu): %s",
6822
0
        c->name, trace_opts, strerror(errno));
6823
0
    }
6824
0
  }
6825
0
#endif /* PR_USE_TRACE */
6826
6827
  /* If "SocketOptions keepalive off" is in effect, disable TCP keepalives
6828
   * for the control connection as well (Bug#4340).
6829
   */
6830
0
  if (main_server->tcp_keepalive->keepalive_enabled == FALSE) {
6831
0
    int keepalive = 0;
6832
6833
0
    pr_trace_msg("inet", 17, "disabling SO_KEEPALIVE on socket fd %d",
6834
0
      session.c->wfd);
6835
0
    if (setsockopt(session.c->wfd, SOL_SOCKET, SO_KEEPALIVE, (void *)
6836
0
        &keepalive, sizeof(int)) < 0) {
6837
0
      pr_log_pri(PR_LOG_NOTICE,
6838
0
        "error setting SO_KEEPALIVE on socket fd %d: %s", session.c->wfd,
6839
0
        strerror(errno));
6840
6841
0
    } else {
6842
0
      pr_trace_msg("inet", 15, "disabled SO_KEEPALIVE on socket fd %d",
6843
0
        session.c->wfd);
6844
0
    }
6845
0
  }
6846
6847
  /* Look for a configured MaxCommandRate. */
6848
0
  c = find_config(main_server->conf, CONF_PARAM, "MaxCommandRate", FALSE);
6849
0
  if (c) {
6850
0
    core_cmd_count = 0UL;
6851
0
    core_max_cmds = *((unsigned long *) c->argv[0]);
6852
0
    core_max_cmd_interval = *((unsigned int *) c->argv[1]);
6853
0
    core_max_cmd_ts = 0;
6854
0
  }
6855
6856
  /* Configure the statcache to start caching for the authenticated session. */
6857
0
  pr_fs_statcache_reset();
6858
0
  c = find_config(main_server->conf, CONF_PARAM, "FSCachePolicy", FALSE);
6859
0
  if (c != NULL) {
6860
0
    int engine;
6861
0
    unsigned int size, max_age;
6862
6863
0
    engine = *((int *) c->argv[0]);
6864
0
    size = *((unsigned int *) c->argv[1]);
6865
0
    max_age = *((unsigned int *) c->argv[2]);
6866
6867
0
    if (engine == TRUE) {
6868
0
      pr_fs_statcache_set_policy(size, max_age, 0);
6869
6870
0
    } else {
6871
0
      pr_fs_statcache_set_policy(0, 0, 0);
6872
0
    }
6873
6874
0
  } else {
6875
    /* Set the default statcache policy. */
6876
0
    pr_fs_statcache_set_policy(0, 0, 0);
6877
0
  }
6878
6879
  /* Register an exit handler here, for clearing the statcache. */
6880
0
  pr_event_register(&core_module, "core.exit", core_exit_ev, NULL);
6881
6882
  /* Note: we MUST return HANDLED here, not DECLINED, to indicate that at
6883
   * least one POST_CMD handler of the PASS command succeeded.  Since
6884
   * mod_core is always the last module to which commands are dispatched,
6885
   * we can rest assured that we are not causing problems for any other
6886
   * PASS POST_CMD handlers by returning HANDLED here.
6887
   */
6888
0
  return PR_HANDLED(cmd);
6889
0
}
6890
6891
/* Configuration directive handlers
6892
 */
6893
6894
0
MODRET set_deferwelcome(cmd_rec *cmd) {
6895
0
  int defer_welcome = -1;
6896
0
  config_rec *c = NULL;
6897
6898
0
  CHECK_ARGS(cmd, 1);
6899
0
  CHECK_CONF(cmd, CONF_ROOT|CONF_VIRTUAL|CONF_GLOBAL);
6900
6901
0
  defer_welcome = get_boolean(cmd, 1);
6902
0
  if (defer_welcome == -1) {
6903
0
    CONF_ERROR(cmd, "expected Boolean parameter");
6904
0
  }
6905
6906
0
  c = add_config_param(cmd->argv[0], 1, NULL);
6907
0
  c->argv[0] = pcalloc(c->pool, sizeof(unsigned char));
6908
0
  *((unsigned char *) c->argv[0]) = defer_welcome;
6909
6910
0
  return PR_HANDLED(cmd);
6911
0
}
6912
6913
/* Variable handlers
6914
 */
6915
6916
0
static const char *core_get_sess_bytes_str(void *data, size_t datasz) {
6917
0
  char buf[256];
6918
0
  off_t bytes = *((off_t *) data);
6919
6920
0
  memset(buf, '\0', sizeof(buf));
6921
0
  pr_snprintf(buf, sizeof(buf)-1, "%" PR_LU, (pr_off_t) bytes);
6922
6923
0
  return pstrdup(session.pool, buf);
6924
0
}
6925
6926
0
static const char *core_get_sess_files_str(void *data, size_t datasz) {
6927
0
  char buf[256];
6928
0
  unsigned int files = *((unsigned int *) data);
6929
6930
0
  memset(buf, '\0', sizeof(buf));
6931
0
  pr_snprintf(buf, sizeof(buf)-1, "%u", files);
6932
6933
0
  return pstrdup(session.pool, buf);
6934
0
}
6935
6936
0
static const char *core_get_xfer_bytes_str(void *data, size_t datasz) {
6937
0
  char buf[256];
6938
0
  off_t bytes = *((off_t *) data);
6939
6940
0
  memset(buf, '\0', sizeof(buf));
6941
0
  pr_snprintf(buf, sizeof(buf)-1, "%" PR_LU, (pr_off_t) bytes);
6942
6943
0
  return pstrdup(session.pool, buf);
6944
0
}
6945
6946
/* Event handlers
6947
 */
6948
6949
0
static void core_chroot_ev(const void *event_data, void *user_data) {
6950
0
  config_rec *c;
6951
6952
  /* Look for any configured DisplayChdir directives that use absolute
6953
   * paths, and open filehandles on them prior to the chroot (Issue #1688).
6954
   */
6955
6956
0
  c = find_config(main_server->conf, CONF_PARAM, "DisplayChdir", FALSE);
6957
0
  while (c != NULL) {
6958
0
    const char *path;
6959
6960
0
    pr_signals_handle();
6961
6962
0
    path = c->argv[0];
6963
0
    if (path[0] == '/') {
6964
0
      pr_fh_t *fh;
6965
6966
0
      fh = pr_fsio_open(path, O_RDONLY);
6967
0
      if (fh == NULL) {
6968
0
        pr_log_debug(DEBUG6, "unable to open DisplayChdir file '%s': %s",
6969
0
          path, strerror(errno));
6970
6971
0
      } else {
6972
0
        c->argv[2] = fh;
6973
0
      }
6974
0
    }
6975
6976
0
    c = find_config_next(c, c->next, CONF_PARAM, "DisplayChdir", FALSE);
6977
0
  }
6978
0
}
6979
6980
0
static void core_connected_ev(const void *event_data, void *user_data) {
6981
0
  session_set_connected();
6982
0
}
6983
6984
0
static void core_exit_ev(const void *event_data, void *user_data) {
6985
0
  pr_fs_statcache_free();
6986
0
}
6987
6988
0
static void core_postparse_ev(const void *event_data, void *user_data) {
6989
0
  server_rec *s;
6990
0
  cmd_rec *cmd;
6991
0
  int res;
6992
0
  pool *tmp_pool;
6993
6994
  /* If the EPRT and/or EPSV commands have been denied by configuration, e.g.:
6995
   *
6996
   *  <Limit EPRT EPSV>
6997
   *    DenyAll
6998
   *  </Limit>
6999
   *
7000
   * then we will also exclude them from the FEAT response.  Some badly
7001
   * behaved clients will try to use EPSV (which cannot honor the
7002
   * MasqueradeAddress directive, needed in NAT environments), and if that
7003
   * fails, they fall back to using PORT (and not PASV as expected).
7004
   *
7005
   * Thus for such tricky situations, we check for <Limit> configurations for
7006
   * this commands; if limited, we omit them from FEAT.  This is why we need
7007
   * to wait until the configuration has been parsed, to do this check.
7008
   * See Issue#1383.
7009
   */
7010
7011
0
  tmp_pool = make_sub_pool(permanent_pool);
7012
7013
0
  cmd = pr_cmd_alloc(tmp_pool, 1, pstrdup(tmp_pool, C_EPRT), NULL);
7014
0
  res = dir_check_limits(cmd, NULL, C_EPRT, FALSE);
7015
0
  if (res == 1) {
7016
0
    pr_feat_add(C_EPRT);
7017
0
  }
7018
7019
0
  destroy_pool(cmd->pool);
7020
0
  cmd = pr_cmd_alloc(tmp_pool, 1, pstrdup(tmp_pool, C_EPSV), NULL);
7021
0
  res = dir_check_limits(cmd, NULL, C_EPSV, FALSE);
7022
0
  if (res == 1) {
7023
0
    pr_feat_add(C_EPSV);
7024
0
  }
7025
7026
0
  destroy_pool(cmd->pool);
7027
0
  destroy_pool(tmp_pool);
7028
7029
  /* Since the Port directive is allowed in <Global> sections (see Issue #1418),
7030
   * we need to check here for the Port to use, on a per-vhost basis.
7031
   */
7032
0
  for (s = (server_rec *) server_list->xas_list; s; s = s->next) {
7033
0
    int *server_port;
7034
7035
0
    pr_signals_handle();
7036
7037
0
    server_port = get_param_ptr(s->conf, "Port", FALSE);
7038
0
    if (server_port != NULL) {
7039
0
      s->ServerPort = *server_port;
7040
0
    }
7041
0
  }
7042
0
}
7043
7044
0
static void core_restart_ev(const void *event_data, void *user_data) {
7045
0
  pr_fs_statcache_reset();
7046
0
  pr_scoreboard_scrub();
7047
7048
0
#ifdef PR_USE_TRACE
7049
0
  if (trace_log) {
7050
0
    (void) pr_trace_set_levels(PR_TRACE_DEFAULT_CHANNEL, -1, -1);
7051
0
    pr_trace_set_file(NULL);
7052
0
    trace_log = NULL;
7053
0
  }
7054
0
#endif /* PR_USE_TRACE */
7055
0
}
7056
7057
0
static void core_startup_ev(const void *event_data, void *user_data) {
7058
7059
  /* Add a scoreboard-scrubbing timer.
7060
   *
7061
   * Note that we do this only for standalone proftpd daemons, not for
7062
   * inetd-run daemons.  There is no "master"/"daemon" process for
7063
   * inetd-run proftpd processes, which means that _all_ processes scrub
7064
   * the scoreboard (which greatly increases lock contention, particularly
7065
   * under high numbers of simultaneous connections), or that _no_
7066
   * processes scrub the scoreboard (which increases the chance of stale/bad
7067
   * scoreboard data).
7068
   */
7069
0
  if (ServerType == SERVER_STANDALONE) {
7070
0
    int scrub_scoreboard = TRUE;
7071
0
    int scrub_interval = PR_TUNABLE_SCOREBOARD_SCRUB_TIMER;
7072
0
    config_rec *c;
7073
7074
0
    c = find_config(main_server->conf, CONF_PARAM, "ScoreboardScrub", FALSE);
7075
0
    if (c) {
7076
0
      scrub_scoreboard = *((int *) c->argv[0]);
7077
7078
0
      if (c->argc == 2) {
7079
0
        scrub_interval = *((int *) c->argv[1]);
7080
0
      }
7081
0
    }
7082
7083
0
    if (scrub_scoreboard) {
7084
0
      core_scrub_timer_id = pr_timer_add(scrub_interval, -1,
7085
0
        &core_module, core_scrub_scoreboard_cb, "scoreboard scrubbing");
7086
0
    }
7087
0
  }
7088
0
}
7089
7090
/* Initialization/finalization routines
7091
 */
7092
7093
0
static int core_init(void) {
7094
  /* Set the default (i.e. FTP) command handler. */
7095
0
  pr_cmd_set_handler(NULL);
7096
7097
  /* Add the commands handled by this module to the HELP list. */
7098
0
  pr_help_add(C_CWD,  _("<sp> pathname"), TRUE);
7099
0
  pr_help_add(C_XCWD, _("<sp> pathname"), TRUE);
7100
0
  pr_help_add(C_CDUP, _("(up one directory)"), TRUE);
7101
0
  pr_help_add(C_XCUP, _("(up one directory)"), TRUE);
7102
0
  pr_help_add(C_SMNT, _("is not implemented"), FALSE);
7103
0
  pr_help_add(C_QUIT, _("(close control connection)"), TRUE);
7104
0
  pr_help_add(C_PORT, _("<sp> h1,h2,h3,h4,p1,p2"), TRUE);
7105
0
  pr_help_add(C_PASV, _("(returns address/port)"), TRUE);
7106
0
  pr_help_add(C_EPRT, _("<sp> |proto|addr|port|"), TRUE);
7107
0
  pr_help_add(C_EPSV, _("(returns port |||port|)"), TRUE);
7108
0
  pr_help_add(C_ALLO, _("<sp> size"), TRUE);
7109
0
  pr_help_add(C_RNFR, _("<sp> pathname"), TRUE);
7110
0
  pr_help_add(C_RNTO, _("<sp> pathname"), TRUE);
7111
0
  pr_help_add(C_DELE, _("<sp> pathname"), TRUE);
7112
0
  pr_help_add(C_MDTM, _("<sp> pathname"), TRUE);
7113
0
  pr_help_add(C_RMD, _("<sp> pathname"), TRUE);
7114
0
  pr_help_add(C_XRMD, _("<sp> pathname"), TRUE);
7115
0
  pr_help_add(C_MKD, _("<sp> pathname"), TRUE);
7116
0
  pr_help_add(C_XMKD, _("<sp> pathname"), TRUE);
7117
0
  pr_help_add(C_PWD, _("(returns current working directory)"), TRUE);
7118
0
  pr_help_add(C_XPWD, _("(returns current working directory)"), TRUE);
7119
0
  pr_help_add(C_SIZE, _("<sp> pathname"), TRUE);
7120
0
  pr_help_add(C_SYST, _("(returns system type)"), TRUE);
7121
0
  pr_help_add(C_HELP, _("[<sp> command]"), TRUE);
7122
0
  pr_help_add(C_NOOP, _("(no operation)"), TRUE);
7123
0
  pr_help_add(C_FEAT, _("(returns feature list)"), TRUE);
7124
0
  pr_help_add(C_OPTS, _("<sp> command [<sp> options]"), TRUE);
7125
0
  pr_help_add(C_HOST, _("<cp> hostname"), TRUE);
7126
0
  pr_help_add(C_CLNT, _("<cp> client-info"), TRUE);
7127
0
  pr_help_add(C_AUTH, _("<sp> base64-data"), FALSE);
7128
0
  pr_help_add(C_CCC, _("(clears protection level)"), FALSE);
7129
0
  pr_help_add(C_CONF, _("<sp> base64-data"), FALSE);
7130
0
  pr_help_add(C_ENC, _("<sp> base64-data"), FALSE);
7131
0
  pr_help_add(C_MIC, _("<sp> base64-data"), FALSE);
7132
0
  pr_help_add(C_PBSZ, _("<sp> protection buffer size"), FALSE);
7133
0
  pr_help_add(C_PROT, _("<sp> protection code"), FALSE);
7134
7135
  /* Add the additional features implemented by this module into the
7136
   * list, to be displayed in response to a FEAT command.
7137
   */
7138
0
  pr_feat_add(C_CLNT);
7139
0
  pr_feat_add(C_CSID);
7140
0
  pr_feat_add(C_MDTM);
7141
0
  pr_feat_add("REST STREAM");
7142
0
  pr_feat_add(C_SIZE);
7143
0
  pr_feat_add(C_HOST);
7144
7145
0
  pr_event_register(&core_module, "core.connected", core_connected_ev, NULL);
7146
0
  pr_event_register(&core_module, "core.postparse", core_postparse_ev, NULL);
7147
0
  pr_event_register(&core_module, "core.restart", core_restart_ev, NULL);
7148
0
  pr_event_register(&core_module, "core.startup", core_startup_ev, NULL);
7149
7150
0
  return 0;
7151
0
}
7152
7153
static const char *auth_syms[] = {
7154
  "setpwent", "endpwent", "setgrent", "endgrent", "getpwent", "getgrent",
7155
  "getpwnam", "getgrnam", "getpwuid", "getgrgid", "auth", "check",
7156
  "uid2name", "gid2name", "name2uid", "name2gid", "getgroups", NULL
7157
};
7158
7159
0
static void reset_server_auth_order(void) {
7160
0
  config_rec *c = NULL;
7161
7162
0
  c = find_config(session.prev_server->conf, CONF_PARAM, "AuthOrder", FALSE);
7163
0
  if (c != NULL) {
7164
0
    register unsigned int i;
7165
0
    unsigned int module_pri = 0;
7166
0
    module *m;
7167
7168
    /* There was an AuthOrder applying to the previous server_rec, which
7169
     * means we need to reset the default AuthOrder symbols.
7170
     */
7171
7172
    /* Delete all auth syms. */
7173
0
    for (i = 0; auth_syms[i] != NULL; i++) {
7174
0
      pr_stash_remove_symbol(PR_SYM_AUTH, auth_syms[i], NULL);
7175
0
    }
7176
7177
    /* Reload all modules' auth syms. Be sure to reset the module
7178
     * priority while doing so.
7179
     */
7180
0
    for (m = loaded_modules; m; m = m->next) {
7181
0
      if (pr_module_load_authtab(m) < 0) {
7182
0
        pr_log_debug(DEBUG0,
7183
0
          "error reloading auth symbols for module 'mod_%s.c': %s", m->name,
7184
0
          strerror(errno));
7185
0
      }
7186
7187
0
      m->priority = module_pri++;
7188
0
    }
7189
0
  }
7190
0
}
7191
7192
0
static void set_server_auth_order(void) {
7193
0
  config_rec *c = NULL;
7194
7195
0
  c = find_config(main_server->conf, CONF_PARAM, "AuthOrder", FALSE);
7196
0
  if (c != NULL) {
7197
0
    array_header *module_list = (array_header *) c->argv[0];
7198
0
    unsigned int modulec = 0;
7199
0
    char **modulev = NULL;
7200
0
    register unsigned int i = 0;
7201
7202
0
    pr_log_debug(DEBUG3, "AuthOrder in effect, resetting auth module order");
7203
7204
0
    modulec = module_list->nelts;
7205
0
    modulev = (char **) module_list->elts;
7206
7207
    /* First, delete all auth symbols. */
7208
0
    for (i = 0; auth_syms[i] != NULL; i++) {
7209
0
      pr_stash_remove_symbol(PR_SYM_AUTH, auth_syms[i], NULL);
7210
0
    }
7211
7212
    /* Now, cycle through the list of configured modules, re-adding their
7213
     * auth symbols, in the order in which they appear.
7214
     */
7215
7216
0
    for (i = 0; i < modulec; i++) {
7217
0
      module *m;
7218
0
      int required = FALSE;
7219
7220
      /* Check for the trailing '*', indicating a required auth module. */
7221
0
      if (modulev[i][strlen(modulev[i])-1] == '*') {
7222
0
        required = TRUE;
7223
0
        modulev[i][strlen(modulev[i])-1] = '\0';
7224
0
      }
7225
7226
0
      m = pr_module_get(modulev[i]);
7227
7228
0
      if (m) {
7229
0
        if (m->authtable) {
7230
0
          authtable *authtab;
7231
7232
          /* Twiddle the module's priority field before insertion into the
7233
           * symbol table, as the insertion operation does so based on that
7234
           * priority.  This has no effect other than during symbol
7235
           * insertion.
7236
           */
7237
0
          m->priority = modulec - i;
7238
7239
0
          for (authtab = m->authtable; authtab->name; authtab++) {
7240
0
            authtab->m = m;
7241
7242
0
            if (required) {
7243
0
              authtab->auth_flags |= PR_AUTH_FL_REQUIRED;
7244
0
            }
7245
7246
0
            pr_stash_add_symbol(PR_SYM_AUTH, authtab);
7247
0
          }
7248
7249
0
        } else {
7250
0
          pr_log_debug(DEBUG0, "AuthOrder: warning: module '%s' is not a valid "
7251
0
            "auth module (no auth handlers), authentication may fail",
7252
0
            modulev[i]);
7253
0
        }
7254
7255
0
      } else {
7256
0
        pr_log_debug(DEBUG0, "AuthOrder: warning: module '%s' not loaded",
7257
0
          modulev[i]);
7258
0
      }
7259
0
    }
7260
7261
    /* NOTE: the master conf/cmd/auth tables/arrays should ideally be
7262
     * rebuilt after this symbol shuffling, but it's not necessary at this
7263
     * point.
7264
     */
7265
0
  }
7266
0
}
7267
7268
0
static int core_sess_init(void) {
7269
0
  int timeout_idle;
7270
0
  char *displayquit = NULL;
7271
0
  config_rec *c = NULL;
7272
0
  unsigned int *debug_level = NULL;
7273
0
  unsigned long fs_opts = 0UL;
7274
7275
0
  init_auth();
7276
7277
  /* Enable any TCP keepalive options on the control connection (Issue #1402).
7278
   * Note that ctrl conns do not have listening fds, but this function uses
7279
   * that fd (due to compatibility with data conns), so we temporarily assign
7280
   * that struct member.
7281
   */
7282
0
  session.c->listen_fd = session.c->wfd;
7283
0
  if (pr_inet_set_proto_keepalive(session.pool, session.c,
7284
0
      main_server->tcp_keepalive) < 0) {
7285
0
    pr_log_debug(DEBUG9, "error setting ctrl conn TCP keepalive: %s",
7286
0
      strerror(errno));
7287
0
  }
7288
0
  session.c->listen_fd = -1;
7289
7290
  /* Start the idle timer. */
7291
7292
0
  c = find_config(main_server->conf, CONF_PARAM, "TimeoutIdle", FALSE);
7293
0
  if (c != NULL) {
7294
0
    int timeout = *((int *) c->argv[0]);
7295
0
    pr_data_set_timeout(PR_DATA_TIMEOUT_IDLE, timeout);
7296
0
  }
7297
7298
0
  timeout_idle = pr_data_get_timeout(PR_DATA_TIMEOUT_IDLE);
7299
0
  if (timeout_idle) {
7300
0
    pr_timer_add(timeout_idle, PR_TIMER_IDLE, &core_module,
7301
0
      core_idle_timeout_cb, "TimeoutIdle");
7302
0
  }
7303
7304
  /* Check for a server-specific TimeoutLinger */
7305
0
  c = find_config(main_server->conf, CONF_PARAM, "TimeoutLinger", FALSE);
7306
0
  if (c != NULL) {
7307
0
    long timeout;
7308
7309
0
    timeout = (long) *((int *) c->argv[0]);
7310
0
    pr_data_set_linger(timeout);
7311
0
  }
7312
7313
  /* Check for a configured DebugLevel. */
7314
0
  debug_level = get_param_ptr(main_server->conf, "DebugLevel", FALSE);
7315
0
  if (debug_level != NULL) {
7316
0
    pr_log_setdebuglevel(*debug_level);
7317
0
  }
7318
7319
0
  c = find_config(main_server->conf, CONF_PARAM, "FSOptions", FALSE);
7320
0
  while (c != NULL) {
7321
0
    unsigned long opts = 0;
7322
7323
0
    pr_signals_handle();
7324
7325
0
    opts = *((unsigned long *) c->argv[0]);
7326
0
    fs_opts |= opts;
7327
7328
0
    c = find_config_next(c, c->next, CONF_PARAM, "FSOptions", FALSE);
7329
0
  }
7330
7331
0
  (void) pr_fsio_set_options(fs_opts);
7332
7333
  /* Check for any server-specific RegexOptions */
7334
0
  c = find_config(main_server->conf, CONF_PARAM, "RegexOptions", FALSE);
7335
0
  if (c != NULL) {
7336
0
    unsigned long match_limit, match_limit_recursion;
7337
0
    const char *engine = NULL;
7338
7339
0
    match_limit = *((unsigned long *) c->argv[0]);
7340
0
    match_limit_recursion = *((unsigned long *) c->argv[1]);
7341
0
    engine = c->argv[2];
7342
7343
0
    pr_trace_msg("regexp", 4,
7344
0
      "using regex options: engine = %s, match limit = %lu, "
7345
0
      "match limit recursion = %lu", engine ? engine : "(default)",
7346
0
      match_limit, match_limit_recursion);
7347
7348
0
    pr_regexp_set_limits(match_limit, match_limit_recursion);
7349
0
    pr_regexp_set_engine(engine);
7350
0
  }
7351
7352
  /* Check for configured SetEnvs. */
7353
0
  c = find_config(main_server->conf, CONF_PARAM, "SetEnv", FALSE);
7354
0
  while (c != NULL) {
7355
0
    pr_signals_handle();
7356
7357
0
    if (pr_env_set(session.pool, c->argv[0], c->argv[1]) < 0) {
7358
0
      pr_log_debug(DEBUG1, "unable to set environment variable '%s': %s",
7359
0
        (char *) c->argv[0], strerror(errno));
7360
7361
0
    } else {
7362
0
      core_handle_locale_env(c->argv[0]);
7363
0
    }
7364
7365
0
    c = find_config_next(c, c->next, CONF_PARAM, "SetEnv", FALSE);
7366
0
  }
7367
7368
  /* Check for configured UnsetEnvs. */
7369
0
  c = find_config(main_server->conf, CONF_PARAM, "UnsetEnv", FALSE);
7370
0
  while (c != NULL) {
7371
0
    pr_signals_handle();
7372
7373
0
    if (pr_env_unset(session.pool, c->argv[0]) < 0) {
7374
0
      pr_log_debug(DEBUG1, "unable to unset environment variable '%s': %s",
7375
0
        (char *) c->argv[0], strerror(errno));
7376
7377
0
    } else {
7378
0
      core_handle_locale_env(c->argv[0]);
7379
0
    }
7380
7381
0
    c = find_config_next(c, c->next, CONF_PARAM, "UnsetEnv", FALSE);
7382
0
  }
7383
7384
0
  set_server_auth_order();
7385
7386
0
#if defined(PR_USE_TRACE)
7387
  /* Handle any session-specific Trace settings. */
7388
0
  c = find_config(main_server->conf, CONF_PARAM, "Trace", FALSE);
7389
0
  if (c != NULL) {
7390
0
    register unsigned int i;
7391
7392
0
    for (i = 0; i < c->argc; i++) {
7393
0
      char *channel, *ptr;
7394
0
      int min_level, max_level, res;
7395
7396
0
      pr_signals_handle();
7397
7398
0
      channel = c->argv[i];
7399
7400
0
      ptr = strchr(channel, ':');
7401
0
      if (ptr == NULL) {
7402
0
        pr_log_debug(DEBUG6, "skipping badly formatted '%s' setting",
7403
0
          channel);
7404
0
        continue;
7405
0
      }
7406
7407
0
      *ptr = '\0';
7408
7409
0
      res = pr_trace_parse_levels(ptr + 1, &min_level, &max_level);
7410
0
      if (res == 0) {
7411
0
        res = pr_trace_set_levels(channel, min_level, max_level);
7412
0
        *ptr = ':';
7413
7414
0
        if (res < 0) {
7415
0
          pr_log_debug(DEBUG6, "%s: error setting levels %d-%d for "
7416
0
            "channel '%s': %s", c->name, min_level, max_level, channel,
7417
0
            strerror(errno));
7418
0
        }
7419
7420
0
      } else {
7421
0
        pr_log_debug(DEBUG6, "%s: error parsing level '%s' for channel '%s': "
7422
0
          "%s", c->name, ptr + 1, channel, strerror(errno));
7423
0
      }
7424
0
    }
7425
0
  }
7426
7427
  /* Handle any session-specific TraceOptions settings. */
7428
0
  c = find_config(main_server->conf, CONF_PARAM, "TraceOptions", FALSE);
7429
0
  if (c != NULL) {
7430
0
    unsigned long trace_opts;
7431
7432
0
    trace_opts = *((unsigned long *) c->argv[0]);
7433
0
    if (pr_trace_set_options(trace_opts) < 0) {
7434
0
      pr_log_debug(DEBUG6, "%s: error setting TraceOptions (%lu): %s",
7435
0
        c->name, trace_opts, strerror(errno));
7436
0
    }
7437
0
  }
7438
0
#endif /* PR_USE_TRACE */
7439
7440
0
  if (ServerType == SERVER_STANDALONE) {
7441
0
    pr_timer_remove(core_scrub_timer_id, &core_module);
7442
7443
0
  } else if (ServerType == SERVER_INETD) {
7444
    /* If we're running as 'ServerType inetd', scrub the scoreboard here.
7445
     * For standalone ServerTypes, the scoreboard scrubber will handle
7446
     * things itself.
7447
     */
7448
7449
0
    c = find_config(main_server->conf, CONF_PARAM, "ScoreboardScrub", FALSE);
7450
0
    if (c != NULL) {
7451
0
      if (*((int *) c->argv[0]) == TRUE) {
7452
0
        pr_scoreboard_scrub();
7453
0
      }
7454
0
    }
7455
0
  }
7456
7457
  /* Set some Variable entries for Display files. */
7458
7459
0
  if (pr_var_set(session.pool, "%{bytes_xfer}",
7460
0
      "Number of bytes transferred in this transfer", PR_VAR_TYPE_FUNC,
7461
0
      (void *) core_get_xfer_bytes_str, &session.xfer.total_bytes,
7462
0
      sizeof(off_t *)) < 0) {
7463
0
    pr_log_debug(DEBUG6, "error setting %%{bytes_fer} variable: %s",
7464
0
      strerror(errno));
7465
0
  }
7466
7467
0
  if (pr_var_set(session.pool, "%{total_bytes_in}",
7468
0
      "Number of bytes uploaded during a session", PR_VAR_TYPE_FUNC,
7469
0
      (void *) core_get_sess_bytes_str, &session.total_bytes_in,
7470
0
      sizeof(off_t *)) < 0) {
7471
0
    pr_log_debug(DEBUG6, "error setting %%{total_bytes_in} variable: %s",
7472
0
      strerror(errno));
7473
0
  }
7474
7475
0
  if (pr_var_set(session.pool, "%{total_bytes_out}",
7476
0
      "Number of bytes downloaded during a session", PR_VAR_TYPE_FUNC,
7477
0
      (void *) core_get_sess_bytes_str, &session.total_bytes_out,
7478
0
      sizeof(off_t *)) < 0) {
7479
0
    pr_log_debug(DEBUG6, "error setting %%{total_bytes_out} variable: %s",
7480
0
      strerror(errno));
7481
0
  }
7482
7483
0
  if (pr_var_set(session.pool, "%{total_bytes_xfer}",
7484
0
      "Number of bytes transferred during a session", PR_VAR_TYPE_FUNC,
7485
0
      (void *) core_get_sess_bytes_str, &session.total_bytes,
7486
0
      sizeof(off_t *)) < 0) {
7487
0
    pr_log_debug(DEBUG6, "error setting %%{total_bytes_fer} variable: %s",
7488
0
      strerror(errno));
7489
0
  }
7490
7491
0
  if (pr_var_set(session.pool, "%{total_files_in}",
7492
0
      "Number of files uploaded during a session", PR_VAR_TYPE_FUNC,
7493
0
      (void *) core_get_sess_files_str, &session.total_files_in,
7494
0
      sizeof(unsigned int *)) < 0) {
7495
0
    pr_log_debug(DEBUG6, "error setting %%{total_files_in} variable: %s",
7496
0
      strerror(errno));
7497
0
  }
7498
7499
0
  if (pr_var_set(session.pool, "%{total_files_out}",
7500
0
      "Number of files downloaded during a session", PR_VAR_TYPE_FUNC,
7501
0
      (void *) core_get_sess_files_str, &session.total_files_out,
7502
0
      sizeof(unsigned int *)) < 0) {
7503
0
    pr_log_debug(DEBUG6, "error setting %%{total_files_out} variable: %s",
7504
0
      strerror(errno));
7505
0
  }
7506
7507
0
  if (pr_var_set(session.pool, "%{total_files_xfer}",
7508
0
      "Number of files transferred during a session", PR_VAR_TYPE_FUNC,
7509
0
      (void *) core_get_sess_files_str, &session.total_files_xfer,
7510
0
      sizeof(unsigned int *)) < 0) {
7511
0
    pr_log_debug(DEBUG6, "error setting %%{total_files_xfer} variable: %s",
7512
0
      strerror(errno));
7513
0
  }
7514
7515
  /* Register our event listeners. */
7516
0
  pr_event_register(&core_module, "core.chroot", core_chroot_ev, NULL);
7517
7518
  /* Look for a DisplayQuit file which has an absolute path.  If we
7519
   * find one, open a filehandle, such that that file can be displayed
7520
   * even if the session is chrooted.  DisplayQuit files with
7521
   * relative paths will be handled after chroot, preserving the old
7522
   * behavior.
7523
   */
7524
0
  displayquit = get_param_ptr(TOPLEVEL_CONF, "DisplayQuit", FALSE);
7525
0
  if (displayquit &&
7526
0
      *displayquit == '/') {
7527
0
    struct stat st;
7528
7529
0
    displayquit_fh = pr_fsio_open(displayquit, O_RDONLY);
7530
0
    if (displayquit_fh == NULL) {
7531
0
      pr_log_debug(DEBUG6, "unable to open DisplayQuit file '%s': %s",
7532
0
        displayquit, strerror(errno));
7533
7534
0
    } else {
7535
0
      if (pr_fsio_fstat(displayquit_fh, &st) < 0) {
7536
0
        pr_log_debug(DEBUG6, "unable to stat DisplayQuit file '%s': %s",
7537
0
          displayquit, strerror(errno));
7538
0
        pr_fsio_close(displayquit_fh);
7539
0
        displayquit_fh = NULL;
7540
7541
0
      } else {
7542
0
        if (S_ISDIR(st.st_mode)) {
7543
0
          errno = EISDIR;
7544
0
          pr_log_debug(DEBUG6, "unable to use DisplayQuit file '%s': %s",
7545
0
            displayquit, strerror(errno));
7546
0
          pr_fsio_close(displayquit_fh);
7547
0
          displayquit_fh = NULL;
7548
0
        }
7549
0
      }
7550
0
    }
7551
0
  }
7552
7553
  /* Check for any ProcessTitles setting. */
7554
0
  c = find_config(main_server->conf, CONF_PARAM, "ProcessTitles", FALSE);
7555
0
  if (c != NULL) {
7556
0
    char *verbosity;
7557
7558
0
    verbosity = c->argv[0];
7559
0
    if (strcasecmp(verbosity, "terse") == 0) {
7560
0
      pr_proctitle_set_static_str("proftpd: processing connection");
7561
0
    }
7562
0
  }
7563
7564
0
  return 0;
7565
0
}
7566
7567
/* Module API tables
7568
 */
7569
7570
static conftable core_conftab[] = {
7571
  { "<Anonymous>",    add_anonymous,      NULL },
7572
  { "</Anonymous>",   end_anonymous,      NULL },
7573
  { "<Class>",      add_class,      NULL },
7574
  { "</Class>",     end_class,      NULL },
7575
  { "<Directory>",    add_directory,      NULL },
7576
  { "</Directory>",   end_directory,      NULL },
7577
  { "<Global>",     add_global,     NULL },
7578
  { "</Global>",    end_global,     NULL },
7579
  { "<IfDefine>",   start_ifdefine,     NULL },
7580
  { "</IfDefine>",    end_ifdefine,     NULL },
7581
  { "<IfModule>",   start_ifmodule,     NULL },
7582
  { "</IfModule>",    end_ifmodule,     NULL },
7583
  { "<Limit>",      add_limit,      NULL },
7584
  { "</Limit>",     end_limit,      NULL },
7585
  { "<VirtualHost>",    add_virtualhost,    NULL },
7586
  { "</VirtualHost>",   end_virtualhost,    NULL },
7587
  { "Allow",      set_allowdeny,      NULL },
7588
  { "AllowAll",     set_allowall,     NULL },
7589
  { "AllowClass",   set_allowdenyusergroupclass,  NULL },
7590
  { "AllowFilter",    set_allowdenyfilter,    NULL },
7591
  { "AllowForeignAddress",  set_allowforeignaddress,  NULL },
7592
  { "AllowGroup",   set_allowdenyusergroupclass,  NULL },
7593
  { "AllowOverride",    set_allowoverride,    NULL },
7594
  { "AllowUser",    set_allowdenyusergroupclass,  NULL },
7595
  { "AuthOrder",    set_authorder,      NULL },
7596
  { "CDPath",     set_cdpath,     NULL },
7597
  { "CommandBufferSize",  set_commandbuffersize,    NULL },
7598
  { "DebugLevel",   set_debuglevel,     NULL },
7599
  { "DefaultAddress",   set_defaultaddress,   NULL },
7600
  { "DefaultServer",    set_defaultserver,    NULL },
7601
  { "DeferWelcome",   set_deferwelcome,   NULL },
7602
  { "Define",     set_define,     NULL },
7603
  { "Deny",     set_allowdeny,      NULL },
7604
  { "DenyAll",      set_denyall,      NULL },
7605
  { "DenyClass",    set_allowdenyusergroupclass,  NULL },
7606
  { "DenyFilter",   set_allowdenyfilter,    NULL },
7607
  { "DenyGroup",    set_allowdenyusergroupclass,  NULL },
7608
  { "DenyUser",     set_allowdenyusergroupclass,  NULL },
7609
  { "DisplayChdir",   set_displaychdir,   NULL },
7610
  { "DisplayConnect",   set_displayconnect,   NULL },
7611
  { "DisplayQuit",    set_displayquit,    NULL },
7612
  { "From",     add_from,     NULL },
7613
  { "FSCachePolicy",    set_fscachepolicy,    NULL },
7614
  { "FSOptions",    set_fsoptions,      NULL },
7615
  { "Group",      set_group,      NULL },
7616
  { "GroupOwner",   add_groupowner,     NULL },
7617
  { "HideFiles",    set_hidefiles,      NULL },
7618
  { "HideGroup",    set_hidegroup,      NULL },
7619
  { "HideNoAccess",   set_hidenoaccess,   NULL },
7620
  { "HideUser",     set_hideuser,     NULL },
7621
  { "IgnoreHidden",   set_ignorehidden,   NULL },
7622
  { "Include",      set_include,      NULL },
7623
  { "IncludeOptions",   set_includeoptions,     NULL },
7624
  { "MasqueradeAddress",  set_masqueradeaddress,    NULL },
7625
  { "MaxCommandRate",   set_maxcommandrate,   NULL },
7626
  { "MaxConnectionRate",  set_maxconnrate,    NULL },
7627
  { "MaxInstances",   set_maxinstances,   NULL },
7628
  { "MultilineRFC2228",   set_multilinerfc2228,   NULL },
7629
  { "Order",      set_order,      NULL },
7630
  { "PassivePorts",   set_passiveports,   NULL },
7631
  { "PathAllowFilter",    set_pathallowfilter,    NULL },
7632
  { "PathDenyFilter",   set_pathdenyfilter,   NULL },
7633
  { "PidFile",      set_pidfile,      NULL },
7634
  { "Port",     set_port,       NULL },
7635
  { "ProcessTitles",    set_processtitles,    NULL },
7636
  { "Protocols",    set_protocols,      NULL },
7637
  { "RegexOptions",   set_regexoptions,   NULL },
7638
  { "Satisfy",      set_satisfy,      NULL },
7639
  { "ScoreboardFile",   set_scoreboardfile,   NULL },
7640
  { "ScoreboardMutex",    set_scoreboardmutex,    NULL },
7641
  { "ScoreboardOptions",  set_scoreboardoptions,    NULL },
7642
  { "ScoreboardScrub",    set_scoreboardscrub,    NULL },
7643
  { "ServerAdmin",    set_serveradmin,    NULL },
7644
  { "ServerAlias",    set_serveralias,    NULL },
7645
  { "ServerIdent",    set_serverident,    NULL },
7646
  { "ServerName",   set_servername,     NULL },
7647
  { "ServerType",   set_servertype,     NULL },
7648
  { "SetEnv",     set_setenv,     NULL },
7649
  { "SocketBindTight",    set_socketbindtight,    NULL },
7650
  { "SocketOptions",    set_socketoptions,    NULL },
7651
  { "SyslogFacility",   set_syslogfacility,   NULL },
7652
  { "SyslogLevel",    set_sysloglevel,    NULL },
7653
  { "TimeoutIdle",    set_timeoutidle,    NULL },
7654
  { "TimeoutLinger",    set_timeoutlinger,    NULL },
7655
  { "TimesGMT",     set_timesgmt,     NULL },
7656
  { "Trace",      set_trace,      NULL },
7657
  { "TraceLog",     set_tracelog,     NULL },
7658
  { "TraceOptions",   set_traceoptions,   NULL },
7659
  { "TransferLog",    add_transferlog,    NULL },
7660
  { "Umask",      set_umask,      NULL },
7661
  { "UnsetEnv",     set_unsetenv,     NULL },
7662
  { "UseIPv6",      set_useipv6,      NULL },
7663
  { "UseReverseDNS",    set_usereversedns,    NULL },
7664
  { "User",     set_user,     NULL },
7665
  { "UserOwner",    add_userowner,      NULL },
7666
  { "TCPBackLog",   set_tcpbacklog,     NULL },
7667
  { "TCPNoDelay",   set_tcpnodelay,     NULL },
7668
7669
  { NULL, NULL, NULL }
7670
};
7671
7672
static cmdtable core_cmdtab[] = {
7673
#ifdef PR_USE_REGEX
7674
  { PRE_CMD, C_ANY, G_NONE,  regex_filters, FALSE, FALSE, CL_NONE },
7675
#endif
7676
  { PRE_CMD, C_ANY, G_NONE, core_pre_any,FALSE, FALSE, CL_NONE },
7677
  { CMD, C_HELP, G_NONE,  core_help,  FALSE,  FALSE, CL_INFO },
7678
  { CMD, C_PORT, G_NONE,  core_port,  TRUE, FALSE, CL_MISC },
7679
  { CMD, C_PASV, G_NONE,  core_pasv,  TRUE, FALSE, CL_MISC },
7680
  { CMD, C_EPRT, G_NONE,  core_eprt,    TRUE, FALSE, CL_MISC },
7681
  { CMD, C_EPSV, G_NONE,  core_epsv,  TRUE, FALSE, CL_MISC },
7682
  { CMD, C_SYST, G_NONE,  core_syst,  FALSE,  FALSE, CL_INFO },
7683
  { CMD, C_PWD,  G_DIRS,  core_pwd, TRUE, FALSE, CL_INFO|CL_DIRS },
7684
  { CMD, C_XPWD, G_DIRS,  core_pwd, TRUE, FALSE, CL_INFO|CL_DIRS },
7685
  { CMD, C_CWD,  G_DIRS,  core_cwd, TRUE, FALSE, CL_DIRS },
7686
  { CMD, C_XCWD, G_DIRS,  core_cwd, TRUE, FALSE, CL_DIRS },
7687
  { CMD, C_MKD,  G_WRITE, core_mkd, TRUE, FALSE, CL_DIRS|CL_WRITE },
7688
  { CMD, C_XMKD, G_WRITE, core_mkd, TRUE, FALSE, CL_DIRS|CL_WRITE },
7689
  { CMD, C_RMD,  G_WRITE, core_rmd, TRUE, FALSE, CL_DIRS|CL_WRITE },
7690
  { CMD, C_XRMD, G_WRITE, core_rmd, TRUE, FALSE, CL_DIRS|CL_WRITE },
7691
  { CMD, C_CDUP, G_DIRS,  core_cdup,  TRUE, FALSE, CL_DIRS },
7692
  { CMD, C_XCUP, G_DIRS,  core_cdup,  TRUE, FALSE, CL_DIRS },
7693
  { CMD, C_DELE, G_WRITE, core_dele,  TRUE, FALSE, CL_WRITE },
7694
  { CMD, C_MDTM, G_DIRS,  core_mdtm,  TRUE, FALSE, CL_INFO|CL_DIRS },
7695
  { CMD, C_RNFR, G_WRITE, core_rnfr,  TRUE, FALSE, CL_MISC|CL_WRITE },
7696
  { CMD, C_RNTO, G_WRITE, core_rnto,  TRUE, FALSE, CL_MISC|CL_WRITE },
7697
  { LOG_CMD,     C_RNTO, G_NONE, core_rnto_cleanup, TRUE, FALSE, CL_NONE },
7698
  { LOG_CMD_ERR, C_RNTO, G_NONE, core_rnto_cleanup, TRUE, FALSE, CL_NONE },
7699
  { CMD, C_SIZE, G_READ,  core_size,  TRUE, FALSE, CL_INFO },
7700
  { CMD, C_QUIT, G_NONE,  core_quit,  FALSE,  FALSE,  CL_INFO },
7701
  { LOG_CMD,   C_QUIT, G_NONE, core_log_quit, FALSE, FALSE },
7702
  { LOG_CMD_ERR, C_QUIT, G_NONE, core_log_quit, FALSE, FALSE },
7703
  { CMD, C_NOOP, G_NONE,  core_noop,  FALSE,  FALSE,  CL_MISC },
7704
  { CMD, C_FEAT, G_NONE,  core_feat,  FALSE,  FALSE,  CL_INFO },
7705
  { CMD, C_OPTS, G_NONE,  core_opts,    FALSE,  FALSE,  CL_MISC },
7706
  { CMD, C_HOST, G_NONE,  core_host,    FALSE,  FALSE,  CL_MISC },
7707
  { POST_CMD, C_PASS, G_NONE, core_post_pass, FALSE, FALSE },
7708
  { CMD, C_HOST, G_NONE,  core_host,  FALSE,  FALSE,  CL_AUTH },
7709
  { POST_CMD, C_HOST, G_NONE, core_post_host, FALSE, FALSE },
7710
  { CMD, C_CLNT, G_NONE,  core_clnt,  FALSE,  FALSE,  CL_INFO },
7711
  { CMD, C_CSID, G_NONE,  core_csid,  TRUE, FALSE,  CL_INFO },
7712
7713
  { 0, NULL }
7714
};
7715
7716
module core_module = {
7717
  NULL, NULL,
7718
7719
  /* Module API version */
7720
  0x20,
7721
7722
  /* Module name */
7723
  "core",
7724
7725
  /* Module configuration directive table */
7726
  core_conftab,
7727
7728
  /* Module command handler table */
7729
  core_cmdtab,
7730
7731
  /* Module authentication handler table */
7732
  NULL,
7733
7734
  /* Module initialization function */
7735
  core_init,
7736
7737
  /* Session initialization function */
7738
  core_sess_init
7739
};