Coverage Report

Created: 2026-10-13 06:55

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
#if defined(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
#if defined(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
#if defined(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
#if defined(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
#if defined(HAVE_LOG_CRON)
2097
0
  { "CRON",   LOG_CRON    },
2098
0
#endif /* HAVE_LOG_CRON */
2099
0
  { "DAEMON",   LOG_DAEMON    },
2100
0
#if defined(HAVE_LOG_FTP)
2101
0
  { "FTP",    LOG_FTP     },
2102
0
#endif /* HAVE_LOG_FTP */
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
#if defined(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
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 /* PR_USE_REGEX */
2238
0
}
2239
2240
0
MODRET set_allowdenyfilter(cmd_rec *cmd) {
2241
0
#if defined(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
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 /* PR_USE_REGEX */
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
#if defined(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
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 /* PR_USE_REGEX */
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
#if defined(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 /* PR_USE_REGEX */
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
#if defined(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 /* PR_USE_REGEX */
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
#if defined(PR_USE_IPV6)
4217
0
    if (pr_netaddr_use_ipv6()) {
4218
0
      const pr_netaddr_t *remote_addr = session.c->remote_addr;
4219
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 ignore 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
      /* FALLTHROUGH */
4359
4360
0
    default:
4361
0
#if defined(PR_USE_IPV6)
4362
0
      if (pr_netaddr_use_ipv6()) {
4363
0
        pr_response_add_err(R_522,
4364
0
          _("Network protocol not supported, use (1,2)"));
4365
4366
0
      } else {
4367
0
        pr_response_add_err(R_522,
4368
0
          _("Network protocol not supported, use (1)"));
4369
0
      }
4370
#else
4371
      pr_response_add_err(R_522, _("Network protocol not supported, use (1)"));
4372
#endif /* PR_USE_IPV6 */
4373
4374
0
      pr_cmd_set_errno(cmd, EINVAL);
4375
0
      errno = EINVAL;
4376
0
      return PR_ERROR(cmd);
4377
0
  }
4378
4379
  /* Now, skip past those numeric characters that atoi() used. */
4380
0
  while (PR_ISDIGIT(*argstr)) {
4381
0
    argstr++;
4382
0
  }
4383
4384
  /* If the next character is not the delimiter, it's a badly formatted
4385
   * parameter.
4386
   */
4387
0
  if (*argstr == delim) {
4388
0
    argstr++;
4389
4390
0
  } else {
4391
0
    pr_response_add_err(R_501, _("Illegal EPRT command"));
4392
4393
0
    pr_cmd_set_errno(cmd, EPERM);
4394
0
    errno = EPERM;
4395
0
    return PR_ERROR(cmd);
4396
0
  }
4397
4398
0
  tmp = strchr(argstr, delim);
4399
0
  if (tmp == NULL) {
4400
0
    pr_log_debug(DEBUG3, "badly formatted EPRT argument: '%s'",
4401
0
      (char *) cmd->argv[1]);
4402
0
    pr_response_add_err(R_501, _("Illegal EPRT command"));
4403
4404
0
    pr_cmd_set_errno(cmd, EPERM);
4405
0
    errno = EPERM;
4406
0
    return PR_ERROR(cmd);
4407
0
  }
4408
4409
  /* Twiddle the string so that just the address portion will be processed
4410
   * by pr_inet_pton().
4411
   */
4412
0
  *tmp = '\0';
4413
4414
  /* Use pr_inet_pton() to translate the address string into the address
4415
   * value.
4416
   */
4417
0
  switch (family) {
4418
0
    case 1: {
4419
0
      struct sockaddr *sa = NULL;
4420
4421
0
      pr_netaddr_set_family(&na, AF_INET);
4422
0
      sa = pr_netaddr_get_sockaddr(&na);
4423
0
      if (sa != NULL) {
4424
0
        sa->sa_family = AF_INET;
4425
0
      }
4426
4427
0
      if (pr_inet_pton(AF_INET, argstr, pr_netaddr_get_inaddr(&na)) <= 0) {
4428
0
        pr_log_debug(DEBUG2, "error converting IPv4 address '%s': %s",
4429
0
          argstr, strerror(errno));
4430
0
        pr_response_add_err(R_501, _("Illegal EPRT command"));
4431
4432
0
        pr_cmd_set_errno(cmd, EPERM);
4433
0
        errno = EPERM;
4434
0
        return PR_ERROR(cmd);
4435
0
      }
4436
0
      break;
4437
0
    }
4438
4439
0
    case 2: {
4440
0
      struct sockaddr *sa = NULL;
4441
4442
0
      pr_netaddr_set_family(&na, AF_INET6);
4443
0
      sa = pr_netaddr_get_sockaddr(&na);
4444
0
      if (sa != NULL) {
4445
0
        sa->sa_family = AF_INET6;
4446
0
      }
4447
4448
0
      if (pr_inet_pton(AF_INET6, argstr, pr_netaddr_get_inaddr(&na)) <= 0) {
4449
0
        pr_log_debug(DEBUG2, "error converting IPv6 address '%s': %s",
4450
0
          argstr, strerror(errno));
4451
0
        pr_response_add_err(R_501, _("Illegal EPRT command"));
4452
4453
0
        pr_cmd_set_errno(cmd, EPERM);
4454
0
        errno = EPERM;
4455
0
        return PR_ERROR(cmd);
4456
0
      }
4457
0
      break;
4458
0
    }
4459
0
  }
4460
4461
  /* Advance past the address portion of the argument. */
4462
0
  argstr = ++tmp;
4463
4464
0
  port = atoi(argstr);
4465
4466
0
  while (PR_ISDIGIT(*argstr)) {
4467
0
    argstr++;
4468
0
  }
4469
4470
  /* If the next character is not the delimiter, it's a badly formatted
4471
   * parameter.
4472
   */
4473
0
  if (*argstr != delim) {
4474
0
    pr_log_debug(DEBUG3, "badly formatted EPRT argument: '%s'",
4475
0
      (char *) cmd->argv[1]);
4476
0
    pr_response_add_err(R_501, _("Illegal EPRT command"));
4477
4478
0
    pr_cmd_set_errno(cmd, EPERM);
4479
0
    errno = EPERM;
4480
0
    return PR_ERROR(cmd);
4481
0
  }
4482
4483
  /* If we are NOT listening on an RFC1918 address, BUT the client HAS
4484
   * sent us an RFC1918 address in its PORT command (which we know to not be
4485
   * routable), then ignore that address, and use the client's remote address.
4486
   */
4487
0
  listen_addr = session.c->local_addr;
4488
4489
0
  proto = pr_session_get_protocol(0);
4490
0
  if (strcmp(proto, "ftps") == 0) {
4491
0
    c = find_config(main_server->conf, CONF_PARAM, "TLSMasqueradeAddress",
4492
0
      FALSE);
4493
0
    if (c != NULL) {
4494
0
      listen_addr = c->argv[0];
4495
4496
0
    } else {
4497
0
      c = find_config(main_server->conf, CONF_PARAM, "MasqueradeAddress",
4498
0
        FALSE);
4499
0
      if (c != NULL) {
4500
0
        if (c->argv[0] != NULL) {
4501
0
          listen_addr = c->argv[0];
4502
0
        }
4503
0
      }
4504
0
    }
4505
4506
0
  } else {
4507
0
    c = find_config(main_server->conf, CONF_PARAM, "MasqueradeAddress", FALSE);
4508
0
    if (c != NULL) {
4509
0
      if (c->argv[0] != NULL) {
4510
0
        listen_addr = c->argv[0];
4511
0
      }
4512
0
    }
4513
0
  }
4514
4515
0
  if (pr_netaddr_is_rfc1918(listen_addr) != TRUE &&
4516
0
      pr_netaddr_is_rfc1918(session.c->remote_addr) != TRUE &&
4517
0
      pr_netaddr_is_rfc1918(&na) == TRUE) {
4518
0
    const char *rfc1918_ipstr;
4519
4520
0
    rfc1918_ipstr = pr_netaddr_get_ipstr(&na);
4521
4522
0
    pr_netaddr_clear(&na);
4523
0
    pr_netaddr_set_family(&na, pr_netaddr_get_family(session.c->remote_addr));
4524
0
    pr_netaddr_set_sockaddr(&na,
4525
0
      pr_netaddr_get_sockaddr(session.c->remote_addr));
4526
4527
0
    pr_log_debug(DEBUG1, "client sent RFC1918 address '%s' in EPRT command, "
4528
0
      "ignoring it and using '%s'", rfc1918_ipstr, pr_netaddr_get_ipstr(&na));
4529
0
  }
4530
4531
  /* Make sure that the address specified matches the address from which
4532
   * the control connection is coming.
4533
   */
4534
4535
0
  c = find_config(TOPLEVEL_CONF, CONF_PARAM, "AllowForeignAddress", FALSE);
4536
0
  res = pr_inet_allowforeignaddress(cmd->tmp_pool, &na,
4537
0
    session.c->remote_addr, c);
4538
0
  if (res != 1 || !port) {
4539
0
    pr_log_pri(PR_LOG_WARNING, "Refused EPRT %s (address mismatch)",
4540
0
      cmd->arg);
4541
0
    pr_response_add_err(R_500, _("Illegal EPRT command"));
4542
4543
0
    pr_cmd_set_errno(cmd, EPERM);
4544
0
    errno = EPERM;
4545
0
    return PR_ERROR(cmd);
4546
0
  }
4547
4548
  /* Additionally, make sure that the port number used is a "high numbered"
4549
   * port, to avoid bounce attacks.  For remote Windows machines, the
4550
   * port numbers mean little.  However, there are also quite a few Unix
4551
   * machines out there for whom the port number matters...
4552
   */
4553
4554
0
  if (port < 1024) {
4555
0
    pr_log_pri(PR_LOG_WARNING,
4556
0
      "Refused EPRT %s (port %d below 1024, possible bounce attack)", cmd->arg,
4557
0
      port);
4558
0
    pr_response_add_err(R_500, _("Illegal EPRT command"));
4559
4560
0
    pr_cmd_set_errno(cmd, EPERM);
4561
0
    errno = EPERM;
4562
0
    return PR_ERROR(cmd);
4563
0
  }
4564
4565
  /* Make sure we're using network byte order. */
4566
0
  pr_netaddr_set_port(&na, htons(port));
4567
4568
0
  switch (family) {
4569
0
    case 1:
4570
0
      pr_netaddr_set_family(&session.data_addr, AF_INET);
4571
0
      break;
4572
4573
0
    case 2:
4574
0
      pr_netaddr_set_family(&session.data_addr, AF_INET6);
4575
0
      break;
4576
0
  }
4577
4578
0
  pr_netaddr_set_sockaddr(&session.data_addr, pr_netaddr_get_sockaddr(&na));
4579
0
  pr_netaddr_set_port(&session.data_addr, pr_netaddr_get_port(&na));
4580
0
  session.data_port = port;
4581
0
  session.sf_flags &= (SF_ALL^SF_PASSIVE);
4582
4583
  /* If we already have a data connection open, kill it. */
4584
0
  if (session.d != NULL) {
4585
0
    pr_inet_close(session.d->pool, session.d);
4586
0
    session.d = NULL;
4587
0
  }
4588
4589
0
  session.sf_flags |= SF_PORT;
4590
0
  pr_response_add(R_200, _("EPRT command successful"));
4591
4592
0
  return PR_HANDLED(cmd);
4593
0
}
4594
4595
0
MODRET core_epsv(cmd_rec *cmd) {
4596
0
  char *addrstr = "";
4597
0
  char *endp = NULL, *arg = NULL;
4598
0
  int family = 0;
4599
0
  int epsv_min_port = 1024, epsv_max_port = 65535, tcp_nodelay = 1;
4600
0
  config_rec *c = NULL;
4601
0
  const pr_netaddr_t *bind_addr;
4602
4603
0
  CHECK_CMD_MIN_ARGS(cmd, 1);
4604
4605
  /* Returning 501 is the best we can do.  It would be nicer if RFC959 allowed
4606
   * 550 as a possible response.
4607
   */
4608
0
  if (!dir_check(cmd->tmp_pool, cmd, cmd->group, session.cwd, NULL)) {
4609
0
    int xerrno = EPERM;
4610
4611
0
    pr_log_debug(DEBUG8, "EPSV denied by <Limit> configuration");
4612
0
    pr_response_add_err(R_501, "%s: %s", (char *) cmd->argv[0],
4613
0
      strerror(xerrno));
4614
4615
0
    pr_cmd_set_errno(cmd, xerrno);
4616
0
    errno = xerrno;
4617
0
    return PR_ERROR(cmd);
4618
0
  }
4619
4620
0
  if (cmd->argc-1 == 1) {
4621
0
    arg = pstrdup(cmd->tmp_pool, cmd->argv[1]);
4622
0
  }
4623
4624
0
  if (arg && strcasecmp(arg, "all") == 0) {
4625
0
    session.sf_flags |= SF_EPSV_ALL;
4626
0
    pr_response_add(R_200, _("EPSV ALL command successful"));
4627
4628
0
    pr_cmd_set_errno(cmd, EPERM);
4629
0
    errno = EPERM;
4630
0
    return PR_HANDLED(cmd);
4631
0
  }
4632
4633
  /* If the optional parameter was given, determine the address family from
4634
   * that.  If not, determine the family from the control connection address
4635
   * family.
4636
   */
4637
0
  if (arg) {
4638
0
    family = strtol(arg, &endp, 10);
4639
4640
0
    if (endp && *endp) {
4641
0
      pr_response_add_err(R_501, _("%s: unknown network protocol"),
4642
0
        (char *) cmd->argv[0]);
4643
4644
0
      pr_cmd_set_errno(cmd, EINVAL);
4645
0
      errno = EINVAL;
4646
0
      return PR_ERROR(cmd);
4647
0
    }
4648
4649
0
  } else {
4650
0
    switch (pr_netaddr_get_family(session.c->local_addr)) {
4651
0
      case AF_INET:
4652
0
        family = 1;
4653
0
        break;
4654
4655
0
#if defined(PR_USE_IPV6)
4656
0
      case AF_INET6:
4657
0
        if (pr_netaddr_use_ipv6()) {
4658
0
          family = 2;
4659
0
          break;
4660
0
        }
4661
0
#endif /* PR_USE_IPV6 */
4662
        /* FALLTHROUGH */
4663
4664
0
      default:
4665
0
        family = 0;
4666
0
        break;
4667
0
    }
4668
0
  }
4669
4670
0
  switch (family) {
4671
0
    case 1:
4672
0
      break;
4673
4674
0
#if defined(PR_USE_IPV6)
4675
0
    case 2:
4676
0
      if (pr_netaddr_use_ipv6()) {
4677
0
        break;
4678
0
      }
4679
0
#endif /* PR_USE_IPV6 */
4680
      /* FALLTHROUGH */
4681
4682
0
    default:
4683
0
#if defined(PR_USE_IPV6)
4684
0
      if (pr_netaddr_use_ipv6()) {
4685
0
        pr_response_add_err(R_522,
4686
0
          _("Network protocol not supported, use (1,2)"));
4687
4688
0
      } else {
4689
0
        pr_response_add_err(R_522,
4690
0
          _("Network protocol not supported, use (1)"));
4691
0
      }
4692
#else
4693
      pr_response_add_err(R_522, _("Network protocol not supported, use (1)"));
4694
#endif /* PR_USE_IPV6 */
4695
4696
0
      pr_cmd_set_errno(cmd, EINVAL);
4697
0
      errno = EINVAL;
4698
0
      return PR_ERROR(cmd);
4699
0
  }
4700
4701
  /* If we already have a passive listen data connection open, kill it. */
4702
0
  if (session.d != NULL) {
4703
0
    pr_inet_close(session.d->pool, session.d);
4704
0
    session.d = NULL;
4705
0
  }
4706
4707
0
  if (pr_netaddr_get_family(session.c->local_addr) == pr_netaddr_get_family(session.c->remote_addr)) {
4708
0
    bind_addr = session.c->local_addr;
4709
4710
0
  } else {
4711
    /* In this scenario, the server has an IPv6 socket, but the remote client
4712
     * is an IPv4 (or IPv4-mapped IPv6) peer.
4713
     */
4714
0
    bind_addr = pr_netaddr_v6tov4(cmd->pool, session.c->local_addr);
4715
0
  }
4716
4717
0
  c = find_config(main_server->conf, CONF_PARAM, "PassivePorts", FALSE);
4718
0
  if (c != NULL) {
4719
0
    epsv_min_port = *((int *) c->argv[0]);
4720
0
    epsv_max_port = *((int *) c->argv[1]);
4721
0
  }
4722
4723
  /* We always use the portrange variant of inet_create_conn() here,
4724
   * since it seems that some Unix kernels have issues when choosing a
4725
   * random port number for IPv6 sockets (see Bug #2900).  By using the
4726
   * portrange variant, proftpd, and not the kernel, will be the one
4727
   * choosing the port number.  We really only need to do this for EPSV
4728
   * and not PASV since only the EPSV command can be used for IPv6
4729
   * connections; using PASV means IPv4 connections, and Unix kernels
4730
   * have more predictable behavior for choosing random IPv4 socket ports.
4731
   */
4732
4733
0
  session.d = pr_inet_create_conn_portrange(session.pool, bind_addr,
4734
0
    epsv_min_port, epsv_max_port);
4735
0
  if (session.d == NULL) {
4736
    /* If not able to open a passive port in the given range, default to
4737
     * normal behavior (using INPORT_ANY), and log the failure.  This
4738
     * indicates a too-small range configuration.
4739
     */
4740
0
    pr_log_pri(PR_LOG_WARNING, "unable to find open port in PassivePorts "
4741
0
      "range %d-%d: defaulting to INPORT_ANY (consider defining a larger "
4742
0
      "PassivePorts range)", epsv_min_port, epsv_max_port);
4743
4744
0
    session.d = pr_inet_create_conn2(session.pool, -1, bind_addr, INPORT_ANY,
4745
0
      0);
4746
0
  }
4747
4748
0
  if (session.d == NULL) {
4749
0
    int xerrno = errno;
4750
4751
0
    pr_response_add_err(R_425,
4752
0
      _("Unable to build data connection: Internal error"));
4753
4754
0
    pr_cmd_set_errno(cmd, xerrno);
4755
0
    errno = xerrno;
4756
0
    return PR_ERROR(cmd);
4757
0
  }
4758
4759
  /* Make sure that necessary socket options are set on the socket prior
4760
   * to the call to listen(2).
4761
   */
4762
4763
0
  c = find_config(main_server->conf, CONF_PARAM, "TCPNoDelay", FALSE);
4764
0
  if (c != NULL) {
4765
0
    int data_use_nodelay;
4766
4767
0
    data_use_nodelay = *((int *) c->argv[1]);
4768
0
    if (data_use_nodelay == FALSE) {
4769
0
      tcp_nodelay = 0;
4770
0
    }
4771
0
  }
4772
4773
0
  session.d->use_nodelay = tcp_nodelay;
4774
0
  pr_inet_set_proto_opts(session.pool, session.d, main_server->tcp_mss_len,
4775
0
    tcp_nodelay, IPTOS_THROUGHPUT, 1);
4776
0
  pr_inet_generate_socket_event("core.data-listen", main_server,
4777
0
    session.d->local_addr, session.d->listen_fd);
4778
4779
0
  pr_inet_set_block(session.pool, session.d);
4780
0
  if (pr_inet_listen(session.pool, session.d, 1, 0) < 0) {
4781
0
    int xerrno = errno;
4782
4783
0
    pr_response_add_err(R_425, "%s: %s", (char *) cmd->argv[0],
4784
0
      strerror(xerrno));
4785
4786
0
    pr_cmd_set_errno(cmd, xerrno);
4787
0
    errno = xerrno;
4788
0
    return PR_ERROR(cmd);
4789
0
  }
4790
4791
0
  session.d->instrm = pr_netio_open(session.pool, PR_NETIO_STRM_DATA,
4792
0
    session.d->listen_fd, PR_NETIO_IO_RD);
4793
4794
  /* Now tell the client our address/port. */
4795
0
  session.data_port = session.d->local_port;
4796
0
  session.sf_flags |= SF_PASSIVE;
4797
4798
  /* Note: what about masquerading IPv6 addresses?  It seems that RFC2428,
4799
   * which defines the EPSV command, does not explicitly handle the
4800
   * case where the server may wish to return a network address in its
4801
   * EPSV response.  The assumption is that in an IPv6 environment, there
4802
   * will be no need for NAT, and hence no need for masquerading.  This
4803
   * may be true in an ideal world, but I think it more likely that current
4804
   * clients will simply use EPSV, rather than PASV, in existing IPv4 networks.
4805
   *
4806
   * Note that this also means that EPSV cannot be use for site-to-site (FXP)
4807
   * transfers; the EPSV response is not allowed to contain a different
4808
   * network address.
4809
   *
4810
   * Disable the honoring of MasqueradeAddress for EPSV until this can
4811
   * be officially determined (Bug#2369).  See also Bug#3862.
4812
   */
4813
#if 0
4814
  c = find_config(main_server->conf, CONF_PARAM, "MasqueradeAddress", FALSE);
4815
  if (c != NULL) {
4816
   addrstr = (char *) pr_netaddr_get_ipstr(c->argv[0]);
4817
  }
4818
#endif
4819
4820
0
  pr_log_debug(DEBUG1, "Entering Extended Passive Mode (||%s|%u|)",
4821
0
    addrstr, (unsigned int) session.data_port);
4822
0
  pr_response_add(R_229, "Entering Extended Passive Mode (||%s|%u|)",
4823
0
    addrstr, (unsigned int) session.data_port);
4824
4825
0
  return PR_HANDLED(cmd);
4826
0
}
4827
4828
0
MODRET core_help(cmd_rec *cmd) {
4829
0
  if (!dir_check(cmd->tmp_pool, cmd, cmd->group, session.vwd, NULL)) {
4830
0
    int xerrno = EACCES;
4831
4832
0
    pr_log_debug(DEBUG7, "%s command denied by <Limit> configuration",
4833
0
      (char *) cmd->argv[0]);
4834
4835
    /* Returning 501 is the best we can do.  It would be nicer if RFC959 allowed
4836
     * 550 as a possible response.
4837
     */
4838
0
    pr_response_add_err(R_501, "%s: %s", (char *) cmd->argv[0],
4839
0
      strerror(xerrno));
4840
4841
0
    pr_cmd_set_errno(cmd, xerrno);
4842
0
    errno = xerrno;
4843
0
    return PR_ERROR(cmd);
4844
0
  }
4845
4846
0
  if (cmd->argc == 1) {
4847
0
    pr_help_add_response(cmd, NULL);
4848
4849
0
  } else {
4850
0
    char *cp;
4851
4852
0
    for (cp = cmd->argv[1]; *cp; cp++) {
4853
0
      if (PR_ISALPHA((int) *cp)) {
4854
0
        *cp = toupper((int) *cp);
4855
0
      }
4856
0
    }
4857
4858
0
    if (strcasecmp(cmd->argv[1], C_SITE) == 0) {
4859
0
      return pr_module_call(&site_module, site_dispatch, cmd);
4860
0
    }
4861
4862
0
    if (pr_help_add_response(cmd, cmd->argv[1]) == 0) {
4863
0
      return PR_HANDLED(cmd);
4864
0
    }
4865
4866
0
    pr_response_add_err(R_502, _("Unknown command '%s'"),
4867
0
      (char *) cmd->argv[1]);
4868
4869
0
    pr_cmd_set_errno(cmd, EINVAL);
4870
0
    errno = EINVAL;
4871
0
    return PR_ERROR(cmd);
4872
0
  }
4873
4874
0
  return PR_HANDLED(cmd);
4875
0
}
4876
4877
0
MODRET core_host(cmd_rec *cmd) {
4878
0
  const char *local_ipstr;
4879
0
  char *host;
4880
0
  size_t hostlen;
4881
0
  server_rec *named_server;
4882
0
  int found_ipv6 = FALSE;
4883
4884
0
  if (cmd->argc != 2) {
4885
0
    pr_response_add_err(R_500, _("'%s' not understood"), (char *) cmd->argv[0]);
4886
4887
0
    pr_cmd_set_errno(cmd, EINVAL);
4888
0
    errno = EINVAL;
4889
0
    return PR_ERROR(cmd);
4890
0
  }
4891
4892
0
  if (session.user != NULL) {
4893
0
    pr_log_debug(DEBUG0,
4894
0
      "HOST '%s' command received after login, refusing HOST command",
4895
0
      (char *) cmd->argv[1]);
4896
4897
    /* Per HOST spec, HOST after successful USER/PASS is not allowed. */
4898
0
    pr_response_add_err(R_503, _("Bad sequence of commands"));
4899
4900
0
    pr_cmd_set_errno(cmd, EPERM);
4901
0
    errno = EPERM;
4902
0
    return PR_ERROR(cmd);
4903
0
  }
4904
4905
0
  if (!dir_check(cmd->tmp_pool, cmd, cmd->group, session.cwd, NULL)) {
4906
0
    int xerrno = EACCES;
4907
4908
0
    pr_log_debug(DEBUG7, "%s command denied by <Limit> configuration",
4909
0
      (char *) cmd->argv[0]);
4910
0
    pr_response_add_err(R_504, "%s: %s", (char *) cmd->argv[1],
4911
0
      strerror(xerrno));
4912
4913
0
    pr_cmd_set_errno(cmd, xerrno);
4914
0
    errno = xerrno;
4915
0
    return PR_ERROR(cmd);
4916
0
  }
4917
4918
  /* Should there be a limit on the number of HOST commands that a client
4919
   * can send?
4920
   *
4921
   * In practice, this will be limited by the TimeoutLogin time interval;
4922
   * a client can send as many HOST commands as it wishes, as long as it
4923
   * successfully authenticates in that time.
4924
   */
4925
4926
  /* If the user has already authenticated or negotiated a RFC2228 mechanism,
4927
   * then the HOST command is too late.
4928
   */
4929
0
  if (session.rfc2228_mech != NULL &&
4930
0
      pr_table_get(session.notes, "mod_tls.sni", NULL) == NULL) {
4931
0
    pr_log_debug(DEBUG0, "HOST '%s' command received after client has "
4932
0
      "requested RFC2228 protection (%s), refusing HOST command",
4933
0
      (char *) cmd->argv[1], session.rfc2228_mech);
4934
4935
0
    pr_response_add_err(R_503, _("Bad sequence of commands"));
4936
4937
0
    pr_cmd_set_errno(cmd, EPERM);
4938
0
    errno = EPERM;
4939
0
    return PR_ERROR(cmd);
4940
0
  }
4941
4942
0
  host = cmd->argv[1];
4943
0
  hostlen = strlen(host);
4944
4945
0
  if (host[0] == '[') {
4946
    /* Check for any literal IPv6 hostnames. Per HOST spec, these IPv6
4947
     * addresses MUST be enclosed within square brackets.
4948
     */
4949
4950
0
    if (pr_netaddr_use_ipv6()) {
4951
0
      if (host[hostlen-1] != ']') {
4952
0
        pr_response_add_err(R_501, _("%s: Invalid IPv6 address provided"),
4953
0
          host);
4954
4955
0
        pr_cmd_set_errno(cmd, EINVAL);
4956
0
        errno = EINVAL;
4957
0
        return PR_ERROR(cmd);
4958
0
      }
4959
4960
0
      host = pstrndup(cmd->tmp_pool, host + 1, hostlen - 2);
4961
0
      hostlen = hostlen - 2;
4962
4963
0
      if (pr_netaddr_is_v6(host) != TRUE) {
4964
0
        pr_log_debug(DEBUG0,
4965
0
          "Client-sent hostname '%s' is not a valid IPv6 address, "
4966
0
          "refusing HOST command", host);
4967
4968
0
        pr_response_add_err(R_501, _("%s: Invalid IPv6 address provided"),
4969
0
          (char *) cmd->argv[1]);
4970
4971
0
        pr_cmd_set_errno(cmd, EINVAL);
4972
0
        errno = EINVAL;
4973
0
        return PR_ERROR(cmd);
4974
0
      }
4975
4976
0
      found_ipv6 = TRUE;
4977
4978
0
    } else {
4979
0
      pr_response_add_err(R_501, _("%s: Invalid hostname provided"),
4980
0
        host);
4981
4982
0
      pr_cmd_set_errno(cmd, EINVAL);
4983
0
      errno = EINVAL;
4984
0
      return PR_ERROR(cmd);
4985
0
    }
4986
0
  }
4987
4988
0
  local_ipstr = pr_netaddr_get_ipstr(session.c->local_addr);
4989
4990
0
  if (pr_netaddr_is_v4(host) == TRUE) {
4991
0
    if (pr_netaddr_is_v4(local_ipstr) == TRUE) {
4992
0
      if (strncmp(host, local_ipstr, hostlen) != 0) {
4993
        /* The client connected to an IP address, but requested a different IP
4994
         * address in its HOST command.  That won't work.
4995
         */
4996
0
        pr_log_debug(DEBUG0, "HOST '%s' requested, but client connected to "
4997
0
          "IPv4 address '%s', refusing HOST command", host, local_ipstr);
4998
0
        pr_response_add_err(R_504, _("%s: Unknown hostname provided"),
4999
0
          (char *) cmd->argv[1]);
5000
5001
0
        pr_cmd_set_errno(cmd, EPERM);
5002
0
        errno = EPERM;
5003
0
        return PR_ERROR(cmd);
5004
0
      }
5005
0
    }
5006
5007
0
    (void) pr_table_remove(session.notes, "mod_core.host", NULL);
5008
0
    if (pr_table_add_dup(session.notes, "mod_core.host", host, 0) < 0) {
5009
0
      pr_trace_msg("command", 3,
5010
0
        "error stashing 'mod_core.host' in session.notes: %s", strerror(errno));
5011
0
    }
5012
5013
    /* No need to send the banner information again, since we didn't actually
5014
     * change the virtual host used by the client.
5015
     */
5016
0
    pr_response_add(R_220, _("HOST command successful"));
5017
0
    return PR_HANDLED(cmd);
5018
0
  }
5019
5020
0
  if (pr_netaddr_is_v6(host) == TRUE) {
5021
0
    if (pr_netaddr_is_v6(local_ipstr) == TRUE) {
5022
5023
0
      if (found_ipv6 == FALSE) {
5024
        /* The client sent us an IPv6 address WITHOUT the '[...]' notation,
5025
         * which is a syntax error.
5026
         */
5027
0
        pr_log_debug(DEBUG0, "Client-sent hostname '%s' is an IPv6 address, "
5028
0
          "but did not have required [] notation, refusing HOST command",
5029
0
          host);
5030
5031
0
        pr_response_add_err(R_501, _("%s: Invalid IPv6 address provided"),
5032
0
          host);
5033
5034
0
        pr_cmd_set_errno(cmd, EINVAL);
5035
0
        errno = EINVAL;
5036
0
        return PR_ERROR(cmd);
5037
0
      }
5038
5039
0
      if (strncmp(host, local_ipstr, hostlen) != 0) {
5040
        /* The client connected to an IP address, but requested a different IP
5041
         * address in its HOST command.  That won't work.
5042
         */
5043
0
        pr_log_debug(DEBUG0, "HOST '%s' requested, but client connected to "
5044
0
          "IPv6 address '%s', refusing HOST command", host, local_ipstr);
5045
0
        pr_response_add_err(R_504, _("%s: Unknown hostname provided"),
5046
0
          (char *) cmd->argv[1]);
5047
5048
0
        pr_cmd_set_errno(cmd, EPERM);
5049
0
        errno = EPERM;
5050
0
        return PR_ERROR(cmd);
5051
0
      }
5052
0
    }
5053
5054
0
    (void) pr_table_remove(session.notes, "mod_core.host", NULL);
5055
0
    if (pr_table_add_dup(session.notes, "mod_core.host", host, 0) < 0) {
5056
0
      pr_trace_msg("command", 3,
5057
0
        "error stashing 'mod_core.host' in session.notes: %s", strerror(errno));
5058
0
    }
5059
5060
    /* No need to send the banner information again, since we didn't actually
5061
     * change the virtual host used by the client.
5062
     */
5063
0
    pr_response_add(R_220, _("HOST command successful"));
5064
0
    return PR_HANDLED(cmd);
5065
0
  }
5066
5067
  /* If we reach this point, the hostname is probably a DNS name.  See if we
5068
   * have a matching namebind based on the current IP address/port.
5069
   */
5070
5071
0
  if (strchr(host, ':') != NULL) {
5072
    /* Hostnames cannot contain colon characters. */
5073
0
    pr_response_add_err(R_501, _("%s: Invalid hostname provided"), host);
5074
5075
0
    pr_cmd_set_errno(cmd, EINVAL);
5076
0
    errno = EINVAL;
5077
0
    return PR_ERROR(cmd);
5078
0
  }
5079
5080
0
  named_server = pr_namebind_get_server(host, main_server->addr,
5081
0
    session.c->local_port);
5082
0
  if (named_server == NULL) {
5083
0
    pr_log_debug(DEBUG0, "Unknown host '%s' requested on %s#%d, "
5084
0
      "refusing HOST command", host, local_ipstr, main_server->ServerPort);
5085
5086
0
    pr_response_add_err(R_504, _("%s: Unknown hostname provided"),
5087
0
      (char *) cmd->argv[1]);
5088
5089
0
    pr_cmd_set_errno(cmd, ENOENT);
5090
0
    errno = ENOENT;
5091
0
    return PR_ERROR(cmd);
5092
0
  }
5093
5094
0
  if (session.rfc2228_mech != NULL &&
5095
0
      strcmp(session.rfc2228_mech, "TLS") == 0) {
5096
0
    const char *sni = NULL;
5097
5098
    /* If the TLS client used the SNI extension, ensure that the SNI name
5099
     * matches the HOST name, per RFC 7151, Section 3.2.2.  Otherwise, we
5100
     * reject the HOST command.
5101
     */
5102
0
    sni = pr_table_get(session.notes, "mod_tls.sni", NULL);
5103
0
    if (sni != NULL) {
5104
0
      if (strcasecmp(sni, host) != 0) {
5105
0
        pr_log_debug(DEBUG0, "HOST '%s' requested, but client connected via "
5106
0
          "TLS to SNI '%s', refusing HOST command", host, sni);
5107
0
        pr_response_add_err(R_504, _("%s: Unknown hostname provided"),
5108
0
          (char *) cmd->argv[1]);
5109
5110
0
        pr_cmd_set_errno(cmd, EPERM);
5111
0
        errno = EPERM;
5112
0
        return PR_ERROR(cmd);
5113
0
      }
5114
0
    }
5115
0
  }
5116
5117
0
  (void) pr_table_remove(session.notes, "mod_core.host", NULL);
5118
0
  if (pr_table_add_dup(session.notes, "mod_core.host", host, 0) < 0) {
5119
0
    pr_trace_msg("command", 3,
5120
0
      "error stashing 'mod_core.host' in session.notes: %s", strerror(errno));
5121
0
  }
5122
5123
0
  if (named_server != main_server) {
5124
    /* Set a session flag indicating that the main_server pointer changed. */
5125
0
    pr_log_debug(DEBUG0,
5126
0
      "Changing to server '%s' (ServerAlias %s) due to HOST command",
5127
0
      named_server->ServerName, host);
5128
0
    session.prev_server = main_server;
5129
0
    main_server = named_server;
5130
5131
0
    pr_event_generate("core.session-reinit", named_server);
5132
0
  }
5133
5134
  /* XXX Ultimately, if HOST is successful, we change the main_server pointer
5135
   * to point to the named server_rec.
5136
   *
5137
   * Check *every single sess_init* function, since there are MANY things
5138
   * which currently happen at sess_init, based on the main_server pointer,
5139
   * that will need to be re-done in a HOST POST_CMD handler.  This includes
5140
   * AuthOrder, timeouts, etc etc.  (Unfortunately, POST_CMD handlers cannot
5141
   * fail the given command; for modules which then need to end the
5142
   * connection, they'll need to use pr_session_disconnect().)
5143
   *
5144
   * Modules implementing post_host handlers:
5145
   *   mod_core
5146
   *
5147
   * Modules implementing 'sess-reinit' event handlers:
5148
   *   mod_auth
5149
   *   mod_auth_file
5150
   *   mod_auth_unix
5151
   *   mod_ban
5152
   *   mod_cap
5153
   *   mod_copy
5154
   *   mod_deflate
5155
   *   mod_delay
5156
   *   mod_dnsbl
5157
   *   mod_exec
5158
   *   mod_facts
5159
   *   mod_ident
5160
   *   mod_ldap
5161
   *   mod_log
5162
   *   mod_log_forensic
5163
   *   mod_memcache
5164
   *   mod_qos
5165
   *   mod_quotatab
5166
   *   mod_radius
5167
   *   mod_rewrite
5168
   *   mod_site_misc
5169
   *   mod_sql
5170
   *   mod_sql_passwd
5171
   *   mod_tls
5172
   *   mod_wrap
5173
   *   mod_wrap2
5174
   *   mod_xfer
5175
   *
5176
   * Modules that MIGHT need a session-reinit listener:
5177
   *   mod_ratio
5178
   *   mod_snmp
5179
   *
5180
   * Modules that DO NOT NEED a session-reinit listener:
5181
   *   mod_auth_pam
5182
   *   mod_ctrls_admin
5183
   *   mod_dynmasq
5184
   *   mod_ifsession
5185
   *   mod_ifversion
5186
   *   mod_load
5187
   *   mod_readme
5188
   *   mod_sftp (HOST command is FTP only)
5189
   *   mod_sftp_pam
5190
   *   mod_sftp_sql
5191
   *   mod_shaper
5192
   *   mod_sql_mysql
5193
   *   mod_sql_postgres
5194
   *   mod_sql_odbc
5195
   *   mod_sql_sqlite
5196
   *   mod_tls_fscache
5197
   *   mod_tls_memcache
5198
   *   mod_tls_shmcache
5199
   *   mod_unique_id
5200
   */
5201
5202
  /* XXX Will this function need to use pr_response_add(), rather than
5203
   * pr_response_send(), in order to accommodate the delaying of sending the
5204
   * response until after POST_CMD/LOG_CMD handlers have run (and thus allowing
5205
   * module e.g. mod_tls to send an error response in the POST_CMD handler,
5206
   * and close the connection)?
5207
   */
5208
5209
0
  pr_session_send_banner(main_server, 0);
5210
0
  return PR_HANDLED(cmd);
5211
0
}
5212
5213
0
MODRET core_post_host(cmd_rec *cmd) {
5214
5215
  /* If the HOST command changed the main_server pointer, reinitialize
5216
   * ourselves.
5217
   */
5218
0
  if (session.prev_server != NULL) {
5219
0
    int res;
5220
0
    config_rec *c;
5221
5222
    /* Reset the FS options */
5223
0
    (void) pr_fsio_set_options(0UL);
5224
5225
    /* Remove the TimeoutIdle timer. */
5226
0
    (void) pr_timer_remove(PR_TIMER_IDLE, ANY_MODULE);
5227
5228
    /* Restore the original TimeoutLinger value. */
5229
0
    pr_data_set_linger(PR_TUNABLE_TIMEOUTLINGER);
5230
5231
    /* Restore original DebugLevel, but only if set via directive, not
5232
     * via the command-line.
5233
     */
5234
0
    c = find_config(session.prev_server->conf, CONF_PARAM, "DebugLevel", FALSE);
5235
0
    if (c != NULL) {
5236
0
      pr_log_setdebuglevel(DEBUG0);
5237
0
    }
5238
5239
    /* Restore the original RegexOptions values. */
5240
0
    pr_regexp_set_engine(NULL);
5241
0
    pr_regexp_set_limits(0, 0);
5242
5243
    /* Remove any configured SetEnvs. */
5244
0
    c = find_config(session.prev_server->conf, CONF_PARAM, "SetEnv", FALSE);
5245
0
    while (c != NULL) {
5246
0
      pr_signals_handle();
5247
5248
0
      if (pr_env_unset(session.pool, c->argv[0]) < 0) {
5249
0
        pr_log_debug(DEBUG0, "unable to unset environment variable '%s': %s",
5250
0
          (char *) c->argv[0], strerror(errno));
5251
0
      }
5252
5253
0
      c = find_config_next(c, c->next, CONF_PARAM, "SetEnv", FALSE);
5254
0
    }
5255
5256
    /* Restore original AuthOrder. */
5257
0
    reset_server_auth_order();
5258
5259
0
#if defined(PR_USE_TRACE)
5260
    /* XXX Restore original Trace settings. */
5261
5262
    /* Restore original TraceOptions settings. */
5263
0
    (void) pr_trace_set_options(PR_TRACE_OPT_DEFAULT);
5264
0
#endif /* PR_USE_TRACE */
5265
5266
    /* Remove the variables set via pr_var_set(). */
5267
0
    (void) pr_var_delete("%{bytes_xfer}");
5268
0
    (void) pr_var_delete("%{total_bytes_in}");
5269
0
    (void) pr_var_delete("%{total_bytes_out}");
5270
0
    (void) pr_var_delete("%{total_bytes_xfer}");
5271
0
    (void) pr_var_delete("%{total_files_in}");
5272
0
    (void) pr_var_delete("%{total_files_out}");
5273
0
    (void) pr_var_delete("%{total_files_xfer}");
5274
5275
    /* Reset the DisplayQuit file. */
5276
0
    if (displayquit_fh != NULL) {
5277
0
      pr_fsio_close(displayquit_fh);
5278
0
      displayquit_fh = NULL;
5279
0
    }
5280
5281
    /* Restore the original ProcessTitles setting. */
5282
0
    pr_proctitle_set_static_str(NULL);
5283
5284
    /* Unregister any event listeners. */
5285
0
    pr_event_unregister(&core_module, "core.chroot", core_chroot_ev);
5286
5287
0
    res = core_sess_init();
5288
0
    if (res < 0) {
5289
0
      pr_session_disconnect(&core_module,
5290
0
        PR_SESS_DISCONNECT_SESSION_INIT_FAILED, NULL);
5291
0
    }
5292
0
  }
5293
5294
0
  return PR_DECLINED(cmd);
5295
0
}
5296
5297
0
MODRET core_clnt(cmd_rec *cmd) {
5298
0
  (void) pr_table_add_dup(session.notes,
5299
0
    pstrdup(session.pool, "clnt"), cmd->arg, 0);
5300
0
  pr_response_add(R_200, _("OK"));
5301
0
  return PR_HANDLED(cmd);
5302
0
}
5303
5304
0
static pr_table_t *parse_client_facts(pool *p, char *text) {
5305
0
  array_header *facts;
5306
0
  pr_table_t *facts_tab = NULL;
5307
5308
0
  facts = pr_str_text_to_array(p, text, ';');
5309
0
  if (facts != NULL) {
5310
0
    register unsigned int i;
5311
5312
0
    facts_tab = pr_table_alloc(p, 0);
5313
5314
0
    for (i = 0; i < facts->nelts; i++) {
5315
0
      char *fact, *ptr;
5316
0
      size_t fact_len;
5317
5318
0
      fact = ((char **) (facts->elts))[i];
5319
0
      fact_len = strlen(fact);
5320
5321
0
      if (fact_len == 0) {
5322
0
        continue;
5323
0
      }
5324
5325
0
      ptr = strchr(fact, '=');
5326
0
      if (ptr == NULL) {
5327
        /* Ignore invalid facts. */
5328
0
        pr_log_debug(DEBUG0, "ignoring invalid client-sent CSID fact '%s'",
5329
0
          fact);
5330
0
        continue;
5331
0
      }
5332
5333
      /* Skip any leading whitespace. */
5334
0
      while (PR_ISSPACE(*fact)) {
5335
0
        pr_signals_handle();
5336
0
        fact++;
5337
0
      }
5338
5339
0
      if (strncasecmp(fact, "Name", ptr - fact) == 0) {
5340
0
        pr_log_debug(DEBUG9, "client sent CSID Name fact: '%s'", ptr+1);
5341
0
        (void) pr_table_add_dup(facts_tab, pstrdup(p, "Name"), ptr+1, 0);
5342
0
        continue;
5343
0
      }
5344
5345
0
      if (strncasecmp(fact, "Version", ptr - fact) == 0) {
5346
0
        pr_log_debug(DEBUG9, "client sent CSID Version fact: '%s'", ptr+1);
5347
0
        (void) pr_table_add_dup(facts_tab, pstrdup(p, "Version"), ptr+1, 0);
5348
0
        continue;
5349
0
      }
5350
5351
0
      if (strncasecmp(fact, "Vendor", ptr - fact) == 0) {
5352
0
        pr_log_debug(DEBUG9, "client sent CSID Vendor fact: '%s'", ptr+1);
5353
0
        (void) pr_table_add_dup(facts_tab, pstrdup(p, "Vendor"), ptr+1, 0);
5354
0
        continue;
5355
0
      }
5356
5357
      /* Ignore unknown/unsupported facts. */
5358
0
      pr_log_debug(DEBUG0, "ignoring unknown client-sent CSID fact '%s'", fact);
5359
0
    }
5360
0
  }
5361
5362
0
  return facts_tab;
5363
0
}
5364
5365
0
MODRET core_csid(cmd_rec *cmd) {
5366
0
  config_rec *c;
5367
0
  int use_server_ident = TRUE;
5368
0
  pr_table_t *client_facts = NULL;
5369
0
  char server_text[PR_TUNABLE_BUFFER_SIZE];
5370
5371
  /* Parse the client-provided facts.  Sadly, per IETF Draft, if there are
5372
   * issues parsing the client-provided text, or if the client does not provide
5373
   * the "required" facts, they are to be silently ignored, but not rejected.
5374
   */
5375
0
  client_facts = parse_client_facts(cmd->tmp_pool, cmd->arg);
5376
0
  if (client_facts != NULL &&
5377
0
      pr_table_count(client_facts) > 0) {
5378
0
    const char *key;
5379
0
    const void *val;
5380
0
    size_t valsz;
5381
5382
0
    key = "Name";
5383
0
    val = pr_table_get(client_facts, key, &valsz);
5384
0
    if (val != NULL) {
5385
0
      (void) pr_table_add_dup(session.notes,
5386
0
        pstrdup(session.pool, "csid.name"), val, valsz);
5387
0
    }
5388
5389
0
    key = "Version";
5390
0
    val = pr_table_get(client_facts, key, &valsz);
5391
0
    if (val != NULL) {
5392
0
      (void) pr_table_add_dup(session.notes,
5393
0
        pstrdup(session.pool, "csid.version"), val, valsz);
5394
0
    }
5395
5396
0
    key = "Vendor";
5397
0
    val = pr_table_get(client_facts, key, &valsz);
5398
0
    if (val != NULL) {
5399
0
      (void) pr_table_add_dup(session.notes,
5400
0
        pstrdup(session.pool, "csid.vendor"), val, valsz);
5401
0
    }
5402
0
  }
5403
5404
0
  c = find_config(main_server->conf, CONF_PARAM, "ServerIdent", FALSE);
5405
0
  if (c != NULL) {
5406
0
    use_server_ident = *((unsigned char *) c->argv[0]);
5407
5408
    /* What if the admin has configured an explicit string via
5409
     * "ServerIdent on <text>"?
5410
     *
5411
     * Perhaps we ignore that custom text, on the assumption that the actual
5412
     * server info here is being provided to a successfully authenticated (and
5413
     * thus allowed) client, rather than to some random client on the Internet.
5414
     */
5415
0
  }
5416
5417
0
  memset(server_text, '\0', sizeof(server_text));
5418
5419
0
  if (use_server_ident == TRUE) {
5420
0
    (void) pr_snprintf(server_text, sizeof(server_text),
5421
0
      "name=ProFTPD;version=%s;casesensitive=1;", PROFTPD_VERSION_TEXT);
5422
5423
0
  } else {
5424
0
    (void) pr_snprintf(server_text, sizeof(server_text), "%s",
5425
0
      "casesensitive=1;");
5426
0
  }
5427
5428
0
  pr_response_add(R_200, "%s", server_text);
5429
0
  return PR_HANDLED(cmd);
5430
0
}
5431
5432
0
MODRET core_syst(cmd_rec *cmd) {
5433
0
  pr_response_add(R_215, "UNIX Type: L8");
5434
0
  return PR_HANDLED(cmd);
5435
0
}
5436
5437
0
int core_chgrp(cmd_rec *cmd, const char *path, uid_t uid, gid_t gid) {
5438
0
  char *cmd_name;
5439
5440
0
  cmd_name = cmd->argv[0];
5441
0
  pr_cmd_set_name(cmd, "SITE_CHGRP");
5442
0
  if (!dir_check(cmd->tmp_pool, cmd, G_WRITE, path, NULL)) {
5443
0
    pr_log_debug(DEBUG7, "SITE CHGRP command denied by <Limit> config");
5444
0
    pr_cmd_set_name(cmd, cmd_name);
5445
5446
0
    errno = EACCES;
5447
0
    return -1;
5448
0
  }
5449
0
  pr_cmd_set_name(cmd, cmd_name);
5450
5451
0
  return pr_fsio_lchown(path, uid, gid);
5452
0
}
5453
5454
0
int core_chmod(cmd_rec *cmd, const char *path, mode_t mode) {
5455
0
  char *cmd_name;
5456
5457
0
  cmd_name = cmd->argv[0];
5458
0
  pr_cmd_set_name(cmd, "SITE_CHMOD");
5459
0
  if (!dir_check(cmd->tmp_pool, cmd, G_WRITE, path, NULL)) {
5460
0
    pr_log_debug(DEBUG7, "SITE CHMOD command denied by <Limit> config");
5461
0
    pr_cmd_set_name(cmd, cmd_name);
5462
5463
0
    errno = EACCES;
5464
0
    return -1;
5465
0
  }
5466
0
  pr_cmd_set_name(cmd, cmd_name);
5467
5468
0
  return pr_fsio_chmod(path, mode);
5469
0
}
5470
5471
0
MODRET core_chdir(cmd_rec *cmd, char *ndir) {
5472
0
  char *dir, *orig_dir, *cdir;
5473
0
  int xerrno = 0;
5474
0
  config_rec *c = NULL, *cdpath;
5475
0
  unsigned char show_symlinks = TRUE, *ptr = NULL;
5476
0
  struct stat st;
5477
5478
0
  orig_dir = ndir;
5479
5480
0
  ptr = get_param_ptr(TOPLEVEL_CONF, "ShowSymlinks", FALSE);
5481
0
  if (ptr != NULL) {
5482
0
    show_symlinks = *ptr;
5483
0
  }
5484
5485
0
  if (show_symlinks) {
5486
0
    int use_cdpath = FALSE;
5487
5488
0
    dir = dir_realpath(cmd->tmp_pool, ndir);
5489
0
    if (dir == NULL) {
5490
0
      use_cdpath = TRUE;
5491
0
    }
5492
5493
0
    if (!use_cdpath) {
5494
0
      int allowed_access = TRUE;
5495
5496
0
      allowed_access = dir_check_full(cmd->tmp_pool, cmd, cmd->group, dir,
5497
0
        NULL);
5498
0
      if (!allowed_access) {
5499
0
        use_cdpath = TRUE;
5500
0
      }
5501
0
    }
5502
5503
0
    if (use_cdpath == FALSE &&
5504
0
        pr_fsio_chdir(dir, 0) < 0) {
5505
0
      xerrno = errno;
5506
0
      use_cdpath = TRUE;
5507
0
    }
5508
5509
0
    if (use_cdpath) {
5510
0
      for (cdpath = find_config(main_server->conf, CONF_PARAM, "CDPath", TRUE);
5511
0
          cdpath != NULL;
5512
0
          cdpath = find_config_next(cdpath, cdpath->next, CONF_PARAM, "CDPath", TRUE)) {
5513
0
        cdir = palloc(cmd->tmp_pool, strlen(cdpath->argv[0]) + strlen(ndir) + 2);
5514
0
        pr_snprintf(cdir, strlen(cdpath->argv[0]) + strlen(ndir) + 2,
5515
0
                 "%s%s%s", (char *) cdpath->argv[0],
5516
0
                 ((char *) cdpath->argv[0])[strlen(cdpath->argv[0]) - 1] == '/' ? "" : "/",
5517
0
                 ndir);
5518
0
        dir = dir_realpath(cmd->tmp_pool, cdir);
5519
5520
0
        if (dir &&
5521
0
            dir_check_full(cmd->tmp_pool, cmd, cmd->group, dir, NULL) &&
5522
0
            pr_fsio_chdir(dir, 0) == 0) {
5523
0
          break;
5524
0
        }
5525
0
      }
5526
5527
0
      if (cdpath == FALSE) {
5528
0
        if (xerrno == 0) {
5529
0
          xerrno = errno;
5530
0
        }
5531
5532
0
        pr_response_add_err(R_550, "%s: %s", orig_dir, strerror(xerrno));
5533
5534
0
        pr_cmd_set_errno(cmd, xerrno);
5535
0
        errno = xerrno;
5536
0
        return PR_ERROR(cmd);
5537
0
      }
5538
0
    }
5539
5540
0
  } else {
5541
0
    int use_cdpath = FALSE;
5542
5543
    /* Virtualize the chdir */
5544
0
    ndir = dir_canonical_vpath(cmd->tmp_pool, ndir);
5545
0
    dir = dir_realpath(cmd->tmp_pool, ndir);
5546
5547
0
    if (dir == NULL) {
5548
0
      use_cdpath = TRUE;
5549
0
    }
5550
5551
0
    if (use_cdpath == FALSE) {
5552
0
      int allowed_access = TRUE;
5553
5554
0
      allowed_access = dir_check_full(cmd->tmp_pool, cmd, cmd->group, dir,
5555
0
        NULL);
5556
0
      if (allowed_access == FALSE) {
5557
0
        use_cdpath = TRUE;
5558
0
      }
5559
0
    }
5560
5561
0
    if (use_cdpath == FALSE &&
5562
0
        pr_fsio_chdir_canon(ndir, 1) < 0) {
5563
0
      use_cdpath = TRUE;
5564
0
    }
5565
5566
0
    if (use_cdpath == TRUE) {
5567
0
      for (cdpath = find_config(main_server->conf, CONF_PARAM, "CDPath", TRUE);
5568
0
          cdpath != NULL;
5569
0
          cdpath = find_config_next(cdpath, cdpath->next, CONF_PARAM, "CDPath", TRUE)) {
5570
0
        cdir = palloc(cmd->tmp_pool, strlen(cdpath->argv[0]) + strlen(ndir) + 2);
5571
0
        pr_snprintf(cdir, strlen(cdpath->argv[0]) + strlen(ndir) + 2,
5572
0
                 "%s%s%s", (char *) cdpath->argv[0],
5573
0
                ((char *)cdpath->argv[0])[strlen(cdpath->argv[0]) - 1] == '/' ? "" : "/",
5574
0
                ndir);
5575
0
        ndir = dir_canonical_vpath(cmd->tmp_pool, cdir);
5576
0
        dir = dir_realpath(cmd->tmp_pool, ndir);
5577
5578
0
        if (dir &&
5579
0
            dir_check_full(cmd->tmp_pool, cmd, cmd->group, dir, NULL) &&
5580
0
            pr_fsio_chdir_canon(ndir, 1) != -1) {
5581
0
          break;
5582
0
        }
5583
0
      }
5584
5585
0
      if (cdpath == NULL) {
5586
0
        if (xerrno == 0) {
5587
0
          xerrno = errno;
5588
0
        }
5589
5590
0
        pr_response_add_err(R_550, "%s: %s", orig_dir, strerror(xerrno));
5591
5592
0
        pr_cmd_set_errno(cmd, xerrno);
5593
0
        errno = xerrno;
5594
0
        return PR_ERROR(cmd);
5595
0
      }
5596
0
    }
5597
0
  }
5598
5599
0
  sstrncpy(session.cwd, pr_fs_getcwd(), sizeof(session.cwd));
5600
0
  sstrncpy(session.vwd, pr_fs_getvwd(), sizeof(session.vwd));
5601
5602
0
  pr_scoreboard_entry_update(session.pid,
5603
0
    PR_SCORE_CWD, session.cwd,
5604
0
    NULL);
5605
5606
0
  if (session.dir_config != NULL) {
5607
0
    c = find_config(session.dir_config->subset, CONF_PARAM, "DisplayChdir",
5608
0
      FALSE);
5609
0
  }
5610
5611
0
  if (c == NULL &&
5612
0
      session.anon_config != NULL) {
5613
0
    c = find_config(session.anon_config->subset, CONF_PARAM, "DisplayChdir",
5614
0
      FALSE);
5615
0
  }
5616
5617
0
  if (c == NULL) {
5618
0
    c = find_config(cmd->server->conf, CONF_PARAM, "DisplayChdir", FALSE);
5619
0
  }
5620
5621
0
  if (c != NULL) {
5622
0
    time_t prev = 0;
5623
0
    int display_once = FALSE, display_now = FALSE, res = -1;
5624
0
    char *display_file = NULL;
5625
0
    pr_fh_t *display_fh = NULL;
5626
5627
0
    display_file = c->argv[0];
5628
0
    display_once = *((int *) c->argv[1]);
5629
0
    display_fh = c->argv[2];
5630
5631
0
    if (display_once == TRUE) {
5632
      /* XXX Get rid of this CONF_USERDATA instance; it's the only
5633
       * occurrence of it in the source.  Use the session.notes table instead.
5634
       */
5635
0
      c = find_config(cmd->server->conf, CONF_USERDATA, session.cwd, FALSE);
5636
0
      if (c == NULL) {
5637
0
        time(&prev);
5638
0
        c = pr_config_add_set(&cmd->server->conf, session.cwd, 0);
5639
0
        c->config_type = CONF_USERDATA;
5640
0
        c->argc = 1;
5641
0
        c->argv = pcalloc(c->pool, sizeof(void **) * 2);
5642
0
        c->argv[0] = palloc(c->pool, sizeof(time_t));
5643
0
        *((time_t *) c->argv[0]) = prev;
5644
0
        prev = (time_t) 0L;
5645
5646
0
      } else {
5647
0
        prev = *((time_t *) c->argv[0]);
5648
5649
        /* Update the timestamp stored for this directory. */
5650
0
        *((time_t *) c->argv[0]) = time(NULL);
5651
0
      }
5652
0
    }
5653
5654
0
    if (display_fh != NULL) {
5655
0
      res = pr_fsio_fstat(display_fh, &st);
5656
0
      if (res < 0) {
5657
0
        pr_log_debug(DEBUG3, "DisplayChdir: error checking '%s': %s",
5658
0
          display_fh->fh_path, strerror(errno));
5659
0
      }
5660
5661
0
    } else {
5662
0
      res = pr_fsio_stat(display_file, &st);
5663
0
      if (res < 0) {
5664
0
        pr_log_debug(DEBUG3, "DisplayChdir: error checking '%s': %s",
5665
0
          display_file, strerror(errno));
5666
0
      }
5667
0
    }
5668
5669
0
    if (res == 0 &&
5670
0
        !S_ISDIR(st.st_mode) &&
5671
0
        (display_once ? st.st_mtime > prev : TRUE)) {
5672
0
      display_now = TRUE;
5673
0
    }
5674
5675
0
    if (display_now == TRUE) {
5676
0
      if (display_fh != NULL) {
5677
0
        if (pr_display_fh(display_fh, session.cwd, R_250, 0) < 0) {
5678
0
          pr_log_debug(DEBUG3, "DisplayChdir: error displaying '%s': %s",
5679
0
            display_fh->fh_path, strerror(errno));
5680
0
        }
5681
5682
0
      } else {
5683
0
        if (pr_display_file(display_file, session.cwd, R_250, 0) < 0) {
5684
0
          pr_log_debug(DEBUG3, "DisplayChdir: error displaying '%s': %s",
5685
0
           display_file, strerror(errno));
5686
0
        }
5687
0
      }
5688
0
    }
5689
0
  }
5690
5691
0
  pr_response_add(R_250, _("%s command successful"), (char *) cmd->argv[0]);
5692
0
  return PR_HANDLED(cmd);
5693
0
}
5694
5695
0
MODRET core_rmd(cmd_rec *cmd) {
5696
0
  int res;
5697
0
  char *decoded_path, *dir;
5698
0
  pr_error_t *err = NULL;
5699
5700
0
  CHECK_CMD_MIN_ARGS(cmd, 2);
5701
5702
0
  decoded_path = pr_fs_decode_path2(cmd->tmp_pool, cmd->arg,
5703
0
    FSIO_DECODE_FL_TELL_ERRORS);
5704
0
  if (decoded_path == NULL) {
5705
0
    int xerrno = errno;
5706
5707
0
    pr_log_debug(DEBUG8, "'%s' failed to decode properly: %s", cmd->arg,
5708
0
      strerror(xerrno));
5709
0
    pr_response_add_err(R_550, _("%s: Illegal character sequence in filename"),
5710
0
      cmd->arg);
5711
5712
0
    pr_cmd_set_errno(cmd, xerrno);
5713
0
    errno = xerrno;
5714
0
    return PR_ERROR(cmd);
5715
0
  }
5716
5717
0
  dir = decoded_path;
5718
5719
0
  res = pr_filter_allow_path(CURRENT_CONF, dir);
5720
0
  switch (res) {
5721
0
    case 0:
5722
0
      break;
5723
5724
0
    case PR_FILTER_ERR_FAILS_ALLOW_FILTER:
5725
0
      pr_log_pri(PR_LOG_NOTICE, "'%s %s' denied by PathAllowFilter",
5726
0
        (char *) cmd->argv[0], dir);
5727
0
      pr_response_add_err(R_550, _("%s: Forbidden filename"), cmd->arg);
5728
5729
0
      pr_cmd_set_errno(cmd, EPERM);
5730
0
      errno = EPERM;
5731
0
      return PR_ERROR(cmd);
5732
5733
0
    case PR_FILTER_ERR_FAILS_DENY_FILTER:
5734
0
      pr_log_pri(PR_LOG_NOTICE, "'%s %s' denied by PathDenyFilter",
5735
0
        (char *) cmd->argv[0], dir);
5736
0
      pr_response_add_err(R_550, _("%s: Forbidden filename"), cmd->arg);
5737
5738
0
      pr_cmd_set_errno(cmd, EPERM);
5739
0
      errno = EPERM;
5740
0
      return PR_ERROR(cmd);
5741
0
  }
5742
5743
0
  dir = dir_canonical_path(cmd->tmp_pool, dir);
5744
0
  if (dir == NULL) {
5745
0
    int xerrno = EINVAL;
5746
5747
0
    pr_response_add_err(R_550, "%s: %s", cmd->arg, strerror(xerrno));
5748
5749
0
    pr_cmd_set_errno(cmd, xerrno);
5750
0
    errno = xerrno;
5751
0
    return PR_ERROR(cmd);
5752
0
  }
5753
5754
0
  if (!dir_check_canon(cmd->tmp_pool, cmd, cmd->group, dir, NULL)) {
5755
0
    int xerrno = EACCES;
5756
5757
0
    pr_log_debug(DEBUG8, "%s command denied by <Limit> config",
5758
0
      (char *) cmd->argv[0]);
5759
0
    pr_response_add_err(R_550, "%s: %s", cmd->arg, strerror(xerrno));
5760
5761
0
    pr_cmd_set_errno(cmd, xerrno);
5762
0
    errno = xerrno;
5763
0
    return PR_ERROR(cmd);
5764
0
  }
5765
5766
0
  res = pr_fsio_rmdir_with_error(cmd->pool, dir, &err);
5767
0
  if (res < 0) {
5768
0
    int xerrno = errno;
5769
5770
0
    pr_error_set_where(err, &core_module, __FILE__, __LINE__ - 4);
5771
0
    pr_error_set_why(err, pstrcat(cmd->pool, "remove directory '", dir, "'",
5772
0
      NULL));
5773
5774
0
    (void) pr_trace_msg("fileperms", 1, "%s, user '%s' (UID %s, GID %s): "
5775
0
      "error removing directory '%s': %s", (char *) cmd->argv[0], session.user,
5776
0
      pr_uid2str(cmd->tmp_pool, session.uid),
5777
0
      pr_gid2str(cmd->tmp_pool, session.gid), dir, strerror(xerrno));
5778
5779
0
    if (err != NULL) {
5780
0
      pr_log_debug(DEBUG9, "%s", pr_error_strerror(err, 0));
5781
0
      pr_error_destroy(err);
5782
0
      err = NULL;
5783
5784
0
    } else {
5785
0
      pr_log_debug(DEBUG9, "error removing directory '%s': %s", dir,
5786
0
        strerror(xerrno));
5787
0
    }
5788
5789
0
    pr_response_add_err(R_550, "%s: %s", cmd->arg, strerror(xerrno));
5790
5791
0
    pr_cmd_set_errno(cmd, xerrno);
5792
0
    errno = xerrno;
5793
0
    return PR_ERROR(cmd);
5794
0
  }
5795
5796
0
  pr_response_add(R_250, _("%s command successful"), (char *) cmd->argv[0]);
5797
0
  return PR_HANDLED(cmd);
5798
0
}
5799
5800
0
MODRET core_mkd(cmd_rec *cmd) {
5801
0
  int res;
5802
0
  char *decoded_path, *dir;
5803
5804
0
  CHECK_CMD_MIN_ARGS(cmd, 2);
5805
5806
  /* XXX Why is there a check to prevent the creation of any directory
5807
   * name containing an asterisk?
5808
   */
5809
0
  if (strchr(cmd->arg, '*')) {
5810
0
    pr_response_add_err(R_550, _("%s: Invalid directory name"), cmd->arg);
5811
5812
0
    pr_cmd_set_errno(cmd, EINVAL);
5813
0
    errno = EINVAL;
5814
0
    return PR_ERROR(cmd);
5815
0
  }
5816
5817
0
  decoded_path = pr_fs_decode_path2(cmd->tmp_pool, cmd->arg,
5818
0
    FSIO_DECODE_FL_TELL_ERRORS);
5819
0
  if (decoded_path == NULL) {
5820
0
    int xerrno = errno;
5821
5822
0
    pr_log_debug(DEBUG8, "'%s' failed to decode properly: %s", cmd->arg,
5823
0
      strerror(xerrno));
5824
0
    pr_response_add_err(R_550, _("%s: Illegal character sequence in filename"),
5825
0
      cmd->arg);
5826
5827
0
    pr_cmd_set_errno(cmd, xerrno);
5828
0
    errno = xerrno;
5829
0
    return PR_ERROR(cmd);
5830
0
  }
5831
5832
0
  dir = decoded_path;
5833
5834
0
  res = pr_filter_allow_path(CURRENT_CONF, dir);
5835
0
  switch (res) {
5836
0
    case 0:
5837
0
      break;
5838
5839
0
    case PR_FILTER_ERR_FAILS_ALLOW_FILTER:
5840
0
      pr_log_pri(PR_LOG_NOTICE, "'%s %s' denied by PathAllowFilter",
5841
0
        (char *) cmd->argv[0], dir);
5842
0
      pr_response_add_err(R_550, _("%s: Forbidden filename"), cmd->arg);
5843
5844
0
      pr_cmd_set_errno(cmd, EPERM);
5845
0
      errno = EPERM;
5846
0
      return PR_ERROR(cmd);
5847
5848
0
    case PR_FILTER_ERR_FAILS_DENY_FILTER:
5849
0
      pr_log_pri(PR_LOG_NOTICE, "'%s %s' denied by PathDenyFilter",
5850
0
        (char *) cmd->argv[0], dir);
5851
0
      pr_response_add_err(R_550, _("%s: Forbidden filename"), cmd->arg);
5852
5853
0
      pr_cmd_set_errno(cmd, EPERM);
5854
0
      errno = EPERM;
5855
0
      return PR_ERROR(cmd);
5856
0
  }
5857
5858
0
  dir = dir_canonical_path(cmd->tmp_pool, dir);
5859
0
  if (dir == NULL) {
5860
0
    int xerrno = EINVAL;
5861
5862
0
    pr_response_add_err(R_550, "%s: %s", cmd->arg, strerror(xerrno));
5863
5864
0
    pr_cmd_set_errno(cmd, xerrno);
5865
0
    errno = xerrno;
5866
0
    return PR_ERROR(cmd);
5867
0
  }
5868
5869
0
  if (!dir_check_canon(cmd->tmp_pool, cmd, cmd->group, dir, NULL)) {
5870
0
    int xerrno = EACCES;
5871
5872
0
    pr_log_debug(DEBUG8, "%s command denied by <Limit> config",
5873
0
      (char *) cmd->argv[0]);
5874
0
    pr_response_add_err(R_550, "%s: %s", cmd->arg, strerror(xerrno));
5875
5876
0
    pr_cmd_set_errno(cmd, xerrno);
5877
0
    errno = xerrno;
5878
0
    return PR_ERROR(cmd);
5879
0
  }
5880
5881
0
  res = pr_fsio_smkdir(cmd->tmp_pool, dir, 0777, session.fsuid, session.fsgid);
5882
0
  if (res < 0 &&
5883
0
      errno != EEXIST) {
5884
0
    int xerrno = errno;
5885
5886
0
    (void) pr_trace_msg("fileperms", 1, "%s, user '%s' (UID %s, GID %s): "
5887
0
      "error making directory '%s': %s", (char *) cmd->argv[0], session.user,
5888
0
      pr_uid2str(cmd->tmp_pool, session.uid),
5889
0
      pr_gid2str(cmd->tmp_pool, session.gid), dir, strerror(xerrno));
5890
5891
0
    pr_response_add_err(R_550, "%s: %s", cmd->arg, strerror(xerrno));
5892
5893
0
    pr_cmd_set_errno(cmd, xerrno);
5894
0
    errno = xerrno;
5895
0
    return PR_ERROR(cmd);
5896
0
  }
5897
5898
0
  pr_response_add(R_257, _("\"%s\" - Directory successfully created"),
5899
0
    quote_dir(cmd->tmp_pool, dir));
5900
5901
0
  return PR_HANDLED(cmd);
5902
0
}
5903
5904
0
MODRET core_cwd(cmd_rec *cmd) {
5905
0
  char *decoded_path;
5906
0
  CHECK_CMD_MIN_ARGS(cmd, 2);
5907
5908
0
  decoded_path = pr_fs_decode_path2(cmd->tmp_pool, cmd->arg,
5909
0
    FSIO_DECODE_FL_TELL_ERRORS);
5910
0
  if (decoded_path == NULL) {
5911
0
    int xerrno = errno;
5912
5913
0
    pr_log_debug(DEBUG8, "'%s' failed to decode properly: %s", cmd->arg,
5914
0
      strerror(xerrno));
5915
0
    pr_response_add_err(R_550, _("%s: Illegal character sequence in filename"),
5916
0
      cmd->arg);
5917
5918
0
    pr_cmd_set_errno(cmd, xerrno);
5919
0
    errno = xerrno;
5920
0
    return PR_ERROR(cmd);
5921
0
  }
5922
5923
0
  return core_chdir(cmd, decoded_path);
5924
0
}
5925
5926
0
MODRET core_cdup(cmd_rec *cmd) {
5927
0
  CHECK_CMD_ARGS(cmd, 1);
5928
0
  return core_chdir(cmd, "..");
5929
0
}
5930
5931
/* Returns the modification time of a file, as per RFC3659. */
5932
0
MODRET core_mdtm(cmd_rec *cmd) {
5933
0
  char *decoded_path, *path;
5934
0
  struct stat st;
5935
0
  char buf[16];
5936
0
  struct tm *tm;
5937
5938
0
  CHECK_CMD_MIN_ARGS(cmd, 2);
5939
5940
0
  decoded_path = pr_fs_decode_path2(cmd->tmp_pool, cmd->arg,
5941
0
    FSIO_DECODE_FL_TELL_ERRORS);
5942
0
  if (decoded_path == NULL) {
5943
0
    int xerrno = errno;
5944
5945
0
    pr_log_debug(DEBUG8, "'%s' failed to decode properly: %s", cmd->arg,
5946
0
      strerror(xerrno));
5947
0
    pr_response_add_err(R_550, _("%s: Illegal character sequence in filename"),
5948
0
      cmd->arg);
5949
5950
0
    pr_cmd_set_errno(cmd, xerrno);
5951
0
    errno = xerrno;
5952
0
    return PR_ERROR(cmd);
5953
0
  }
5954
5955
0
  path = decoded_path;
5956
5957
0
  pr_fs_clear_cache2(path);
5958
0
  path = dir_realpath(cmd->tmp_pool, decoded_path);
5959
0
  if (!path ||
5960
0
      !dir_check(cmd->tmp_pool, cmd, cmd->group, path, NULL) ||
5961
0
      pr_fsio_stat(path, &st) == -1) {
5962
0
    int xerrno = errno;
5963
5964
0
    pr_response_add_err(R_550, "%s: %s", cmd->arg, strerror(xerrno));
5965
5966
0
    pr_cmd_set_errno(cmd, xerrno);
5967
0
    errno = xerrno;
5968
0
    return PR_ERROR(cmd);
5969
0
  }
5970
5971
0
  if (!S_ISREG(st.st_mode)) {
5972
0
    pr_response_add_err(R_550, _("%s: not a plain file"), cmd->arg);
5973
5974
0
    pr_cmd_set_errno(cmd, EINVAL);
5975
0
    errno = EINVAL;
5976
0
    return PR_ERROR(cmd);
5977
0
  }
5978
5979
0
  memset(buf, '\0', sizeof(buf));
5980
5981
0
  tm = pr_gmtime(cmd->tmp_pool, &st.st_mtime);
5982
0
  if (tm != NULL) {
5983
0
    pr_snprintf(buf, sizeof(buf), "%04d%02d%02d%02d%02d%02d",
5984
0
      tm->tm_year+1900, tm->tm_mon+1, tm->tm_mday, tm->tm_hour,
5985
0
      tm->tm_min, tm->tm_sec);
5986
5987
0
  } else {
5988
0
    pr_snprintf(buf, sizeof(buf), "00000000000000");
5989
0
  }
5990
5991
0
  pr_response_add(R_213, "%s", buf);
5992
0
  return PR_HANDLED(cmd);
5993
0
}
5994
5995
0
MODRET core_size(cmd_rec *cmd) {
5996
0
  char *decoded_path, *path;
5997
0
  struct stat st;
5998
5999
0
  CHECK_CMD_MIN_ARGS(cmd, 2);
6000
6001
  /* The PR_ALLOW_ASCII_MODE_SIZE macro should ONLY be defined at compile time,
6002
   * e.g. using:
6003
   *
6004
   *  $ ./configure CPPFLAGS=-DPR_ALLOW_ASCII_MODE_SIZE ...
6005
   *
6006
   * Define this macro if you want proftpd to handle a SIZE command while in
6007
   * ASCII mode.  Note, however, that ProFTPD will NOT properly calculate
6008
   * CRLF sequences EVEN if this macro is defined: ProFTPD will always return
6009
   * the number of bytes on disk for the requested file, even if the number of
6010
   * bytes transferred when that file is downloaded is different.  Thus this
6011
   * behavior will not comply with RFC 3659, Section 4.  Caveat emptor.
6012
   */
6013
0
#ifndef PR_ALLOW_ASCII_MODE_SIZE
6014
  /* Refuse the command if we're in ASCII mode. */
6015
0
  if (session.sf_flags & SF_ASCII) {
6016
0
    pr_log_debug(DEBUG5, "%s not allowed in ASCII mode", (char *) cmd->argv[0]);
6017
0
    pr_response_add_err(R_550, _("%s not allowed in ASCII mode"),
6018
0
      (char *) cmd->argv[0]);
6019
6020
0
    pr_cmd_set_errno(cmd, EPERM);
6021
0
    errno = EPERM;
6022
0
    return PR_ERROR(cmd);
6023
0
  }
6024
0
#endif /* PR_ALLOW_ASCII_MODE_SIZE */
6025
6026
0
  decoded_path = pr_fs_decode_path2(cmd->tmp_pool, cmd->arg,
6027
0
    FSIO_DECODE_FL_TELL_ERRORS);
6028
0
  if (decoded_path == NULL) {
6029
0
    int xerrno = errno;
6030
6031
0
    pr_log_debug(DEBUG8, "'%s' failed to decode properly: %s", cmd->arg,
6032
0
      strerror(xerrno));
6033
0
    pr_response_add_err(R_550, _("%s: Illegal character sequence in filename"),
6034
0
      cmd->arg);
6035
6036
0
    pr_cmd_set_errno(cmd, xerrno);
6037
0
    errno = xerrno;
6038
0
    return PR_ERROR(cmd);
6039
0
  }
6040
6041
0
  pr_fs_clear_cache2(decoded_path);
6042
0
  path = dir_realpath(cmd->tmp_pool, decoded_path);
6043
0
  if (path != NULL) {
6044
0
    pr_fs_clear_cache2(path);
6045
0
  }
6046
6047
0
  if (path == NULL ||
6048
0
      !dir_check(cmd->tmp_pool, cmd, cmd->group, path, NULL) ||
6049
0
      pr_fsio_stat(path, &st) == -1) {
6050
0
    int xerrno = errno;
6051
6052
0
    pr_response_add_err(R_550, "%s: %s", cmd->arg, strerror(xerrno));
6053
6054
0
    pr_cmd_set_errno(cmd, xerrno);
6055
0
    errno = xerrno;
6056
0
    return PR_ERROR(cmd);
6057
0
  }
6058
6059
0
  if (!S_ISREG(st.st_mode)) {
6060
0
    pr_response_add_err(R_550, _("%s: not a regular file"), cmd->arg);
6061
6062
0
    pr_cmd_set_errno(cmd, EINVAL);
6063
0
    errno = EINVAL;
6064
0
    return PR_ERROR(cmd);
6065
0
  }
6066
6067
0
  pr_response_add(R_213, "%" PR_LU, (pr_off_t) st.st_size);
6068
0
  return PR_HANDLED(cmd);
6069
0
}
6070
6071
0
MODRET core_dele(cmd_rec *cmd) {
6072
0
  int res;
6073
0
  char *decoded_path, *path, *fullpath;
6074
0
  struct stat st;
6075
0
  pr_error_t *err = NULL;
6076
6077
0
  CHECK_CMD_MIN_ARGS(cmd, 2);
6078
6079
0
  decoded_path = pr_fs_decode_path2(cmd->tmp_pool, cmd->arg,
6080
0
    FSIO_DECODE_FL_TELL_ERRORS);
6081
0
  if (decoded_path == NULL) {
6082
0
    int xerrno = errno;
6083
6084
0
    pr_log_debug(DEBUG8, "'%s' failed to decode properly: %s", cmd->arg,
6085
0
      strerror(xerrno));
6086
0
    pr_response_add_err(R_550, _("%s: Illegal character sequence in filename"),
6087
0
      cmd->arg);
6088
6089
0
    pr_cmd_set_errno(cmd, xerrno);
6090
0
    errno = xerrno;
6091
0
    return PR_ERROR(cmd);
6092
0
  }
6093
6094
0
  path = decoded_path;
6095
6096
0
  res = pr_filter_allow_path(CURRENT_CONF, path);
6097
0
  switch (res) {
6098
0
    case 0:
6099
0
      break;
6100
6101
0
    case PR_FILTER_ERR_FAILS_ALLOW_FILTER:
6102
0
      pr_log_pri(PR_LOG_NOTICE, "'%s %s' denied by PathAllowFilter",
6103
0
        (char *) cmd->argv[0], path);
6104
0
      pr_response_add_err(R_550, _("%s: Forbidden filename"), cmd->arg);
6105
6106
0
      pr_cmd_set_errno(cmd, EPERM);
6107
0
      errno = EPERM;
6108
0
      return PR_ERROR(cmd);
6109
6110
0
    case PR_FILTER_ERR_FAILS_DENY_FILTER:
6111
0
      pr_log_pri(PR_LOG_NOTICE, "'%s %s' denied by PathDenyFilter",
6112
0
        (char *) cmd->argv[0], path);
6113
0
      pr_response_add_err(R_550, _("%s: Forbidden filename"), cmd->arg);
6114
6115
0
      pr_cmd_set_errno(cmd, EPERM);
6116
0
      errno = EPERM;
6117
0
      return PR_ERROR(cmd);
6118
0
  }
6119
6120
  /* If told to delete a symlink, don't delete the file it points to!  */
6121
0
  path = dir_canonical_path(cmd->tmp_pool, path);
6122
0
  if (path == NULL) {
6123
0
    int xerrno = ENOENT;
6124
6125
0
    pr_response_add_err(R_550, "%s: %s", cmd->arg, strerror(xerrno));
6126
6127
0
    pr_cmd_set_errno(cmd, xerrno);
6128
0
    errno = xerrno;
6129
0
    return PR_ERROR(cmd);
6130
0
  }
6131
6132
0
  if (!dir_check(cmd->tmp_pool, cmd, cmd->group, path, NULL)) {
6133
0
    int xerrno = errno;
6134
6135
0
    pr_log_debug(DEBUG7, "deleting '%s' denied by <Limit> configuration", path);
6136
0
    pr_response_add_err(R_550, "%s: %s", cmd->arg, strerror(xerrno));
6137
6138
0
    pr_cmd_set_errno(cmd, xerrno);
6139
0
    errno = xerrno;
6140
0
    return PR_ERROR(cmd);
6141
0
  }
6142
6143
  /* Stat the path, before it is deleted, so that the size of the file
6144
   * being deleted can be logged.  Note that unlink() doesn't follow symlinks,
6145
   * so we need to use lstat(), not stat(), lest we log the wrong size.
6146
   */
6147
0
  memset(&st, 0, sizeof(st));
6148
0
  pr_fs_clear_cache2(path);
6149
0
  res = pr_fsio_lstat_with_error(cmd->tmp_pool, path, &st, &err);
6150
0
  if (res < 0) {
6151
0
    int xerrno = errno;
6152
6153
0
    pr_error_set_where(err, &core_module, __FILE__, __LINE__ - 4);
6154
0
    pr_error_set_why(err, pstrcat(cmd->pool, "check file '", path, "'", NULL));
6155
6156
0
    if (err != NULL) {
6157
0
      pr_log_debug(DEBUG3, "%s", pr_error_strerror(err, 0));
6158
0
      pr_error_destroy(err);
6159
0
      err = NULL;
6160
6161
0
    } else {
6162
0
      pr_log_debug(DEBUG3, "unable to lstat '%s': %s", path, strerror(xerrno));
6163
0
    }
6164
6165
0
    pr_response_add_err(R_550, "%s: %s", cmd->arg, strerror(xerrno));
6166
6167
0
    pr_cmd_set_errno(cmd, xerrno);
6168
0
    errno = xerrno;
6169
0
    return PR_ERROR(cmd);
6170
0
  }
6171
6172
0
#if defined(EISDIR)
6173
  /* If the path is a directory, try to return a good error message (e.g.
6174
   * EISDIR).
6175
   */
6176
0
  if (S_ISDIR(st.st_mode)) {
6177
0
    int xerrno = EISDIR;
6178
6179
0
    (void) pr_trace_msg("fileperms", 1, "%s, user '%s' (UID %s, GID %s): "
6180
0
      "error deleting '%s': %s", (char *) cmd->argv[0], session.user,
6181
0
      pr_uid2str(cmd->tmp_pool, session.uid),
6182
0
      pr_gid2str(cmd->tmp_pool, session.gid), path, strerror(xerrno));
6183
6184
0
    pr_log_debug(DEBUG3, "error deleting '%s': %s", path, strerror(xerrno));
6185
0
    pr_response_add_err(R_550, "%s: %s", cmd->arg, strerror(xerrno));
6186
6187
0
    pr_cmd_set_errno(cmd, xerrno);
6188
0
    errno = xerrno;
6189
0
    return PR_ERROR(cmd);
6190
0
  }
6191
0
#endif /* EISDIR */
6192
6193
0
  res = pr_fsio_unlink_with_error(cmd->pool, path, &err);
6194
0
  if (res < 0) {
6195
0
    int xerrno = errno;
6196
6197
0
    pr_error_set_where(err, &core_module, __FILE__, __LINE__ - 4);
6198
0
    pr_error_set_why(err, pstrcat(cmd->pool, "delete file '", path, "'", NULL));
6199
6200
0
    (void) pr_trace_msg("fileperms", 1, "%s, user '%s' (UID %s, GID %s): "
6201
0
      "error deleting '%s': %s", (char *) cmd->argv[0], session.user,
6202
0
      pr_uid2str(cmd->tmp_pool, session.uid),
6203
0
      pr_gid2str(cmd->tmp_pool, session.gid), path, strerror(xerrno));
6204
6205
0
    if (err != NULL) {
6206
0
      pr_log_debug(DEBUG3, "%s", pr_error_strerror(err, 0));
6207
0
      pr_error_destroy(err);
6208
0
      err = NULL;
6209
6210
0
    } else {
6211
0
      pr_log_debug(DEBUG3, "error deleting '%s': %s", path, strerror(xerrno));
6212
0
    }
6213
6214
0
    pr_response_add_err(R_550, "%s: %s", cmd->arg, strerror(xerrno));
6215
6216
0
    pr_cmd_set_errno(cmd, xerrno);
6217
0
    errno = xerrno;
6218
0
    return PR_ERROR(cmd);
6219
0
  }
6220
6221
0
  fullpath = dir_abs_path(cmd->tmp_pool, path, TRUE);
6222
6223
0
  if (session.sf_flags & SF_ANON) {
6224
0
    xferlog_write(0, session.c->remote_name, st.st_size, fullpath,
6225
0
      (session.sf_flags & SF_ASCII ? 'a' : 'b'), 'd', 'a', session.anon_user,
6226
0
      'c', "_");
6227
6228
0
  } else {
6229
0
    xferlog_write(0, session.c->remote_name, st.st_size, fullpath,
6230
0
      (session.sf_flags & SF_ASCII ? 'a' : 'b'), 'd', 'r', session.user, 'c',
6231
0
      "_");
6232
0
  }
6233
6234
0
  pr_response_add(R_250, _("%s command successful"), (char *) cmd->argv[0]);
6235
0
  return PR_HANDLED(cmd);
6236
0
}
6237
6238
static int handle_cross_mount_rename(cmd_rec *cmd, const char *src_path,
6239
0
    const char *dst_path, struct stat *dst_st) {
6240
0
  int res, xerrno;
6241
0
  struct stat src_st;
6242
6243
0
  pr_fs_clear_cache2(src_path);
6244
0
  res = pr_fsio_lstat(src_path, &src_st);
6245
0
  if (res < 0) {
6246
0
    xerrno = errno;
6247
6248
0
    pr_log_debug(DEBUG4, "Unable to stat '%s' for cross filesystem mount "
6249
0
      "point rename: %s", src_path, strerror(xerrno));
6250
6251
0
    pr_response_add_err(R_550, _("%s: %s"), cmd->arg, strerror(xerrno));
6252
0
    pr_cmd_set_errno(cmd, xerrno);
6253
0
    errno = xerrno;
6254
6255
0
    return -1;
6256
0
  }
6257
6258
0
  if (S_ISDIR(src_st.st_mode)) {
6259
0
    xerrno = EPERM;
6260
6261
0
    pr_log_debug(DEBUG4, "Cannot rename directory '%s' across a filesystem "
6262
0
      "mount", src_path);
6263
6264
0
    pr_response_add_err(R_550, _("%s: %s"), cmd->arg, strerror(xerrno));
6265
0
    pr_cmd_set_errno(cmd, xerrno);
6266
0
    errno = xerrno;
6267
6268
0
    return -1;
6269
0
  }
6270
6271
0
  if (!S_ISREG(src_st.st_mode) &&
6272
0
      !S_ISLNK(src_st.st_mode)) {
6273
0
    xerrno = EPERM;
6274
6275
0
    pr_log_debug(DEBUG4, "Cannot rename unsupported file type '%s' across "
6276
0
      "a filesystem mount", src_path);
6277
6278
0
    pr_response_add_err(R_550, _("%s: %s"), cmd->arg, strerror(xerrno));
6279
0
    pr_cmd_set_errno(cmd, xerrno);
6280
0
    errno = xerrno;
6281
6282
0
    return -1;
6283
0
  }
6284
6285
  /* If the destination exists, unlink it first.  Unless it is a directory,
6286
   * in which case we error out now; we do not copy into directories across
6287
   * mounts at the moment.
6288
   */
6289
0
  if (dst_st != NULL) {
6290
0
    if (S_ISDIR(dst_st->st_mode)) {
6291
0
      xerrno = EPERM;
6292
6293
0
      pr_log_debug(DEBUG4, "Cannot rename path '%s' across a filesystem mount "
6294
0
        "point into directory '%s'", src_path, dst_path);
6295
6296
0
      pr_response_add_err(R_550, _("%s: %s"), cmd->arg, strerror(xerrno));
6297
0
      pr_cmd_set_errno(cmd, xerrno);
6298
0
      errno = xerrno;
6299
6300
0
      return -1;
6301
0
    }
6302
6303
0
    res = pr_fsio_unlink(dst_path);
6304
0
    if (res < 0) {
6305
0
      xerrno = errno;
6306
6307
0
      pr_log_debug(DEBUG4, "Unable to delete '%s' for cross filesystem mount "
6308
0
        "point rename: %s", dst_path, strerror(xerrno));
6309
6310
0
      pr_response_add_err(R_550, _("%s: %s"), cmd->arg, strerror(xerrno));
6311
0
      pr_cmd_set_errno(cmd, xerrno);
6312
0
      errno = xerrno;
6313
6314
0
      return -1;
6315
0
    }
6316
6317
0
    dst_st = NULL;
6318
0
  }
6319
6320
  /* At this point, we know that the source path exists, and is a regular file
6321
   * or symlink.  And we know that the destination path does not exist.
6322
   */
6323
6324
0
  if (S_ISREG(src_st.st_mode)) {
6325
0
    res = pr_fs_copy_file(src_path, dst_path);
6326
0
    xerrno = errno;
6327
6328
0
  } else if (S_ISLNK(src_st.st_mode)) {
6329
0
    res = pr_fs_copy_symlink(src_path, dst_path, 0);
6330
0
    xerrno = errno;
6331
0
  }
6332
6333
0
  if (res < 0) {
6334
0
    (void) pr_trace_msg("fileperms", 1, "%s, user '%s' (UID %s, GID %s): "
6335
0
      "error copying '%s' to '%s': %s", (char *) cmd->argv[0], session.user,
6336
0
      pr_uid2str(cmd->tmp_pool, session.uid),
6337
0
      pr_gid2str(cmd->tmp_pool, session.gid), src_path, dst_path,
6338
0
      strerror(xerrno));
6339
6340
0
    pr_response_add_err(R_550, _("Rename %s: %s"), cmd->arg,
6341
0
      strerror(xerrno));
6342
0
    pr_cmd_set_errno(cmd, xerrno);
6343
0
    errno = xerrno;
6344
6345
0
    return -1;
6346
0
  }
6347
6348
  /* Once copied, unlink the original file. */
6349
0
  res = pr_fsio_unlink(src_path);
6350
0
  if (res < 0) {
6351
0
    pr_log_debug(DEBUG0, "error deleting '%s': %s", src_path, strerror(errno));
6352
0
  }
6353
6354
0
  return 0;
6355
0
}
6356
6357
0
MODRET core_rnto(cmd_rec *cmd) {
6358
0
  int res, xerrno, dst_exists = FALSE;
6359
0
  char *decoded_path, *path;
6360
0
  unsigned char *allow_overwrite = NULL;
6361
0
  struct stat st;
6362
0
  pr_error_t *err = NULL;
6363
6364
0
  CHECK_CMD_MIN_ARGS(cmd, 2);
6365
6366
0
  if (session.xfer.path == NULL) {
6367
0
    pr_data_clear_xfer_pool();
6368
6369
0
    pr_response_add_err(R_503, _("Bad sequence of commands"));
6370
6371
0
    pr_cmd_set_errno(cmd, EPERM);
6372
0
    errno = EPERM;
6373
0
    return PR_ERROR(cmd);
6374
0
  }
6375
6376
0
  decoded_path = pr_fs_decode_path2(cmd->tmp_pool, cmd->arg,
6377
0
    FSIO_DECODE_FL_TELL_ERRORS);
6378
0
  if (decoded_path == NULL) {
6379
0
    xerrno = errno;
6380
6381
0
    pr_log_debug(DEBUG8, "'%s' failed to decode properly: %s", cmd->arg,
6382
0
      strerror(xerrno));
6383
0
    pr_response_add_err(R_550, _("%s: Illegal character sequence in filename"),
6384
0
      cmd->arg);
6385
6386
0
    pr_cmd_set_errno(cmd, xerrno);
6387
0
    errno = xerrno;
6388
0
    return PR_ERROR(cmd);
6389
0
  }
6390
6391
0
  path = decoded_path;
6392
6393
0
  res = pr_filter_allow_path(CURRENT_CONF, path);
6394
0
  switch (res) {
6395
0
    case 0:
6396
0
      break;
6397
6398
0
    case PR_FILTER_ERR_FAILS_ALLOW_FILTER:
6399
0
      pr_log_pri(PR_LOG_NOTICE, "'%s %s' denied by PathAllowFilter",
6400
0
        (char *) cmd->argv[0], path);
6401
0
      pr_response_add_err(R_550, _("%s: Forbidden filename"), cmd->arg);
6402
6403
0
      pr_cmd_set_errno(cmd, EPERM);
6404
0
      errno = EPERM;
6405
0
      return PR_ERROR(cmd);
6406
6407
0
    case PR_FILTER_ERR_FAILS_DENY_FILTER:
6408
0
      pr_log_pri(PR_LOG_NOTICE, "'%s %s' denied by PathDenyFilter",
6409
0
        (char *) cmd->argv[0], path);
6410
0
      pr_response_add_err(R_550, _("%s: Forbidden filename"), cmd->arg);
6411
6412
0
      pr_cmd_set_errno(cmd, EPERM);
6413
0
      errno = EPERM;
6414
0
      return PR_ERROR(cmd);
6415
0
  }
6416
6417
0
  path = dir_canonical_path(cmd->tmp_pool, path);
6418
6419
  /* Deny the rename if AllowOverwrites are not allowed, and the destination
6420
   * rename file already exists.
6421
   */
6422
0
  allow_overwrite = get_param_ptr(CURRENT_CONF, "AllowOverwrite", FALSE);
6423
6424
0
  pr_fs_clear_cache2(path);
6425
0
  res = pr_fsio_lstat(path, &st);
6426
0
  if (res == 0) {
6427
0
    dst_exists = TRUE;
6428
6429
0
    if (allow_overwrite == NULL ||
6430
0
        *allow_overwrite == FALSE) {
6431
0
      pr_log_debug(DEBUG6, "AllowOverwrite denied permission for %s", path);
6432
0
      pr_response_add_err(R_550, _("%s: Rename permission denied"), cmd->arg);
6433
6434
0
      pr_cmd_set_errno(cmd, EACCES);
6435
0
      errno = EACCES;
6436
0
      return PR_ERROR(cmd);
6437
0
    }
6438
0
  }
6439
6440
0
  if (path == NULL ||
6441
0
      !dir_check_canon(cmd->tmp_pool, cmd, cmd->group, path, NULL)) {
6442
0
    pr_log_debug(DEBUG8, "%s command denied by <Limit> config",
6443
0
      (char *) cmd->argv[0]);
6444
0
    pr_response_add_err(R_550, _("%s: %s"), cmd->arg, strerror(EPERM));
6445
6446
0
    pr_cmd_set_errno(cmd, EPERM);
6447
0
    errno = EPERM;
6448
0
    return PR_ERROR(cmd);
6449
0
  }
6450
6451
0
  res = pr_fsio_rename_with_error(cmd->pool, session.xfer.path, path, &err);
6452
0
  if (res < 0) {
6453
0
    xerrno = errno;
6454
6455
0
    pr_error_set_where(err, &core_module, __FILE__, __LINE__ - 4);
6456
0
    pr_error_set_why(err, pstrcat(cmd->pool, "rename '", session.xfer.path,
6457
0
      "' to '", path, "'", NULL));
6458
6459
0
    if (xerrno == EISDIR) {
6460
      /* In this case, the client has requested that a directory be renamed
6461
       * across mount points.  The pr_fs_copy_file() function can't handle
6462
       * copying directories; it only knows about files.  (This could be
6463
       * fixed to work later, e.g. using code from the mod_copy module.)
6464
       *
6465
       * For now, error out now with a more informative error message to the
6466
       * client.
6467
       */
6468
6469
0
      (void) pr_trace_msg("fileperms", 1, "%s, user '%s' (UID %s, GID %s): "
6470
0
        "error copying '%s' to '%s': %s (previous error was '%s')",
6471
0
        (char *) cmd->argv[0], session.user,
6472
0
        pr_uid2str(cmd->tmp_pool, session.uid),
6473
0
        pr_gid2str(cmd->tmp_pool, session.gid), session.xfer.path, path,
6474
0
        strerror(xerrno), strerror(EXDEV));
6475
6476
0
      pr_log_debug(DEBUG4,
6477
0
        "Cannot rename directory '%s' across a filesystem mount point",
6478
0
        session.xfer.path);
6479
6480
0
      if (err != NULL) {
6481
0
        pr_error_destroy(err);
6482
0
        err = NULL;
6483
0
      }
6484
6485
      /* Use EPERM, rather than EISDIR, to get slightly more informative
6486
       * error messages.
6487
       */
6488
0
      xerrno = EPERM;
6489
6490
0
      pr_response_add_err(R_550, _("%s: %s"), cmd->arg, strerror(xerrno));
6491
6492
0
      pr_cmd_set_errno(cmd, xerrno);
6493
0
      errno = xerrno;
6494
0
      return PR_ERROR(cmd);
6495
0
    }
6496
6497
0
    if (xerrno != EXDEV) {
6498
0
      (void) pr_trace_msg("fileperms", 1, "%s, user '%s' (UID %s, GID %s): "
6499
0
        "error renaming '%s' to '%s': %s", (char *) cmd->argv[0], session.user,
6500
0
        pr_uid2str(cmd->tmp_pool, session.uid),
6501
0
        pr_gid2str(cmd->tmp_pool, session.gid), session.xfer.path, path,
6502
0
        strerror(xerrno));
6503
6504
0
      if (err != NULL) {
6505
0
        pr_log_debug(DEBUG9, "%s", pr_error_strerror(err, 0));
6506
0
        pr_error_destroy(err);
6507
0
        err = NULL;
6508
0
      }
6509
6510
0
      pr_response_add_err(R_550, _("%s: %s"), cmd->arg, strerror(xerrno));
6511
6512
0
      pr_cmd_set_errno(cmd, xerrno);
6513
0
      errno = xerrno;
6514
0
      return PR_ERROR(cmd);
6515
0
    }
6516
6517
    /* In this case, we'll need to manually copy the file from the source
6518
     * to the destination paths.
6519
     */
6520
0
    if (handle_cross_mount_rename(cmd, session.xfer.path, path,
6521
0
        dst_exists == TRUE ? &st : NULL) < 0) {
6522
0
      return PR_ERROR(cmd);
6523
0
    }
6524
0
  }
6525
6526
  /* Change the xfer path to the name of the destination file, for logging. */
6527
0
  session.xfer.path = pstrdup(session.xfer.p, path);
6528
6529
0
  pr_response_add(R_250, _("Rename successful"));
6530
0
  return PR_HANDLED(cmd);
6531
0
}
6532
6533
0
MODRET core_rnto_cleanup(cmd_rec *cmd) {
6534
0
  pr_data_clear_xfer_pool();
6535
6536
0
  pr_table_remove(session.notes, "mod_core.rnfr-path", NULL);
6537
0
  return PR_DECLINED(cmd);
6538
0
}
6539
6540
0
MODRET core_rnfr(cmd_rec *cmd) {
6541
0
  int res;
6542
0
  const char *abs_path;
6543
0
  char *decoded_path, *path;
6544
6545
0
  CHECK_CMD_MIN_ARGS(cmd, 2);
6546
6547
0
  decoded_path = pr_fs_decode_path2(cmd->tmp_pool, cmd->arg,
6548
0
    FSIO_DECODE_FL_TELL_ERRORS);
6549
0
  if (decoded_path == NULL) {
6550
0
    int xerrno = errno;
6551
6552
0
    pr_log_debug(DEBUG8, "'%s' failed to decode properly: %s", cmd->arg,
6553
0
      strerror(xerrno));
6554
0
    pr_response_add_err(R_550, _("%s: Illegal character sequence in filename"),
6555
0
      cmd->arg);
6556
6557
0
    pr_cmd_set_errno(cmd, xerrno);
6558
0
    errno = xerrno;
6559
0
    return PR_ERROR(cmd);
6560
0
  }
6561
6562
0
  path = decoded_path;
6563
6564
0
  res = pr_filter_allow_path(CURRENT_CONF, path);
6565
0
  switch (res) {
6566
0
    case 0:
6567
0
      break;
6568
6569
0
    case PR_FILTER_ERR_FAILS_ALLOW_FILTER:
6570
0
      pr_log_pri(PR_LOG_NOTICE, "'%s %s' denied by PathAllowFilter",
6571
0
        (char *) cmd->argv[0], path);
6572
0
      pr_response_add_err(R_550, _("%s: Forbidden filename"), cmd->arg);
6573
6574
0
      pr_cmd_set_errno(cmd, EPERM);
6575
0
      errno = EPERM;
6576
0
      return PR_ERROR(cmd);
6577
6578
0
    case PR_FILTER_ERR_FAILS_DENY_FILTER:
6579
0
      pr_log_pri(PR_LOG_NOTICE, "'%s %s' denied by PathDenyFilter",
6580
0
        (char *) cmd->argv[0], path);
6581
0
      pr_response_add_err(R_550, _("%s: Forbidden filename"), cmd->arg);
6582
6583
0
      pr_cmd_set_errno(cmd, EPERM);
6584
0
      errno = EPERM;
6585
0
      return PR_ERROR(cmd);
6586
0
  }
6587
6588
0
  abs_path = dir_abs_path(cmd->tmp_pool, path, FALSE);
6589
6590
  /* Allow renaming a symlink, even a dangling one. */
6591
0
  path = dir_canonical_path(cmd->tmp_pool, path);
6592
0
  if (abs_path == NULL) {
6593
0
    abs_path = path;
6594
0
  }
6595
6596
0
  if (path == NULL ||
6597
0
      !dir_check(cmd->tmp_pool, cmd, cmd->group, path, NULL) ||
6598
0
      !exists2(cmd->tmp_pool, path)) {
6599
0
    int xerrno = errno;
6600
6601
0
    pr_response_add_err(R_550, _("%s: %s"), cmd->arg, strerror(xerrno));
6602
6603
0
    pr_cmd_set_errno(cmd, xerrno);
6604
0
    errno = xerrno;
6605
0
    return PR_ERROR(cmd);
6606
0
  }
6607
6608
  /* We store the path in session.xfer.path */
6609
6610
0
  pr_data_clear_xfer_pool();
6611
0
  session.xfer.p = make_sub_pool(session.pool);
6612
0
  pr_pool_tag(session.xfer.p, "session xfer pool");
6613
6614
0
  session.xfer.path = pstrdup(session.xfer.p, path);
6615
6616
  /* Make sure we store the absolute path for LogFormat %w (Issue #1808). */
6617
0
  abs_path = pr_fsio_realpath(session.xfer.p, abs_path);
6618
6619
0
  pr_table_remove(session.notes, "mod_core.rnfr-path", NULL);
6620
0
  pr_table_add(session.notes, "mod_core.rnfr-path",
6621
0
    pstrdup(session.xfer.p, abs_path), 0);
6622
6623
0
  pr_response_add(R_350,
6624
0
    _("File or directory exists, ready for destination name"));
6625
0
  return PR_HANDLED(cmd);
6626
0
}
6627
6628
0
MODRET core_noop(cmd_rec *cmd) {
6629
0
  if (!dir_check(cmd->tmp_pool, cmd, cmd->group, session.vwd, NULL)) {
6630
0
    int xerrno = EPERM;
6631
6632
0
    pr_log_debug(DEBUG7, "%s command denied by <Limit> configuration",
6633
0
      (char *) cmd->argv[0]);
6634
0
    pr_response_add_err(R_550, "%s", strerror(xerrno));
6635
6636
0
    pr_cmd_set_errno(cmd, xerrno);
6637
0
    errno = xerrno;
6638
0
    return PR_ERROR(cmd);
6639
0
  }
6640
6641
0
  pr_response_add(R_200, _("NOOP command successful"));
6642
0
  return PR_HANDLED(cmd);
6643
0
}
6644
6645
0
static int feat_cmp(const void *a, const void *b) {
6646
0
  return strcasecmp(*((const char **) a), *((const char **) b));
6647
0
}
6648
6649
0
MODRET core_feat(cmd_rec *cmd) {
6650
0
  register unsigned int i;
6651
0
  const char *feat = NULL;
6652
0
  array_header *feats = NULL;
6653
6654
0
  CHECK_CMD_ARGS(cmd, 1);
6655
6656
0
  if (!dir_check(cmd->tmp_pool, cmd, cmd->group, session.vwd, NULL)) {
6657
0
    int xerrno = EPERM;
6658
6659
0
    pr_log_debug(DEBUG3, "%s command denied by <Limit> configuration",
6660
0
      (char *) cmd->argv[0]);
6661
0
    pr_response_add_err(R_550, "%s: %s", (char *) cmd->argv[0],
6662
0
      strerror(xerrno));
6663
6664
0
    pr_cmd_set_errno(cmd, xerrno);
6665
0
    errno = xerrno;
6666
0
    return PR_ERROR(cmd);
6667
0
  }
6668
6669
0
  feat = pr_feat_get();
6670
0
  if (feat == NULL) {
6671
0
    pr_response_add(R_211, _("No features supported"));
6672
0
    return PR_HANDLED(cmd);
6673
0
  }
6674
6675
0
  feats = make_array(cmd->tmp_pool, 0, sizeof(char **));
6676
6677
0
  while (feat != NULL) {
6678
0
    pr_signals_handle();
6679
0
    *((char **) push_array(feats)) = pstrdup(cmd->tmp_pool, feat);
6680
0
    feat = pr_feat_get_next();
6681
0
  }
6682
6683
  /* Sort the features, for a prettier output. */
6684
0
  qsort(feats->elts, feats->nelts, sizeof(char *), feat_cmp);
6685
6686
0
  pr_response_add(R_211, "%s", _("Features:"));
6687
0
  for (i = 0; i < feats->nelts; i++) {
6688
0
    pr_response_add(R_DUP, "%s", ((const char **) feats->elts)[i]);
6689
0
  }
6690
0
  pr_response_add(R_DUP, _("End"));
6691
6692
0
  return PR_HANDLED(cmd);
6693
0
}
6694
6695
0
MODRET core_opts(cmd_rec *cmd) {
6696
0
  register unsigned int i;
6697
0
  int res;
6698
0
  char *arg = "";
6699
0
  cmd_rec *subcmd;
6700
6701
0
  CHECK_CMD_MIN_ARGS(cmd, 2);
6702
6703
  /* Impose a maximum number of allowed arguments, to prevent malicious
6704
   * clients from trying to do Bad Things(tm).  See Bug#3870.
6705
   */
6706
0
  if ((cmd->argc-1) > PR_OPTS_MAX_PARAM_COUNT) {
6707
0
    int xerrno = EINVAL;
6708
6709
0
    pr_log_debug(DEBUG2,
6710
0
      "OPTS command with too many parameters (%d), rejecting", cmd->argc-1);
6711
0
    pr_response_add_err(R_550, "%s: %s", (char *) cmd->argv[0],
6712
0
      strerror(xerrno));
6713
6714
0
    pr_cmd_set_errno(cmd, xerrno);
6715
0
    errno = xerrno;
6716
0
    return PR_ERROR(cmd);
6717
0
  }
6718
6719
0
  subcmd = pr_cmd_alloc(cmd->tmp_pool, cmd->argc-1, NULL);
6720
0
  subcmd->argv[0] = pstrcat(cmd->tmp_pool, "OPTS_", cmd->argv[1], NULL);
6721
0
  subcmd->group = cmd->group;
6722
6723
0
  if (!dir_check(cmd->tmp_pool, subcmd, subcmd->group, session.vwd, NULL)) {
6724
0
    int xerrno = EACCES;
6725
6726
0
    pr_log_debug(DEBUG7, "OPTS %s denied by <Limit> configuration",
6727
0
      (char *) cmd->argv[1]);
6728
0
    pr_response_add_err(R_550, "%s %s: %s", (char *) cmd->argv[0],
6729
0
      (char *) cmd->argv[1], strerror(xerrno));
6730
6731
0
    pr_cmd_set_errno(cmd, xerrno);
6732
0
    errno = xerrno;
6733
0
    return PR_ERROR(cmd);
6734
0
  }
6735
6736
0
  for (i = 2; i < cmd->argc; i++) {
6737
0
    subcmd->argv[i-1] = cmd->argv[i];
6738
6739
0
    arg = pstrcat(cmd->tmp_pool, arg, *arg ? " " : "", cmd->argv[i], NULL);
6740
0
  }
6741
6742
0
  subcmd->arg = arg;
6743
6744
0
  res = pr_cmd_dispatch(subcmd);
6745
0
  if (res < 0) {
6746
0
    return PR_ERROR(cmd);
6747
0
  }
6748
6749
0
  return PR_HANDLED(cmd);
6750
0
}
6751
6752
0
MODRET core_post_pass(cmd_rec *cmd) {
6753
0
  config_rec *c;
6754
6755
0
  c = find_config(TOPLEVEL_CONF, CONF_PARAM, "TimeoutIdle", FALSE);
6756
0
  if (c != NULL) {
6757
0
    int prev_timeout, timeout;
6758
6759
0
    prev_timeout = pr_data_get_timeout(PR_DATA_TIMEOUT_IDLE);
6760
0
    timeout = *((int *) c->argv[0]);
6761
6762
0
    if (timeout != prev_timeout) {
6763
0
      pr_data_set_timeout(PR_DATA_TIMEOUT_IDLE, timeout);
6764
6765
      /* Remove the old timer, and add a new one with the changed
6766
       * timeout value.
6767
       */
6768
0
      pr_timer_remove(PR_TIMER_IDLE, &core_module);
6769
6770
0
      if (timeout > 0) {
6771
0
        pr_timer_add(timeout, PR_TIMER_IDLE, &core_module, core_idle_timeout_cb,
6772
0
          "TimeoutIdle");
6773
0
      }
6774
0
    }
6775
0
  }
6776
6777
0
#if defined(PR_USE_TRACE)
6778
  /* Handle any user/group-specific Trace settings. */
6779
0
  c = find_config(main_server->conf, CONF_PARAM, "Trace", FALSE);
6780
0
  if (c != NULL) {
6781
0
    register unsigned int i;
6782
6783
0
    for (i = 0; i < c->argc; i++) {
6784
0
      char *channel, *ptr;
6785
0
      int min_level, max_level, res;
6786
6787
0
      pr_signals_handle();
6788
6789
0
      channel = c->argv[i];
6790
0
      ptr = strchr(channel, ':');
6791
0
      if (ptr == NULL) {
6792
0
        pr_log_debug(DEBUG6, "skipping badly formatted '%s' setting",
6793
0
          channel);
6794
0
        continue;
6795
0
      }
6796
6797
0
      *ptr = '\0';
6798
6799
0
      res = pr_trace_parse_levels(ptr + 1, &min_level, &max_level);
6800
0
      if (res == 0) {
6801
0
        res = pr_trace_set_levels(channel, min_level, max_level);
6802
0
        *ptr = ':';
6803
6804
0
        if (res < 0) {
6805
0
          pr_log_debug(DEBUG6, "%s: error setting levels %d-%d for "
6806
0
            "channel '%s': %s", c->name, min_level, max_level, channel,
6807
0
            strerror(errno));
6808
0
        }
6809
6810
0
      } else {
6811
0
        pr_log_debug(DEBUG6, "%s: error parsing level '%s' for channel '%s': "
6812
0
          "%s", c->name, ptr + 1, channel, strerror(errno));
6813
0
      }
6814
0
    }
6815
0
  }
6816
6817
  /* Handle any user/group-specific TraceOptions settings. */
6818
0
  c = find_config(main_server->conf, CONF_PARAM, "TraceOptions", FALSE);
6819
0
  if (c != NULL) {
6820
0
    unsigned long trace_opts;
6821
6822
0
    trace_opts = *((unsigned long *) c->argv[0]);
6823
0
    if (pr_trace_set_options(trace_opts) < 0) {
6824
0
      pr_log_debug(DEBUG6, "%s: error setting TraceOptions (%lu): %s",
6825
0
        c->name, trace_opts, strerror(errno));
6826
0
    }
6827
0
  }
6828
0
#endif /* PR_USE_TRACE */
6829
6830
  /* If "SocketOptions keepalive off" is in effect, disable TCP keepalives
6831
   * for the control connection as well (Bug#4340).
6832
   */
6833
0
  if (main_server->tcp_keepalive->keepalive_enabled == FALSE) {
6834
0
    int keepalive = 0;
6835
6836
0
    pr_trace_msg("inet", 17, "disabling SO_KEEPALIVE on socket fd %d",
6837
0
      session.c->wfd);
6838
0
    if (setsockopt(session.c->wfd, SOL_SOCKET, SO_KEEPALIVE, (void *)
6839
0
        &keepalive, sizeof(int)) < 0) {
6840
0
      pr_log_pri(PR_LOG_NOTICE,
6841
0
        "error setting SO_KEEPALIVE on socket fd %d: %s", session.c->wfd,
6842
0
        strerror(errno));
6843
6844
0
    } else {
6845
0
      pr_trace_msg("inet", 15, "disabled SO_KEEPALIVE on socket fd %d",
6846
0
        session.c->wfd);
6847
0
    }
6848
0
  }
6849
6850
  /* Look for a configured MaxCommandRate. */
6851
0
  c = find_config(main_server->conf, CONF_PARAM, "MaxCommandRate", FALSE);
6852
0
  if (c) {
6853
0
    core_cmd_count = 0UL;
6854
0
    core_max_cmds = *((unsigned long *) c->argv[0]);
6855
0
    core_max_cmd_interval = *((unsigned int *) c->argv[1]);
6856
0
    core_max_cmd_ts = 0;
6857
0
  }
6858
6859
  /* Configure the statcache to start caching for the authenticated session. */
6860
0
  pr_fs_statcache_reset();
6861
0
  c = find_config(main_server->conf, CONF_PARAM, "FSCachePolicy", FALSE);
6862
0
  if (c != NULL) {
6863
0
    int engine;
6864
0
    unsigned int size, max_age;
6865
6866
0
    engine = *((int *) c->argv[0]);
6867
0
    size = *((unsigned int *) c->argv[1]);
6868
0
    max_age = *((unsigned int *) c->argv[2]);
6869
6870
0
    if (engine == TRUE) {
6871
0
      pr_fs_statcache_set_policy(size, max_age, 0);
6872
6873
0
    } else {
6874
0
      pr_fs_statcache_set_policy(0, 0, 0);
6875
0
    }
6876
6877
0
  } else {
6878
    /* Set the default statcache policy. */
6879
0
    pr_fs_statcache_set_policy(0, 0, 0);
6880
0
  }
6881
6882
  /* Register an exit handler here, for clearing the statcache. */
6883
0
  pr_event_register(&core_module, "core.exit", core_exit_ev, NULL);
6884
6885
  /* Note: we MUST return HANDLED here, not DECLINED, to indicate that at
6886
   * least one POST_CMD handler of the PASS command succeeded.  Since
6887
   * mod_core is always the last module to which commands are dispatched,
6888
   * we can rest assured that we are not causing problems for any other
6889
   * PASS POST_CMD handlers by returning HANDLED here.
6890
   */
6891
0
  return PR_HANDLED(cmd);
6892
0
}
6893
6894
/* Configuration directive handlers
6895
 */
6896
6897
0
MODRET set_deferwelcome(cmd_rec *cmd) {
6898
0
  int defer_welcome = -1;
6899
0
  config_rec *c = NULL;
6900
6901
0
  CHECK_ARGS(cmd, 1);
6902
0
  CHECK_CONF(cmd, CONF_ROOT|CONF_VIRTUAL|CONF_GLOBAL);
6903
6904
0
  defer_welcome = get_boolean(cmd, 1);
6905
0
  if (defer_welcome == -1) {
6906
0
    CONF_ERROR(cmd, "expected Boolean parameter");
6907
0
  }
6908
6909
0
  c = add_config_param(cmd->argv[0], 1, NULL);
6910
0
  c->argv[0] = pcalloc(c->pool, sizeof(unsigned char));
6911
0
  *((unsigned char *) c->argv[0]) = defer_welcome;
6912
6913
0
  return PR_HANDLED(cmd);
6914
0
}
6915
6916
/* Variable handlers
6917
 */
6918
6919
0
static const char *core_get_sess_bytes_str(void *data, size_t datasz) {
6920
0
  char buf[256];
6921
0
  off_t bytes = *((off_t *) data);
6922
6923
0
  memset(buf, '\0', sizeof(buf));
6924
0
  pr_snprintf(buf, sizeof(buf)-1, "%" PR_LU, (pr_off_t) bytes);
6925
6926
0
  return pstrdup(session.pool, buf);
6927
0
}
6928
6929
0
static const char *core_get_sess_files_str(void *data, size_t datasz) {
6930
0
  char buf[256];
6931
0
  unsigned int files = *((unsigned int *) data);
6932
6933
0
  memset(buf, '\0', sizeof(buf));
6934
0
  pr_snprintf(buf, sizeof(buf)-1, "%u", files);
6935
6936
0
  return pstrdup(session.pool, buf);
6937
0
}
6938
6939
0
static const char *core_get_xfer_bytes_str(void *data, size_t datasz) {
6940
0
  char buf[256];
6941
0
  off_t bytes = *((off_t *) data);
6942
6943
0
  memset(buf, '\0', sizeof(buf));
6944
0
  pr_snprintf(buf, sizeof(buf)-1, "%" PR_LU, (pr_off_t) bytes);
6945
6946
0
  return pstrdup(session.pool, buf);
6947
0
}
6948
6949
/* Event handlers
6950
 */
6951
6952
0
static void core_chroot_ev(const void *event_data, void *user_data) {
6953
0
  config_rec *c;
6954
6955
  /* Look for any configured DisplayChdir directives that use absolute
6956
   * paths, and open filehandles on them prior to the chroot (Issue #1688).
6957
   */
6958
6959
0
  c = find_config(main_server->conf, CONF_PARAM, "DisplayChdir", FALSE);
6960
0
  while (c != NULL) {
6961
0
    const char *path;
6962
6963
0
    pr_signals_handle();
6964
6965
0
    path = c->argv[0];
6966
0
    if (path[0] == '/') {
6967
0
      pr_fh_t *fh;
6968
6969
0
      fh = pr_fsio_open(path, O_RDONLY);
6970
0
      if (fh == NULL) {
6971
0
        pr_log_debug(DEBUG6, "unable to open DisplayChdir file '%s': %s",
6972
0
          path, strerror(errno));
6973
6974
0
      } else {
6975
0
        c->argv[2] = fh;
6976
0
      }
6977
0
    }
6978
6979
0
    c = find_config_next(c, c->next, CONF_PARAM, "DisplayChdir", FALSE);
6980
0
  }
6981
0
}
6982
6983
0
static void core_connected_ev(const void *event_data, void *user_data) {
6984
0
  session_set_connected();
6985
0
}
6986
6987
0
static void core_exit_ev(const void *event_data, void *user_data) {
6988
0
  pr_fs_statcache_free();
6989
0
}
6990
6991
0
static void core_postparse_ev(const void *event_data, void *user_data) {
6992
0
  server_rec *s;
6993
0
  cmd_rec *cmd;
6994
0
  int res;
6995
0
  pool *tmp_pool;
6996
6997
  /* If the EPRT and/or EPSV commands have been denied by configuration, e.g.:
6998
   *
6999
   *  <Limit EPRT EPSV>
7000
   *    DenyAll
7001
   *  </Limit>
7002
   *
7003
   * then we will also exclude them from the FEAT response.  Some badly
7004
   * behaved clients will try to use EPSV (which cannot honor the
7005
   * MasqueradeAddress directive, needed in NAT environments), and if that
7006
   * fails, they fall back to using PORT (and not PASV as expected).
7007
   *
7008
   * Thus for such tricky situations, we check for <Limit> configurations for
7009
   * this commands; if limited, we omit them from FEAT.  This is why we need
7010
   * to wait until the configuration has been parsed, to do this check.
7011
   * See Issue#1383.
7012
   */
7013
7014
0
  tmp_pool = make_sub_pool(permanent_pool);
7015
7016
0
  cmd = pr_cmd_alloc(tmp_pool, 1, pstrdup(tmp_pool, C_EPRT), NULL);
7017
0
  res = dir_check_limits(cmd, NULL, C_EPRT, FALSE);
7018
0
  if (res == 1) {
7019
0
    pr_feat_add(C_EPRT);
7020
0
  }
7021
7022
0
  destroy_pool(cmd->pool);
7023
0
  cmd = pr_cmd_alloc(tmp_pool, 1, pstrdup(tmp_pool, C_EPSV), NULL);
7024
0
  res = dir_check_limits(cmd, NULL, C_EPSV, FALSE);
7025
0
  if (res == 1) {
7026
0
    pr_feat_add(C_EPSV);
7027
0
  }
7028
7029
0
  destroy_pool(cmd->pool);
7030
0
  destroy_pool(tmp_pool);
7031
7032
  /* Since the Port directive is allowed in <Global> sections (see Issue #1418),
7033
   * we need to check here for the Port to use, on a per-vhost basis.
7034
   */
7035
0
  for (s = (server_rec *) server_list->xas_list; s; s = s->next) {
7036
0
    int *server_port;
7037
7038
0
    pr_signals_handle();
7039
7040
0
    server_port = get_param_ptr(s->conf, "Port", FALSE);
7041
0
    if (server_port != NULL) {
7042
0
      s->ServerPort = *server_port;
7043
0
    }
7044
0
  }
7045
0
}
7046
7047
0
static void core_restart_ev(const void *event_data, void *user_data) {
7048
0
  pr_fs_statcache_reset();
7049
0
  pr_scoreboard_scrub();
7050
7051
0
#if defined(PR_USE_TRACE)
7052
0
  if (trace_log != NULL) {
7053
0
    (void) pr_trace_set_levels(PR_TRACE_DEFAULT_CHANNEL, -1, -1);
7054
0
    pr_trace_set_file(NULL);
7055
0
    trace_log = NULL;
7056
0
  }
7057
0
#endif /* PR_USE_TRACE */
7058
0
}
7059
7060
0
static void core_startup_ev(const void *event_data, void *user_data) {
7061
7062
  /* Add a scoreboard-scrubbing timer.
7063
   *
7064
   * Note that we do this only for standalone proftpd daemons, not for
7065
   * inetd-run daemons.  There is no "master"/"daemon" process for
7066
   * inetd-run proftpd processes, which means that _all_ processes scrub
7067
   * the scoreboard (which greatly increases lock contention, particularly
7068
   * under high numbers of simultaneous connections), or that _no_
7069
   * processes scrub the scoreboard (which increases the chance of stale/bad
7070
   * scoreboard data).
7071
   */
7072
0
  if (ServerType == SERVER_STANDALONE) {
7073
0
    int scrub_scoreboard = TRUE;
7074
0
    int scrub_interval = PR_TUNABLE_SCOREBOARD_SCRUB_TIMER;
7075
0
    config_rec *c;
7076
7077
0
    c = find_config(main_server->conf, CONF_PARAM, "ScoreboardScrub", FALSE);
7078
0
    if (c) {
7079
0
      scrub_scoreboard = *((int *) c->argv[0]);
7080
7081
0
      if (c->argc == 2) {
7082
0
        scrub_interval = *((int *) c->argv[1]);
7083
0
      }
7084
0
    }
7085
7086
0
    if (scrub_scoreboard) {
7087
0
      core_scrub_timer_id = pr_timer_add(scrub_interval, -1,
7088
0
        &core_module, core_scrub_scoreboard_cb, "scoreboard scrubbing");
7089
0
    }
7090
0
  }
7091
0
}
7092
7093
/* Initialization/finalization routines
7094
 */
7095
7096
0
static int core_init(void) {
7097
  /* Set the default (i.e. FTP) command handler. */
7098
0
  pr_cmd_set_handler(NULL);
7099
7100
  /* Add the commands handled by this module to the HELP list. */
7101
0
  pr_help_add(C_CWD,  _("<sp> pathname"), TRUE);
7102
0
  pr_help_add(C_XCWD, _("<sp> pathname"), TRUE);
7103
0
  pr_help_add(C_CDUP, _("(up one directory)"), TRUE);
7104
0
  pr_help_add(C_XCUP, _("(up one directory)"), TRUE);
7105
0
  pr_help_add(C_SMNT, _("is not implemented"), FALSE);
7106
0
  pr_help_add(C_QUIT, _("(close control connection)"), TRUE);
7107
0
  pr_help_add(C_PORT, _("<sp> h1,h2,h3,h4,p1,p2"), TRUE);
7108
0
  pr_help_add(C_PASV, _("(returns address/port)"), TRUE);
7109
0
  pr_help_add(C_EPRT, _("<sp> |proto|addr|port|"), TRUE);
7110
0
  pr_help_add(C_EPSV, _("(returns port |||port|)"), TRUE);
7111
0
  pr_help_add(C_ALLO, _("<sp> size"), TRUE);
7112
0
  pr_help_add(C_RNFR, _("<sp> pathname"), TRUE);
7113
0
  pr_help_add(C_RNTO, _("<sp> pathname"), TRUE);
7114
0
  pr_help_add(C_DELE, _("<sp> pathname"), TRUE);
7115
0
  pr_help_add(C_MDTM, _("<sp> pathname"), TRUE);
7116
0
  pr_help_add(C_RMD, _("<sp> pathname"), TRUE);
7117
0
  pr_help_add(C_XRMD, _("<sp> pathname"), TRUE);
7118
0
  pr_help_add(C_MKD, _("<sp> pathname"), TRUE);
7119
0
  pr_help_add(C_XMKD, _("<sp> pathname"), TRUE);
7120
0
  pr_help_add(C_PWD, _("(returns current working directory)"), TRUE);
7121
0
  pr_help_add(C_XPWD, _("(returns current working directory)"), TRUE);
7122
0
  pr_help_add(C_SIZE, _("<sp> pathname"), TRUE);
7123
0
  pr_help_add(C_SYST, _("(returns system type)"), TRUE);
7124
0
  pr_help_add(C_HELP, _("[<sp> command]"), TRUE);
7125
0
  pr_help_add(C_NOOP, _("(no operation)"), TRUE);
7126
0
  pr_help_add(C_FEAT, _("(returns feature list)"), TRUE);
7127
0
  pr_help_add(C_OPTS, _("<sp> command [<sp> options]"), TRUE);
7128
0
  pr_help_add(C_HOST, _("<cp> hostname"), TRUE);
7129
0
  pr_help_add(C_CLNT, _("<cp> client-info"), TRUE);
7130
0
  pr_help_add(C_AUTH, _("<sp> base64-data"), FALSE);
7131
0
  pr_help_add(C_CCC, _("(clears protection level)"), FALSE);
7132
0
  pr_help_add(C_CONF, _("<sp> base64-data"), FALSE);
7133
0
  pr_help_add(C_ENC, _("<sp> base64-data"), FALSE);
7134
0
  pr_help_add(C_MIC, _("<sp> base64-data"), FALSE);
7135
0
  pr_help_add(C_PBSZ, _("<sp> protection buffer size"), FALSE);
7136
0
  pr_help_add(C_PROT, _("<sp> protection code"), FALSE);
7137
7138
  /* Add the additional features implemented by this module into the
7139
   * list, to be displayed in response to a FEAT command.
7140
   */
7141
0
  pr_feat_add(C_CLNT);
7142
0
  pr_feat_add(C_CSID);
7143
0
  pr_feat_add(C_MDTM);
7144
0
  pr_feat_add("REST STREAM");
7145
0
  pr_feat_add(C_SIZE);
7146
0
  pr_feat_add(C_HOST);
7147
7148
0
  pr_event_register(&core_module, "core.connected", core_connected_ev, NULL);
7149
0
  pr_event_register(&core_module, "core.postparse", core_postparse_ev, NULL);
7150
0
  pr_event_register(&core_module, "core.restart", core_restart_ev, NULL);
7151
0
  pr_event_register(&core_module, "core.startup", core_startup_ev, NULL);
7152
7153
0
  return 0;
7154
0
}
7155
7156
static const char *auth_syms[] = {
7157
  "setpwent", "endpwent", "setgrent", "endgrent", "getpwent", "getgrent",
7158
  "getpwnam", "getgrnam", "getpwuid", "getgrgid", "auth", "check",
7159
  "uid2name", "gid2name", "name2uid", "name2gid", "getgroups", NULL
7160
};
7161
7162
0
static void reset_server_auth_order(void) {
7163
0
  config_rec *c = NULL;
7164
7165
0
  c = find_config(session.prev_server->conf, CONF_PARAM, "AuthOrder", FALSE);
7166
0
  if (c != NULL) {
7167
0
    register unsigned int i;
7168
0
    unsigned int module_pri = 0;
7169
0
    module *m;
7170
7171
    /* There was an AuthOrder applying to the previous server_rec, which
7172
     * means we need to reset the default AuthOrder symbols.
7173
     */
7174
7175
    /* Delete all auth syms. */
7176
0
    for (i = 0; auth_syms[i] != NULL; i++) {
7177
0
      pr_stash_remove_symbol(PR_SYM_AUTH, auth_syms[i], NULL);
7178
0
    }
7179
7180
    /* Reload all modules' auth syms. Be sure to reset the module
7181
     * priority while doing so.
7182
     */
7183
0
    for (m = loaded_modules; m; m = m->next) {
7184
0
      if (pr_module_load_authtab(m) < 0) {
7185
0
        pr_log_debug(DEBUG0,
7186
0
          "error reloading auth symbols for module 'mod_%s.c': %s", m->name,
7187
0
          strerror(errno));
7188
0
      }
7189
7190
0
      m->priority = module_pri++;
7191
0
    }
7192
0
  }
7193
0
}
7194
7195
0
static void set_server_auth_order(void) {
7196
0
  config_rec *c = NULL;
7197
7198
0
  c = find_config(main_server->conf, CONF_PARAM, "AuthOrder", FALSE);
7199
0
  if (c != NULL) {
7200
0
    array_header *module_list = (array_header *) c->argv[0];
7201
0
    unsigned int modulec = 0;
7202
0
    char **modulev = NULL;
7203
0
    register unsigned int i = 0;
7204
7205
0
    pr_log_debug(DEBUG3, "AuthOrder in effect, resetting auth module order");
7206
7207
0
    modulec = module_list->nelts;
7208
0
    modulev = (char **) module_list->elts;
7209
7210
    /* First, delete all auth symbols. */
7211
0
    for (i = 0; auth_syms[i] != NULL; i++) {
7212
0
      pr_stash_remove_symbol(PR_SYM_AUTH, auth_syms[i], NULL);
7213
0
    }
7214
7215
    /* Now, cycle through the list of configured modules, re-adding their
7216
     * auth symbols, in the order in which they appear.
7217
     */
7218
7219
0
    for (i = 0; i < modulec; i++) {
7220
0
      module *m;
7221
0
      int required = FALSE;
7222
7223
      /* Check for the trailing '*', indicating a required auth module. */
7224
0
      if (modulev[i][strlen(modulev[i])-1] == '*') {
7225
0
        required = TRUE;
7226
0
        modulev[i][strlen(modulev[i])-1] = '\0';
7227
0
      }
7228
7229
0
      m = pr_module_get(modulev[i]);
7230
7231
0
      if (m) {
7232
0
        if (m->authtable) {
7233
0
          authtable *authtab;
7234
7235
          /* Twiddle the module's priority field before insertion into the
7236
           * symbol table, as the insertion operation does so based on that
7237
           * priority.  This has no effect other than during symbol
7238
           * insertion.
7239
           */
7240
0
          m->priority = modulec - i;
7241
7242
0
          for (authtab = m->authtable; authtab->name; authtab++) {
7243
0
            authtab->m = m;
7244
7245
0
            if (required) {
7246
0
              authtab->auth_flags |= PR_AUTH_FL_REQUIRED;
7247
0
            }
7248
7249
0
            pr_stash_add_symbol(PR_SYM_AUTH, authtab);
7250
0
          }
7251
7252
0
        } else {
7253
0
          pr_log_debug(DEBUG0, "AuthOrder: warning: module '%s' is not a valid "
7254
0
            "auth module (no auth handlers), authentication may fail",
7255
0
            modulev[i]);
7256
0
        }
7257
7258
0
      } else {
7259
0
        pr_log_debug(DEBUG0, "AuthOrder: warning: module '%s' not loaded",
7260
0
          modulev[i]);
7261
0
      }
7262
0
    }
7263
7264
    /* NOTE: the master conf/cmd/auth tables/arrays should ideally be
7265
     * rebuilt after this symbol shuffling, but it's not necessary at this
7266
     * point.
7267
     */
7268
0
  }
7269
0
}
7270
7271
0
static int core_sess_init(void) {
7272
0
  int timeout_idle;
7273
0
  char *displayquit = NULL;
7274
0
  config_rec *c = NULL;
7275
0
  unsigned int *debug_level = NULL;
7276
0
  unsigned long fs_opts = 0UL;
7277
7278
0
  init_auth();
7279
7280
  /* Enable any TCP keepalive options on the control connection (Issue #1402).
7281
   * Note that ctrl conns do not have listening fds, but this function uses
7282
   * that fd (due to compatibility with data conns), so we temporarily assign
7283
   * that struct member.
7284
   */
7285
0
  session.c->listen_fd = session.c->wfd;
7286
0
  if (pr_inet_set_proto_keepalive(session.pool, session.c,
7287
0
      main_server->tcp_keepalive) < 0) {
7288
0
    pr_log_debug(DEBUG9, "error setting ctrl conn TCP keepalive: %s",
7289
0
      strerror(errno));
7290
0
  }
7291
0
  session.c->listen_fd = -1;
7292
7293
  /* Start the idle timer. */
7294
7295
0
  c = find_config(main_server->conf, CONF_PARAM, "TimeoutIdle", FALSE);
7296
0
  if (c != NULL) {
7297
0
    int timeout = *((int *) c->argv[0]);
7298
0
    pr_data_set_timeout(PR_DATA_TIMEOUT_IDLE, timeout);
7299
0
  }
7300
7301
0
  timeout_idle = pr_data_get_timeout(PR_DATA_TIMEOUT_IDLE);
7302
0
  if (timeout_idle) {
7303
0
    pr_timer_add(timeout_idle, PR_TIMER_IDLE, &core_module,
7304
0
      core_idle_timeout_cb, "TimeoutIdle");
7305
0
  }
7306
7307
  /* Check for a server-specific TimeoutLinger */
7308
0
  c = find_config(main_server->conf, CONF_PARAM, "TimeoutLinger", FALSE);
7309
0
  if (c != NULL) {
7310
0
    long timeout;
7311
7312
0
    timeout = (long) *((int *) c->argv[0]);
7313
0
    pr_data_set_linger(timeout);
7314
0
  }
7315
7316
  /* Check for a configured DebugLevel. */
7317
0
  debug_level = get_param_ptr(main_server->conf, "DebugLevel", FALSE);
7318
0
  if (debug_level != NULL) {
7319
0
    pr_log_setdebuglevel(*debug_level);
7320
0
  }
7321
7322
0
  c = find_config(main_server->conf, CONF_PARAM, "FSOptions", FALSE);
7323
0
  while (c != NULL) {
7324
0
    unsigned long opts = 0;
7325
7326
0
    pr_signals_handle();
7327
7328
0
    opts = *((unsigned long *) c->argv[0]);
7329
0
    fs_opts |= opts;
7330
7331
0
    c = find_config_next(c, c->next, CONF_PARAM, "FSOptions", FALSE);
7332
0
  }
7333
7334
0
  (void) pr_fsio_set_options(fs_opts);
7335
7336
  /* Check for any server-specific RegexOptions */
7337
0
  c = find_config(main_server->conf, CONF_PARAM, "RegexOptions", FALSE);
7338
0
  if (c != NULL) {
7339
0
    unsigned long match_limit, match_limit_recursion;
7340
0
    const char *engine = NULL;
7341
7342
0
    match_limit = *((unsigned long *) c->argv[0]);
7343
0
    match_limit_recursion = *((unsigned long *) c->argv[1]);
7344
0
    engine = c->argv[2];
7345
7346
0
    pr_trace_msg("regexp", 4,
7347
0
      "using regex options: engine = %s, match limit = %lu, "
7348
0
      "match limit recursion = %lu", engine ? engine : "(default)",
7349
0
      match_limit, match_limit_recursion);
7350
7351
0
    pr_regexp_set_limits(match_limit, match_limit_recursion);
7352
0
    pr_regexp_set_engine(engine);
7353
0
  }
7354
7355
  /* Check for configured SetEnvs. */
7356
0
  c = find_config(main_server->conf, CONF_PARAM, "SetEnv", FALSE);
7357
0
  while (c != NULL) {
7358
0
    pr_signals_handle();
7359
7360
0
    if (pr_env_set(session.pool, c->argv[0], c->argv[1]) < 0) {
7361
0
      pr_log_debug(DEBUG1, "unable to set environment variable '%s': %s",
7362
0
        (char *) c->argv[0], strerror(errno));
7363
7364
0
    } else {
7365
0
      core_handle_locale_env(c->argv[0]);
7366
0
    }
7367
7368
0
    c = find_config_next(c, c->next, CONF_PARAM, "SetEnv", FALSE);
7369
0
  }
7370
7371
  /* Check for configured UnsetEnvs. */
7372
0
  c = find_config(main_server->conf, CONF_PARAM, "UnsetEnv", FALSE);
7373
0
  while (c != NULL) {
7374
0
    pr_signals_handle();
7375
7376
0
    if (pr_env_unset(session.pool, c->argv[0]) < 0) {
7377
0
      pr_log_debug(DEBUG1, "unable to unset environment variable '%s': %s",
7378
0
        (char *) c->argv[0], strerror(errno));
7379
7380
0
    } else {
7381
0
      core_handle_locale_env(c->argv[0]);
7382
0
    }
7383
7384
0
    c = find_config_next(c, c->next, CONF_PARAM, "UnsetEnv", FALSE);
7385
0
  }
7386
7387
0
  set_server_auth_order();
7388
7389
0
#if defined(PR_USE_TRACE)
7390
  /* Handle any session-specific Trace settings. */
7391
0
  c = find_config(main_server->conf, CONF_PARAM, "Trace", FALSE);
7392
0
  if (c != NULL) {
7393
0
    register unsigned int i;
7394
7395
0
    for (i = 0; i < c->argc; i++) {
7396
0
      char *channel, *ptr;
7397
0
      int min_level, max_level, res;
7398
7399
0
      pr_signals_handle();
7400
7401
0
      channel = c->argv[i];
7402
7403
0
      ptr = strchr(channel, ':');
7404
0
      if (ptr == NULL) {
7405
0
        pr_log_debug(DEBUG6, "skipping badly formatted '%s' setting",
7406
0
          channel);
7407
0
        continue;
7408
0
      }
7409
7410
0
      *ptr = '\0';
7411
7412
0
      res = pr_trace_parse_levels(ptr + 1, &min_level, &max_level);
7413
0
      if (res == 0) {
7414
0
        res = pr_trace_set_levels(channel, min_level, max_level);
7415
0
        *ptr = ':';
7416
7417
0
        if (res < 0) {
7418
0
          pr_log_debug(DEBUG6, "%s: error setting levels %d-%d for "
7419
0
            "channel '%s': %s", c->name, min_level, max_level, channel,
7420
0
            strerror(errno));
7421
0
        }
7422
7423
0
      } else {
7424
0
        pr_log_debug(DEBUG6, "%s: error parsing level '%s' for channel '%s': "
7425
0
          "%s", c->name, ptr + 1, channel, strerror(errno));
7426
0
      }
7427
0
    }
7428
0
  }
7429
7430
  /* Handle any session-specific TraceOptions settings. */
7431
0
  c = find_config(main_server->conf, CONF_PARAM, "TraceOptions", FALSE);
7432
0
  if (c != NULL) {
7433
0
    unsigned long trace_opts;
7434
7435
0
    trace_opts = *((unsigned long *) c->argv[0]);
7436
0
    if (pr_trace_set_options(trace_opts) < 0) {
7437
0
      pr_log_debug(DEBUG6, "%s: error setting TraceOptions (%lu): %s",
7438
0
        c->name, trace_opts, strerror(errno));
7439
0
    }
7440
0
  }
7441
0
#endif /* PR_USE_TRACE */
7442
7443
0
  if (ServerType == SERVER_STANDALONE) {
7444
0
    pr_timer_remove(core_scrub_timer_id, &core_module);
7445
7446
0
  } else if (ServerType == SERVER_INETD) {
7447
    /* If we're running as 'ServerType inetd', scrub the scoreboard here.
7448
     * For standalone ServerTypes, the scoreboard scrubber will handle
7449
     * things itself.
7450
     */
7451
7452
0
    c = find_config(main_server->conf, CONF_PARAM, "ScoreboardScrub", FALSE);
7453
0
    if (c != NULL) {
7454
0
      if (*((int *) c->argv[0]) == TRUE) {
7455
0
        pr_scoreboard_scrub();
7456
0
      }
7457
0
    }
7458
0
  }
7459
7460
  /* Set some Variable entries for Display files. */
7461
7462
0
  if (pr_var_set(session.pool, "%{bytes_xfer}",
7463
0
      "Number of bytes transferred in this transfer", PR_VAR_TYPE_FUNC,
7464
0
      (void *) core_get_xfer_bytes_str, &session.xfer.total_bytes,
7465
0
      sizeof(off_t *)) < 0) {
7466
0
    pr_log_debug(DEBUG6, "error setting %%{bytes_fer} variable: %s",
7467
0
      strerror(errno));
7468
0
  }
7469
7470
0
  if (pr_var_set(session.pool, "%{total_bytes_in}",
7471
0
      "Number of bytes uploaded during a session", PR_VAR_TYPE_FUNC,
7472
0
      (void *) core_get_sess_bytes_str, &session.total_bytes_in,
7473
0
      sizeof(off_t *)) < 0) {
7474
0
    pr_log_debug(DEBUG6, "error setting %%{total_bytes_in} variable: %s",
7475
0
      strerror(errno));
7476
0
  }
7477
7478
0
  if (pr_var_set(session.pool, "%{total_bytes_out}",
7479
0
      "Number of bytes downloaded during a session", PR_VAR_TYPE_FUNC,
7480
0
      (void *) core_get_sess_bytes_str, &session.total_bytes_out,
7481
0
      sizeof(off_t *)) < 0) {
7482
0
    pr_log_debug(DEBUG6, "error setting %%{total_bytes_out} variable: %s",
7483
0
      strerror(errno));
7484
0
  }
7485
7486
0
  if (pr_var_set(session.pool, "%{total_bytes_xfer}",
7487
0
      "Number of bytes transferred during a session", PR_VAR_TYPE_FUNC,
7488
0
      (void *) core_get_sess_bytes_str, &session.total_bytes,
7489
0
      sizeof(off_t *)) < 0) {
7490
0
    pr_log_debug(DEBUG6, "error setting %%{total_bytes_fer} variable: %s",
7491
0
      strerror(errno));
7492
0
  }
7493
7494
0
  if (pr_var_set(session.pool, "%{total_files_in}",
7495
0
      "Number of files uploaded during a session", PR_VAR_TYPE_FUNC,
7496
0
      (void *) core_get_sess_files_str, &session.total_files_in,
7497
0
      sizeof(unsigned int *)) < 0) {
7498
0
    pr_log_debug(DEBUG6, "error setting %%{total_files_in} variable: %s",
7499
0
      strerror(errno));
7500
0
  }
7501
7502
0
  if (pr_var_set(session.pool, "%{total_files_out}",
7503
0
      "Number of files downloaded during a session", PR_VAR_TYPE_FUNC,
7504
0
      (void *) core_get_sess_files_str, &session.total_files_out,
7505
0
      sizeof(unsigned int *)) < 0) {
7506
0
    pr_log_debug(DEBUG6, "error setting %%{total_files_out} variable: %s",
7507
0
      strerror(errno));
7508
0
  }
7509
7510
0
  if (pr_var_set(session.pool, "%{total_files_xfer}",
7511
0
      "Number of files transferred during a session", PR_VAR_TYPE_FUNC,
7512
0
      (void *) core_get_sess_files_str, &session.total_files_xfer,
7513
0
      sizeof(unsigned int *)) < 0) {
7514
0
    pr_log_debug(DEBUG6, "error setting %%{total_files_xfer} variable: %s",
7515
0
      strerror(errno));
7516
0
  }
7517
7518
  /* Register our event listeners. */
7519
0
  pr_event_register(&core_module, "core.chroot", core_chroot_ev, NULL);
7520
7521
  /* Look for a DisplayQuit file which has an absolute path.  If we
7522
   * find one, open a filehandle, such that that file can be displayed
7523
   * even if the session is chrooted.  DisplayQuit files with
7524
   * relative paths will be handled after chroot, preserving the old
7525
   * behavior.
7526
   */
7527
0
  displayquit = get_param_ptr(TOPLEVEL_CONF, "DisplayQuit", FALSE);
7528
0
  if (displayquit &&
7529
0
      *displayquit == '/') {
7530
0
    struct stat st;
7531
7532
0
    displayquit_fh = pr_fsio_open(displayquit, O_RDONLY);
7533
0
    if (displayquit_fh == NULL) {
7534
0
      pr_log_debug(DEBUG6, "unable to open DisplayQuit file '%s': %s",
7535
0
        displayquit, strerror(errno));
7536
7537
0
    } else {
7538
0
      if (pr_fsio_fstat(displayquit_fh, &st) < 0) {
7539
0
        pr_log_debug(DEBUG6, "unable to stat DisplayQuit file '%s': %s",
7540
0
          displayquit, strerror(errno));
7541
0
        pr_fsio_close(displayquit_fh);
7542
0
        displayquit_fh = NULL;
7543
7544
0
      } else {
7545
0
        if (S_ISDIR(st.st_mode)) {
7546
0
          errno = EISDIR;
7547
0
          pr_log_debug(DEBUG6, "unable to use DisplayQuit file '%s': %s",
7548
0
            displayquit, strerror(errno));
7549
0
          pr_fsio_close(displayquit_fh);
7550
0
          displayquit_fh = NULL;
7551
0
        }
7552
0
      }
7553
0
    }
7554
0
  }
7555
7556
  /* Check for any ProcessTitles setting. */
7557
0
  c = find_config(main_server->conf, CONF_PARAM, "ProcessTitles", FALSE);
7558
0
  if (c != NULL) {
7559
0
    char *verbosity;
7560
7561
0
    verbosity = c->argv[0];
7562
0
    if (strcasecmp(verbosity, "terse") == 0) {
7563
0
      pr_proctitle_set_static_str("proftpd: processing connection");
7564
0
    }
7565
0
  }
7566
7567
0
  return 0;
7568
0
}
7569
7570
/* Module API tables
7571
 */
7572
7573
static conftable core_conftab[] = {
7574
  { "<Anonymous>",    add_anonymous,      NULL },
7575
  { "</Anonymous>",   end_anonymous,      NULL },
7576
  { "<Class>",      add_class,      NULL },
7577
  { "</Class>",     end_class,      NULL },
7578
  { "<Directory>",    add_directory,      NULL },
7579
  { "</Directory>",   end_directory,      NULL },
7580
  { "<Global>",     add_global,     NULL },
7581
  { "</Global>",    end_global,     NULL },
7582
  { "<IfDefine>",   start_ifdefine,     NULL },
7583
  { "</IfDefine>",    end_ifdefine,     NULL },
7584
  { "<IfModule>",   start_ifmodule,     NULL },
7585
  { "</IfModule>",    end_ifmodule,     NULL },
7586
  { "<Limit>",      add_limit,      NULL },
7587
  { "</Limit>",     end_limit,      NULL },
7588
  { "<VirtualHost>",    add_virtualhost,    NULL },
7589
  { "</VirtualHost>",   end_virtualhost,    NULL },
7590
  { "Allow",      set_allowdeny,      NULL },
7591
  { "AllowAll",     set_allowall,     NULL },
7592
  { "AllowClass",   set_allowdenyusergroupclass,  NULL },
7593
  { "AllowFilter",    set_allowdenyfilter,    NULL },
7594
  { "AllowForeignAddress",  set_allowforeignaddress,  NULL },
7595
  { "AllowGroup",   set_allowdenyusergroupclass,  NULL },
7596
  { "AllowOverride",    set_allowoverride,    NULL },
7597
  { "AllowUser",    set_allowdenyusergroupclass,  NULL },
7598
  { "AuthOrder",    set_authorder,      NULL },
7599
  { "CDPath",     set_cdpath,     NULL },
7600
  { "CommandBufferSize",  set_commandbuffersize,    NULL },
7601
  { "DebugLevel",   set_debuglevel,     NULL },
7602
  { "DefaultAddress",   set_defaultaddress,   NULL },
7603
  { "DefaultServer",    set_defaultserver,    NULL },
7604
  { "DeferWelcome",   set_deferwelcome,   NULL },
7605
  { "Define",     set_define,     NULL },
7606
  { "Deny",     set_allowdeny,      NULL },
7607
  { "DenyAll",      set_denyall,      NULL },
7608
  { "DenyClass",    set_allowdenyusergroupclass,  NULL },
7609
  { "DenyFilter",   set_allowdenyfilter,    NULL },
7610
  { "DenyGroup",    set_allowdenyusergroupclass,  NULL },
7611
  { "DenyUser",     set_allowdenyusergroupclass,  NULL },
7612
  { "DisplayChdir",   set_displaychdir,   NULL },
7613
  { "DisplayConnect",   set_displayconnect,   NULL },
7614
  { "DisplayQuit",    set_displayquit,    NULL },
7615
  { "From",     add_from,     NULL },
7616
  { "FSCachePolicy",    set_fscachepolicy,    NULL },
7617
  { "FSOptions",    set_fsoptions,      NULL },
7618
  { "Group",      set_group,      NULL },
7619
  { "GroupOwner",   add_groupowner,     NULL },
7620
  { "HideFiles",    set_hidefiles,      NULL },
7621
  { "HideGroup",    set_hidegroup,      NULL },
7622
  { "HideNoAccess",   set_hidenoaccess,   NULL },
7623
  { "HideUser",     set_hideuser,     NULL },
7624
  { "IgnoreHidden",   set_ignorehidden,   NULL },
7625
  { "Include",      set_include,      NULL },
7626
  { "IncludeOptions",   set_includeoptions,     NULL },
7627
  { "MasqueradeAddress",  set_masqueradeaddress,    NULL },
7628
  { "MaxCommandRate",   set_maxcommandrate,   NULL },
7629
  { "MaxConnectionRate",  set_maxconnrate,    NULL },
7630
  { "MaxInstances",   set_maxinstances,   NULL },
7631
  { "MultilineRFC2228",   set_multilinerfc2228,   NULL },
7632
  { "Order",      set_order,      NULL },
7633
  { "PassivePorts",   set_passiveports,   NULL },
7634
  { "PathAllowFilter",    set_pathallowfilter,    NULL },
7635
  { "PathDenyFilter",   set_pathdenyfilter,   NULL },
7636
  { "PidFile",      set_pidfile,      NULL },
7637
  { "Port",     set_port,       NULL },
7638
  { "ProcessTitles",    set_processtitles,    NULL },
7639
  { "Protocols",    set_protocols,      NULL },
7640
  { "RegexOptions",   set_regexoptions,   NULL },
7641
  { "Satisfy",      set_satisfy,      NULL },
7642
  { "ScoreboardFile",   set_scoreboardfile,   NULL },
7643
  { "ScoreboardMutex",    set_scoreboardmutex,    NULL },
7644
  { "ScoreboardOptions",  set_scoreboardoptions,    NULL },
7645
  { "ScoreboardScrub",    set_scoreboardscrub,    NULL },
7646
  { "ServerAdmin",    set_serveradmin,    NULL },
7647
  { "ServerAlias",    set_serveralias,    NULL },
7648
  { "ServerIdent",    set_serverident,    NULL },
7649
  { "ServerName",   set_servername,     NULL },
7650
  { "ServerType",   set_servertype,     NULL },
7651
  { "SetEnv",     set_setenv,     NULL },
7652
  { "SocketBindTight",    set_socketbindtight,    NULL },
7653
  { "SocketOptions",    set_socketoptions,    NULL },
7654
  { "SyslogFacility",   set_syslogfacility,   NULL },
7655
  { "SyslogLevel",    set_sysloglevel,    NULL },
7656
  { "TimeoutIdle",    set_timeoutidle,    NULL },
7657
  { "TimeoutLinger",    set_timeoutlinger,    NULL },
7658
  { "TimesGMT",     set_timesgmt,     NULL },
7659
  { "Trace",      set_trace,      NULL },
7660
  { "TraceLog",     set_tracelog,     NULL },
7661
  { "TraceOptions",   set_traceoptions,   NULL },
7662
  { "TransferLog",    add_transferlog,    NULL },
7663
  { "Umask",      set_umask,      NULL },
7664
  { "UnsetEnv",     set_unsetenv,     NULL },
7665
  { "UseIPv6",      set_useipv6,      NULL },
7666
  { "UseReverseDNS",    set_usereversedns,    NULL },
7667
  { "User",     set_user,     NULL },
7668
  { "UserOwner",    add_userowner,      NULL },
7669
  { "TCPBackLog",   set_tcpbacklog,     NULL },
7670
  { "TCPNoDelay",   set_tcpnodelay,     NULL },
7671
7672
  { NULL, NULL, NULL }
7673
};
7674
7675
static cmdtable core_cmdtab[] = {
7676
#if defined(PR_USE_REGEX)
7677
  { PRE_CMD, C_ANY, G_NONE,  regex_filters, FALSE, FALSE, CL_NONE },
7678
#endif /* PR_USE_REGEX */
7679
  { PRE_CMD, C_ANY, G_NONE, core_pre_any,FALSE, FALSE, CL_NONE },
7680
  { CMD, C_HELP, G_NONE,  core_help,  FALSE,  FALSE, CL_INFO },
7681
  { CMD, C_PORT, G_NONE,  core_port,  TRUE, FALSE, CL_MISC },
7682
  { CMD, C_PASV, G_NONE,  core_pasv,  TRUE, FALSE, CL_MISC },
7683
  { CMD, C_EPRT, G_NONE,  core_eprt,    TRUE, FALSE, CL_MISC },
7684
  { CMD, C_EPSV, G_NONE,  core_epsv,  TRUE, FALSE, CL_MISC },
7685
  { CMD, C_SYST, G_NONE,  core_syst,  FALSE,  FALSE, CL_INFO },
7686
  { CMD, C_PWD,  G_DIRS,  core_pwd, TRUE, FALSE, CL_INFO|CL_DIRS },
7687
  { CMD, C_XPWD, G_DIRS,  core_pwd, TRUE, FALSE, CL_INFO|CL_DIRS },
7688
  { CMD, C_CWD,  G_DIRS,  core_cwd, TRUE, FALSE, CL_DIRS },
7689
  { CMD, C_XCWD, G_DIRS,  core_cwd, TRUE, FALSE, CL_DIRS },
7690
  { CMD, C_MKD,  G_WRITE, core_mkd, TRUE, FALSE, CL_DIRS|CL_WRITE },
7691
  { CMD, C_XMKD, G_WRITE, core_mkd, TRUE, FALSE, CL_DIRS|CL_WRITE },
7692
  { CMD, C_RMD,  G_WRITE, core_rmd, TRUE, FALSE, CL_DIRS|CL_WRITE },
7693
  { CMD, C_XRMD, G_WRITE, core_rmd, TRUE, FALSE, CL_DIRS|CL_WRITE },
7694
  { CMD, C_CDUP, G_DIRS,  core_cdup,  TRUE, FALSE, CL_DIRS },
7695
  { CMD, C_XCUP, G_DIRS,  core_cdup,  TRUE, FALSE, CL_DIRS },
7696
  { CMD, C_DELE, G_WRITE, core_dele,  TRUE, FALSE, CL_WRITE },
7697
  { CMD, C_MDTM, G_DIRS,  core_mdtm,  TRUE, FALSE, CL_INFO|CL_DIRS },
7698
  { CMD, C_RNFR, G_WRITE, core_rnfr,  TRUE, FALSE, CL_MISC|CL_WRITE },
7699
  { CMD, C_RNTO, G_WRITE, core_rnto,  TRUE, FALSE, CL_MISC|CL_WRITE },
7700
  { LOG_CMD,     C_RNTO, G_NONE, core_rnto_cleanup, TRUE, FALSE, CL_NONE },
7701
  { LOG_CMD_ERR, C_RNTO, G_NONE, core_rnto_cleanup, TRUE, FALSE, CL_NONE },
7702
  { CMD, C_SIZE, G_READ,  core_size,  TRUE, FALSE, CL_INFO },
7703
  { CMD, C_QUIT, G_NONE,  core_quit,  FALSE,  FALSE,  CL_INFO },
7704
  { LOG_CMD,   C_QUIT, G_NONE, core_log_quit, FALSE, FALSE },
7705
  { LOG_CMD_ERR, C_QUIT, G_NONE, core_log_quit, FALSE, FALSE },
7706
  { CMD, C_NOOP, G_NONE,  core_noop,  FALSE,  FALSE,  CL_MISC },
7707
  { CMD, C_FEAT, G_NONE,  core_feat,  FALSE,  FALSE,  CL_INFO },
7708
  { CMD, C_OPTS, G_NONE,  core_opts,    FALSE,  FALSE,  CL_MISC },
7709
  { CMD, C_HOST, G_NONE,  core_host,    FALSE,  FALSE,  CL_MISC },
7710
  { POST_CMD, C_PASS, G_NONE, core_post_pass, FALSE, FALSE },
7711
  { CMD, C_HOST, G_NONE,  core_host,  FALSE,  FALSE,  CL_AUTH },
7712
  { POST_CMD, C_HOST, G_NONE, core_post_host, FALSE, FALSE },
7713
  { CMD, C_CLNT, G_NONE,  core_clnt,  FALSE,  FALSE,  CL_INFO },
7714
  { CMD, C_CSID, G_NONE,  core_csid,  TRUE, FALSE,  CL_INFO },
7715
7716
  { 0, NULL }
7717
};
7718
7719
module core_module = {
7720
  NULL, NULL,
7721
7722
  /* Module API version */
7723
  0x20,
7724
7725
  /* Module name */
7726
  "core",
7727
7728
  /* Module configuration directive table */
7729
  core_conftab,
7730
7731
  /* Module command handler table */
7732
  core_cmdtab,
7733
7734
  /* Module authentication handler table */
7735
  NULL,
7736
7737
  /* Module initialization function */
7738
  core_init,
7739
7740
  /* Session initialization function */
7741
  core_sess_init
7742
};