Coverage Report

Created: 2025-08-05 06:56

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