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