source: src/router/proftpd/src/parser.c @ 14672

Last change on this file since 14672 was 14672, checked in by BrainSlayer, 3 years ago

proftp update

File size: 15.2 KB
Line 
1/*
2 * ProFTPD - FTP server daemon
3 * Copyright (c) 2004-2009 The ProFTPD Project team
4 *
5 * This program is free software; you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation; either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program; if not, write to the Free Software
17 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307, USA.
18 *
19 * As a special exemption, The ProFTPD Project team and other respective
20 * copyright holders give permission to link this program with OpenSSL, and
21 * distribute the resulting executable, without including the source code
22 * for OpenSSL in the source distribution.
23 */
24
25/* Configuration parser
26 * $Id: parser.c,v 1.19 2009/03/05 06:01:51 castaglia Exp $
27 */
28
29#include "conf.h"
30
31extern xaset_t *server_list;
32extern pool *global_config_pool;
33
34static pool *parser_pool = NULL;
35
36static array_header *parser_confstack = NULL;
37static config_rec **parser_curr_config = NULL;
38
39static array_header *parser_servstack = NULL;
40static server_rec **parser_curr_server = NULL;
41static unsigned int parser_sid = 0;
42
43static xaset_t **parser_server_list = NULL;
44
45static const char *trace_channel = "config";
46
47struct config_src {
48  struct config_src *cs_next;
49  pool *cs_pool;
50  pr_fh_t *cs_fh;
51  unsigned int cs_lineno;
52};
53
54static unsigned int parser_curr_lineno = 0;
55
56/* Note: the parser seems to be touchy about this particular value.  If
57 * you see strange segfaults occurring in the mergedown() function, it
58 * might be because this pool size is too small.
59 */
60#define PARSER_CONFIG_SRC_POOL_SZ       512
61
62static struct config_src *parser_sources = NULL;
63
64/* Private functions
65 */
66
67static void add_config_ctxt(config_rec *c) {
68  if (!*parser_curr_config)
69    *parser_curr_config = c;
70
71  else {
72    parser_curr_config = (config_rec **) push_array(parser_confstack);
73    *parser_curr_config = c;
74  }
75}
76
77static struct config_src *add_config_source(pr_fh_t *fh) {
78  pool *p = pr_pool_create_sz(parser_pool, PARSER_CONFIG_SRC_POOL_SZ);
79  struct config_src *cs = pcalloc(p, sizeof(struct config_src));
80
81  pr_pool_tag(p, "configuration source pool");
82  cs->cs_next = NULL;
83  cs->cs_pool = p;
84  cs->cs_fh = fh;
85  cs->cs_lineno = 0;
86
87  if (!parser_sources)
88    parser_sources = cs;
89
90  else {
91    cs->cs_next = parser_sources;
92    parser_sources = cs;
93  }
94
95  return cs;
96}
97
98static char *get_config_word(pool *p, char *word) {
99
100  /* Should this word be replaced with a value from the environment?
101   * If so, tmp will contain the expanded value, otherwise tmp will
102   * contain a string duped from the given pool.
103   */
104
105  /* Does the given word use the environment syntax? */
106  if (strlen(word) > 7 &&
107      strncmp(word, "%{env:", 6) == 0 &&
108      word[strlen(word)-1] == '}') {
109    char *env;
110
111    word[strlen(word)-1] = '\0';
112
113    env = pr_env_get(p, word + 6);
114
115    return env ? pstrdup(p, env) : "";
116  }
117
118  return pstrdup(p, word);
119}
120
121static void remove_config_source(void) {
122  struct config_src *cs = parser_sources;
123
124  if (cs) {
125    parser_sources = cs->cs_next;
126    destroy_pool(cs->cs_pool);
127  }
128
129  return;
130}
131
132/* Public API
133 */
134
135int pr_parser_cleanup(void) {
136  if (parser_pool) {
137    if (parser_servstack->nelts > 1 ||
138        (parser_curr_config && *parser_curr_config)) {
139      errno = EPERM;
140      return -1;
141    }
142
143    destroy_pool(parser_pool);
144    parser_pool = NULL;
145  }
146
147  parser_servstack = NULL;
148  parser_curr_server = NULL;
149
150  parser_confstack = NULL;
151  parser_curr_config = NULL;
152
153  /* Reset the SID counter. */
154  parser_sid = 0;
155
156  return 0;
157}
158
159config_rec *pr_parser_config_ctxt_close(int *empty) {
160  config_rec *c = *parser_curr_config;
161
162  /* Note that if the current config is empty, it should simply be removed.
163   * Such empty configs can happen for <Directory> sections that
164   * contain no directives, for example.
165   */
166
167  if (parser_curr_config == (config_rec **) parser_confstack->elts) {
168    if (!c->subset || !c->subset->xas_list) {
169      xaset_remove(c->set, (xasetmember_t *) c);
170      destroy_pool(c->pool);
171
172      if (empty)
173        *empty = TRUE;
174    }
175
176    if (*parser_curr_config)
177      *parser_curr_config = NULL;
178
179    return NULL;
180  }
181
182  if (!c->subset || !c->subset->xas_list) {
183    xaset_remove(c->set, (xasetmember_t *) c);
184    destroy_pool(c->pool);
185
186    if (empty)
187      *empty = TRUE;
188  }
189
190  parser_curr_config--;
191  parser_confstack->nelts--;
192
193  return *parser_curr_config;
194}
195
196config_rec *pr_parser_config_ctxt_get(void) {
197  if (parser_curr_config)
198    return *parser_curr_config;
199
200  errno = ENOENT;
201  return NULL;
202}
203
204config_rec *pr_parser_config_ctxt_open(const char *name) {
205  config_rec *c = NULL, *parent = *parser_curr_config;
206  pool *c_pool = NULL, *parent_pool = NULL;
207  xaset_t **set = NULL;
208
209  if (!name) {
210    errno = EINVAL;
211    return NULL;
212  }
213
214  if (parent) {
215    parent_pool = parent->pool;
216    set = &parent->subset;
217
218  } else {
219    parent_pool = (*parser_curr_server)->pool;
220    set = &(*parser_curr_server)->conf;
221  }
222
223  /* Allocate a sub-pool for this config_rec.
224   *
225   * Note: special exception for <Global> configs: the parent pool is
226   * 'global_config_pool' (a pool just for that context), not the pool of the
227   * parent server.  This keeps <Global> config recs from being freed
228   * prematurely, and helps to avoid memory leaks.
229   */
230  if (strcmp(name, "<Global>") == 0) {
231    if (!global_config_pool) {
232      global_config_pool = make_sub_pool(permanent_pool);
233      pr_pool_tag(global_config_pool, "<Global> Pool");
234    }
235
236    parent_pool = global_config_pool;
237  }
238
239  c_pool = make_sub_pool(parent_pool);
240  pr_pool_tag(c_pool, "sub-config pool");
241
242  c = (config_rec *) pcalloc(c_pool, sizeof(config_rec));
243
244  if (!*set) {
245    pool *set_pool = make_sub_pool(parent_pool);
246    *set = xaset_create(set_pool, NULL);
247    (*set)->pool = set_pool;
248  }
249
250  xaset_insert(*set, (xasetmember_t *) c);
251
252  c->pool = c_pool;
253  c->set = *set;
254  c->parent = parent;
255  c->name = pstrdup(c->pool, name);
256
257  if (parent) {
258    if (parent->config_type == CONF_DYNDIR)
259      c->flags |= CF_DYNAMIC;
260  }
261
262  add_config_ctxt(c);
263  return c;
264}
265
266unsigned int pr_parser_get_lineno(void) {
267  return parser_curr_lineno;
268}
269
270int pr_parser_parse_file(pool *p, const char *path, config_rec *start,
271    int flags) {
272  pr_fh_t *fh;
273  struct stat st;
274  struct config_src *cs;
275  cmd_rec *cmd;
276  pool *tmp_pool;
277  char *report_path;
278
279  if (!path) {
280    errno = EINVAL;
281    return -1;
282  }
283
284  tmp_pool = make_sub_pool(p ? p : permanent_pool);
285  pr_pool_tag(tmp_pool, "parser file pool");
286
287  report_path = (char *) path;
288  if (session.chroot_path)
289    report_path = pdircat(tmp_pool, session.chroot_path, path, NULL);
290
291  if (!(flags & PR_PARSER_FL_DYNAMIC_CONFIG))
292    pr_trace_msg(trace_channel, 3, "parsing '%s' configuration", report_path);
293
294  fh = pr_fsio_open(path, O_RDONLY);
295  if (fh == NULL) {
296    destroy_pool(tmp_pool);
297    return -1;
298  }
299
300  /* Stat the opened file to determine the optimal buffer size for IO. */
301  memset(&st, 0, sizeof(st));
302  pr_fsio_fstat(fh, &st);
303  fh->fh_iosz = st.st_blksize;
304
305  /* Push the configuration information onto the stack of configuration
306   * sources.
307   */
308  cs = add_config_source(fh);
309
310  if (start)
311    add_config_ctxt(start);
312
313  while ((cmd = pr_parser_parse_line(tmp_pool)) != NULL) {
314    pr_signals_handle();
315
316    if (cmd->argc) {
317      conftable *conftab;
318      char found = FALSE;
319
320      cmd->server = *parser_curr_server;
321      cmd->config = *parser_curr_config;
322
323      conftab = pr_stash_get_symbol(PR_SYM_CONF, cmd->argv[0], NULL,
324        &cmd->stash_index);
325
326      while (conftab) {
327        modret_t *mr;
328
329        pr_signals_handle();
330
331        cmd->argv[0] = conftab->directive;
332
333        pr_trace_msg(trace_channel, 7,
334          "dispatching directive '%s' to module mod_%s", conftab->directive,
335          conftab->m->name);
336
337        mr = pr_module_call(conftab->m, conftab->handler, cmd);
338        if (mr != NULL) {
339          if (MODRET_ISERROR(mr)) {
340
341            if (!(flags & PR_PARSER_FL_DYNAMIC_CONFIG)) {
342              pr_log_pri(PR_LOG_ERR, "Fatal: %s on line %u of '%s'",
343                MODRET_ERRMSG(mr), cs->cs_lineno, report_path);
344              exit(1);
345
346            } else
347              pr_log_pri(PR_LOG_WARNING, "warning: %s on line %u of '%s'",
348                MODRET_ERRMSG(mr), cs->cs_lineno, report_path);
349          }
350        }
351
352        if (!MODRET_ISDECLINED(mr))
353          found = TRUE;
354
355        conftab = pr_stash_get_symbol(PR_SYM_CONF, cmd->argv[0], conftab,
356          &cmd->stash_index);
357      }
358
359      if (cmd->tmp_pool)
360        destroy_pool(cmd->tmp_pool);
361
362      if (!found) {
363
364        if (!(flags & PR_PARSER_FL_DYNAMIC_CONFIG)) {
365          pr_log_pri(PR_LOG_ERR, "Fatal: unknown configuration directive "
366            "'%s' on line %u of '%s'", cmd->argv[0], cs->cs_lineno,
367            report_path);
368          exit(1);
369
370        } else
371          pr_log_pri(PR_LOG_WARNING, "warning: unknown configuration directive "
372            "'%s' on line %u of '%s'", cmd->argv[0], cs->cs_lineno,
373            report_path);
374      }
375    }
376
377    destroy_pool(cmd->pool);
378  }
379
380  /* Pop this configuration stream from the stack. */
381  remove_config_source();
382
383  pr_fsio_close(fh);
384
385  destroy_pool(tmp_pool);
386  return 0;
387}
388
389cmd_rec *pr_parser_parse_line(pool *p) {
390  char buf[PR_TUNABLE_BUFFER_SIZE], *word = NULL;
391  cmd_rec *cmd = NULL;
392  pool *sub_pool = NULL;
393  array_header *arr = NULL;
394
395  if (!p) {
396    errno = EINVAL;
397    return NULL;
398  }
399
400  memset(buf, '\0', sizeof(buf));
401
402  while (pr_parser_read_line(buf, sizeof(buf)-1) != NULL) {
403    char *bufp = buf;
404
405    pr_signals_handle();
406
407    /* Build a new pool for the command structure and array */
408    sub_pool = make_sub_pool(p);
409    pr_pool_tag(sub_pool, "parser cmd subpool");
410
411    cmd = pcalloc(sub_pool, sizeof(cmd_rec));
412    cmd->pool = sub_pool;
413    cmd->stash_index = -1;
414
415    /* Add each word to the array */
416    arr = make_array(cmd->pool, 4, sizeof(char **));
417    while ((word = pr_str_get_word(&bufp, 0)) != NULL) {
418      char *tmp = get_config_word(cmd->pool, word);
419
420      *((char **) push_array(arr)) = tmp;
421      cmd->argc++;
422    }
423
424    /* Terminate the array with a NULL. */
425    *((char **) push_array(arr)) = NULL;
426
427    /* The array header's job is done, we can forget about it and
428     * it will get purged when the command's pool is destroyed.
429     */
430
431    cmd->argv = (char **) arr->elts;
432
433    /* Perform a fixup on configuration directives so that:
434     *
435     *   -argv[0]--  -argv[1]-- ----argv[2]-----
436     *   <Option     /etc/adir  /etc/anotherdir>
437     *
438     *  becomes:
439     *
440     *   -argv[0]--  -argv[1]-  ----argv[2]----
441     *   <Option>    /etc/adir  /etc/anotherdir
442     */
443
444    if (cmd->argc &&
445        *(cmd->argv[0]) == '<') {
446      char *cp = cmd->argv[cmd->argc-1];
447
448      if (*(cp + strlen(cp)-1) == '>' &&
449          cmd->argc > 1) {
450
451        if (strcmp(cp, ">") == 0) {
452          cmd->argv[cmd->argc-1] = NULL;
453          cmd->argc--;
454
455        } else
456          *(cp + strlen(cp)-1) = '\0';
457
458        cp = cmd->argv[0];
459        if (*(cp + strlen(cp)-1) != '>')
460          cmd->argv[0] = pstrcat(cmd->pool, cp, ">", NULL);
461      }
462    }
463
464    return cmd;
465  }
466
467  return NULL;
468}
469
470int pr_parser_prepare(pool *p, xaset_t **parsed_servers) {
471
472  if (!p) {
473    if (!parser_pool) {
474      parser_pool = make_sub_pool(permanent_pool);
475      pr_pool_tag(parser_pool, "Parser Pool");
476    }
477
478    p = parser_pool;
479  }
480
481  if (!parsed_servers)
482    parser_server_list = &server_list;
483
484  else
485    parser_server_list = parsed_servers;
486
487  parser_servstack = make_array(p, 1, sizeof(server_rec *));
488  parser_curr_server = (server_rec **) push_array(parser_servstack);
489  *parser_curr_server = main_server;
490
491  parser_confstack = make_array(p, 10, sizeof(config_rec *));
492  parser_curr_config = (config_rec **) push_array(parser_confstack);
493  *parser_curr_config = NULL;
494
495  return 0;
496}
497
498/* This functions returns the next line from the configuration stream,
499 * skipping commented-out lines and trimming trailing and leading whitespace,
500 * returning, in effect, the next line of configuration data on which to
501 * act.  This function has the advantage that it can be called by functions
502 * that don't have access to configuration file handle, such as the
503 * <IfDefine> and <IfModule> configuration handlers.
504 */
505char *pr_parser_read_line(char *buf, size_t bufsz) {
506  struct config_src *cs;
507
508  /* Always use the config stream at the top of the stack. */
509  cs = parser_sources;
510
511  if (!buf) {
512    errno = EINVAL;
513    return NULL;
514  }
515
516  if (!cs->cs_fh) {
517    errno = EPERM;
518    return NULL;
519  }
520
521  parser_curr_lineno = cs->cs_lineno;
522
523  /* Check for error conditions. */
524
525  while ((pr_fsio_getline(buf, bufsz, cs->cs_fh, &(cs->cs_lineno))) != NULL) {
526    int have_eol = FALSE;
527    char *bufp = NULL;
528    size_t buflen = strlen(buf);
529
530    parser_curr_lineno = cs->cs_lineno;
531
532    /* Trim off the trailing newline, if present. */
533    if (buflen &&
534        buf[buflen - 1] == '\n') {
535      have_eol = TRUE;
536      buf[buflen - 1] = '\0';
537      buflen = strlen(buf);
538    }
539
540    while (buflen &&
541           buf[buflen - 1] == '\r') {
542      pr_signals_handle();
543      buf[buflen - 1] = '\0';
544      buflen = strlen(buf);
545    }
546
547    if (!have_eol) {
548      pr_log_pri(PR_LOG_WARNING,
549        "warning: handling possibly truncated configuration data at "
550        "line %u of '%s'", cs->cs_lineno, cs->cs_fh->fh_path);
551    }
552
553    /* Advance past any leading whitespace. */
554    for (bufp = buf; *bufp && isspace((int) *bufp); bufp++);
555
556    /* Check for commented or blank lines at this point, and just continue on
557     * to the next configuration line if found.  If not, return the
558     * configuration line.
559     */
560    if (*bufp == '#' || !*bufp) {
561      continue;
562
563    } else {
564
565      /* Copy the value of bufp back into the pointer passed in
566       * and return it.
567       */
568      buf = bufp;
569
570      return buf;
571    }
572  }
573
574  return NULL;
575}
576
577server_rec *pr_parser_server_ctxt_close(void) {
578  if (!parser_curr_server) {
579    errno = ENOENT;
580    return NULL;
581  }
582
583  /* Disallow underflows. */
584  if (parser_curr_server == (server_rec **) parser_servstack->elts) {
585    errno = EPERM;
586    return NULL;
587  }
588
589  parser_curr_server--;
590  parser_servstack->nelts--;
591
592  return *parser_curr_server;
593}
594
595server_rec *pr_parser_server_ctxt_get(void) {
596  if (parser_curr_server)
597    return *parser_curr_server;
598
599  errno = ENOENT;
600  return NULL;
601}
602
603server_rec *pr_parser_server_ctxt_open(const char *addrstr) {
604  server_rec *s;
605  pool *p;
606
607  p = make_sub_pool(permanent_pool);
608  pr_pool_tag(p, "<VirtualHost> Pool");
609
610  s = (server_rec *) pcalloc(p, sizeof(server_rec));
611  s->pool = p;
612  s->config_type = CONF_VIRTUAL;
613  s->sid = ++parser_sid;
614
615  /* Have to make sure it ends up on the end of the chain, otherwise
616   * main_server becomes useless.
617   */
618  xaset_insert_end(*parser_server_list, (xasetmember_t *) s);
619  s->set = *parser_server_list;
620  if (addrstr) {
621    s->ServerAddress = pstrdup(s->pool, addrstr);
622  }
623
624  /* Default server port */
625  s->ServerPort = pr_inet_getservport(s->pool, "ftp", "tcp");
626
627  parser_curr_server = (server_rec **) push_array(parser_servstack);
628  *parser_curr_server = s;
629
630  return s;
631}
Note: See TracBrowser for help on using the repository browser.