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