Coverage Report

Created: 2024-02-11 06:31

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