1 /*
2 * ProFTPD - FTP server daemon
3 * Copyright (c) 2004-2020 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., 51 Franklin Street, Suite 500, Boston, MA 02110-1335, 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
27 #include "conf.h"
28 #include "privs.h"
29
30 /* Maximum depth of Include patterns/files. */
31 #define PR_PARSER_INCLUDE_MAX_DEPTH 64
32
33 extern xaset_t *server_list;
34 extern pool *global_config_pool;
35
36 static pool *parser_pool = NULL;
37 static unsigned long parser_include_opts = 0UL;
38
39 static array_header *parser_confstack = NULL;
40 static config_rec **parser_curr_config = NULL;
41
42 static array_header *parser_servstack = NULL;
43 static server_rec **parser_curr_server = NULL;
44 static unsigned int parser_sid = 1;
45
46 static xaset_t **parser_server_list = NULL;
47
48 static const char *trace_channel = "config";
49
50 struct config_src {
51 struct config_src *cs_next;
52 pool *cs_pool;
53 pr_fh_t *cs_fh;
54 unsigned int cs_lineno;
55 };
56
57 static unsigned int parser_curr_lineno = 0;
58
59 /* Note: the parser seems to be touchy about this particular value. If
60 * you see strange segfaults occurring in the mergedown() function, it
61 * might be because this pool size is too small.
62 */
63 #define PARSER_CONFIG_SRC_POOL_SZ 512
64
65 static struct config_src *parser_sources = NULL;
66
67 /* Private functions
68 */
69
add_config_source(pr_fh_t * fh)70 static struct config_src *add_config_source(pr_fh_t *fh) {
71 pool *p = pr_pool_create_sz(parser_pool, PARSER_CONFIG_SRC_POOL_SZ);
72 struct config_src *cs = pcalloc(p, sizeof(struct config_src));
73
74 pr_pool_tag(p, "configuration source pool");
75 cs->cs_next = NULL;
76 cs->cs_pool = p;
77 cs->cs_fh = fh;
78 cs->cs_lineno = 0;
79
80 if (!parser_sources) {
81 parser_sources = cs;
82
83 } else {
84 cs->cs_next = parser_sources;
85 parser_sources = cs;
86 }
87
88 return cs;
89 }
90
get_config_word(pool * p,char * word)91 static char *get_config_word(pool *p, char *word) {
92 size_t wordlen;
93
94 /* Should this word be replaced with a value from the environment?
95 * If so, tmp will contain the expanded value, otherwise tmp will
96 * contain a string duped from the given pool.
97 */
98
99 wordlen = strlen(word);
100 if (wordlen > 7) {
101 char *ptr = NULL;
102
103 pr_trace_msg(trace_channel, 27, "word '%s' long enough for environment "
104 "variable (%lu > minimum required 7)", word, (unsigned long) wordlen);
105
106 /* Does the given word use the environment syntax? We handle this in a
107 * while loop in order to handle a) multiple different variables, and b)
108 * cases where the substituted value is itself a variable. Hopefully no
109 * one is so clever as to want to actually _use_ the latter approach.
110 */
111 ptr = strstr(word, "%{env:");
112 while (ptr != NULL) {
113 char *env, *key, *ptr2, *var;
114 unsigned int keylen;
115
116 pr_signals_handle();
117
118 ptr2 = strchr(ptr + 6, '}');
119 if (ptr2 == NULL) {
120 /* No terminating marker; continue on to the next potential
121 * variable in the word.
122 */
123 ptr2 = ptr + 6;
124 ptr = strstr(ptr2, "%{env:");
125 continue;
126 }
127
128 keylen = (ptr2 - ptr - 6);
129 var = pstrndup(p, ptr, (ptr2 - ptr) + 1);
130
131 key = pstrndup(p, ptr + 6, keylen);
132
133 pr_trace_msg(trace_channel, 17,
134 "word '%s' uses environment variable '%s'", word, key);
135 env = pr_env_get(p, key);
136 if (env == NULL) {
137 /* No value in the environment; continue on to the next potential
138 * variable in the word.
139 */
140 pr_trace_msg(trace_channel, 17, "no value found for environment "
141 "variable '%s' for word '%s', ignoring", key, word);
142
143 word = (char *) sreplace(p, word, var, "", NULL);
144 ptr = strstr(word, "%{env:");
145 continue;
146 }
147
148 pr_trace_msg(trace_channel, 17,
149 "resolved environment variable '%s' to value '%s' for word '%s'", key,
150 env, word);
151
152 word = (char *) sreplace(p, word, var, env, NULL);
153 ptr = strstr(word, "%{env:");
154 }
155
156 } else {
157 pr_trace_msg(trace_channel, 27, "word '%s' not long enough for environment "
158 "variable (%lu < minimum required 7)", word, (unsigned long) wordlen);
159 }
160
161 return pstrdup(p, word);
162 }
163
remove_config_source(void)164 static void remove_config_source(void) {
165 struct config_src *cs = parser_sources;
166
167 if (cs) {
168 parser_sources = cs->cs_next;
169 destroy_pool(cs->cs_pool);
170 }
171
172 return;
173 }
174
175 /* Public API
176 */
177
pr_parser_cleanup(void)178 int pr_parser_cleanup(void) {
179 if (parser_pool) {
180 if (parser_servstack->nelts > 1 ||
181 (parser_curr_config && *parser_curr_config)) {
182 errno = EPERM;
183 return -1;
184 }
185
186 destroy_pool(parser_pool);
187 parser_pool = NULL;
188 }
189
190 parser_servstack = NULL;
191 parser_curr_server = NULL;
192
193 parser_confstack = NULL;
194 parser_curr_config = NULL;
195
196 /* Reset the SID counter. */
197 parser_sid = 1;
198
199 return 0;
200 }
201
pr_parser_config_ctxt_close(int * empty)202 config_rec *pr_parser_config_ctxt_close(int *empty) {
203 config_rec *c = *parser_curr_config;
204
205 /* Note that if the current config is empty, it should simply be removed.
206 * Such empty configs can happen for <Directory> sections that
207 * contain no directives, for example.
208 */
209
210 if (parser_curr_config == (config_rec **) parser_confstack->elts) {
211 if (c != NULL &&
212 (!c->subset || !c->subset->xas_list)) {
213 xaset_remove(c->set, (xasetmember_t *) c);
214 destroy_pool(c->pool);
215
216 if (empty) {
217 *empty = TRUE;
218 }
219 }
220
221 if (*parser_curr_config) {
222 *parser_curr_config = NULL;
223 }
224
225 return NULL;
226 }
227
228 if (c != NULL &&
229 (!c->subset || !c->subset->xas_list)) {
230 xaset_remove(c->set, (xasetmember_t *) c);
231 destroy_pool(c->pool);
232
233 if (empty) {
234 *empty = TRUE;
235 }
236 }
237
238 parser_curr_config--;
239 parser_confstack->nelts--;
240
241 return *parser_curr_config;
242 }
243
pr_parser_config_ctxt_get(void)244 config_rec *pr_parser_config_ctxt_get(void) {
245 if (parser_curr_config) {
246 return *parser_curr_config;
247 }
248
249 errno = ENOENT;
250 return NULL;
251 }
252
pr_parser_config_ctxt_open(const char * name)253 config_rec *pr_parser_config_ctxt_open(const char *name) {
254 config_rec *c = NULL, *parent = *parser_curr_config;
255 pool *c_pool = NULL, *parent_pool = NULL;
256 xaset_t **set = NULL;
257
258 if (name == NULL) {
259 errno = EINVAL;
260 return NULL;
261 }
262
263 if (parent) {
264 parent_pool = parent->pool;
265 set = &parent->subset;
266
267 } else {
268 parent_pool = (*parser_curr_server)->pool;
269 set = &(*parser_curr_server)->conf;
270 }
271
272 /* Allocate a sub-pool for this config_rec.
273 *
274 * Note: special exception for <Global> configs: the parent pool is
275 * 'global_config_pool' (a pool just for that context), not the pool of the
276 * parent server. This keeps <Global> config recs from being freed
277 * prematurely, and helps to avoid memory leaks.
278 */
279 if (strncasecmp(name, "<Global>", 9) == 0) {
280 if (global_config_pool == NULL) {
281 global_config_pool = make_sub_pool(permanent_pool);
282 pr_pool_tag(global_config_pool, "<Global> Pool");
283 }
284
285 parent_pool = global_config_pool;
286 }
287
288 c_pool = make_sub_pool(parent_pool);
289 pr_pool_tag(c_pool, "sub-config pool");
290
291 c = (config_rec *) pcalloc(c_pool, sizeof(config_rec));
292
293 if (!*set) {
294 pool *set_pool = make_sub_pool(parent_pool);
295 *set = xaset_create(set_pool, NULL);
296 (*set)->pool = set_pool;
297 }
298
299 xaset_insert(*set, (xasetmember_t *) c);
300
301 c->pool = c_pool;
302 c->set = *set;
303 c->parent = parent;
304 c->name = pstrdup(c->pool, name);
305
306 if (parent) {
307 if (parent->config_type == CONF_DYNDIR) {
308 c->flags |= CF_DYNAMIC;
309 }
310 }
311
312 (void) pr_parser_config_ctxt_push(c);
313 return c;
314 }
315
pr_parser_config_ctxt_push(config_rec * c)316 int pr_parser_config_ctxt_push(config_rec *c) {
317 if (c == NULL) {
318 errno = EINVAL;
319 return -1;
320 }
321
322 if (parser_confstack == NULL) {
323 errno = EPERM;
324 return -1;
325 }
326
327 if (!*parser_curr_config) {
328 *parser_curr_config = c;
329
330 } else {
331 parser_curr_config = (config_rec **) push_array(parser_confstack);
332 *parser_curr_config = c;
333 }
334
335 return 0;
336 }
337
pr_parser_get_lineno(void)338 unsigned int pr_parser_get_lineno(void) {
339 return parser_curr_lineno;
340 }
341
342 /* Return an array of all supported/known configuration directives. */
get_all_directives(pool * p)343 static array_header *get_all_directives(pool *p) {
344 array_header *names;
345 conftable *tab;
346 int idx;
347 unsigned int hash;
348
349 names = make_array(p, 1, sizeof(const char *));
350
351 idx = -1;
352 hash = 0;
353 tab = pr_stash_get_symbol2(PR_SYM_CONF, NULL, NULL, &idx, &hash);
354 while (idx != -1) {
355 pr_signals_handle();
356
357 if (tab != NULL) {
358 *((const char **) push_array(names)) = pstrdup(p, tab->directive);
359
360 } else {
361 idx++;
362 }
363
364 tab = pr_stash_get_symbol2(PR_SYM_CONF, NULL, tab, &idx, &hash);
365 }
366
367 return names;
368 }
369
pr_parser_parse_file(pool * p,const char * path,config_rec * start,int flags)370 int pr_parser_parse_file(pool *p, const char *path, config_rec *start,
371 int flags) {
372 pr_fh_t *fh;
373 struct stat st;
374 struct config_src *cs;
375 cmd_rec *cmd;
376 pool *tmp_pool;
377 char *buf, *report_path;
378 size_t bufsz;
379
380 if (path == NULL) {
381 errno = EINVAL;
382 return -1;
383 }
384
385 if (parser_servstack == NULL) {
386 errno = EPERM;
387 return -1;
388 }
389
390 tmp_pool = make_sub_pool(p ? p : permanent_pool);
391 pr_pool_tag(tmp_pool, "parser file pool");
392
393 report_path = (char *) path;
394 if (session.chroot_path) {
395 report_path = pdircat(tmp_pool, session.chroot_path, path, NULL);
396 }
397
398 if (!(flags & PR_PARSER_FL_DYNAMIC_CONFIG)) {
399 pr_trace_msg(trace_channel, 3, "parsing '%s' configuration", report_path);
400 }
401
402 fh = pr_fsio_open(path, O_RDONLY);
403 if (fh == NULL) {
404 int xerrno = errno;
405
406 destroy_pool(tmp_pool);
407
408 errno = xerrno;
409 return -1;
410 }
411
412 /* Stat the opened file to determine the optimal buffer size for IO. */
413 memset(&st, 0, sizeof(st));
414 if (pr_fsio_fstat(fh, &st) < 0) {
415 int xerrno = errno;
416
417 pr_fsio_close(fh);
418 destroy_pool(tmp_pool);
419
420 errno = xerrno;
421 return -1;
422 }
423
424 if (S_ISDIR(st.st_mode)) {
425 pr_fsio_close(fh);
426 destroy_pool(tmp_pool);
427
428 errno = EISDIR;
429 return -1;
430 }
431
432 /* Advise the platform that we will be only reading this file
433 * sequentially.
434 */
435 pr_fs_fadvise(PR_FH_FD(fh), 0, 0, PR_FS_FADVISE_SEQUENTIAL);
436
437 /* Check for world-writable files (and later, files in world-writable
438 * directories).
439 *
440 * For now, just warn about these; later, we will be more draconian.
441 */
442 if (st.st_mode & S_IWOTH) {
443 pr_log_pri(PR_LOG_WARNING, "warning: config file '%s' is world-writable",
444 path);
445 }
446
447 fh->fh_iosz = st.st_blksize;
448
449 /* Push the configuration information onto the stack of configuration
450 * sources.
451 */
452 cs = add_config_source(fh);
453
454 if (start != NULL) {
455 (void) pr_parser_config_ctxt_push(start);
456 }
457
458 bufsz = PR_TUNABLE_PARSER_BUFFER_SIZE;
459 buf = pcalloc(tmp_pool, bufsz + 1);
460
461 while (pr_parser_read_line(buf, bufsz) != NULL) {
462 pr_signals_handle();
463
464 cmd = pr_parser_parse_line(tmp_pool, buf, 0);
465 if (cmd == NULL) {
466 continue;
467 }
468
469 if (cmd->argc) {
470 conftable *conftab;
471 char found = FALSE;
472
473 cmd->server = *parser_curr_server;
474 cmd->config = *parser_curr_config;
475
476 conftab = pr_stash_get_symbol2(PR_SYM_CONF, cmd->argv[0], NULL,
477 &cmd->stash_index, &cmd->stash_hash);
478 while (conftab != NULL) {
479 modret_t *mr;
480
481 pr_signals_handle();
482
483 cmd->argv[0] = conftab->directive;
484
485 pr_trace_msg(trace_channel, 7,
486 "dispatching directive '%s' to module mod_%s", conftab->directive,
487 conftab->m->name);
488
489 mr = pr_module_call(conftab->m, conftab->handler, cmd);
490 if (mr != NULL) {
491 if (MODRET_ISERROR(mr)) {
492 if (!(flags & PR_PARSER_FL_DYNAMIC_CONFIG)) {
493 pr_log_pri(PR_LOG_WARNING, "fatal: %s on line %u of '%s'",
494 MODRET_ERRMSG(mr), cs->cs_lineno, report_path);
495 destroy_pool(tmp_pool);
496 errno = EPERM;
497 return -1;
498 }
499
500 pr_log_pri(PR_LOG_WARNING, "warning: %s on line %u of '%s'",
501 MODRET_ERRMSG(mr), cs->cs_lineno, report_path);
502 }
503 }
504
505 if (!MODRET_ISDECLINED(mr)) {
506 found = TRUE;
507 }
508
509 conftab = pr_stash_get_symbol2(PR_SYM_CONF, cmd->argv[0], conftab,
510 &cmd->stash_index, &cmd->stash_hash);
511 }
512
513 if (cmd->tmp_pool) {
514 destroy_pool(cmd->tmp_pool);
515 }
516
517 if (found == FALSE) {
518 register unsigned int i;
519 char *name;
520 size_t namelen;
521 int non_ascii = FALSE;
522
523 /* I encountered a case where a particular configuration file had
524 * what APPEARED to be a valid directive, but the parser kept reporting
525 * that the directive was unknown. I now suspect that the file in
526 * question had embedded UTF8 characters (spaces, perhaps), which
527 * would appear as normal spaces in e.g. UTF8-aware editors/terminals,
528 * but which the parser would rightly refuse.
529 *
530 * So to indicate that this might be the case, check for any non-ASCII
531 * characters in the "unknown" directive name, and if found, log
532 * about them.
533 */
534
535 name = cmd->argv[0];
536 namelen = strlen(name);
537
538 for (i = 0; i < namelen; i++) {
539 if (!isascii((int) name[i])) {
540 non_ascii = TRUE;
541 break;
542 }
543 }
544
545 if (!(flags & PR_PARSER_FL_DYNAMIC_CONFIG)) {
546 pr_log_pri(PR_LOG_WARNING, "fatal: unknown configuration directive "
547 "'%s' on line %u of '%s'", name, cs->cs_lineno, report_path);
548 if (non_ascii) {
549 pr_log_pri(PR_LOG_WARNING, "fatal: malformed directive name "
550 "'%s' (contains non-ASCII characters)", name);
551
552 } else {
553 array_header *directives, *similars;
554
555 directives = get_all_directives(tmp_pool);
556 similars = pr_str_get_similars(tmp_pool, name, directives, 0,
557 PR_STR_FL_IGNORE_CASE);
558 if (similars != NULL &&
559 similars->nelts > 0) {
560 unsigned int nelts;
561 const char **names, *msg;
562
563 names = similars->elts;
564 nelts = similars->nelts;
565 if (nelts > 4) {
566 nelts = 4;
567 }
568
569 msg = "fatal: Did you mean:";
570
571 if (nelts == 1) {
572 msg = pstrcat(tmp_pool, msg, " ", names[0], NULL);
573
574 } else {
575 for (i = 0; i < nelts; i++) {
576 msg = pstrcat(tmp_pool, msg, "\n ", names[i], NULL);
577 }
578 }
579
580 pr_log_pri(PR_LOG_WARNING, "%s", msg);
581 }
582 }
583
584 destroy_pool(tmp_pool);
585 errno = EPERM;
586 return -1;
587 }
588
589 pr_log_pri(PR_LOG_WARNING, "warning: unknown configuration directive "
590 "'%s' on line %u of '%s'", name, cs->cs_lineno, report_path);
591 if (non_ascii) {
592 pr_log_pri(PR_LOG_WARNING, "warning: malformed directive name "
593 "'%s' (contains non-ASCII characters)", name);
594 }
595 }
596 }
597
598 destroy_pool(cmd->pool);
599 memset(buf, '\0', bufsz);
600 }
601
602 /* Pop this configuration stream from the stack. */
603 remove_config_source();
604
605 pr_fsio_close(fh);
606
607 destroy_pool(tmp_pool);
608 return 0;
609 }
610
pr_parser_parse_line(pool * p,const char * text,size_t text_len)611 cmd_rec *pr_parser_parse_line(pool *p, const char *text, size_t text_len) {
612 register unsigned int i;
613 char *arg = "", *ptr, *word = NULL;
614 cmd_rec *cmd = NULL;
615 pool *sub_pool = NULL;
616 array_header *arr = NULL;
617
618 if (p == NULL ||
619 text == NULL) {
620 errno = EINVAL;
621 return NULL;
622 }
623
624 if (text_len == 0) {
625 text_len = strlen(text);
626 }
627
628 if (text_len == 0) {
629 errno = ENOENT;
630 return NULL;
631 }
632
633 ptr = (char *) text;
634
635 /* Build a new pool for the command structure and array */
636 sub_pool = make_sub_pool(p);
637 pr_pool_tag(sub_pool, "parser cmd subpool");
638
639 cmd = pcalloc(sub_pool, sizeof(cmd_rec));
640 cmd->pool = sub_pool;
641 cmd->stash_index = -1;
642 cmd->stash_hash = 0;
643
644 /* Add each word to the array */
645 arr = make_array(cmd->pool, 4, sizeof(char **));
646 while ((word = pr_str_get_word(&ptr, 0)) != NULL) {
647 char *ptr2;
648
649 pr_signals_handle();
650 ptr2 = get_config_word(cmd->pool, word);
651 *((char **) push_array(arr)) = ptr2;
652 cmd->argc++;
653 }
654
655 /* Terminate the array with a NULL. */
656 *((char **) push_array(arr)) = NULL;
657
658 /* The array header's job is done, we can forget about it and
659 * it will get purged when the command's pool is destroyed.
660 */
661
662 cmd->argv = (void **) arr->elts;
663
664 /* Perform a fixup on configuration directives so that:
665 *
666 * -argv[0]-- -argv[1]-- ----argv[2]-----
667 * <Option /etc/adir /etc/anotherdir>
668 *
669 * becomes:
670 *
671 * -argv[0]-- -argv[1]- ----argv[2]----
672 * <Option> /etc/adir /etc/anotherdir
673 */
674
675 if (cmd->argc &&
676 *((char *) cmd->argv[0]) == '<') {
677 char *cp;
678 size_t cp_len;
679
680 cp = cmd->argv[cmd->argc-1];
681 cp_len = strlen(cp);
682
683 if (*(cp + cp_len-1) == '>' &&
684 cmd->argc > 1) {
685
686 if (strncmp(cp, ">", 2) == 0) {
687 cmd->argv[cmd->argc-1] = NULL;
688 cmd->argc--;
689
690 } else {
691 *(cp + cp_len-1) = '\0';
692 }
693
694 cp = cmd->argv[0];
695 cp_len = strlen(cp);
696 if (*(cp + cp_len-1) != '>') {
697 cmd->argv[0] = pstrcat(cmd->pool, cp, ">", NULL);
698 }
699 }
700 }
701
702 if (cmd->argc < 2) {
703 arg = pstrdup(cmd->pool, arg);
704 }
705
706 for (i = 1; i < cmd->argc; i++) {
707 arg = pstrcat(cmd->pool, arg, *arg ? " " : "", cmd->argv[i], NULL);
708 }
709
710 cmd->arg = arg;
711 return cmd;
712 }
713
pr_parser_prepare(pool * p,xaset_t ** parsed_servers)714 int pr_parser_prepare(pool *p, xaset_t **parsed_servers) {
715
716 if (p == NULL) {
717 if (parser_pool == NULL) {
718 parser_pool = make_sub_pool(permanent_pool);
719 pr_pool_tag(parser_pool, "Parser Pool");
720 }
721
722 p = parser_pool;
723 }
724
725 if (parsed_servers == NULL) {
726 parser_server_list = &server_list;
727
728 } else {
729 parser_server_list = parsed_servers;
730 }
731
732 parser_servstack = make_array(p, 1, sizeof(server_rec *));
733 parser_curr_server = (server_rec **) push_array(parser_servstack);
734 *parser_curr_server = main_server;
735
736 parser_confstack = make_array(p, 10, sizeof(config_rec *));
737 parser_curr_config = (config_rec **) push_array(parser_confstack);
738 *parser_curr_config = NULL;
739
740 return 0;
741 }
742
743 /* This functions returns the next line from the configuration stream,
744 * skipping commented-out lines and trimming trailing and leading whitespace,
745 * returning, in effect, the next line of configuration data on which to
746 * act. This function has the advantage that it can be called by functions
747 * that don't have access to configuration file handle, such as the
748 * <IfDefine> and <IfModule> configuration handlers.
749 */
pr_parser_read_line(char * buf,size_t bufsz)750 char *pr_parser_read_line(char *buf, size_t bufsz) {
751 struct config_src *cs;
752
753 /* Always use the config stream at the top of the stack. */
754 cs = parser_sources;
755
756 if (buf == NULL ||
757 cs == NULL) {
758 errno = EINVAL;
759 return NULL;
760 }
761
762 if (cs->cs_fh == NULL) {
763 errno = EPERM;
764 return NULL;
765 }
766
767 parser_curr_lineno = cs->cs_lineno;
768
769 /* Check for error conditions. */
770
771 while ((pr_fsio_getline(buf, bufsz, cs->cs_fh, &(cs->cs_lineno))) != NULL) {
772 int have_eol = FALSE;
773 char *bufp = NULL;
774 size_t buflen;
775
776 pr_signals_handle();
777
778 buflen = strlen(buf);
779 parser_curr_lineno = cs->cs_lineno;
780
781 /* Trim off the trailing newline, if present. */
782 if (buflen &&
783 buf[buflen - 1] == '\n') {
784 have_eol = TRUE;
785 buf[buflen-1] = '\0';
786 buflen--;
787 }
788
789 if (buflen &&
790 buf[buflen - 1] == '\r') {
791 buf[buflen-1] = '\0';
792 buflen--;
793 }
794
795 if (have_eol == FALSE) {
796 pr_log_pri(PR_LOG_WARNING,
797 "warning: handling possibly truncated configuration data at "
798 "line %u of '%s'", cs->cs_lineno, cs->cs_fh->fh_path);
799 }
800
801 /* Advance past any leading whitespace. */
802 for (bufp = buf; *bufp && PR_ISSPACE(*bufp); bufp++);
803
804 /* Check for commented or blank lines at this point, and just continue on
805 * to the next configuration line if found. If not, return the
806 * configuration line.
807 */
808 if (*bufp == '#' || !*bufp) {
809 continue;
810
811 } else {
812
813 /* Copy the value of bufp back into the pointer passed in
814 * and return it.
815 */
816 buf = bufp;
817
818 return buf;
819 }
820 }
821
822 return NULL;
823 }
824
pr_parser_server_ctxt_close(void)825 server_rec *pr_parser_server_ctxt_close(void) {
826 if (!parser_curr_server) {
827 errno = ENOENT;
828 return NULL;
829 }
830
831 /* Disallow underflows. */
832 if (parser_curr_server == (server_rec **) parser_servstack->elts) {
833 errno = EPERM;
834 return NULL;
835 }
836
837 parser_curr_server--;
838 parser_servstack->nelts--;
839
840 return *parser_curr_server;
841 }
842
pr_parser_server_ctxt_get(void)843 server_rec *pr_parser_server_ctxt_get(void) {
844 if (parser_curr_server) {
845 return *parser_curr_server;
846 }
847
848 errno = ENOENT;
849 return NULL;
850 }
851
pr_parser_server_ctxt_push(server_rec * s)852 int pr_parser_server_ctxt_push(server_rec *s) {
853 if (s == NULL) {
854 errno = EINVAL;
855 return -1;
856 }
857
858 if (parser_servstack == NULL) {
859 errno = EPERM;
860 return -1;
861 }
862
863 parser_curr_server = (server_rec **) push_array(parser_servstack);
864 *parser_curr_server = s;
865
866 return 0;
867 }
868
pr_parser_server_ctxt_open(const char * addrstr)869 server_rec *pr_parser_server_ctxt_open(const char *addrstr) {
870 server_rec *s;
871 pool *p;
872
873 p = make_sub_pool(permanent_pool);
874 pr_pool_tag(p, "<VirtualHost> Pool");
875
876 s = (server_rec *) pcalloc(p, sizeof(server_rec));
877 s->pool = p;
878 s->config_type = CONF_VIRTUAL;
879 s->sid = ++parser_sid;
880 s->notes = pr_table_nalloc(p, 0, 8);
881
882 /* TCP KeepAlive is enabled by default, with the system defaults. */
883 s->tcp_keepalive = palloc(s->pool, sizeof(struct tcp_keepalive));
884 s->tcp_keepalive->keepalive_enabled = TRUE;
885 s->tcp_keepalive->keepalive_idle = -1;
886 s->tcp_keepalive->keepalive_count = -1;
887 s->tcp_keepalive->keepalive_intvl = -1;
888
889 /* Have to make sure it ends up on the end of the chain, otherwise
890 * main_server becomes useless.
891 */
892 xaset_insert_end(*parser_server_list, (xasetmember_t *) s);
893 s->set = *parser_server_list;
894 if (addrstr) {
895 s->ServerAddress = pstrdup(s->pool, addrstr);
896 }
897
898 /* Default server port */
899 s->ServerPort = pr_inet_getservport(s->pool, "ftp", "tcp");
900
901 (void) pr_parser_server_ctxt_push(s);
902 return s;
903 }
904
pr_parser_set_include_opts(unsigned long opts)905 unsigned long pr_parser_set_include_opts(unsigned long opts) {
906 unsigned long prev_opts;
907
908 prev_opts = parser_include_opts;
909 parser_include_opts = opts;
910
911 return prev_opts;
912 }
913
914 static const char *tmpfile_patterns[] = {
915 "*~",
916 "*.sw?",
917 NULL
918 };
919
is_tmp_file(const char * file)920 static int is_tmp_file(const char *file) {
921 register unsigned int i;
922
923 for (i = 0; tmpfile_patterns[i]; i++) {
924 if (pr_fnmatch(tmpfile_patterns[i], file, PR_FNM_PERIOD) == 0) {
925 return TRUE;
926 }
927 }
928
929 return FALSE;
930 }
931
config_filename_cmp(const void * a,const void * b)932 static int config_filename_cmp(const void *a, const void *b) {
933 return strcmp(*((char **) a), *((char **) b));
934 }
935
parse_wildcard_config_path(pool * p,const char * path,unsigned int depth)936 static int parse_wildcard_config_path(pool *p, const char *path,
937 unsigned int depth) {
938 register unsigned int i;
939 int res, xerrno;
940 pool *tmp_pool;
941 array_header *globbed_dirs = NULL;
942 const char *component = NULL, *parent_path = NULL, *suffix_path = NULL;
943 struct stat st;
944 size_t path_len, component_len;
945 char *name_pattern = NULL;
946 void *dirh = NULL;
947 struct dirent *dent = NULL;
948
949 if (depth > PR_PARSER_INCLUDE_MAX_DEPTH) {
950 pr_log_pri(PR_LOG_WARNING, "error: resolving wildcard pattern in '%s' "
951 "exceeded maximum filesystem depth (%u)", path,
952 (unsigned int) PR_PARSER_INCLUDE_MAX_DEPTH);
953 errno = EINVAL;
954 return -1;
955 }
956
957 path_len = strlen(path);
958 if (path_len < 2) {
959 pr_trace_msg(trace_channel, 7, "path '%s' too short to be wildcard path",
960 path);
961
962 /* The first character must be a slash, and we need at least one more
963 * character in the path as a glob character.
964 */
965 errno = EINVAL;
966 return -1;
967 }
968
969 tmp_pool = make_sub_pool(p);
970 pr_pool_tag(tmp_pool, "Include sub-pool");
971
972 /* We need to find the first component of the path which contains glob
973 * characters. We then use the path up to the previous component as the
974 * parent directory to open, and the glob-bearing component as the filter
975 * for directories within the parent.
976 */
977
978 component = path + 1;
979 while (TRUE) {
980 int last_component = FALSE;
981 char *ptr;
982
983 pr_signals_handle();
984
985 ptr = strchr(component, '/');
986 if (ptr != NULL) {
987 component_len = ptr - component;
988
989 } else {
990 component_len = strlen(component);
991 last_component = TRUE;
992 }
993
994 if (memchr(component, (int) '*', component_len) != NULL ||
995 memchr(component, (int) '?', component_len) != NULL ||
996 memchr(component, (int) '[', component_len) != NULL) {
997
998 name_pattern = pstrndup(tmp_pool, component, component_len);
999
1000 if (parent_path == NULL) {
1001 parent_path = pstrndup(tmp_pool, "/", 1);
1002 }
1003
1004 if (ptr != NULL) {
1005 suffix_path = pstrdup(tmp_pool, ptr + 1);
1006 }
1007
1008 break;
1009 }
1010
1011 if (parent_path != NULL) {
1012 parent_path = pdircat(tmp_pool, parent_path,
1013 pstrndup(tmp_pool, component, component_len), NULL);
1014
1015 } else {
1016 parent_path = pstrndup(tmp_pool, "/", 1);
1017 }
1018
1019 if (last_component) {
1020 break;
1021 }
1022
1023 component = ptr + 1;
1024 }
1025
1026 if (name_pattern == NULL) {
1027 pr_trace_msg(trace_channel, 4,
1028 "unable to process invalid, non-globbed path '%s'", path);
1029 errno = ENOENT;
1030 return -1;
1031 }
1032
1033 pr_fs_clear_cache2(parent_path);
1034 res = pr_fsio_lstat(parent_path, &st);
1035 xerrno = errno;
1036
1037 if (res < 0) {
1038 pr_log_pri(PR_LOG_WARNING,
1039 "error: failed to check configuration path '%s': %s", parent_path,
1040 strerror(xerrno));
1041
1042 destroy_pool(tmp_pool);
1043 errno = xerrno;
1044 return -1;
1045 }
1046
1047 if (S_ISLNK(st.st_mode) &&
1048 !(parser_include_opts & PR_PARSER_INCLUDE_OPT_ALLOW_SYMLINKS)) {
1049 pr_log_pri(PR_LOG_WARNING,
1050 "error: cannot read configuration path '%s': Symbolic link", parent_path);
1051 destroy_pool(tmp_pool);
1052 errno = ENOTDIR;
1053 return -1;
1054 }
1055
1056 pr_log_pri(PR_LOG_DEBUG,
1057 "processing configuration directory '%s' using pattern '%s', suffix '%s'",
1058 parent_path, name_pattern, suffix_path);
1059
1060 dirh = pr_fsio_opendir(parent_path);
1061 if (dirh == NULL) {
1062 pr_log_pri(PR_LOG_WARNING,
1063 "error: unable to open configuration directory '%s': %s", parent_path,
1064 strerror(errno));
1065 destroy_pool(tmp_pool);
1066 errno = EINVAL;
1067 return -1;
1068 }
1069
1070 globbed_dirs = make_array(tmp_pool, 0, sizeof(char *));
1071
1072 while ((dent = pr_fsio_readdir(dirh)) != NULL) {
1073 pr_signals_handle();
1074
1075 if (strncmp(dent->d_name, ".", 2) == 0 ||
1076 strncmp(dent->d_name, "..", 3) == 0) {
1077 continue;
1078 }
1079
1080 if (parser_include_opts & PR_PARSER_INCLUDE_OPT_IGNORE_TMP_FILES) {
1081 if (is_tmp_file(dent->d_name) == TRUE) {
1082 pr_trace_msg(trace_channel, 19,
1083 "ignoring temporary file '%s' found in directory '%s'", dent->d_name,
1084 parent_path);
1085 continue;
1086 }
1087 }
1088
1089 if (pr_fnmatch(name_pattern, dent->d_name, PR_FNM_PERIOD) == 0) {
1090 pr_trace_msg(trace_channel, 17,
1091 "matched '%s' path with wildcard pattern '%s'", dent->d_name,
1092 name_pattern);
1093
1094 *((char **) push_array(globbed_dirs)) = pdircat(tmp_pool, parent_path,
1095 dent->d_name, suffix_path, NULL);
1096 }
1097 }
1098
1099 pr_fsio_closedir(dirh);
1100
1101 if (globbed_dirs->nelts == 0) {
1102 pr_log_pri(PR_LOG_WARNING,
1103 "error: no matches found for wildcard directory '%s'", path);
1104 destroy_pool(tmp_pool);
1105 errno = ENOENT;
1106 return -1;
1107 }
1108
1109 depth++;
1110
1111 qsort((void *) globbed_dirs->elts, globbed_dirs->nelts, sizeof(char *),
1112 config_filename_cmp);
1113
1114 for (i = 0; i < globbed_dirs->nelts; i++) {
1115 const char *globbed_dir;
1116
1117 globbed_dir = ((const char **) globbed_dirs->elts)[i];
1118 res = parse_config_path2(p, globbed_dir, depth);
1119 if (res < 0) {
1120 xerrno = errno;
1121
1122 pr_trace_msg(trace_channel, 7, "error parsing wildcard path '%s': %s",
1123 globbed_dir, strerror(xerrno));
1124
1125 destroy_pool(tmp_pool);
1126 errno = xerrno;
1127 return -1;
1128 }
1129 }
1130
1131 destroy_pool(tmp_pool);
1132 return 0;
1133 }
1134
parse_config_path2(pool * p,const char * path,unsigned int depth)1135 int parse_config_path2(pool *p, const char *path, unsigned int depth) {
1136 struct stat st;
1137 int have_glob;
1138 void *dirh;
1139 struct dirent *dent;
1140 array_header *file_list;
1141 char *dup_path, *ptr;
1142 pool *tmp_pool;
1143
1144 if (p == NULL ||
1145 path == NULL ||
1146 (depth > PR_PARSER_INCLUDE_MAX_DEPTH)) {
1147 errno = EINVAL;
1148 return -1;
1149 }
1150
1151 if (pr_fs_valid_path(path) < 0) {
1152 errno = EINVAL;
1153 return -1;
1154 }
1155
1156 have_glob = pr_str_is_fnmatch(path);
1157 if (have_glob) {
1158 /* Even though the path may be valid, it also may not be a filesystem
1159 * path; consider custom FSIO modules. Thus if the path does not start
1160 * with a slash, it should not be treated as having globs.
1161 */
1162 if (*path != '/') {
1163 have_glob = FALSE;
1164 }
1165 }
1166
1167 pr_fs_clear_cache2(path);
1168
1169 if (have_glob) {
1170 pr_trace_msg(trace_channel, 19, "parsing '%s' as a globbed path", path);
1171 }
1172
1173 if (!have_glob &&
1174 pr_fsio_lstat(path, &st) < 0) {
1175 return -1;
1176 }
1177
1178 /* If path is not a glob pattern, and is a symlink OR is not a directory,
1179 * then use the normal parsing function for the file.
1180 */
1181 if (have_glob == FALSE &&
1182 (S_ISLNK(st.st_mode) ||
1183 !S_ISDIR(st.st_mode))) {
1184 int res, xerrno;
1185
1186 PRIVS_ROOT
1187 res = pr_parser_parse_file(p, path, NULL, 0);
1188 xerrno = errno;
1189 PRIVS_RELINQUISH
1190
1191 errno = xerrno;
1192 return res;
1193 }
1194
1195 tmp_pool = make_sub_pool(p);
1196 pr_pool_tag(tmp_pool, "Include sub-pool");
1197
1198 /* Handle the glob/directory. */
1199 dup_path = pstrdup(tmp_pool, path);
1200
1201 ptr = strrchr(dup_path, '/');
1202
1203 if (have_glob) {
1204 int have_glob_dir;
1205
1206 /* Note that we know, by definition, that ptr CANNOT be null here; dup_path
1207 * is a duplicate of path, and the first character (if nothing else) of
1208 * path MUST be a slash, per earlier checks.
1209 */
1210 *ptr = '\0';
1211
1212 /* We just changed ptr, thus we DO need to check whether the now-modified
1213 * path contains fnmatch(3) characters again.
1214 */
1215 have_glob_dir = pr_str_is_fnmatch(dup_path);
1216 if (have_glob_dir) {
1217 const char *glob_dir;
1218
1219 if (parser_include_opts & PR_PARSER_INCLUDE_OPT_IGNORE_WILDCARDS) {
1220 pr_log_pri(PR_LOG_WARNING, "error: wildcard patterns not allowed in "
1221 "configuration directory name '%s'", dup_path);
1222 destroy_pool(tmp_pool);
1223 errno = EINVAL;
1224 return -1;
1225 }
1226
1227 *ptr = '/';
1228 glob_dir = pstrdup(p, dup_path);
1229 destroy_pool(tmp_pool);
1230
1231 return parse_wildcard_config_path(p, glob_dir, depth);
1232 }
1233
1234 ptr++;
1235
1236 /* Check the directory component. */
1237 pr_fs_clear_cache2(dup_path);
1238 if (pr_fsio_lstat(dup_path, &st) < 0) {
1239 int xerrno = errno;
1240
1241 pr_log_pri(PR_LOG_WARNING,
1242 "error: failed to check configuration path '%s': %s", dup_path,
1243 strerror(xerrno));
1244
1245 destroy_pool(tmp_pool);
1246 errno = xerrno;
1247 return -1;
1248 }
1249
1250 if (S_ISLNK(st.st_mode) &&
1251 !(parser_include_opts & PR_PARSER_INCLUDE_OPT_ALLOW_SYMLINKS)) {
1252 pr_log_pri(PR_LOG_WARNING,
1253 "error: cannot read configuration path '%s': Symbolic link", path);
1254 destroy_pool(tmp_pool);
1255 errno = ENOTDIR;
1256 return -1;
1257 }
1258
1259 if (have_glob_dir == FALSE &&
1260 pr_str_is_fnmatch(ptr) == FALSE) {
1261 pr_log_pri(PR_LOG_WARNING,
1262 "error: wildcard pattern required for file '%s'", ptr);
1263 destroy_pool(tmp_pool);
1264 errno = EINVAL;
1265 return -1;
1266 }
1267 }
1268
1269 pr_trace_msg(trace_channel, 3, "processing configuration directory '%s'", dup_path);
1270
1271 dirh = pr_fsio_opendir(dup_path);
1272 if (dirh == NULL) {
1273 pr_log_pri(PR_LOG_WARNING,
1274 "error: unable to open configuration directory '%s': %s", dup_path,
1275 strerror(errno));
1276 destroy_pool(tmp_pool);
1277 errno = EINVAL;
1278 return -1;
1279 }
1280
1281 file_list = make_array(tmp_pool, 0, sizeof(char *));
1282
1283 while ((dent = pr_fsio_readdir(dirh)) != NULL) {
1284 pr_signals_handle();
1285
1286 if (strncmp(dent->d_name, ".", 2) == 0 ||
1287 strncmp(dent->d_name, "..", 3) == 0) {
1288 continue;
1289 }
1290
1291 if (parser_include_opts & PR_PARSER_INCLUDE_OPT_IGNORE_TMP_FILES) {
1292 if (is_tmp_file(dent->d_name) == TRUE) {
1293 pr_trace_msg(trace_channel, 19,
1294 "ignoring temporary file '%s' found in directory '%s'", dent->d_name,
1295 dup_path);
1296 continue;
1297 }
1298 }
1299
1300 if (have_glob == FALSE ||
1301 (ptr != NULL &&
1302 pr_fnmatch(ptr, dent->d_name, PR_FNM_PERIOD) == 0)) {
1303 *((char **) push_array(file_list)) = pdircat(tmp_pool, dup_path,
1304 dent->d_name, NULL);
1305 }
1306 }
1307
1308 pr_fsio_closedir(dirh);
1309
1310 if (file_list->nelts) {
1311 register unsigned int i;
1312
1313 qsort((void *) file_list->elts, file_list->nelts, sizeof(char *),
1314 config_filename_cmp);
1315
1316 for (i = 0; i < file_list->nelts; i++) {
1317 int res, xerrno;
1318 char *file;
1319
1320 file = ((char **) file_list->elts)[i];
1321
1322 /* Make sure we always parse the files with root privs. The
1323 * previously parsed file might have had root privs relinquished
1324 * (e.g. by its directive handlers), but when we first start up,
1325 * we have root privs. See Bug#3855.
1326 */
1327 PRIVS_ROOT
1328 res = pr_parser_parse_file(tmp_pool, file, NULL, 0);
1329 xerrno = errno;
1330 PRIVS_RELINQUISH
1331
1332 if (res < 0) {
1333 pr_log_pri(PR_LOG_WARNING,
1334 "error: unable to open parse file '%s': %s", file,
1335 strerror(xerrno));
1336 }
1337 }
1338 }
1339
1340 destroy_pool(tmp_pool);
1341 return 0;
1342 }
1343
parse_config_path(pool * p,const char * path)1344 int parse_config_path(pool *p, const char *path) {
1345 return parse_config_path2(p, path, 0);
1346 }
1347