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-2021 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 /* Read configuration file(s), and manage server/configuration structures. */
28
29 #include "conf.h"
30
31 #ifdef HAVE_ARPA_INET_H
32 # include <arpa/inet.h>
33 #endif
34
35 xaset_t *server_list = NULL;
36 server_rec *main_server = NULL;
37 int tcpBackLog = PR_TUNABLE_DEFAULT_BACKLOG;
38 int SocketBindTight = FALSE;
39 char ServerType = SERVER_STANDALONE;
40 unsigned long ServerMaxInstances = 0UL;
41 int ServerUseReverseDNS = TRUE;
42
43 /* Default TCP send/receive buffer sizes. */
44 static int tcp_rcvbufsz = 0;
45 static int tcp_sndbufsz = 0;
46 static int xfer_bufsz = 0;
47
48 static unsigned char _kludge_disable_umask = 0;
49
50 /* We have two different lists for Defines. The 'perm' pool/list are
51 * for "permanent" defines, i.e. those set on the command-line via the
52 * -D/--define options.
53 */
54 static pool *defines_pool = NULL;
55 static array_header *defines_list = NULL;
56
57 static pool *defines_perm_pool = NULL;
58 static array_header *defines_perm_list = NULL;
59
allow_dyn_config(const char * path)60 static int allow_dyn_config(const char *path) {
61 config_rec *c = NULL;
62 unsigned int ctxt_precedence = 0;
63 unsigned char allow = TRUE, found_config = FALSE;
64
65 c = find_config(CURRENT_CONF, CONF_PARAM, "AllowOverride", FALSE);
66 while (c) {
67 pr_signals_handle();
68
69 if (*((unsigned int *) c->argv[1]) > ctxt_precedence) {
70
71 /* Set the context precedence. */
72 ctxt_precedence = *((unsigned int *) c->argv[1]);
73
74 allow = *((int *) c->argv[0]);
75
76 found_config = TRUE;
77 }
78
79 c = find_config_next(c, c->next, CONF_PARAM, "AllowOverride", FALSE);
80 }
81
82 /* Print out some nice debugging information, but only if we have a real
83 * path.
84 */
85 if (found_config &&
86 *path) {
87 pr_trace_msg("config", 8,
88 "AllowOverride for path '%s' %s .ftpaccess files", path,
89 allow ? "allows" : "denies");
90 }
91
92 return allow;
93 }
94
95 /* Imported this function from modules/mod_ls.c -- it belongs more with the
96 * dir_* functions here, rather than the ls_* functions there.
97 */
98
99 /* Return true if dir is ".", "./", "../", or "..". */
is_dotdir(const char * dir)100 int is_dotdir(const char *dir) {
101 if (strncmp(dir, ".", 2) == 0 ||
102 strncmp(dir, "./", 2) == 0 ||
103 strncmp(dir, "..", 3) == 0 ||
104 strncmp(dir, "../", 3) == 0) {
105 return TRUE;
106 }
107
108 return FALSE;
109 }
110
111 /* Lookup the best configuration set from which to retrieve configuration
112 * values if the config_rec can appear in <Directory>. This function
113 * works around the issue caused by using the cached directory pointer
114 * in session.dir_config.
115 *
116 * The issue with using session.dir_config is that it is assigned when
117 * the client changes directories or doing other directory lookups, and so
118 * dir_config may actually point to the configuration for a directory other
119 * than the target directory for an uploaded, for example. Unfortunately,
120 * it is more expensive to lookup the configuration for the target directory
121 * every time. Perhaps some caching of looked up directory configurations
122 * into a table, rather than a single pointer like session.dir_config,
123 * might help.
124 */
get_dir_ctxt(pool * p,char * dir_path)125 xaset_t *get_dir_ctxt(pool *p, char *dir_path) {
126 config_rec *c = NULL;
127 char *full_path = dir_path;
128
129 if (session.chroot_path) {
130 if (*dir_path != '/') {
131 full_path = pdircat(p, session.chroot_path, session.cwd, dir_path, NULL);
132
133 } else {
134 full_path = pdircat(p, session.chroot_path, dir_path, NULL);
135 }
136
137 } else if (*dir_path != '/') {
138 full_path = pdircat(p, session.cwd, dir_path, NULL);
139 }
140
141 c = dir_match_path(p, full_path);
142
143 return c ? c->subset : session.anon_config ? session.anon_config->subset :
144 main_server->conf;
145 }
146
147 /* Check for configured HideFiles directives, and check the given path (full
148 * _path_, not just filename) against those regexes if configured.
149 *
150 * Returns FALSE if the path should be shown/listed, TRUE if it should not
151 * be visible.
152 */
dir_hide_file(const char * path)153 unsigned char dir_hide_file(const char *path) {
154 #ifdef PR_USE_REGEX
155 char *file_name = NULL, *dir_name = NULL;
156 config_rec *c = NULL;
157 pr_regex_t *pre = NULL;
158 pool *tmp_pool;
159 unsigned int ctxt_precedence = 0;
160 unsigned char have_user_regex, have_group_regex, have_class_regex,
161 have_all_regex, negated = FALSE;
162
163 if (path == NULL) {
164 return FALSE;
165 }
166
167 tmp_pool = make_sub_pool(session.pool);
168 pr_pool_tag(tmp_pool, "dir_hide_file() tmp pool");
169
170 have_user_regex = have_group_regex = have_class_regex = have_all_regex =
171 FALSE;
172
173 /* Separate the given path into directory and file components. */
174 dir_name = pstrdup(tmp_pool, path);
175
176 file_name = strrchr(dir_name, '/');
177 if (file_name != NULL) {
178
179 if (file_name != dir_name) {
180 /* Handle paths like "/path". */
181 *file_name = '\0';
182 file_name++;
183
184 } else {
185 /* Handle "/". */
186 dir_name = "/";
187
188 if (strlen(file_name) > 1) {
189 file_name++;
190
191 } else {
192 /* Handle "/". */
193 file_name = "/";
194 }
195 }
196
197 } else {
198 file_name = dir_name;
199 }
200
201 /* Check for any configured HideFiles */
202 c = find_config(get_dir_ctxt(tmp_pool, dir_name), CONF_PARAM, "HideFiles",
203 FALSE);
204
205 while (c) {
206 pr_signals_handle();
207
208 if (c->argc >= 4) {
209
210 /* check for a specified "user" classifier first... */
211 if (strncmp(c->argv[3], "user", 5) == 0) {
212 if (pr_expr_eval_user_or((char **) &c->argv[4]) == TRUE) {
213
214 if (*((unsigned int *) c->argv[2]) > ctxt_precedence) {
215 ctxt_precedence = *((unsigned int *) c->argv[2]);
216
217 pre = *((pr_regex_t **) c->argv[0]);
218 negated = *((unsigned char *) c->argv[1]);
219
220 have_group_regex = have_class_regex = have_all_regex = FALSE;
221 have_user_regex = TRUE;
222 }
223 }
224
225 /* ...then for a "group" classifier... */
226 } else if (strncmp(c->argv[3], "group", 6) == 0) {
227 if (pr_expr_eval_group_and((char **) &c->argv[4]) == TRUE) {
228 if (*((unsigned int *) c->argv[2]) > ctxt_precedence) {
229 ctxt_precedence = *((unsigned int *) c->argv[2]);
230
231 pre = *((pr_regex_t **) c->argv[0]);
232 negated = *((unsigned char *) c->argv[1]);
233
234 have_user_regex = have_class_regex = have_all_regex = FALSE;
235 have_group_regex = TRUE;
236 }
237 }
238
239 /* ...finally, for a "class" classifier. NOTE: mod_time's
240 * class_expression functionality should really be added into the
241 * core code at some point. When that happens, then this code will
242 * need to be updated to process class-expressions.
243 */
244 } else if (strncmp(c->argv[3], "class", 6) == 0) {
245 if (pr_expr_eval_class_or((char **) &c->argv[4]) == TRUE) {
246 if (*((unsigned int *) c->argv[2]) > ctxt_precedence) {
247 ctxt_precedence = *((unsigned int *) c->argv[2]);
248
249 pre = *((pr_regex_t **) c->argv[0]);
250 negated = *((unsigned char *) c->argv[1]);
251
252 have_user_regex = have_group_regex = have_all_regex = FALSE;
253 have_class_regex = TRUE;
254 }
255 }
256 }
257
258 } else if (c->argc == 1) {
259
260 /* This is the "none" HideFiles parameter. */
261 destroy_pool(tmp_pool);
262 return FALSE;
263
264 } else {
265 if (*((unsigned int *) c->argv[2]) > ctxt_precedence) {
266 ctxt_precedence = *((unsigned int *) c->argv[2]);
267
268 pre = *((pr_regex_t **) c->argv[0]);
269 negated = *((unsigned char *) c->argv[1]);
270
271 have_user_regex = have_group_regex = have_class_regex = FALSE;
272 have_all_regex = TRUE;
273 }
274 }
275
276 c = find_config_next(c, c->next, CONF_PARAM, "HideFiles", FALSE);
277 }
278
279 if (have_user_regex || have_group_regex ||
280 have_class_regex || have_all_regex) {
281
282 pr_log_debug(DEBUG4, "checking %sHideFiles pattern for current %s",
283 negated ? "negated " : "",
284 have_user_regex ? "user" : have_group_regex ? "group" :
285 have_class_regex ? "class" : "session");
286
287 if (pre == NULL) {
288 destroy_pool(tmp_pool);
289
290 /* HideFiles none for this user/group/class */
291
292 pr_log_debug(DEBUG9, "file '%s' did not match HideFiles pattern 'none'",
293 file_name);
294 return FALSE;
295 }
296
297 if (pr_regexp_exec(pre, file_name, 0, NULL, 0, 0, 0) != 0) {
298 destroy_pool(tmp_pool);
299
300 pr_log_debug(DEBUG9, "file '%s' did not match %sHideFiles pattern",
301 file_name, negated ? "negated " : "");
302
303 /* The file failed to match the HideFiles regex, which means it should
304 * be treated as a "visible" file. If the regex was negated, though,
305 * switch the result.
306 */
307 return (negated ? TRUE : FALSE);
308
309 } else {
310 destroy_pool(tmp_pool);
311
312 pr_log_debug(DEBUG9, "file '%s' matched %sHideFiles pattern", file_name,
313 negated ? "negated " : "");
314
315 /* The file matched the HideFiles regex, which means it should be
316 * considered a "hidden" file. If the regex was negated, though,
317 * switch the result.
318 */
319 return (negated ? FALSE : TRUE);
320 }
321 }
322
323 destroy_pool(tmp_pool);
324 #endif /* regex support */
325
326 /* Return FALSE by default. */
327 return FALSE;
328 }
329
define_restart_ev(const void * event_data,void * user_data)330 static void define_restart_ev(const void *event_data, void *user_data) {
331 if (defines_pool) {
332 destroy_pool(defines_pool);
333 defines_pool = NULL;
334 defines_list = NULL;
335 }
336
337 pr_event_unregister(NULL, "core.restart", define_restart_ev);
338 }
339
340 /* The 'survive_restarts' boolean indicates whether this Define is to be
341 * permanent for the lifetime of the daemon (i.e. survives across restarts)
342 * or whether it should be cleared when restarted.
343 *
344 * Right now, defines from the command-line will surive restarts, but
345 * defines from the config (via the Define directive) will not.
346 */
pr_define_add(const char * definition,int survive_restarts)347 int pr_define_add(const char *definition, int survive_restarts) {
348
349 if (definition == NULL ||
350 (survive_restarts != FALSE && survive_restarts != TRUE)) {
351 errno = EINVAL;
352 return -1;
353 }
354
355 if (survive_restarts == FALSE) {
356 if (defines_pool == NULL) {
357 defines_pool = make_sub_pool(permanent_pool);
358 pr_pool_tag(defines_pool, "Defines Pool");
359 pr_event_register(NULL, "core.restart", define_restart_ev, NULL);
360 }
361
362 if (!defines_list) {
363 defines_list = make_array(defines_pool, 0, sizeof(char *));
364
365 }
366
367 *((char **) push_array(defines_list)) = pstrdup(defines_pool, definition);
368 return 0;
369 }
370
371 if (defines_perm_pool == NULL) {
372 defines_perm_pool = make_sub_pool(permanent_pool);
373 pr_pool_tag(defines_perm_pool, "Permanent Defines Pool");
374 }
375
376 if (!defines_perm_list) {
377 defines_perm_list = make_array(defines_perm_pool, 0, sizeof(char *));
378 }
379
380 *((char **) push_array(defines_perm_list)) =
381 pstrdup(defines_perm_pool, definition);
382 return 0;
383 }
384
pr_define_exists(const char * definition)385 unsigned char pr_define_exists(const char *definition) {
386 if (definition == NULL) {
387 errno = EINVAL;
388 return FALSE;
389 }
390
391 if (defines_list) {
392 char **defines = defines_list->elts;
393 register unsigned int i = 0;
394
395 for (i = 0; i < defines_list->nelts; i++) {
396 if (defines[i] &&
397 strcmp(defines[i], definition) == 0)
398 return TRUE;
399 }
400 }
401
402 if (defines_perm_list) {
403 char **defines = defines_perm_list->elts;
404 register unsigned int i = 0;
405
406 for (i = 0; i < defines_perm_list->nelts; i++) {
407 if (defines[i] &&
408 strcmp(defines[i], definition) == 0)
409 return TRUE;
410 }
411 }
412
413 errno = ENOENT;
414 return FALSE;
415 }
416
kludge_disable_umask(void)417 void kludge_disable_umask(void) {
418 _kludge_disable_umask = TRUE;
419 }
420
kludge_enable_umask(void)421 void kludge_enable_umask(void) {
422 _kludge_disable_umask = FALSE;
423 }
424
425 /* Per-directory configuration */
426
_strmatch(register char * s1,register char * s2)427 static size_t _strmatch(register char *s1, register char *s2) {
428 register size_t len = 0;
429
430 while (*s1 && *s2 && *s1++ == *s2++)
431 len++;
432
433 return len;
434 }
435
recur_match_path(pool * p,xaset_t * s,char * path)436 static config_rec *recur_match_path(pool *p, xaset_t *s, char *path) {
437 char *suffixed_path = NULL, *tmp_path = NULL;
438 config_rec *c = NULL, *res = NULL;
439
440 if (!s) {
441 errno = EINVAL;
442 return NULL;
443 }
444
445 for (c = (config_rec *) s->xas_list; c; c = c->next) {
446 if (c->config_type == CONF_DIR) {
447 size_t path_len;
448
449 tmp_path = c->name;
450
451 if (c->argv[1]) {
452 if (*(char *)(c->argv[1]) == '~') {
453 c->argv[1] = dir_canonical_path(c->pool, (char *) c->argv[1]);
454 }
455
456 tmp_path = pdircat(p, (char *) c->argv[1], tmp_path, NULL);
457 }
458
459 /* Exact path match */
460 if (strcmp(tmp_path, path) == 0) {
461 pr_trace_msg("directory", 8,
462 "<Directory %s> is an exact path match for '%s'", c->name, path);
463 return c;
464 }
465
466 /* Bug#3146 occurred because using strstr(3) works well for paths
467 * which DO NOT contain the glob sequence, i.e. we used to do:
468 *
469 * if (strstr(tmp_path, slash_star) == NULL) {
470 *
471 * But what if they do, just not at the end of the path?
472 *
473 * The fix is to explicitly check the last two characters of the path
474 * for '/' and '*', rather than using strstr(3). (Again, I wish there
475 * was a strrstr(3) libc function.)
476 */
477 path_len = strlen(tmp_path);
478 if (path_len >= 2 &&
479 !(tmp_path[path_len-2] == '/' && tmp_path[path_len-1] == '*')) {
480
481 /* Trim a trailing path separator, if present. */
482 if (*tmp_path &&
483 *(tmp_path + path_len - 1) == '/') {
484 *(tmp_path + path_len - 1) = '\0';
485 path_len--;
486
487 if (strcmp(tmp_path, path) == 0) {
488 pr_trace_msg("directory", 8,
489 "<Directory %s> is an exact path match for '%s'", c->name, path);
490 return c;
491 }
492 }
493
494 suffixed_path = pdircat(p, tmp_path, "*", NULL);
495
496 } else if (path_len == 1) {
497 /* We still need to append the "*" if the path is just '/'. */
498 suffixed_path = pstrcat(p, tmp_path, "*", NULL);
499 }
500
501 if (suffixed_path == NULL) {
502 /* Default to treating the given path as the suffixed path */
503 suffixed_path = tmp_path;
504 }
505
506 pr_trace_msg("directory", 9,
507 "checking if <Directory %s> is a glob match for %s", tmp_path, path);
508
509 /* The flags argument here needs to include PR_FNM_PATHNAME in order
510 * to prevent globs from matching the '/' character.
511 *
512 * As per Bug#3491, we need to check if either a) the automatically
513 * suffixed path (i.e. with the slash-star pattern) is a pattern match,
514 * OR if b) the given path, as is, is a pattern match.
515 */
516
517 if (pr_fnmatch(suffixed_path, path, 0) == 0 ||
518 (pr_str_is_fnmatch(tmp_path) &&
519 pr_fnmatch(tmp_path, path, 0) == 0)) {
520 pr_trace_msg("directory", 8,
521 "<Directory %s> is a glob match for '%s'", tmp_path, path);
522
523 if (c->subset) {
524 /* If there's a subset config, check to see if there's a closer
525 * match there.
526 */
527 res = recur_match_path(p, c->subset, path);
528 if (res) {
529 pr_trace_msg("directory", 8,
530 "found closer matching <Directory %s> for '%s' in <Directory %s> "
531 "sub-config", res->name, path, tmp_path);
532 return res;
533 }
534 }
535
536 pr_trace_msg("directory", 8, "found <Directory %s> for '%s'",
537 c->name, path);
538 return c;
539 }
540 }
541 }
542
543 errno = ENOENT;
544 return NULL;
545 }
546
dir_match_path(pool * p,char * path)547 config_rec *dir_match_path(pool *p, char *path) {
548 config_rec *res = NULL;
549 char *tmp = NULL;
550 size_t tmplen;
551
552 if (p == NULL ||
553 path == NULL ||
554 *path == '\0') {
555 errno = EINVAL;
556 return NULL;
557 }
558
559 tmp = pstrdup(p, path);
560 tmplen = strlen(tmp);
561
562 if (*(tmp + tmplen - 1) == '*') {
563 *(tmp + tmplen - 1) = '\0';
564 tmplen = strlen(tmp);
565 }
566
567 if (*(tmp + tmplen - 1) == '/' && tmplen > 1) {
568 *(tmp + tmplen - 1) = '\0';
569 }
570
571 if (session.anon_config) {
572 res = recur_match_path(p, session.anon_config->subset, tmp);
573
574 if (!res) {
575 if (session.chroot_path &&
576 !strncmp(session.chroot_path, tmp, strlen(session.chroot_path))) {
577 return NULL;
578 }
579 }
580 }
581
582 if (!res) {
583 res = recur_match_path(p, main_server->conf, tmp);
584 }
585
586 if (res) {
587 pr_trace_msg("directory", 3, "matched <Directory %s> for path '%s'",
588 res->name, tmp);
589
590 } else {
591 pr_trace_msg("directory", 3, "no matching <Directory> found for '%s': %s",
592 tmp, strerror(errno));
593 }
594
595 return res;
596 }
597
598 /* Returns TRUE to allow, FALSE to deny. */
dir_check_op(pool * p,xaset_t * set,int op,const char * path,uid_t file_uid,gid_t file_gid,mode_t mode)599 static int dir_check_op(pool *p, xaset_t *set, int op, const char *path,
600 uid_t file_uid, gid_t file_gid, mode_t mode) {
601 int res = TRUE;
602 config_rec *c;
603
604 /* Default is to allow. */
605 if (!set)
606 return TRUE;
607
608 switch (op) {
609 case OP_HIDE:
610 c = find_config(set, CONF_PARAM, "HideUser", FALSE);
611 while (c) {
612 int inverted = FALSE;
613 const char *hide_user = NULL;
614 uid_t hide_uid = -1;
615
616 pr_signals_handle();
617
618 hide_user = c->argv[0];
619 inverted = *((unsigned char *) c->argv[1]);
620
621 if (strncmp(hide_user, "~", 2) == 0) {
622 hide_uid = session.uid;
623
624 } else {
625 struct passwd *pw;
626
627 pw = pr_auth_getpwnam(p, hide_user);
628 if (pw == NULL) {
629 pr_log_debug(DEBUG1,
630 "HideUser '%s' is not a known/valid user, ignoring", hide_user);
631
632 c = find_config_next(c, c->next, CONF_PARAM, "HideUser", FALSE);
633 continue;
634 }
635
636 hide_uid = pw->pw_uid;
637 }
638
639 if (file_uid == hide_uid) {
640 if (!inverted) {
641 pr_trace_msg("hiding", 8,
642 "hiding file '%s' because of HideUser %s", path, hide_user);
643 res = FALSE;
644 }
645 break;
646
647 } else {
648 if (inverted) {
649 pr_trace_msg("hiding", 8,
650 "hiding file '%s' because of HideUser !%s", path, hide_user);
651 res = FALSE;
652 break;
653 }
654 }
655
656 c = find_config_next(c, c->next, CONF_PARAM, "HideUser", FALSE);
657 }
658
659 /* We only need to check for HideGroup restrictions if we are not
660 * already hiding the file. I.e. if res = FALSE, then the path is to
661 * be hidden, and we don't need to check for other reasons to hide it
662 * (Bug#3530).
663 */
664 if (res == TRUE) {
665 c = find_config(set, CONF_PARAM, "HideGroup", FALSE);
666 while (c) {
667 int inverted = FALSE;
668 const char *hide_group = NULL;
669 gid_t hide_gid = -1;
670
671 pr_signals_handle();
672
673 hide_group = c->argv[0];
674 inverted = *((int *) c->argv[1]);
675
676 if (strncmp(hide_group, "~", 2) == 0) {
677 hide_gid = session.gid;
678
679 } else {
680 struct group *gr;
681
682 gr = pr_auth_getgrnam(p, hide_group);
683 if (gr == NULL) {
684 pr_log_debug(DEBUG1,
685 "HideGroup '%s' is not a known/valid group, ignoring",
686 hide_group);
687
688 c = find_config_next(c, c->next, CONF_PARAM, "HideGroup", FALSE);
689 continue;
690 }
691
692 hide_gid = gr->gr_gid;
693 }
694
695 if (hide_gid != (gid_t) -1) {
696 if (file_gid == hide_gid) {
697 if (!inverted) {
698 pr_trace_msg("hiding", 8,
699 "hiding file '%s' because of HideGroup %s", path, hide_group);
700 res = FALSE;
701 }
702
703 break;
704
705 } else {
706 if (inverted) {
707 pr_trace_msg("hiding", 8,
708 "hiding file '%s' because of HideGroup !%s", path,
709 hide_group);
710 res = FALSE;
711 break;
712 }
713 }
714
715 } else {
716 register unsigned int i;
717 gid_t *group_ids = session.gids->elts;
718
719 /* First check to see if the file GID matches the session GID. */
720 if (file_gid == session.gid) {
721 if (!inverted) {
722 pr_trace_msg("hiding", 8,
723 "hiding file '%s' because of HideGroup %s", path, hide_group);
724 res = FALSE;
725 }
726
727 break;
728 }
729
730 /* Next, scan the list of supplemental groups for this user. */
731 for (i = 0; i < session.gids->nelts; i++) {
732 if (file_gid == group_ids[i]) {
733 if (!inverted) {
734 pr_trace_msg("hiding", 8,
735 "hiding file '%s' because of HideGroup %s", path,
736 hide_group);
737 res = FALSE;
738 }
739
740 break;
741 }
742 }
743
744 if (inverted) {
745 pr_trace_msg("hiding", 8,
746 "hiding file '%s' because of HideGroup !%s", path, hide_group);
747 res = FALSE;
748 break;
749 }
750 }
751
752 c = find_config_next(c, c->next, CONF_PARAM, "HideGroup", FALSE);
753 }
754 }
755
756 /* If we have already decided to hide this path (i.e. res = FALSE),
757 * then we do not need to check for HideNoAccess. Hence why we
758 * only look for HideNoAccess here if res = TRUE (Bug#3530).
759 */
760 if (res == TRUE) {
761 unsigned char *hide_no_access = NULL;
762
763 hide_no_access = get_param_ptr(set, "HideNoAccess", FALSE);
764 if (hide_no_access &&
765 *hide_no_access == TRUE) {
766
767 if (S_ISDIR(mode)) {
768 /* Check to see if the mode of this directory allows the
769 * current user to list its contents.
770 */
771 res = pr_fsio_access(path, X_OK, session.uid, session.gid,
772 session.gids) == 0 ? TRUE : FALSE;
773 if (res == FALSE) {
774 int xerrno = errno;
775
776 pr_trace_msg("hiding", 8,
777 "hiding directory '%s' because of HideNoAccess (errno = %s)",
778 path, strerror(xerrno));
779 errno = xerrno;
780 }
781
782 } else {
783 /* Check to see if the mode of this file allows the current
784 * user to read it.
785 */
786 res = pr_fsio_access(path, R_OK, session.uid, session.gid,
787 session.gids) == 0 ? TRUE : FALSE;
788 if (res == FALSE) {
789 int xerrno = errno;
790
791 pr_trace_msg("hiding", 8,
792 "hiding file '%s' because of HideNoAccess (errno = %s)", path,
793 strerror(xerrno));
794 errno = xerrno;
795 }
796 }
797 }
798 }
799 break;
800
801 case OP_COMMAND: {
802 unsigned char *allow_all = get_param_ptr(set, "AllowAll", FALSE);
803 unsigned char *deny_all = get_param_ptr(set, "DenyAll", FALSE);
804
805 if (allow_all &&
806 *allow_all == TRUE) {
807 /* No-op */
808 ;
809
810 } else if (deny_all &&
811 *deny_all == TRUE) {
812 pr_trace_msg("hiding", 8,
813 "hiding file '%s' because of DenyAll limit for command (errno = %s)",
814 path, strerror(EACCES));
815 res = FALSE;
816 errno = EACCES;
817 }
818 }
819
820 break;
821 }
822
823 return res;
824 }
825
check_user_access(xaset_t * set,const char * name)826 static int check_user_access(xaset_t *set, const char *name) {
827 int res = 0;
828 config_rec *c;
829
830 /* If no user has been authenticated yet for this session, short-circuit the
831 * check.
832 */
833 if (session.user == NULL) {
834 return 0;
835 }
836
837 c = find_config(set, CONF_PARAM, name, FALSE);
838 while (c) {
839 pr_signals_handle();
840
841 #ifdef PR_USE_REGEX
842 if (*((unsigned char *) c->argv[0]) == PR_EXPR_EVAL_REGEX) {
843 pr_regex_t *pre = (pr_regex_t *) c->argv[1];
844
845 if (pr_regexp_exec(pre, session.user, 0, NULL, 0, 0, 0) == 0) {
846 res = TRUE;
847 break;
848 }
849
850 } else
851 #endif /* regex support */
852
853 if (*((unsigned char *) c->argv[0]) == PR_EXPR_EVAL_OR) {
854 res = pr_expr_eval_user_or((char **) &c->argv[1]);
855 if (res == TRUE) {
856 break;
857 }
858
859 } else if (*((unsigned char *) c->argv[0]) == PR_EXPR_EVAL_AND) {
860 res = pr_expr_eval_user_and((char **) &c->argv[1]);
861 if (res == TRUE) {
862 break;
863 }
864 }
865
866 c = find_config_next(c, c->next, CONF_PARAM, name, FALSE);
867 }
868
869 return res;
870 }
871
check_group_access(xaset_t * set,const char * name)872 static int check_group_access(xaset_t *set, const char *name) {
873 int res = 0;
874 config_rec *c;
875
876 /* If no groups has been authenticated yet for this session, short-circuit the
877 * check.
878 */
879 if (session.group == NULL) {
880 return 0;
881 }
882
883 c = find_config(set, CONF_PARAM, name, FALSE);
884 while (c) {
885 #ifdef PR_USE_REGEX
886 if (*((unsigned char *) c->argv[0]) == PR_EXPR_EVAL_REGEX) {
887 pr_regex_t *pre = (pr_regex_t *) c->argv[1];
888
889 if (session.group &&
890 pr_regexp_exec(pre, session.group, 0, NULL, 0, 0, 0) == 0) {
891 res = TRUE;
892 break;
893
894 } else if (session.groups) {
895 register int i = 0;
896
897 for (i = session.groups->nelts-1; i >= 0; i--) {
898 if (pr_regexp_exec(pre, *(((char **) session.groups->elts) + i), 0,
899 NULL, 0, 0, 0) == 0) {
900 res = TRUE;
901 break;
902 }
903 }
904 }
905
906 } else
907 #endif /* regex support */
908
909 if (*((unsigned char *) c->argv[0]) == PR_EXPR_EVAL_OR) {
910 res = pr_expr_eval_group_or((char **) &c->argv[1]);
911 if (res == TRUE) {
912 break;
913 }
914
915 } else if (*((unsigned char *) c->argv[0]) == PR_EXPR_EVAL_AND) {
916 res = pr_expr_eval_group_and((char **) &c->argv[1]);
917 if (res == TRUE) {
918 break;
919 }
920 }
921
922 c = find_config_next(c, c->next, CONF_PARAM, name, FALSE);
923 }
924
925 return res;
926 }
927
check_class_access(xaset_t * set,const char * name)928 static int check_class_access(xaset_t *set, const char *name) {
929 int res = 0;
930 config_rec *c;
931
932 /* If no class was found for this session, short-circuit the check. */
933 if (session.conn_class == NULL) {
934 return res;
935 }
936
937 c = find_config(set, CONF_PARAM, name, FALSE);
938 while (c) {
939 pr_signals_handle();
940
941 #ifdef PR_USE_REGEX
942 if (*((unsigned char *) c->argv[0]) == PR_EXPR_EVAL_REGEX) {
943 pr_regex_t *pre = (pr_regex_t *) c->argv[1];
944
945 if (session.conn_class &&
946 pr_regexp_exec(pre, session.conn_class->cls_name, 0, NULL, 0,
947 0, 0) == 0) {
948 res = TRUE;
949 break;
950 }
951
952 } else
953 #endif /* regex support */
954
955 if (*((unsigned char *) c->argv[0]) == PR_EXPR_EVAL_OR) {
956 res = pr_expr_eval_class_or((char **) &c->argv[1]);
957 if (res == TRUE) {
958 break;
959 }
960
961 } else if (*((unsigned char *) c->argv[0]) == PR_EXPR_EVAL_AND) {
962 res = pr_expr_eval_class_and((char **) &c->argv[1]);
963 if (res == TRUE) {
964 break;
965 }
966 }
967
968 c = find_config_next(c, c->next, CONF_PARAM, name, FALSE);
969 }
970
971 return res;
972 }
973
check_filter_access(xaset_t * set,const char * name,cmd_rec * cmd)974 static int check_filter_access(xaset_t *set, const char *name, cmd_rec *cmd) {
975 #ifdef PR_USE_REGEX
976 int res = 0;
977 config_rec *c;
978
979 if (cmd == NULL) {
980 return 0;
981 }
982
983 c = find_config(set, CONF_PARAM, name, FALSE);
984 while (c) {
985 int matched = 0;
986 pr_regex_t *pre = (pr_regex_t *) c->argv[0];
987
988 pr_signals_handle();
989
990 pr_trace_msg("filter", 8,
991 "comparing %s argument '%s' against %s pattern '%s'",
992 (char *) cmd->argv[0], cmd->arg, name, pr_regexp_get_pattern(pre));
993 matched = pr_regexp_exec(pre, cmd->arg, 0, NULL, 0, 0, 0);
994 pr_trace_msg("filter", 8,
995 "comparing %s argument '%s' against %s pattern '%s' returned %d",
996 (char *) cmd->argv[0], cmd->arg, name, pr_regexp_get_pattern(pre),
997 matched);
998
999 if (matched == 0) {
1000 res = TRUE;
1001 break;
1002 }
1003
1004 c = find_config_next(c, c->next, CONF_PARAM, name, FALSE);
1005 }
1006
1007 pr_trace_msg("filter", 8,
1008 "comparing %s argument '%s' against %s patterns returned %d",
1009 (char *) cmd->argv[0], cmd->arg, name, res);
1010 return res;
1011 #else
1012 return 0;
1013 #endif /* regex support */
1014 }
1015
1016 /* As of 1.2.0rc3, a '!' character in front of the IP address
1017 * negates the logic (i.e. doesn't match).
1018 *
1019 * Here are our rules for matching an IP/host list:
1020 *
1021 * (negate-cond-1 && negate-cond-2 && ... negate-cond-n) &&
1022 * (cond-1 || cond-2 || ... cond-n)
1023 *
1024 * This boils down to the following two rules:
1025 *
1026 * 1. ALL negative ('!') conditions must evaluate to logically TRUE.
1027 * 2. One (or more) normal conditions must evaluate to logically TRUE.
1028 */
1029
1030 /* Check an ACL for negated rules and make sure all of them evaluate to TRUE.
1031 * Default (if none exist) is TRUE.
1032 */
check_ip_negative(const config_rec * c)1033 static int check_ip_negative(const config_rec *c) {
1034 int aclc;
1035 pr_netacl_t **aclv;
1036
1037 for (aclc = c->argc, aclv = (pr_netacl_t **) c->argv; aclc; aclc--, aclv++) {
1038 if (pr_netacl_get_negated(*aclv) == FALSE)
1039 continue;
1040
1041 switch (pr_netacl_match(*aclv, session.c->remote_addr)) {
1042 case 1:
1043 /* This actually means we DIDN'T match, and it's ok to short circuit
1044 * everything (negative).
1045 */
1046 return FALSE;
1047
1048 case -1:
1049 /* -1 signifies a NONE match, which isn't valid for negative
1050 * conditions.
1051 */
1052 pr_log_pri(PR_LOG_NOTICE,
1053 "ooops, it looks like !NONE was used in an ACL somehow");
1054 return FALSE;
1055
1056 default:
1057 /* This means our match is actually true and we can continue */
1058 break;
1059 }
1060 }
1061
1062 /* If we got this far either all conditions were TRUE or there were no
1063 * conditions.
1064 */
1065
1066 return TRUE;
1067 }
1068
1069 /* Check an ACL for positive conditions, short-circuiting if ANY of them are
1070 * TRUE. Default return is FALSE.
1071 */
check_ip_positive(const config_rec * c)1072 static int check_ip_positive(const config_rec *c) {
1073 int aclc;
1074 pr_netacl_t **aclv;
1075
1076 for (aclc = c->argc, aclv = (pr_netacl_t **) c->argv; aclc; aclc--, aclv++) {
1077 if (pr_netacl_get_negated(*aclv) == TRUE)
1078 continue;
1079
1080 switch (pr_netacl_match(*aclv, session.c->remote_addr)) {
1081 case 1:
1082 /* Found it! */
1083 return TRUE;
1084
1085 case -1:
1086 /* Special value "NONE", meaning nothing can match, so we can
1087 * short-circuit on this as well.
1088 */
1089 return FALSE;
1090
1091 default:
1092 /* No match, keep trying */
1093 break;
1094 }
1095 }
1096
1097 /* default return value is FALSE */
1098 return FALSE;
1099 }
1100
check_ip_access(xaset_t * set,char * name)1101 static int check_ip_access(xaset_t *set, char *name) {
1102 int res = FALSE;
1103
1104 config_rec *c = find_config(set, CONF_PARAM, name, FALSE);
1105
1106 while (c) {
1107 pr_signals_handle();
1108
1109 /* If the negative check failed (default is success), short-circuit and
1110 * return FALSE
1111 */
1112 if (check_ip_negative(c) != TRUE) {
1113 return FALSE;
1114 }
1115
1116 /* Otherwise, continue on with boolean or check */
1117 if (check_ip_positive(c) == TRUE) {
1118 res = TRUE;
1119 }
1120
1121 /* Continue on, in case there are other acls that need to be checked
1122 * (multiple acls are logically OR'd)
1123 */
1124 c = find_config_next(c, c->next, CONF_PARAM, name, FALSE);
1125 }
1126
1127 return res;
1128 }
1129
1130 /* 1 if allowed, 0 otherwise */
1131
check_limit_allow(config_rec * c,cmd_rec * cmd)1132 static int check_limit_allow(config_rec *c, cmd_rec *cmd) {
1133 unsigned char *allow_all = NULL;
1134
1135 /* If session.groups is null, this means no authentication attempt has been
1136 * made, so we simply check for the very existence of an AllowGroup, and
1137 * assume (for now) it's allowed. This works because later calls to
1138 * check_limit_allow() WILL have filled in the group members and we can
1139 * truly check group membership at that time. Same goes for AllowUser.
1140 */
1141
1142 if (!session.user) {
1143 if (find_config(c->subset, CONF_PARAM, "AllowUser", FALSE)) {
1144 return 1;
1145 }
1146
1147 } else if (check_user_access(c->subset, "AllowUser")) {
1148 return 1;
1149 }
1150
1151 if (!session.groups) {
1152 if (find_config(c->subset, CONF_PARAM, "AllowGroup", FALSE)) {
1153 return 1;
1154 }
1155
1156 } else if (check_group_access(c->subset, "AllowGroup")) {
1157 return 1;
1158 }
1159
1160 if (session.conn_class != NULL &&
1161 check_class_access(c->subset, "AllowClass")) {
1162 return 1;
1163 }
1164
1165 if (check_ip_access(c->subset, "Allow")) {
1166 return 1;
1167 }
1168
1169 if (check_filter_access(c->subset, "AllowFilter", cmd)) {
1170 return 1;
1171 }
1172
1173 allow_all = get_param_ptr(c->subset, "AllowAll", FALSE);
1174 if (allow_all &&
1175 *allow_all == TRUE) {
1176 return 1;
1177 }
1178
1179 return 0;
1180 }
1181
check_limit_deny(config_rec * c,cmd_rec * cmd)1182 static int check_limit_deny(config_rec *c, cmd_rec *cmd) {
1183 unsigned char *deny_all = get_param_ptr(c->subset, "DenyAll", FALSE);
1184
1185 if (deny_all &&
1186 *deny_all == TRUE) {
1187 return 1;
1188 }
1189
1190 if (session.user &&
1191 check_user_access(c->subset, "DenyUser")) {
1192 return 1;
1193 }
1194
1195 if (session.groups &&
1196 check_group_access(c->subset, "DenyGroup")) {
1197 return 1;
1198 }
1199
1200 if (session.conn_class != NULL &&
1201 check_class_access(c->subset, "DenyClass")) {
1202 return 1;
1203 }
1204
1205 if (check_ip_access(c->subset, "Deny")) {
1206 return 1;
1207 }
1208
1209 if (check_filter_access(c->subset, "DenyFilter", cmd)) {
1210 return 1;
1211 }
1212
1213 return 0;
1214 }
1215
1216 /* check_limit returns 1 if allowed, 0 if implicitly allowed,
1217 * and -1 if implicitly denied and -2 if explicitly denied.
1218 */
1219
check_limit(config_rec * c,cmd_rec * cmd)1220 static int check_limit(config_rec *c, cmd_rec *cmd) {
1221 int *tmp = get_param_ptr(c->subset, "Order", FALSE);
1222 int order = tmp ? *tmp : ORDER_ALLOWDENY;
1223
1224 if (order == ORDER_DENYALLOW) {
1225 /* Check deny first */
1226
1227 if (check_limit_deny(c, cmd)) {
1228 /* Explicit deny */
1229 errno = EPERM;
1230 return -2;
1231 }
1232
1233 if (check_limit_allow(c, cmd)) {
1234 /* Explicit allow */
1235 return 1;
1236 }
1237
1238 /* Implicit deny */
1239 errno = EPERM;
1240 return -1;
1241 }
1242
1243 /* Check allow first */
1244 if (check_limit_allow(c, cmd)) {
1245 /* Explicit allow */
1246 return 1;
1247 }
1248
1249 if (check_limit_deny(c, cmd)) {
1250 /* Explicit deny */
1251 errno = EPERM;
1252 return -2;
1253 }
1254
1255 /* Implicit allow */
1256 return 0;
1257 }
1258
1259 /* Note: if and == 1, the logic is short circuited so that the first
1260 * failure results in a FALSE return from the entire function, if and
1261 * == 0, an ORing operation is assumed and the function will return
1262 * TRUE if any <limit LOGIN> allows access.
1263 */
1264
login_check_limits(xaset_t * set,int recurse,int and,int * found)1265 int login_check_limits(xaset_t *set, int recurse, int and, int *found) {
1266 int res = and;
1267 int rfound = 0;
1268 config_rec *c;
1269 int argc;
1270 char **argv;
1271
1272 *found = 0;
1273
1274 if (!set || !set->xas_list)
1275 return TRUE; /* default is to allow */
1276
1277 /* First check top level */
1278 for (c = (config_rec *) set->xas_list; c; c = c->next) {
1279 if (c->config_type == CONF_LIMIT) {
1280 for (argc = c->argc, argv = (char **) c->argv; argc; argc--, argv++) {
1281 if (strncasecmp(*argv, "LOGIN", 6) == 0) {
1282 break;
1283 }
1284 }
1285
1286 if (argc) {
1287 if (and) {
1288 switch (check_limit(c, NULL)) {
1289 case 1:
1290 res = (res && TRUE);
1291 (*found)++;
1292 break;
1293
1294 case -1:
1295 case -2:
1296 res = (res && FALSE);
1297 (*found)++;
1298 break;
1299 }
1300
1301 if (!res)
1302 break;
1303
1304 } else {
1305 switch (check_limit(c, NULL)) {
1306 case 1:
1307 res = TRUE;
1308 (*found)++;
1309 break;
1310
1311 case -1:
1312 case -2:
1313 (*found)++;
1314 break;
1315 }
1316 }
1317 }
1318 }
1319 }
1320
1321 if (((res && and) || (!res && !and && *found)) && recurse) {
1322 for (c = (config_rec *) set->xas_list; c; c = c->next) {
1323 if (c->config_type == CONF_ANON &&
1324 c->subset &&
1325 c->subset->xas_list) {
1326 if (and) {
1327 res = (res && login_check_limits(c->subset, recurse, and, &rfound));
1328 (*found) += rfound;
1329 if (!res)
1330 break;
1331
1332 } else {
1333 int rres;
1334
1335 rres = login_check_limits(c->subset, recurse, and, &rfound);
1336 if (rfound) {
1337 res = (res || rres);
1338 }
1339
1340 (*found) += rfound;
1341 if (res)
1342 break;
1343 }
1344 }
1345 }
1346 }
1347
1348 if (!*found && !and)
1349 return TRUE; /* Default is to allow */
1350
1351 return res;
1352 }
1353
1354 /* Check limit directives.
1355 */
check_limits(xaset_t * set,cmd_rec * cmd,const char * cmd_name,int hidden)1356 static int check_limits(xaset_t *set, cmd_rec *cmd, const char *cmd_name,
1357 int hidden) {
1358 int res = 1, ignore_hidden = -1;
1359 config_rec *lc = NULL;
1360
1361 errno = 0;
1362
1363 if (!set)
1364 return res;
1365
1366 for (lc = (config_rec *) set->xas_list; lc && (res == 1); lc = lc->next) {
1367 pr_signals_handle();
1368
1369 if (lc->config_type == CONF_LIMIT) {
1370 register unsigned int i = 0;
1371
1372 for (i = 0; i < lc->argc; i++) {
1373 if (strcasecmp(cmd_name, (char *) lc->argv[i]) == 0) {
1374 break;
1375 }
1376 }
1377
1378 if (i == lc->argc)
1379 continue;
1380
1381 /* Found a <Limit> directive associated with the current command.
1382 * ignore_hidden defaults to -1, if an explicit IgnoreHidden off is seen,
1383 * it is set to 0 and the check will not be done again up the chain. If
1384 * an explicit "IgnoreHidden on" is seen, checking short-circuits and we
1385 * set ENOENT.
1386 */
1387
1388 if (hidden && ignore_hidden == -1) {
1389 unsigned char *ignore = get_param_ptr(lc->subset, "IgnoreHidden",
1390 FALSE);
1391
1392 if (ignore)
1393 ignore_hidden = *ignore;
1394
1395 if (ignore_hidden == 1) {
1396 res = 0;
1397 errno = ENOENT;
1398 break;
1399 }
1400 }
1401
1402 switch (check_limit(lc, cmd)) {
1403 case 1:
1404 res++;
1405 break;
1406
1407 case -1:
1408 case -2:
1409 res = 0;
1410 break;
1411
1412 default:
1413 continue;
1414 }
1415 }
1416 }
1417
1418 if (!res && !errno)
1419 errno = EACCES;
1420
1421 return res;
1422 }
1423
dir_check_limits(cmd_rec * cmd,config_rec * c,const char * cmd_name,int hidden)1424 int dir_check_limits(cmd_rec *cmd, config_rec *c, const char *cmd_name,
1425 int hidden) {
1426 int res = 1;
1427
1428 for (; c && (res == 1); c = c->parent) {
1429 res = check_limits(c->subset, cmd, cmd_name, hidden);
1430 }
1431
1432 if (!c && (res == 1)) {
1433 /* vhost or main server has been reached without an explicit permit or deny,
1434 * so try the current server.
1435 */
1436 res = check_limits(main_server->conf, cmd, cmd_name, hidden);
1437 }
1438
1439 return res;
1440 }
1441
1442 /* Manage .ftpaccess dynamic directory sections
1443 *
1444 * build_dyn_config() is called to check for and then handle .ftpaccess
1445 * files. It determines:
1446 *
1447 * - whether an .ftpaccess file exists in a directory
1448 * - whether an existing .ftpaccess section for that file exists
1449 * - whether a new .ftpaccess section needs to be constructed
1450 * - whether an existing .ftpaccess section needs rebuilding
1451 * as its corresponding .ftpaccess file has been modified
1452 * - whether an existing .ftpaccess section must now be removed
1453 * as its corresponding .ftpaccess file has disappeared
1454 *
1455 * The routine must check for .ftpaccess files in each directory that is
1456 * a component of the path argument. The input path may be for either a
1457 * directory or file, and that may or may not already exist.
1458 *
1459 * build_dyn_config() may be called with a path to:
1460 *
1461 * - an existing directory - start check in that dir
1462 * - an existing file - start check in containing dir
1463 * - a proposed directory - start check in containing dir
1464 * - a proposed file - start check in containing dir
1465 *
1466 * As in 1.3.3b code, the key is that for path "/a/b/c", one of either
1467 * "/a/b/c" or "/a/b" is an existing directory, or we MUST give up as we
1468 * cannot even start scanning for .ftpaccess files without a valid starting
1469 * directory.
1470 */
build_dyn_config(pool * p,const char * _path,struct stat * stp,unsigned char recurse)1471 void build_dyn_config(pool *p, const char *_path, struct stat *stp,
1472 unsigned char recurse) {
1473 struct stat st;
1474 config_rec *d = NULL;
1475 xaset_t **set = NULL;
1476 int isfile, removed = 0;
1477 char *ptr = NULL;
1478
1479 /* Need three path strings:
1480 *
1481 * curr_dir_path: current relative directory path, for tracking our
1482 * progress as we scan upwards
1483 *
1484 * ftpaccess_path: current relative file path to the .ftpaccess file for
1485 * which to check.
1486 *
1487 * ftpaccess_name: absolute directory path of the .ftpaccess file,
1488 * to be used as the name for the new config_rec.
1489 */
1490 char *curr_dir_path = NULL, *ftpaccess_path = NULL, *ftpaccess_name = NULL;
1491
1492 /* Switch through each directory, from "deepest" up looking for
1493 * new or updated .ftpaccess files
1494 */
1495
1496 if (!_path)
1497 return;
1498
1499 /* Check to see whether .ftpaccess files are allowed to be parsed. */
1500 if (!allow_dyn_config(_path))
1501 return;
1502
1503 /* Determine the starting directory path for the .ftpaccess file scan. */
1504 memcpy(&st, stp, sizeof(st));
1505 curr_dir_path = pstrdup(p, _path);
1506
1507 if (!S_ISDIR(st.st_mode)) {
1508
1509 /* If the given st is not for a directory (i.e. path is for a file),
1510 * then construct the path for the .ftpaccess file to check.
1511 *
1512 * strrchr(3) should always return non-NULL here, right?
1513 */
1514 ptr = strrchr(curr_dir_path, '/');
1515 if (ptr != NULL) {
1516 *ptr = '\0';
1517 }
1518 }
1519
1520 while (curr_dir_path) {
1521 size_t curr_dir_pathlen;
1522
1523 pr_signals_handle();
1524
1525 curr_dir_pathlen = strlen(curr_dir_path);
1526
1527 /* Remove any trailing "*" character. */
1528 if (curr_dir_pathlen > 1 &&
1529 *(curr_dir_path + curr_dir_pathlen - 1) == '*') {
1530 *(curr_dir_path + curr_dir_pathlen - 1) = '\0';
1531 curr_dir_pathlen--;
1532 }
1533
1534 /* Trim any trailing path separator (unless it is the first AND last
1535 * character, e.g. "/"). For example:
1536 *
1537 * "/a/b/" --> "/a/b"
1538 * "/a/" --> "/a"
1539 * "/" --> "/"
1540 *
1541 * The check for a string length greater than 1 character skips the
1542 * "/" case effectively.
1543 */
1544
1545 if (curr_dir_pathlen > 1 &&
1546 *(curr_dir_path + curr_dir_pathlen - 1) == '/') {
1547 *(curr_dir_path + curr_dir_pathlen - 1) = '\0';
1548 curr_dir_pathlen--;
1549 }
1550
1551 ftpaccess_path = pdircat(p, curr_dir_path, ".ftpaccess", NULL);
1552
1553 /* Construct the name for the config_rec name for the .ftpaccess file
1554 * from curr_dir_path.
1555 */
1556
1557 if (session.chroot_path) {
1558 size_t ftpaccess_namelen;
1559
1560 ftpaccess_name = pdircat(p, session.chroot_path, curr_dir_path,
1561 NULL);
1562
1563 ftpaccess_namelen = strlen(ftpaccess_name);
1564
1565 if (ftpaccess_namelen > 1 &&
1566 *(ftpaccess_name + ftpaccess_namelen - 1) == '/') {
1567 *(ftpaccess_name + ftpaccess_namelen - 1) = '\0';
1568 ftpaccess_namelen--;
1569 }
1570
1571 } else {
1572 ftpaccess_name = curr_dir_path;
1573 }
1574
1575 if (ftpaccess_path != NULL) {
1576 pr_trace_msg("ftpaccess", 6, "checking for .ftpaccess file '%s'",
1577 ftpaccess_path);
1578 isfile = pr_fsio_stat(ftpaccess_path, &st);
1579
1580 } else {
1581 isfile = -1;
1582 }
1583
1584 d = dir_match_path(p, ftpaccess_name);
1585
1586 if (!d &&
1587 isfile != -1 &&
1588 st.st_size > 0) {
1589 set = (session.anon_config ? &session.anon_config->subset :
1590 &main_server->conf);
1591
1592 pr_trace_msg("ftpaccess", 6, "adding config for '%s'", ftpaccess_name);
1593
1594 d = pr_config_add_set(set, ftpaccess_name, 0);
1595 d->config_type = CONF_DIR;
1596 d->argc = 1;
1597 d->argv = pcalloc(d->pool, 2 * sizeof (void *));
1598
1599 } else if (d) {
1600 config_rec *newd, *dnext;
1601
1602 if (isfile != -1 &&
1603 st.st_size > 0 &&
1604 strcmp(d->name, ftpaccess_name) != 0) {
1605 set = &d->subset;
1606
1607 pr_trace_msg("ftpaccess", 6, "adding config for '%s'", ftpaccess_name);
1608
1609 newd = pr_config_add_set(set, ftpaccess_name, 0);
1610 newd->config_type = CONF_DIR;
1611 newd->argc = 1;
1612 newd->argv = pcalloc(newd->pool, 2 * sizeof(void *));
1613 newd->parent = d;
1614
1615 d = newd;
1616
1617 } else if (strcmp(d->name, ftpaccess_name) == 0 &&
1618 (isfile == -1 ||
1619 st.st_mtime > (d->argv[0] ? *((time_t *) d->argv[0]) : 0))) {
1620
1621 set = (d->parent ? &d->parent->subset : &main_server->conf);
1622
1623 if (d->subset &&
1624 d->subset->xas_list) {
1625
1626 /* Remove all old dynamic entries. */
1627 for (newd = (config_rec *) d->subset->xas_list; newd; newd = dnext) {
1628 dnext = newd->next;
1629
1630 if (newd->flags & CF_DYNAMIC) {
1631 xaset_remove(d->subset, (xasetmember_t *) newd);
1632 removed++;
1633 }
1634 }
1635 }
1636
1637 if (d->subset &&
1638 !d->subset->xas_list) {
1639 destroy_pool(d->subset->pool);
1640 d->subset = NULL;
1641 d->argv[0] = NULL;
1642
1643 /* If the file has been removed and no entries exist in this
1644 * dynamic entry, remove it completely.
1645 */
1646 if (isfile == -1) {
1647 xaset_remove(*set, (xasetmember_t *) d);
1648 }
1649 }
1650 }
1651 }
1652
1653 if (isfile != -1 &&
1654 d &&
1655 st.st_size > 0 &&
1656 st.st_mtime > (d->argv[0] ? *((time_t *) d->argv[0]) : 0)) {
1657 int res;
1658
1659 /* File has been modified or not loaded yet */
1660 d->argv[0] = pcalloc(d->pool, sizeof(time_t));
1661 *((time_t *) d->argv[0]) = st.st_mtime;
1662
1663 d->config_type = CONF_DYNDIR;
1664
1665 pr_trace_msg("ftpaccess", 3, "parsing '%s'", ftpaccess_path);
1666
1667 pr_parser_prepare(p, NULL);
1668 res = pr_parser_parse_file(p, ftpaccess_path, d,
1669 PR_PARSER_FL_DYNAMIC_CONFIG);
1670 pr_parser_cleanup();
1671
1672 if (res == 0) {
1673 d->config_type = CONF_DIR;
1674 pr_config_merge_down(*set, TRUE);
1675
1676 pr_trace_msg("ftpaccess", 3, "fixing up directory configs");
1677 fixup_dirs(main_server, CF_SILENT);
1678
1679 } else {
1680 int xerrno = errno;
1681
1682 pr_trace_msg("ftpaccess", 2, "error parsing '%s': %s", ftpaccess_path,
1683 strerror(xerrno));
1684 pr_log_debug(DEBUG0, "error parsing '%s': %s", ftpaccess_path,
1685 strerror(xerrno));
1686 }
1687 }
1688
1689 if (isfile == -1 &&
1690 removed &&
1691 d &&
1692 set) {
1693 pr_trace_msg("ftpaccess", 6, "adding config for '%s'", ftpaccess_name);
1694 pr_config_merge_down(*set, FALSE);
1695 }
1696
1697 if (!recurse)
1698 break;
1699
1700 /* Remove the last path component of current directory path. */
1701 ptr = strrchr(curr_dir_path, '/');
1702 if (ptr != NULL) {
1703 /* We need to handle the case where path might be "/path". We
1704 * can't just set *ptr to '\0', as that would result in the empty
1705 * string. Thus check if ptr is the same value as curr_dir_path, i.e.
1706 * that ptr points to the start of the string. If so, by definition
1707 * we know that we are dealing with the "/path" case.
1708 */
1709 if (ptr == curr_dir_path) {
1710 if (strncmp(curr_dir_path, "/", 2) == 0) {
1711 /* We've reached the top; stop scanning. */
1712 curr_dir_path = NULL;
1713
1714 } else {
1715 *(ptr+1) = '\0';
1716 }
1717
1718 } else {
1719 *ptr = '\0';
1720 }
1721
1722 } else {
1723 curr_dir_path = NULL;
1724 }
1725 }
1726
1727 return;
1728 }
1729
1730 /* dir_check_full() fully recurses the path passed
1731 * returns 1 if operation is allowed on current path,
1732 * or 0 if not.
1733 */
1734
1735 /* dir_check_full() and dir_check() both take a `hidden' argument which is a
1736 * pointer to an integer. This is provided so that they can tell the calling
1737 * function if an entry should be hidden or not. This is used by mod_ls to
1738 * determine if a file should be displayed. Note that in this context, hidden
1739 * means "hidden by configuration" (HideUser, etc), NOT "hidden because it's a
1740 * .dotfile".
1741 */
1742
dir_check_full(pool * pp,cmd_rec * cmd,const char * group,const char * path,int * hidden)1743 int dir_check_full(pool *pp, cmd_rec *cmd, const char *group, const char *path,
1744 int *hidden) {
1745 char *fullpath, *owner;
1746 config_rec *c;
1747 struct stat st;
1748 pool *p;
1749 mode_t _umask = (mode_t) -1;
1750 int res = 1, isfile;
1751 int op_hidden = FALSE, regex_hidden = FALSE;
1752
1753 if (path == NULL) {
1754 errno = EINVAL;
1755 return -1;
1756 }
1757
1758 p = make_sub_pool(pp);
1759 pr_pool_tag(p, "dir_check_full() subpool");
1760
1761 fullpath = (char *) path;
1762
1763 if (session.chroot_path) {
1764 fullpath = pdircat(p, session.chroot_path, fullpath, NULL);
1765 }
1766
1767 if (*path) {
1768 /* Only log this debug line if we are dealing with a real path. */
1769 pr_log_debug(DEBUG5, "in dir_check_full(): path = '%s', fullpath = '%s'",
1770 path, fullpath);
1771 }
1772
1773 /* Check and build all appropriate dynamic configuration entries */
1774 isfile = pr_fsio_stat(path, &st);
1775 if (isfile < 0) {
1776 memset(&st, '\0', sizeof(st));
1777 }
1778
1779 build_dyn_config(p, path, &st, TRUE);
1780
1781 /* Check to see if this path is hidden by HideFiles. */
1782 regex_hidden = dir_hide_file(path);
1783
1784 /* Cache a pointer to the set of configuration data for this directory in
1785 * session.dir_config.
1786 */
1787 session.dir_config = c = dir_match_path(p, fullpath);
1788 if (session.dir_config) {
1789 pr_trace_msg("directory", 2, "matched <Directory %s> for '%s'",
1790 session.dir_config->name, fullpath);
1791 }
1792
1793 if (!c && session.anon_config) {
1794 c = session.anon_config;
1795 }
1796
1797 /* Make sure this cmd_rec has a cmd_id. */
1798 if (cmd->cmd_id == 0) {
1799 cmd->cmd_id = pr_cmd_get_id(cmd->argv[0]);
1800 }
1801
1802 if (!_kludge_disable_umask) {
1803 /* Check for a directory Umask. */
1804 if (S_ISDIR(st.st_mode) ||
1805 pr_cmd_cmp(cmd, PR_CMD_MKD_ID) == 0 ||
1806 pr_cmd_cmp(cmd, PR_CMD_XMKD_ID) == 0) {
1807 mode_t *dir_umask = NULL;
1808
1809 dir_umask = get_param_ptr(CURRENT_CONF, "DirUmask", FALSE);
1810 if (dir_umask) {
1811 pr_trace_msg("directory", 2, "found DirUmask %04o for directory '%s'",
1812 *dir_umask, path);
1813 }
1814
1815 _umask = dir_umask ? *dir_umask : (mode_t) -1;
1816 }
1817
1818 /* It's either a file, or we had no directory Umask. */
1819 if (_umask == (mode_t) -1) {
1820 mode_t *file_umask = get_param_ptr(CURRENT_CONF, "Umask", FALSE);
1821 _umask = file_umask ? *file_umask : (mode_t) 0022;
1822 }
1823 }
1824
1825 session.fsuid = (uid_t) -1;
1826 session.fsgid = (gid_t) -1;
1827
1828 owner = get_param_ptr(CURRENT_CONF, "UserOwner", FALSE);
1829 if (owner != NULL) {
1830 /* Attempt chown() on all new files. */
1831 struct passwd *pw;
1832
1833 pw = pr_auth_getpwnam(p, owner);
1834 if (pw != NULL) {
1835 session.fsuid = pw->pw_uid;
1836 }
1837 }
1838
1839 owner = get_param_ptr(CURRENT_CONF, "GroupOwner", FALSE);
1840 if (owner != NULL) {
1841 /* Attempt chgrp() on all new files. */
1842
1843 if (strncmp(owner, "~", 2) != 0) {
1844 struct group *gr;
1845
1846 gr = pr_auth_getgrnam(p, owner);
1847 if (gr != NULL) {
1848 session.fsgid = gr->gr_gid;
1849 }
1850
1851 } else {
1852 session.fsgid = session.gid;
1853 }
1854 }
1855
1856 if (isfile != -1) {
1857 /* Check to see if the current config "hides" the path or not. */
1858 op_hidden = !dir_check_op(p, CURRENT_CONF, OP_HIDE,
1859 session.chroot_path ? path : fullpath, st.st_uid, st.st_gid, st.st_mode);
1860
1861 res = dir_check_op(p, CURRENT_CONF, OP_COMMAND,
1862 session.chroot_path ? path : fullpath, st.st_uid, st.st_gid, st.st_mode);
1863 }
1864
1865 if (res) {
1866 /* Note that dir_check_limits() also handles IgnoreHidden. If it is set,
1867 * these return 0 (no access), and also set errno to ENOENT so it looks
1868 * like the file doesn't exist.
1869 */
1870 res = dir_check_limits(cmd, c, cmd->argv[0], op_hidden || regex_hidden);
1871
1872 /* If specifically allowed, res will be > 1 and we don't want to
1873 * check the command group limit.
1874 */
1875 if (res == 1 && group) {
1876 res = dir_check_limits(cmd, c, group, op_hidden || regex_hidden);
1877 }
1878
1879 /* If still == 1, no explicit allow so check lowest priority "ALL" group.
1880 * Note that certain commands are deliberately excluded from the
1881 * ALL group (i.e. EPRT, EPSV, PASV, PORT, and OPTS).
1882 */
1883 if (res == 1 &&
1884 pr_cmd_cmp(cmd, PR_CMD_EPRT_ID) != 0 &&
1885 pr_cmd_cmp(cmd, PR_CMD_EPSV_ID) != 0 &&
1886 pr_cmd_cmp(cmd, PR_CMD_PASV_ID) != 0 &&
1887 pr_cmd_cmp(cmd, PR_CMD_PORT_ID) != 0 &&
1888 pr_cmd_cmp(cmd, PR_CMD_PROT_ID) != 0 &&
1889 strncmp(cmd->argv[0], C_OPTS, 4) != 0) {
1890 res = dir_check_limits(cmd, c, "ALL", op_hidden || regex_hidden);
1891 }
1892 }
1893
1894 if (res &&
1895 _umask != (mode_t) -1) {
1896 pr_log_debug(DEBUG5,
1897 "in dir_check_full(): setting umask to %04o (was %04o)",
1898 (unsigned int) _umask, (unsigned int) umask(_umask));
1899 }
1900
1901 destroy_pool(p);
1902
1903 if (hidden) {
1904 *hidden = op_hidden || regex_hidden;
1905 }
1906
1907 return res;
1908 }
1909
1910 /* dir_check() checks the current dir configuration against the path,
1911 * if it matches (partially), a search is done only in the subconfig,
1912 * otherwise handed off to dir_check_full
1913 */
1914
dir_check(pool * pp,cmd_rec * cmd,const char * group,const char * path,int * hidden)1915 int dir_check(pool *pp, cmd_rec *cmd, const char *group, const char *path,
1916 int *hidden) {
1917 char *fullpath, *owner;
1918 config_rec *c;
1919 struct stat st;
1920 pool *p;
1921 mode_t _umask = (mode_t) -1;
1922 int res = 1, isfile;
1923 int op_hidden = FALSE, regex_hidden = FALSE;
1924
1925 if (path == NULL) {
1926 errno = EINVAL;
1927 return -1;
1928 }
1929
1930 p = make_sub_pool(pp);
1931 pr_pool_tag(p, "dir_check() subpool");
1932
1933 fullpath = (char *) path;
1934
1935 if (session.chroot_path) {
1936 fullpath = pdircat(p, session.chroot_path, fullpath, NULL);
1937 }
1938
1939 c = (session.dir_config ? session.dir_config :
1940 (session.anon_config ? session.anon_config : NULL));
1941
1942 if (!c || strncmp(c->name, fullpath, strlen(c->name)) != 0) {
1943 destroy_pool(p);
1944 return dir_check_full(pp, cmd, group, path, hidden);
1945 }
1946
1947 /* Check and build all appropriate dynamic configuration entries */
1948 isfile = pr_fsio_stat(path, &st);
1949 if (isfile < 0) {
1950 memset(&st, 0, sizeof(st));
1951 }
1952
1953 build_dyn_config(p, path, &st, FALSE);
1954
1955 /* Check to see if this path is hidden by HideFiles. */
1956 regex_hidden = dir_hide_file(path);
1957
1958 /* Cache a pointer to the set of configuration data for this directory in
1959 * session.dir_config.
1960 */
1961 session.dir_config = c = dir_match_path(p, fullpath);
1962 if (session.dir_config) {
1963 pr_trace_msg("directory", 2, "matched <Directory %s> for '%s'",
1964 session.dir_config->name, fullpath);
1965 }
1966
1967 if (!c && session.anon_config) {
1968 c = session.anon_config;
1969 }
1970
1971 /* Make sure this cmd_rec has a cmd_id. */
1972 if (cmd->cmd_id == 0) {
1973 cmd->cmd_id = pr_cmd_get_id(cmd->argv[0]);
1974 }
1975
1976 if (!_kludge_disable_umask) {
1977 /* Check for a directory Umask. */
1978 if (S_ISDIR(st.st_mode) ||
1979 pr_cmd_cmp(cmd, PR_CMD_MKD_ID) == 0 ||
1980 pr_cmd_cmp(cmd, PR_CMD_XMKD_ID) == 0) {
1981 mode_t *dir_umask = NULL;
1982
1983 dir_umask = get_param_ptr(CURRENT_CONF, "DirUmask", FALSE);
1984 if (dir_umask) {
1985 pr_trace_msg("directory", 2, "found DirUmask %04o for directory '%s'",
1986 *dir_umask, path);
1987 }
1988
1989 _umask = dir_umask ? *dir_umask : (mode_t) -1;
1990 }
1991
1992 /* It's either a file, or we had no directory Umask. */
1993 if (_umask == (mode_t) -1) {
1994 mode_t *file_umask = get_param_ptr(CURRENT_CONF, "Umask", FALSE);
1995 _umask = file_umask ? *file_umask : (mode_t) 0022;
1996 }
1997 }
1998
1999 session.fsuid = (uid_t) -1;
2000 session.fsgid = (gid_t) -1;
2001
2002 owner = get_param_ptr(CURRENT_CONF, "UserOwner", FALSE);
2003 if (owner != NULL) {
2004 /* Attempt chown() on all new files. */
2005 struct passwd *pw;
2006
2007 pw = pr_auth_getpwnam(p, owner);
2008 if (pw != NULL) {
2009 session.fsuid = pw->pw_uid;
2010 }
2011 }
2012
2013 owner = get_param_ptr(CURRENT_CONF, "GroupOwner", FALSE);
2014 if (owner != NULL) {
2015 /* Attempt chgrp() on all new files. */
2016
2017 if (strncmp(owner, "~", 2) != 0) {
2018 struct group *gr;
2019
2020 gr = pr_auth_getgrnam(p, owner);
2021 if (gr != NULL) {
2022 session.fsgid = gr->gr_gid;
2023 }
2024
2025 } else {
2026 session.fsgid = session.gid;
2027 }
2028 }
2029
2030 if (isfile != -1) {
2031 /* If not already marked as hidden by its name, check to see if the path
2032 * is to be hidden by nature of its mode
2033 */
2034 op_hidden = !dir_check_op(p, CURRENT_CONF, OP_HIDE,
2035 session.chroot_path ? path : fullpath, st.st_uid, st.st_gid, st.st_mode);
2036
2037 res = dir_check_op(p, CURRENT_CONF, OP_COMMAND,
2038 session.chroot_path ? path : fullpath, st.st_uid, st.st_gid, st.st_mode);
2039 }
2040
2041 if (res) {
2042 res = dir_check_limits(cmd, c, cmd->argv[0], op_hidden || regex_hidden);
2043
2044 /* If specifically allowed, res will be > 1 and we don't want to
2045 * check the command group limit.
2046 */
2047 if (res == 1 && group) {
2048 res = dir_check_limits(cmd, c, group, op_hidden || regex_hidden);
2049 }
2050
2051 /* If still == 1, no explicit allow so check lowest priority "ALL" group.
2052 * Note that certain commands are deliberately excluded from the
2053 * ALL group (i.e. EPRT, EPSV, PASV, PORT, and OPTS).
2054 */
2055 if (res == 1 &&
2056 pr_cmd_cmp(cmd, PR_CMD_EPRT_ID) != 0 &&
2057 pr_cmd_cmp(cmd, PR_CMD_EPSV_ID) != 0 &&
2058 pr_cmd_cmp(cmd, PR_CMD_PASV_ID) != 0 &&
2059 pr_cmd_cmp(cmd, PR_CMD_PORT_ID) != 0 &&
2060 pr_cmd_cmp(cmd, PR_CMD_PROT_ID) != 0 &&
2061 strncmp(cmd->argv[0], C_OPTS, 4) != 0) {
2062 res = dir_check_limits(cmd, c, "ALL", op_hidden || regex_hidden);
2063 }
2064 }
2065
2066 if (res &&
2067 _umask != (mode_t) -1) {
2068 pr_log_debug(DEBUG5, "in dir_check(): setting umask to %04o (was %04o)",
2069 (unsigned int) _umask, (unsigned int) umask(_umask));
2070 }
2071
2072 destroy_pool(p);
2073
2074 if (hidden) {
2075 *hidden = op_hidden || regex_hidden;
2076 }
2077
2078 return res;
2079 }
2080
2081 /* dir_check_canon() canonocalizes as much of the path as possible (which may
2082 * not be all of it, as the target may not yet exist) then we hand off to
2083 * dir_check().
2084 */
dir_check_canon(pool * pp,cmd_rec * cmd,const char * group,const char * path,int * hidden)2085 int dir_check_canon(pool *pp, cmd_rec *cmd, const char *group,
2086 const char *path, int *hidden) {
2087 return dir_check(pp, cmd, group, dir_best_path(pp, path), hidden);
2088 }
2089
2090 /* Move all the members (i.e. a "branch") of one config set to a different
2091 * parent.
2092 */
reparent_all(config_rec * newparent,xaset_t * set)2093 static void reparent_all(config_rec *newparent, xaset_t *set) {
2094 config_rec *c, *cnext;
2095
2096 if (!newparent->subset)
2097 newparent->subset = xaset_create(newparent->pool, NULL);
2098
2099 for (c = (config_rec *) set->xas_list; c; c = cnext) {
2100 cnext = c->next;
2101 xaset_remove(set, (xasetmember_t *) c);
2102 xaset_insert(newparent->subset, (xasetmember_t *) c);
2103 c->set = newparent->subset;
2104 c->parent = newparent;
2105 }
2106 }
2107
2108 /* Recursively find the most appropriate place to move a CONF_DIR
2109 * directive to.
2110 */
find_best_dir(xaset_t * set,char * path,size_t * matchlen)2111 static config_rec *find_best_dir(xaset_t *set, char *path, size_t *matchlen) {
2112 config_rec *c, *res = NULL, *rres;
2113 size_t len, pathlen, imatchlen, tmatchlen;
2114
2115 *matchlen = 0;
2116
2117 if (set == NULL ||
2118 set->xas_list == NULL) {
2119 errno = EINVAL;
2120 return NULL;
2121 }
2122
2123 pathlen = strlen(path);
2124
2125 for (c = (config_rec *) set->xas_list; c; c = c->next) {
2126 if (c->config_type == CONF_DIR) {
2127 /* Note: this comparison of pointers, rather than of strings, is
2128 * intentional. DO NOT CHANGE THIS TO A strcmp()!
2129 *
2130 * This function is only called by reorder_dirs(), and reorder_dirs()
2131 * always uses a c->name as the path parameter. This means that
2132 * doing direct pointer/address comparisons is valid. If ever this
2133 * assumption is broken, we will need to revert back to a more
2134 * costly (especially when there are many <Directory> config sections)
2135 * use of strcmp(3).
2136 */
2137 if (c->name == path) {
2138 continue;
2139 }
2140
2141 len = strlen(c->name);
2142
2143 /* Do NOT change the zero here to a one; the expression IS correct. */
2144 while (len > 0 &&
2145 (*(c->name+len-1) == '*' || *(c->name+len-1) == '/')) {
2146 len--;
2147 }
2148
2149 /* Just a partial match on the pathname does not mean that the longer
2150 * path is the subdirectory of the other -- they might just be sharing
2151 * the last path component!
2152 * /var/www/.1
2153 * /var/www/.14
2154 * ^ -- not /, not subdir
2155 * /var/www/.1
2156 * /var/www/.1/images
2157 * ^ -- /, is subdir
2158 *
2159 * And then there are glob considerations, e.g.:
2160 *
2161 * /var/www/<glob>/dir2
2162 * /var/www/dir1/dir2
2163 *
2164 * In these cases, we need to make sure that the glob path appears
2165 * BEFORE the exact path. Right?
2166 */
2167 if (pathlen > len &&
2168 path[len] != '/') {
2169 continue;
2170 }
2171
2172 if (len < pathlen &&
2173 strncmp(c->name, path, len) == 0) {
2174 rres = find_best_dir(c->subset ,path, &imatchlen);
2175 tmatchlen = _strmatch(path, c->name);
2176 if (!rres &&
2177 tmatchlen > *matchlen) {
2178 res = c;
2179 *matchlen = tmatchlen;
2180
2181 } else if (imatchlen > *matchlen) {
2182 res = rres;
2183 *matchlen = imatchlen;
2184 }
2185 }
2186 }
2187 }
2188
2189 return res;
2190 }
2191
2192 /* Reorder all the CONF_DIR configuration sections, so that they are
2193 * in directory tree order
2194 */
2195
reorder_dirs(xaset_t * set,int flags)2196 static void reorder_dirs(xaset_t *set, int flags) {
2197 config_rec *c = NULL, *cnext = NULL, *newparent = NULL;
2198 int defer = 0;
2199 size_t tmp;
2200
2201 if (set == NULL ||
2202 set->xas_list == NULL) {
2203 return;
2204 }
2205
2206 /* Ignore the CF_SILENT flag for purposes of reordering. */
2207 flags &= ~CF_SILENT;
2208
2209 if (!(flags & CF_DEFER)) {
2210 defer = 1;
2211 }
2212
2213 for (c = (config_rec *) set->xas_list; c; c = cnext) {
2214 cnext = c->next;
2215
2216 pr_signals_handle();
2217
2218 if (c->config_type == CONF_DIR) {
2219 if (flags && !(c->flags & flags))
2220 continue;
2221
2222 if (defer && (c->flags & CF_DEFER))
2223 continue;
2224
2225 /* If <Directory *> is used inside <Anonymous>, move all
2226 * the directives from '*' into the higher level.
2227 */
2228 if (c->parent &&
2229 c->parent->config_type == CONF_ANON &&
2230 strncmp(c->name, "*", 2) == 0) {
2231
2232 if (c->subset)
2233 reparent_all(c->parent, c->subset);
2234
2235 xaset_remove(c->parent->subset, (xasetmember_t *) c);
2236
2237 } else {
2238 newparent = find_best_dir(set, c->name, &tmp);
2239 if (newparent) {
2240 if (!newparent->subset)
2241 newparent->subset = xaset_create(newparent->pool, NULL);
2242
2243 xaset_remove(c->set, (xasetmember_t *) c);
2244 xaset_insert(newparent->subset, (xasetmember_t *) c);
2245 c->set = newparent->subset;
2246 c->parent = newparent;
2247 }
2248 }
2249 }
2250 }
2251
2252 /* Top level is now sorted, now we recursively sort all the sublevels. */
2253 for (c = (config_rec *) set->xas_list; c; c = c->next) {
2254 if (c->config_type == CONF_DIR || c->config_type == CONF_ANON) {
2255 reorder_dirs(c->subset, flags);
2256 }
2257 }
2258 }
2259
2260 #ifdef PR_USE_DEVEL
pr_dirs_dump(void (* dumpf)(const char *,...),xaset_t * s,char * indent)2261 void pr_dirs_dump(void (*dumpf)(const char *, ...), xaset_t *s, char *indent) {
2262 config_rec *c;
2263
2264 if (s == NULL) {
2265 return;
2266 }
2267
2268 if (indent == NULL) {
2269 indent = " ";
2270 }
2271
2272 for (c = (config_rec *) s->xas_list; c; c = c->next) {
2273 pr_signals_handle();
2274
2275 if (c->config_type != CONF_DIR) {
2276 continue;
2277 }
2278
2279 dumpf("%s<Directory %s>", indent, c->name);
2280
2281 if (c->subset) {
2282 pr_dirs_dump(dumpf, c->subset, pstrcat(c->pool, indent, " ", NULL));
2283 }
2284 }
2285
2286 return;
2287 }
2288 #endif /* PR_USE_DEVEL */
2289
2290 /* Iterate through <Directory> blocks inside of anonymous and
2291 * resolve each one.
2292 */
resolve_anonymous_dirs(xaset_t * clist)2293 void resolve_anonymous_dirs(xaset_t *clist) {
2294 config_rec *c;
2295 char *realdir;
2296
2297 if (!clist) {
2298 return;
2299 }
2300
2301 for (c = (config_rec *) clist->xas_list; c; c = c->next) {
2302 if (c->config_type == CONF_DIR) {
2303 if (c->argv[1]) {
2304 realdir = dir_best_path(c->pool, c->argv[1]);
2305 if (realdir) {
2306 c->argv[1] = realdir;
2307
2308 } else {
2309 realdir = dir_canonical_path(c->pool, c->argv[1]);
2310 if (realdir) {
2311 c->argv[1] = realdir;
2312 }
2313 }
2314 }
2315
2316 if (c->subset) {
2317 resolve_anonymous_dirs(c->subset);
2318 }
2319 }
2320 }
2321 }
2322
2323 /* Iterate through directory configuration items and resolve ~ references. */
resolve_deferred_dirs(server_rec * s)2324 void resolve_deferred_dirs(server_rec *s) {
2325 config_rec *c;
2326
2327 if (s == NULL ||
2328 s->conf == NULL) {
2329 return;
2330 }
2331
2332 for (c = (config_rec *) s->conf->xas_list; c; c = c->next) {
2333 if (c->config_type == CONF_DIR &&
2334 (c->flags & CF_DEFER)) {
2335 char *interp_dir = NULL, *real_dir = NULL, *orig_name = NULL;
2336 const char *trace_channel = "directory";
2337
2338 if (pr_trace_get_level(trace_channel) >= 11) {
2339 orig_name = pstrdup(c->pool, c->name);
2340 }
2341
2342 /* Check for any expandable variables. */
2343 c->name = (char *) path_subst_uservar(c->pool, (const char **) &c->name);
2344
2345 /* Handle any '~' interpolation. */
2346 interp_dir = dir_interpolate(c->pool, c->name);
2347 if (interp_dir == NULL) {
2348 /* This can happen when the '~' is just that, and does not refer
2349 * to any known user.
2350 */
2351 interp_dir = c->name;
2352 }
2353
2354 real_dir = dir_best_path(c->pool, interp_dir);
2355 if (real_dir) {
2356 c->name = real_dir;
2357
2358 } else {
2359 real_dir = dir_canonical_path(c->pool, interp_dir);
2360 if (real_dir) {
2361 c->name = real_dir;
2362 }
2363 }
2364
2365 pr_trace_msg(trace_channel, 11,
2366 "resolved <Directory %s> to <Directory %s>", orig_name, c->name);
2367
2368 /* Clear the CF_DEFER flag. */
2369 c->flags &= ~CF_DEFER;
2370 }
2371 }
2372 }
2373
copy_recur(xaset_t ** set,pool * p,config_rec * c,config_rec * new_parent)2374 static void copy_recur(xaset_t **set, pool *p, config_rec *c,
2375 config_rec *new_parent) {
2376 config_rec *newconf;
2377 int argc;
2378 void **argv, **sargv;
2379
2380 if (!*set) {
2381 *set = xaset_create(p, NULL);
2382 }
2383
2384 newconf = pr_config_add_set(set, c->name, 0);
2385 newconf->config_type = c->config_type;
2386 newconf->flags = c->flags;
2387 newconf->parent = new_parent;
2388 newconf->argc = c->argc;
2389
2390 if (c->argc) {
2391 newconf->argv = pcalloc(newconf->pool, (c->argc+1) * sizeof(void *));
2392 argv = newconf->argv;
2393 sargv = c->argv;
2394 argc = newconf->argc;
2395
2396 while (argc--) {
2397 *argv++ = *sargv++;
2398 }
2399
2400 if (argv) {
2401 *argv++ = NULL;
2402 }
2403 }
2404
2405 if (c->subset) {
2406 for (c = (config_rec *) c->subset->xas_list; c; c = c->next) {
2407 pr_signals_handle();
2408 copy_recur(&newconf->subset, p, c, newconf);
2409 }
2410 }
2411 }
2412
copy_global_to_all(xaset_t * set)2413 static void copy_global_to_all(xaset_t *set) {
2414 server_rec *s;
2415 config_rec *c;
2416
2417 if (!set || !set->xas_list) {
2418 return;
2419 }
2420
2421 for (c = (config_rec *) set->xas_list; c; c = c->next) {
2422 for (s = (server_rec *) server_list->xas_list; s; s = s->next) {
2423 pr_signals_handle();
2424 copy_recur(&s->conf, s->pool, c, NULL);
2425 }
2426 }
2427 }
2428
fixup_globals(xaset_t * list)2429 static void fixup_globals(xaset_t *list) {
2430 server_rec *s = NULL, *smain = NULL;
2431 config_rec *c = NULL, *cnext = NULL;
2432
2433 smain = (server_rec *) list->xas_list;
2434 for (s = smain; s; s = s->next) {
2435 /* Loop through each top level directive looking for a CONF_GLOBAL
2436 * context.
2437 */
2438 if (!s->conf ||
2439 !s->conf->xas_list) {
2440 continue;
2441 }
2442
2443 for (c = (config_rec *) s->conf->xas_list; c; c = cnext) {
2444 cnext = c->next;
2445
2446 if (c->config_type == CONF_GLOBAL &&
2447 strncmp(c->name, "<Global>", 9) == 0) {
2448 /* Copy the contents of the block to all other servers
2449 * (including this one), then pull the block "out of play".
2450 */
2451 if (c->subset &&
2452 c->subset->xas_list) {
2453 copy_global_to_all(c->subset);
2454 }
2455
2456 xaset_remove(s->conf, (xasetmember_t *) c);
2457
2458 if (!s->conf->xas_list) {
2459 destroy_pool(s->conf->pool);
2460 s->conf = NULL;
2461 }
2462 }
2463 }
2464 }
2465 }
2466
fixup_dirs(server_rec * s,int flags)2467 void fixup_dirs(server_rec *s, int flags) {
2468 if (s == NULL) {
2469 return;
2470 }
2471
2472 if (s->conf == NULL) {
2473 if (!(flags & CF_SILENT)) {
2474 pr_log_debug(DEBUG5, "%s", "");
2475 pr_log_debug(DEBUG5, "Config for %s:", s->ServerName);
2476 }
2477
2478 return;
2479 }
2480
2481 reorder_dirs(s->conf, flags);
2482
2483 /* Merge mergeable configuration items down. */
2484 pr_config_merge_down(s->conf, FALSE);
2485
2486 if (!(flags & CF_SILENT)) {
2487 pr_log_debug(DEBUG5, "%s", "");
2488 pr_log_debug(DEBUG5, "Config for %s:", s->ServerName);
2489 pr_config_dump(NULL, s->conf, NULL);
2490 }
2491
2492 return;
2493 }
2494
2495 /* Go through each server configuration and complain if important information
2496 * is missing (post reading configuration files). Otherwise, fill in defaults
2497 * where applicable.
2498 */
fixup_servers(xaset_t * list)2499 int fixup_servers(xaset_t *list) {
2500 config_rec *c = NULL;
2501 server_rec *s = NULL, *next_s = NULL;
2502
2503 fixup_globals(list);
2504
2505 s = (server_rec *) list->xas_list;
2506 if (s && !s->ServerName)
2507 s->ServerName = pstrdup(s->pool, "ProFTPD");
2508
2509 for (; s; s = next_s) {
2510 unsigned char *default_server = NULL;
2511
2512 next_s = s->next;
2513 if (s->ServerAddress == NULL) {
2514 array_header *addrs = NULL;
2515
2516 s->ServerAddress = pr_netaddr_get_localaddr_str(s->pool);
2517 s->addr = pr_netaddr_get_addr(s->pool, s->ServerAddress, &addrs);
2518
2519 if (addrs) {
2520 register unsigned int i;
2521 pr_netaddr_t **elts = addrs->elts;
2522
2523 /* For every additional address, implicitly add a bind record. */
2524 for (i = 0; i < addrs->nelts; i++) {
2525 const char *ipstr = pr_netaddr_get_ipstr(elts[i]);
2526
2527 #ifdef PR_USE_IPV6
2528 if (pr_netaddr_use_ipv6()) {
2529 char *ipbuf = pcalloc(s->pool, INET6_ADDRSTRLEN + 1);
2530 if (pr_netaddr_get_family(elts[i]) == AF_INET) {
2531
2532 /* Create the bind record using the IPv4-mapped IPv6 version of
2533 * this address.
2534 */
2535 pr_snprintf(ipbuf, INET6_ADDRSTRLEN, "::ffff:%s", ipstr);
2536 ipstr = pstrdup(s->pool, ipbuf);
2537 }
2538 }
2539 #endif /* PR_USE_IPV6 */
2540
2541 if (ipstr) {
2542 pr_conf_add_server_config_param_str(s, "_bind_", 1, ipstr);
2543 }
2544 }
2545 }
2546
2547 } else {
2548 int flags = PR_NETADDR_GET_ADDR_FL_INCL_DEVICE;
2549
2550 /* Make sure we properly handle a ServerAddress that is an
2551 * interface/device name here (Issue #1282).
2552 */
2553 s->addr = pr_netaddr_get_addr2(s->pool, s->ServerAddress, NULL, flags);
2554 }
2555
2556 if (s->addr == NULL) {
2557 pr_log_pri(PR_LOG_WARNING,
2558 "warning: unable to determine IP address of '%s'", s->ServerAddress);
2559
2560 if (s == main_server) {
2561 main_server = NULL;
2562 }
2563
2564 xaset_remove(list, (xasetmember_t *) s);
2565 destroy_pool(s->pool);
2566 s->pool = NULL;
2567 continue;
2568 }
2569
2570 s->ServerFQDN = pr_netaddr_get_dnsstr(s->addr);
2571
2572 if (s->ServerFQDN == NULL) {
2573 s->ServerFQDN = s->ServerAddress;
2574 }
2575
2576 if (s->ServerAdmin == NULL) {
2577 s->ServerAdmin = pstrcat(s->pool, "root@", s->ServerFQDN, NULL);
2578 }
2579
2580 if (s->ServerName == NULL) {
2581 server_rec *m = (server_rec *) list->xas_list;
2582 s->ServerName = pstrdup(s->pool, m->ServerName);
2583 }
2584
2585 if (s->tcp_rcvbuf_len == 0) {
2586 s->tcp_rcvbuf_len = tcp_rcvbufsz;
2587 }
2588
2589 if (s->tcp_sndbuf_len == 0) {
2590 s->tcp_sndbuf_len = tcp_sndbufsz;
2591 }
2592
2593 c = find_config(s->conf, CONF_PARAM, "MasqueradeAddress", FALSE);
2594 if (c != NULL) {
2595 const char *masq_addr;
2596
2597 if (c->argv[0] != NULL) {
2598 masq_addr = pr_netaddr_get_ipstr(c->argv[0]);
2599
2600 } else {
2601 masq_addr = c->argv[1];
2602 }
2603
2604 pr_log_pri(PR_LOG_INFO, "%s:%d masquerading as %s",
2605 pr_netaddr_get_ipstr(s->addr), s->ServerPort, masq_addr);
2606 }
2607
2608 /* Honor the DefaultServer directive only if SocketBindTight is not
2609 * in effect.
2610 */
2611 default_server = get_param_ptr(s->conf, "DefaultServer", FALSE);
2612
2613 if (default_server &&
2614 *default_server == TRUE) {
2615
2616 if (SocketBindTight == FALSE) {
2617 pr_netaddr_set_sockaddr_any((pr_netaddr_t *) s->addr);
2618
2619 } else {
2620 pr_log_pri(PR_LOG_NOTICE,
2621 "SocketBindTight in effect, ignoring DefaultServer");
2622 }
2623 }
2624
2625 fixup_dirs(s, 0);
2626 }
2627
2628 /* Make sure there actually are server_recs remaining in the list
2629 * before continuing. Badly configured/resolved vhosts are rejected, and
2630 * it's possible to have all vhosts (even the default) rejected.
2631 */
2632 if (list->xas_list == NULL) {
2633 pr_log_pri(PR_LOG_WARNING, "error: no valid servers configured");
2634 return -1;
2635 }
2636
2637 pr_inet_clear();
2638 return 0;
2639 }
2640
set_tcp_bufsz(server_rec * s)2641 static void set_tcp_bufsz(server_rec *s) {
2642 int proto = -1, sockfd;
2643 socklen_t optlen = 0;
2644 struct protoent *p = NULL;
2645
2646 #ifdef HAVE_SETPROTOENT
2647 setprotoent(FALSE);
2648 #endif
2649
2650 p = getprotobyname("tcp");
2651 if (p != NULL) {
2652 proto = p->p_proto;
2653 }
2654
2655 #ifdef HAVE_ENDPROTOENT
2656 endprotoent();
2657 #endif
2658
2659 if (p == NULL) {
2660 #ifndef PR_TUNABLE_RCVBUFSZ
2661 s->tcp_rcvbuf_len = tcp_rcvbufsz = PR_TUNABLE_DEFAULT_RCVBUFSZ;
2662 #else
2663 s->tcp_rcvbuf_len = tcp_rcvbufsz = PR_TUNABLE_RCVBUFSZ;
2664 #endif /* PR_TUNABLE_RCVBUFSZ */
2665
2666 #ifndef PR_TUNABLE_SNDBUFSZ
2667 s->tcp_sndbuf_len = tcp_sndbufsz = PR_TUNABLE_DEFAULT_SNDBUFSZ;
2668 #else
2669 s->tcp_sndbuf_len = tcp_sndbufsz = PR_TUNABLE_SNDBUFSZ;
2670 #endif /* PR_TUNABLE_SNDBUFSZ */
2671
2672 pr_log_debug(DEBUG3, "getprotobyname error for 'tcp': %s", strerror(errno));
2673 pr_log_debug(DEBUG4, "using default TCP receive/send buffer sizes");
2674
2675 #ifndef PR_TUNABLE_XFER_BUFFER_SIZE
2676 /* Choose the smaller of the two TCP buffer sizes as the overall transfer
2677 * size (for use by the data transfer layer).
2678 */
2679 xfer_bufsz = tcp_sndbufsz < tcp_rcvbufsz ? tcp_sndbufsz : tcp_rcvbufsz;
2680 #else
2681 xfer_bufsz = PR_TUNABLE_XFER_BUFFER_SIZE;
2682 #endif /* PR_TUNABLE_XFER_BUFFER_SIZE */
2683
2684 return;
2685 }
2686
2687 sockfd = socket(AF_INET, SOCK_STREAM, proto);
2688 if (sockfd < 0) {
2689 s->tcp_rcvbuf_len = tcp_rcvbufsz = PR_TUNABLE_DEFAULT_RCVBUFSZ;
2690 s->tcp_sndbuf_len = tcp_sndbufsz = PR_TUNABLE_DEFAULT_SNDBUFSZ;
2691
2692 pr_log_debug(DEBUG3, "socket error: %s", strerror(errno));
2693 pr_log_debug(DEBUG4, "using default TCP receive/send buffer sizes");
2694
2695 return;
2696 }
2697
2698 #ifndef PR_TUNABLE_RCVBUFSZ
2699 /* Determine the optimal size of the TCP receive buffer. */
2700 optlen = sizeof(tcp_rcvbufsz);
2701 if (getsockopt(sockfd, SOL_SOCKET, SO_RCVBUF, (void *) &tcp_rcvbufsz,
2702 &optlen) < 0) {
2703 s->tcp_rcvbuf_len = tcp_rcvbufsz = PR_TUNABLE_DEFAULT_RCVBUFSZ;
2704
2705 pr_log_debug(DEBUG3, "getsockopt error for SO_RCVBUF: %s", strerror(errno));
2706 pr_log_debug(DEBUG4, "using default TCP receive buffer size of %d bytes",
2707 tcp_rcvbufsz);
2708
2709 } else {
2710 pr_log_debug(DEBUG5, "using TCP receive buffer size of %d bytes",
2711 tcp_rcvbufsz);
2712 s->tcp_rcvbuf_len = tcp_rcvbufsz;
2713 }
2714 #else
2715 optlen = -1;
2716 s->tcp_rcvbuf_len = tcp_rcvbufsz = PR_TUNABLE_RCVBUFSZ;
2717 pr_log_debug(DEBUG5, "using preset TCP receive buffer size of %d bytes",
2718 tcp_rcvbufsz);
2719 #endif /* PR_TUNABLE_RCVBUFSZ */
2720
2721 #ifndef PR_TUNABLE_SNDBUFSZ
2722 /* Determine the optimal size of the TCP send buffer. */
2723 optlen = sizeof(tcp_sndbufsz);
2724 if (getsockopt(sockfd, SOL_SOCKET, SO_SNDBUF, (void *) &tcp_sndbufsz,
2725 &optlen) < 0) {
2726 s->tcp_sndbuf_len = tcp_sndbufsz = PR_TUNABLE_DEFAULT_SNDBUFSZ;
2727
2728 pr_log_debug(DEBUG3, "getsockopt error for SO_SNDBUF: %s", strerror(errno));
2729 pr_log_debug(DEBUG4, "using default TCP send buffer size of %d bytes",
2730 tcp_sndbufsz);
2731
2732 } else {
2733 pr_log_debug(DEBUG5, "using TCP send buffer size of %d bytes",
2734 tcp_sndbufsz);
2735 s->tcp_sndbuf_len = tcp_sndbufsz;
2736 }
2737 #else
2738 optlen = -1;
2739 s->tcp_sndbuf_len = tcp_sndbufsz = PR_TUNABLE_SNDBUFSZ;
2740 pr_log_debug(DEBUG5, "using preset TCP send buffer size of %d bytes",
2741 tcp_sndbufsz);
2742 #endif /* PR_TUNABLE_SNDBUFSZ */
2743
2744 /* Choose the smaller of the two TCP buffer sizes as the overall transfer
2745 * size (for use by the data transfer layer).
2746 */
2747 xfer_bufsz = tcp_sndbufsz < tcp_rcvbufsz ? tcp_sndbufsz : tcp_rcvbufsz;
2748
2749 (void) close(sockfd);
2750 }
2751
init_dirtree(void)2752 void init_dirtree(void) {
2753 pool *dirtree_pool = make_sub_pool(permanent_pool);
2754 pr_pool_tag(dirtree_pool, "Dirtree Pool");
2755
2756 if (server_list) {
2757 server_rec *s, *s_next;
2758
2759 /* Free the old configuration completely */
2760 for (s = (server_rec *) server_list->xas_list; s; s = s_next) {
2761 s_next = s->next;
2762
2763 /* Make sure that any pointers are explicitly nulled; this does not
2764 * automatically happen as part of pool destruction.
2765 */
2766 s->conf = NULL;
2767 s->set = NULL;
2768
2769 destroy_pool(s->pool);
2770 }
2771
2772 destroy_pool(server_list->pool);
2773 server_list = NULL;
2774 }
2775
2776 /* Note: xaset_create() assigns the given pool to the 'pool' member
2777 * of the created list, i.e. server_list->pool == conf_pool. Hence
2778 * why we create yet another subpool, reusing the conf_pool pointer.
2779 * The pool creation below is not redundant.
2780 */
2781 server_list = xaset_create(dirtree_pool, NULL);
2782
2783 dirtree_pool = make_sub_pool(permanent_pool);
2784 pr_pool_tag(dirtree_pool, "main_server pool");
2785
2786 main_server = (server_rec *) pcalloc(dirtree_pool, sizeof(server_rec));
2787 xaset_insert(server_list, (xasetmember_t *) main_server);
2788
2789 main_server->pool = dirtree_pool;
2790 main_server->set = server_list;
2791 main_server->sid = 1;
2792 main_server->notes = pr_table_nalloc(dirtree_pool, 0, 8);
2793
2794 /* TCP KeepAlive is enabled by default, with the system defaults. */
2795 main_server->tcp_keepalive = palloc(main_server->pool,
2796 sizeof(struct tcp_keepalive));
2797 main_server->tcp_keepalive->keepalive_enabled = TRUE;
2798 main_server->tcp_keepalive->keepalive_idle = -1;
2799 main_server->tcp_keepalive->keepalive_count = -1;
2800 main_server->tcp_keepalive->keepalive_intvl = -1;
2801
2802 /* Default server port */
2803 main_server->ServerPort = pr_inet_getservport(main_server->pool,
2804 "ftp", "tcp");
2805
2806 set_tcp_bufsz(main_server);
2807 return;
2808 }
2809
2810 /* These functions are used by modules to help parse configuration. */
2811
check_context(cmd_rec * cmd,int allowed)2812 unsigned char check_context(cmd_rec *cmd, int allowed) {
2813 int ctxt = (cmd->config && cmd->config->config_type != CONF_PARAM ?
2814 cmd->config->config_type : cmd->server->config_type ?
2815 cmd->server->config_type : CONF_ROOT);
2816
2817 if (ctxt & allowed)
2818 return TRUE;
2819
2820 /* default */
2821 return FALSE;
2822 }
2823
get_context_name(cmd_rec * cmd)2824 char *get_context_name(cmd_rec *cmd) {
2825 static char cbuf[20];
2826
2827 if (!cmd->config || cmd->config->config_type == CONF_PARAM) {
2828 if (cmd->server->config_type == CONF_VIRTUAL) {
2829 return "<VirtualHost>";
2830 }
2831
2832 return "server config";
2833 }
2834
2835 switch (cmd->config->config_type) {
2836 case CONF_DIR:
2837 return "<Directory>";
2838
2839 case CONF_ANON:
2840 return "<Anonymous>";
2841
2842 case CONF_CLASS:
2843 return "<Class>";
2844
2845 case CONF_LIMIT:
2846 return "<Limit>";
2847
2848 case CONF_DYNDIR:
2849 return ".ftpaccess";
2850
2851 case CONF_GLOBAL:
2852 return "<Global>";
2853
2854 case CONF_USERDATA:
2855 return "user data";
2856
2857 default:
2858 /* XXX should dispatch to modules here, to allow them to create and
2859 * handle their own arbitrary configuration contexts.
2860 */
2861 memset(cbuf, '\0', sizeof(cbuf));
2862 pr_snprintf(cbuf, sizeof(cbuf), "%d", cmd->config->config_type);
2863 return cbuf;
2864 }
2865 }
2866
get_boolean(cmd_rec * cmd,int av)2867 int get_boolean(cmd_rec *cmd, int av) {
2868 char *cp = cmd->argv[av];
2869
2870 return pr_str_is_boolean(cp);
2871 }
2872
get_full_cmd(cmd_rec * cmd)2873 const char *get_full_cmd(cmd_rec *cmd) {
2874 return pr_cmd_get_displayable_str(cmd, NULL);
2875 }
2876
pr_config_get_xfer_bufsz(void)2877 int pr_config_get_xfer_bufsz(void) {
2878 return xfer_bufsz;
2879 }
2880
pr_config_get_xfer_bufsz2(int direction)2881 int pr_config_get_xfer_bufsz2(int direction) {
2882 switch (direction) {
2883 case PR_NETIO_IO_RD:
2884 return tcp_rcvbufsz;
2885
2886 case PR_NETIO_IO_WR:
2887 return tcp_sndbufsz;
2888 }
2889
2890 return xfer_bufsz;
2891 }
2892
pr_config_get_server_xfer_bufsz(int direction)2893 int pr_config_get_server_xfer_bufsz(int direction) {
2894 if (main_server != NULL) {
2895 switch (direction) {
2896 case PR_NETIO_IO_RD:
2897 return main_server->tcp_rcvbuf_len;
2898
2899 case PR_NETIO_IO_WR:
2900 return main_server->tcp_sndbuf_len;
2901 }
2902 }
2903
2904 return pr_config_get_xfer_bufsz2(direction);
2905 }
2906