1 /*
2  * Copyright (c) 2002-2012 Balabit
3  * Copyright (c) 1998-2012 Balázs Scheidler
4  *
5  * This library is free software; you can redistribute it and/or
6  * modify it under the terms of the GNU Lesser General Public
7  * License as published by the Free Software Foundation; either
8  * version 2.1 of the License, or (at your option) any later version.
9  *
10  * This library 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 GNU
13  * Lesser General Public License for more details.
14  *
15  * You should have received a copy of the GNU Lesser General Public
16  * License along with this library; if not, write to the Free Software
17  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
18  *
19  * As an additional exemption you are allowed to compile & link against the
20  * OpenSSL libraries as published by the OpenSSL project. See the file
21  * COPYING for details.
22  *
23  */
24 
25 #include "cfg-lexer.h"
26 #include "cfg-lexer-subst.h"
27 #include "cfg-block-generator.h"
28 #include "cfg-grammar.h"
29 #include "block-ref-parser.h"
30 #include "pragma-parser.h"
31 #include "messages.h"
32 #include "pathutils.h"
33 #include "plugin.h"
34 #include "plugin-types.h"
35 
36 #include <string.h>
37 #include <glob.h>
38 #include <sys/stat.h>
39 
40 /* include header for generated lexer */
41 #define YYSTYPE CFG_STYPE
42 #define YYLTYPE CFG_LTYPE
43 #include "cfg-lex.h"
44 #undef YYSTYPE
45 #undef YYLTYPE
46 
47 /*
48  * A token block is a series of tokens to be injected into the tokens
49  * fetched by the lexer.  It is assumed to be filled and then depleted, the
50  * two operations cannot be intermixed.
51  */
52 struct _CfgTokenBlock
53 {
54   gint pos;
55   GArray *tokens;
56 };
57 
58 /**
59  * CfgLexerContext:
60  *
61  * This object tells the lexer in which context it is operating right
62  * now. The context influences the way the lexer works, for example in
63  * LL_CONTEXT_BLOCK_DEF/REF all keyword resolutions are disabled.
64  *
65  * A textual description is also associated with the current context
66  * in order to give better error messages.
67  **/
68 typedef struct _CfgLexerContext
69 {
70   gint type;
71   CfgLexerKeyword *keywords;
72   gchar desc[0];
73 } CfgLexerContext;
74 
75 typedef enum
76 {
77   CLPR_ERROR,
78   CLPR_OK,
79   CLPR_LEX_AGAIN
80 } CfgLexerPreprocessResult;
81 
82 /*
83  * cfg_lexer_push_context:
84  *
85  * This function can be used to push a lexer context to the stack. The top
86  * of the stack determines how an error is reported and can also influence
87  * the lexer.
88  */
89 void
cfg_lexer_push_context(CfgLexer * self,gint type,CfgLexerKeyword * keywords,const gchar * desc)90 cfg_lexer_push_context(CfgLexer *self, gint type, CfgLexerKeyword *keywords, const gchar *desc)
91 {
92   CfgLexerContext *context;
93 
94   context = g_malloc(sizeof(CfgLexerContext) + strlen(desc) + 1);
95   context->type = type ? type : cfg_lexer_get_context_type(self);
96   context->keywords = keywords;
97   memcpy(&context->desc, desc, strlen(desc) + 1);
98   self->context_stack = g_list_prepend(self->context_stack, context);
99 }
100 
101 /*
102  * cfg_lexer_pop_context:
103  *
104  * Pop the topmost item off the stack.
105  */
106 void
cfg_lexer_pop_context(CfgLexer * self)107 cfg_lexer_pop_context(CfgLexer *self)
108 {
109   if (self->context_stack)
110     {
111       g_free((gchar *) self->context_stack->data);
112       self->context_stack = g_list_delete_link(self->context_stack, self->context_stack);
113     }
114 }
115 
116 /*
117  * cfg_lexer_get_context_type:
118  *
119  * Get the current context type (one of LL_CONTEXT_* values).
120  */
121 gint
cfg_lexer_get_context_type(CfgLexer * self)122 cfg_lexer_get_context_type(CfgLexer *self)
123 {
124   GList *l;
125 
126   l = self->context_stack;
127   if (l)
128     return ((CfgLexerContext *) l->data)->type;
129   return 0;
130 }
131 
132 /*
133  * cfg_lexer_get_context_description:
134  *
135  * Get the description of the current context.
136  */
137 const gchar *
cfg_lexer_get_context_description(CfgLexer * self)138 cfg_lexer_get_context_description(CfgLexer *self)
139 {
140   GList *l;
141 
142   l = self->context_stack;
143   if (l)
144     return ((CfgLexerContext *) l->data)->desc;
145   return "configuration";
146 }
147 
148 /* this can only be called from the grammar */
149 static CfgIncludeLevel *
_find_closest_file_inclusion(CfgLexer * self,CFG_LTYPE * yylloc)150 _find_closest_file_inclusion(CfgLexer *self, CFG_LTYPE *yylloc)
151 {
152   for (gint level_ndx = self->include_depth; level_ndx >= 0; level_ndx--)
153     {
154       CfgIncludeLevel *level = &self->include_stack[level_ndx];
155 
156       if (level->include_type == CFGI_FILE)
157         return level;
158     }
159   return NULL;
160 }
161 
162 const gchar *
cfg_lexer_format_location(CfgLexer * self,CFG_LTYPE * yylloc,gchar * buf,gsize buf_len)163 cfg_lexer_format_location(CfgLexer *self, CFG_LTYPE *yylloc, gchar *buf, gsize buf_len)
164 {
165   CfgIncludeLevel *level;
166 
167   level = _find_closest_file_inclusion(self, yylloc);
168   if (level)
169     g_snprintf(buf, buf_len, "%s:%d:%d",
170                level->name,
171                level->lloc.first_line, level->lloc.first_column);
172   else
173     g_snprintf(buf, buf_len, "%s:%d:%d", "#buffer", yylloc->first_line, yylloc->first_column);
174   return buf;
175 }
176 
177 EVTTAG *
cfg_lexer_format_location_tag(CfgLexer * self,CFG_LTYPE * yylloc)178 cfg_lexer_format_location_tag(CfgLexer *self, CFG_LTYPE *yylloc)
179 {
180   gchar buf[256];
181 
182   return evt_tag_str("location", cfg_lexer_format_location(self, yylloc, buf, sizeof(buf)));
183 }
184 
185 int
cfg_lexer_lookup_keyword(CfgLexer * self,CFG_STYPE * yylval,CFG_LTYPE * yylloc,const char * token)186 cfg_lexer_lookup_keyword(CfgLexer *self, CFG_STYPE *yylval, CFG_LTYPE *yylloc, const char *token)
187 {
188   GList *l;
189 
190   l = self->context_stack;
191   while (l)
192     {
193       CfgLexerContext *context = ((CfgLexerContext *) l->data);
194       CfgLexerKeyword *keywords = context->keywords;
195 
196       if (keywords)
197         {
198           int i, j;
199 
200           for (i = 0; keywords[i].kw_name; i++)
201             {
202               if (strcmp(keywords[i].kw_name, CFG_KEYWORD_STOP) == 0)
203                 {
204                   yylval->type = LL_IDENTIFIER;
205                   yylval->cptr = strdup(token);
206                   return LL_IDENTIFIER;
207                 }
208 
209               for (j = 0; token[j] && keywords[i].kw_name[j]; j++)
210                 {
211                   if (token[j] == '-' || token[j] == '_')
212                     {
213                       if (keywords[i].kw_name[j] != '_')
214                         break;
215                     }
216                   else if (token[j] != keywords[i].kw_name[j])
217                     break;
218                 }
219               if (token[j] == 0 && keywords[i].kw_name[j] == 0)
220                 {
221                   /* match */
222                   switch (keywords[i].kw_status)
223                     {
224                     case KWS_OBSOLETE:
225                       msg_warning("WARNING: Your configuration file uses an obsoleted keyword, please update your configuration",
226                                   evt_tag_str("keyword", keywords[i].kw_name),
227                                   evt_tag_str("change", keywords[i].kw_explain),
228                                   cfg_lexer_format_location_tag(self, yylloc));
229                       break;
230                     default:
231                       break;
232                     }
233                   keywords[i].kw_status = KWS_NORMAL;
234                   yylval->type = LL_TOKEN;
235                   yylval->token = keywords[i].kw_token;
236                   return keywords[i].kw_token;
237                 }
238             }
239         }
240       l = l->next;
241     }
242   yylval->type = LL_IDENTIFIER;
243   yylval->cptr = strdup(token);
244   return LL_IDENTIFIER;
245 }
246 
247 void
cfg_lexer_clear_include_level(CfgLexer * self,CfgIncludeLevel * level)248 cfg_lexer_clear_include_level(CfgLexer *self, CfgIncludeLevel *level)
249 {
250   g_free(level->name);
251   if (level->yybuf)
252     _cfg_lexer__delete_buffer(level->yybuf, self->state);
253 
254   if (level->include_type == CFGI_FILE)
255     {
256       if (level->file.include_file)
257         fclose(level->file.include_file);
258       g_slist_foreach(level->file.files, (GFunc) g_free, NULL);
259       g_slist_free(level->file.files);
260     }
261   else if (level->include_type == CFGI_BUFFER)
262     {
263       g_free(level->buffer.content);
264       g_free(level->buffer.original_content);
265     }
266   memset(level, 0, sizeof(*level));
267 }
268 
269 gboolean
cfg_lexer_start_next_include(CfgLexer * self)270 cfg_lexer_start_next_include(CfgLexer *self)
271 {
272   CfgIncludeLevel *level = &self->include_stack[self->include_depth];
273   gchar *filename;
274   gboolean buffer_processed = FALSE;
275 
276   if (self->include_depth == 0)
277     {
278       return FALSE;
279     }
280 
281   if (level->yybuf)
282     {
283       msg_debug("Finishing include",
284                 evt_tag_str((level->include_type == CFGI_FILE ? "filename" : "content"), level->name),
285                 evt_tag_int("depth", self->include_depth));
286       buffer_processed = TRUE;
287     }
288 
289   /* reset the include state, should also handle initial invocations, in which case everything is NULL */
290   if (level->yybuf)
291     {
292       _cfg_lexer__delete_buffer(level->yybuf, self->state);
293       level->yybuf = NULL;
294     }
295 
296   if (level->include_type == CFGI_FILE)
297     {
298       if (level->file.include_file)
299         {
300           fclose(level->file.include_file);
301           level->file.include_file = NULL;
302         }
303     }
304 
305   if ((level->include_type == CFGI_BUFFER && buffer_processed) ||
306       (level->include_type == CFGI_FILE && !level->file.files))
307     {
308       /* we finished with an include statement that included a series of
309        * files (e.g.  directory include). */
310 
311 
312       /* NOTE: this couple of lines should become just a call to
313        * cfg_lexer_clear_include_level(), however this entire function is
314        * playing nasty tricks with the data members within the
315        * CfgIncludeLevel, which I can't decipher right now, so I am leaving
316        * this as is. Memory management in the lexer is clearly messed
317        * up.  */
318 
319       g_free(level->name);
320 
321       if (level->include_type == CFGI_BUFFER)
322         {
323           g_free(level->buffer.content);
324           g_free(level->buffer.original_content);
325         }
326 
327       memset(level, 0, sizeof(*level));
328 
329       self->include_depth--;
330       _cfg_lexer__switch_to_buffer(self->include_stack[self->include_depth].yybuf, self->state);
331 
332       return TRUE;
333     }
334 
335   /* now populate "level" with the new include information */
336   if (level->include_type == CFGI_BUFFER)
337     {
338       level->yybuf = _cfg_lexer__scan_buffer(level->buffer.content, level->buffer.content_length, self->state);
339     }
340   else if (level->include_type == CFGI_FILE)
341     {
342       FILE *include_file;
343 
344       filename = (gchar *) level->file.files->data;
345       level->file.files = g_slist_delete_link(level->file.files, level->file.files);
346 
347       include_file = fopen(filename, "r");
348       if (!include_file)
349         {
350           msg_error("Error opening include file",
351                     evt_tag_str("filename", filename),
352                     evt_tag_int("depth", self->include_depth));
353           g_free(filename);
354           return FALSE;
355         }
356       msg_debug("Starting to read include file",
357                 evt_tag_str("filename", filename),
358                 evt_tag_int("depth", self->include_depth));
359       g_free(level->name);
360       level->name = filename;
361 
362       level->file.include_file = include_file;
363       level->yybuf = _cfg_lexer__create_buffer(level->file.include_file, YY_BUF_SIZE, self->state);
364     }
365   else
366     {
367       g_assert_not_reached();
368     }
369 
370   level->lloc.first_line = level->lloc.last_line = 1;
371   level->lloc.first_column = level->lloc.last_column = 1;
372   level->lloc.level = level;
373 
374   _cfg_lexer__switch_to_buffer(level->yybuf, self->state);
375   return TRUE;
376 }
377 
378 static gboolean
cfg_lexer_include_file_simple(CfgLexer * self,const gchar * filename)379 cfg_lexer_include_file_simple(CfgLexer *self, const gchar *filename)
380 {
381   CfgIncludeLevel *level;
382   struct stat st;
383 
384   if (stat(filename, &st) < 0)
385     {
386       return FALSE;
387     }
388 
389   self->include_depth++;
390   level = &self->include_stack[self->include_depth];
391   level->include_type = CFGI_FILE;
392   if (S_ISDIR(st.st_mode))
393     {
394       GDir *dir;
395       GError *error = NULL;
396       const gchar *entry;
397 
398       dir = g_dir_open(filename, 0, &error);
399       if (!dir)
400         {
401           msg_error("Error opening directory for reading",
402                     evt_tag_str("filename", filename),
403                     evt_tag_str("error", error->message));
404           g_error_free(error);
405           goto drop_level;
406         }
407       while ((entry = g_dir_read_name(dir)))
408         {
409           const gchar *p;
410           if (entry[0] == '.')
411             {
412               msg_debug("Skipping include file, it cannot begin with .",
413                         evt_tag_str("filename", entry));
414               continue;
415             }
416           for (p = entry; *p; p++)
417             {
418               if (!((*p >= 'a' && *p <= 'z') ||
419                     (*p >= 'A' && *p <= 'Z') ||
420                     (*p >= '0' && *p <= '9') ||
421                     (*p == '_') || (*p == '-') || (*p == '.')))
422                 {
423                   msg_debug("Skipping include file, does not match pattern [\\-_a-zA-Z0-9]+",
424                             evt_tag_str("filename", entry));
425                   p = NULL;
426                   break;
427                 }
428             }
429           if (p)
430             {
431               gchar *full_filename = g_build_filename(filename, entry, NULL);
432               if (stat(full_filename, &st) < 0 || S_ISDIR(st.st_mode))
433                 {
434                   msg_debug("Skipping include file as it is a directory",
435                             evt_tag_str("filename", entry));
436                   g_free(full_filename);
437                   continue;
438                 }
439               level->file.files = g_slist_insert_sorted(level->file.files, full_filename, (GCompareFunc) strcmp);
440               msg_debug("Adding include file",
441                         evt_tag_str("filename", entry),
442                         evt_tag_int("depth", self->include_depth));
443             }
444         }
445       g_dir_close(dir);
446       if (!level->file.files)
447         {
448           /* no include files in the specified directory */
449           msg_debug("No files in this include directory",
450                     evt_tag_str("dir", filename));
451           self->include_depth--;
452           return TRUE;
453         }
454     }
455   else
456     {
457       g_assert(level->file.files == NULL);
458       level->file.files = g_slist_prepend(level->file.files, g_strdup(filename));
459     }
460   return cfg_lexer_start_next_include(self);
461 drop_level:
462   g_slist_foreach(level->file.files, (GFunc) g_free, NULL);
463   g_slist_free(level->file.files);
464   level->file.files = NULL;
465 
466   return FALSE;
467 }
468 
469 static int
_cfg_lexer_glob_err(const char * p,gint e)470 _cfg_lexer_glob_err (const char *p, gint e)
471 {
472   if (e != ENOENT)
473     {
474       msg_debug ("Error processing path for inclusion",
475                  evt_tag_str("path", p),
476                  evt_tag_errno("errno", e));
477       return -1;
478     }
479   return 0;
480 }
481 
482 #ifndef GLOB_NOMAGIC
483 #define GLOB_NOMAGIC 0
484 
485 int
__glob_pattern_p(const char * pattern)486 __glob_pattern_p (const char *pattern)
487 {
488   register const char *p;
489   int open = 0;
490 
491   for (p = pattern; *p != '\0'; ++p)
492     switch (*p)
493       {
494       case '?':
495       case '*':
496         return 1;
497 
498       case '\\':
499         if (p[1] != '\0')
500           ++p;
501         break;
502 
503       case '[':
504         open = 1;
505         break;
506 
507       case ']':
508         if (open)
509           return 1;
510         break;
511       }
512 
513   return 0;
514 }
515 #else
516 #define SYSLOG_NG_HAVE_GLOB_NOMAGIC 1
517 #endif
518 
519 static gboolean
cfg_lexer_include_file_add(CfgLexer * self,const gchar * fn)520 cfg_lexer_include_file_add(CfgLexer *self, const gchar *fn)
521 {
522   CfgIncludeLevel *level;
523 
524   level = &self->include_stack[self->include_depth];
525   level->include_type = CFGI_FILE;
526 
527   level->file.files = g_slist_insert_sorted(level->file.files,
528                                             strdup(fn),
529                                             (GCompareFunc) strcmp);
530 
531   msg_debug("Adding include file",
532             evt_tag_str("filename", fn),
533             evt_tag_int("depth", self->include_depth));
534 
535   return TRUE;
536 }
537 
538 static gboolean
cfg_lexer_include_file_glob_at(CfgLexer * self,const gchar * pattern)539 cfg_lexer_include_file_glob_at(CfgLexer *self, const gchar *pattern)
540 {
541   glob_t globbuf;
542   size_t i;
543   int r;
544 
545   r = glob(pattern, GLOB_NOMAGIC, _cfg_lexer_glob_err, &globbuf);
546 
547   if (r != 0)
548     {
549       globfree(&globbuf);
550       if (r == GLOB_NOMATCH)
551         {
552 #ifndef SYSLOG_NG_HAVE_GLOB_NOMAGIC
553           if (!__glob_pattern_p (pattern))
554             {
555               return cfg_lexer_include_file_add(self, pattern);
556             }
557 #endif
558           return FALSE;
559         }
560       return TRUE;
561     }
562 
563   for (i = 0; i < globbuf.gl_pathc; i++)
564     {
565       cfg_lexer_include_file_add(self, globbuf.gl_pathv[i]);
566     }
567 
568   globfree(&globbuf);
569 
570   return TRUE;
571 }
572 
573 static const gchar *
_get_include_path(CfgLexer * self)574 _get_include_path(CfgLexer *self)
575 {
576   return self->cfg ? cfg_args_get(self->cfg->globals, "include-path") : NULL;
577 }
578 
579 static gboolean
cfg_lexer_include_file_glob(CfgLexer * self,const gchar * filename_)580 cfg_lexer_include_file_glob(CfgLexer *self, const gchar *filename_)
581 {
582   const gchar *path = _get_include_path(self);
583   gboolean process = FALSE;
584 
585   self->include_depth++;
586 
587   if (filename_[0] == '/' || !path)
588     process = cfg_lexer_include_file_glob_at(self, filename_);
589   else
590     {
591       gchar **dirs;
592       gchar *cf;
593       gint i = 0;
594 
595       dirs = g_strsplit(path, G_SEARCHPATH_SEPARATOR_S, 0);
596       while (dirs && dirs[i])
597         {
598           cf = g_build_filename(dirs[i], filename_, NULL);
599           process |= cfg_lexer_include_file_glob_at(self, cf);
600           g_free(cf);
601           i++;
602         }
603       g_strfreev(dirs);
604     }
605   if (process)
606     {
607       return cfg_lexer_start_next_include(self);
608     }
609   else
610     {
611       self->include_depth--;
612       return TRUE;
613     }
614 }
615 
616 gboolean
cfg_lexer_include_file(CfgLexer * self,const gchar * filename_)617 cfg_lexer_include_file(CfgLexer *self, const gchar *filename_)
618 {
619   struct stat st;
620   gchar *filename;
621 
622   msg_debug("Processing @include statement",
623             evt_tag_str("filename", filename_),
624             evt_tag_str("include-path", _get_include_path(self)));
625 
626   if (self->include_depth >= MAX_INCLUDE_DEPTH - 1)
627     {
628       msg_error("Include file depth is too deep, increase MAX_INCLUDE_DEPTH and recompile",
629                 evt_tag_str("filename", filename_),
630                 evt_tag_int("depth", self->include_depth));
631       return FALSE;
632     }
633 
634   filename = find_file_in_path(_get_include_path(self), filename_, G_FILE_TEST_EXISTS);
635   if (!filename || stat(filename, &st) < 0)
636     {
637       if (filename)
638         g_free(filename);
639 
640       if (cfg_lexer_include_file_glob(self, filename_))
641         return TRUE;
642 
643       msg_error("Include file/directory not found",
644                 evt_tag_str("filename", filename_),
645                 evt_tag_str("include-path", _get_include_path(self)),
646                 evt_tag_error("error"));
647       return FALSE;
648     }
649   else
650     {
651       gboolean result;
652 
653       result = cfg_lexer_include_file_simple(self, filename);
654       g_free(filename);
655       return result;
656     }
657 }
658 
659 gboolean
cfg_lexer_include_buffer_without_backtick_substitution(CfgLexer * self,const gchar * name,const gchar * buffer,gsize length)660 cfg_lexer_include_buffer_without_backtick_substitution(CfgLexer *self, const gchar *name, const gchar *buffer,
661                                                        gsize length)
662 {
663   CfgIncludeLevel *level;
664   gchar *lexer_buffer;
665   gsize lexer_buffer_len;
666 
667   g_assert(length >= 0);
668 
669   if (self->include_depth >= MAX_INCLUDE_DEPTH - 1)
670     {
671       msg_error("Include file depth is too deep, increase MAX_INCLUDE_DEPTH and recompile",
672                 evt_tag_str("buffer", name),
673                 evt_tag_int("depth", self->include_depth));
674       return FALSE;
675     }
676 
677   /* lex requires two NUL characters at the end of the input */
678   lexer_buffer_len = length + 2;
679   lexer_buffer = g_malloc(lexer_buffer_len);
680   memcpy(lexer_buffer, buffer, length);
681   lexer_buffer[length] = 0;
682   lexer_buffer[length + 1] = 0;
683 
684   self->include_depth++;
685   level = &self->include_stack[self->include_depth];
686 
687   level->include_type = CFGI_BUFFER;
688   level->buffer.content = lexer_buffer;
689   level->buffer.content_length = lexer_buffer_len;
690   level->buffer.original_content = g_strdup(lexer_buffer);
691   level->name = g_strdup(name);
692 
693   return cfg_lexer_start_next_include(self);
694 }
695 
696 /* NOTE: if length is negative, it indicates zero-terminated buffer and
697  * length should be determined based on that */
698 gboolean
cfg_lexer_include_buffer(CfgLexer * self,const gchar * name,const gchar * buffer,gssize length)699 cfg_lexer_include_buffer(CfgLexer *self, const gchar *name, const gchar *buffer, gssize length)
700 {
701   gchar *substituted_buffer;
702   gsize substituted_length = 0;
703   GError *error = NULL;
704   gboolean result = FALSE;
705 
706   substituted_buffer = cfg_lexer_subst_args_in_input(self->cfg ? self->cfg->globals : NULL, NULL, NULL, buffer, length,
707                                                      &substituted_length, &error);
708   if (!substituted_buffer)
709     {
710       msg_error("Error resolving backtick references in block or buffer",
711                 evt_tag_str("buffer", name),
712                 evt_tag_str("error", error->message));
713       g_clear_error(&error);
714       return FALSE;
715     }
716 
717   result = cfg_lexer_include_buffer_without_backtick_substitution(self, name, substituted_buffer, substituted_length);
718   g_free(substituted_buffer);
719   return result;
720 }
721 
722 void
cfg_lexer_inject_token_block(CfgLexer * self,CfgTokenBlock * block)723 cfg_lexer_inject_token_block(CfgLexer *self, CfgTokenBlock *block)
724 {
725   self->token_blocks = g_list_append(self->token_blocks, block);
726 }
727 
728 
729 typedef struct _GeneratorPlugin
730 {
731   Plugin super;
732   CfgBlockGenerator *gen;
733 } GeneratorPlugin;
734 
735 static gpointer
_generator_plugin_construct(Plugin * s)736 _generator_plugin_construct(Plugin *s)
737 {
738   GeneratorPlugin *self = (GeneratorPlugin *) s;
739 
740   return cfg_block_generator_ref(self->gen);
741 }
742 
743 static void
_generator_plugin_free(Plugin * s)744 _generator_plugin_free(Plugin *s)
745 {
746   GeneratorPlugin *self = (GeneratorPlugin *) s;
747 
748   cfg_block_generator_unref(self->gen);
749   g_free((gchar *) self->super.name);
750   g_free(s);
751 }
752 
753 void
cfg_lexer_register_generator_plugin(PluginContext * context,CfgBlockGenerator * gen)754 cfg_lexer_register_generator_plugin(PluginContext *context, CfgBlockGenerator *gen)
755 {
756   GeneratorPlugin *plugin = g_new0(GeneratorPlugin, 1);
757 
758   plugin->super.type = gen->context | LL_CONTEXT_FLAG_GENERATOR;
759   plugin->super.name = g_strdup(gen->name);
760   plugin->super.free_fn = _generator_plugin_free;
761   plugin->super.construct = _generator_plugin_construct;
762   plugin->super.parser = &block_ref_parser;
763   plugin->gen = gen;
764 
765   plugin_register(context, &plugin->super, 1);
766 }
767 
768 static gboolean
_is_generator_plugin(Plugin * p)769 _is_generator_plugin(Plugin *p)
770 {
771   return p->type & LL_CONTEXT_FLAG_GENERATOR;
772 }
773 
774 static Plugin *
cfg_lexer_find_generator_plugin(CfgLexer * self,GlobalConfig * cfg,gint context,const gchar * name)775 cfg_lexer_find_generator_plugin(CfgLexer *self, GlobalConfig *cfg, gint context, const gchar *name)
776 {
777   Plugin *p;
778 
779   p = plugin_find(&cfg->plugin_context, context | LL_CONTEXT_FLAG_GENERATOR, name);
780   if (!p || !_is_generator_plugin(p))
781     return NULL;
782 
783   return p;
784 }
785 
786 static CFG_STYPE
cfg_lexer_copy_token(const CFG_STYPE * original)787 cfg_lexer_copy_token(const CFG_STYPE *original)
788 {
789   CFG_STYPE dest;
790   int type = original->type;
791   dest.type = type;
792 
793   if (type == LL_TOKEN)
794     {
795       dest.token = original->token;
796     }
797   else if (type == LL_IDENTIFIER ||
798            type == LL_STRING ||
799            type == LL_BLOCK)
800     {
801       dest.cptr = strdup(original->cptr);
802     }
803   else if (type == LL_NUMBER)
804     {
805       dest.num = original->num;
806     }
807   else if (type == LL_FLOAT)
808     {
809       dest.fnum = original->fnum;
810     }
811 
812   return dest;
813 }
814 
815 void
cfg_lexer_unput_token(CfgLexer * self,CFG_STYPE * yylval)816 cfg_lexer_unput_token(CfgLexer *self, CFG_STYPE *yylval)
817 {
818   CfgTokenBlock *block;
819 
820   block = cfg_token_block_new();
821   cfg_token_block_add_token(block, yylval);
822   cfg_lexer_inject_token_block(self, block);
823 }
824 
825 /*
826  * NOTE: the caller is expected to manage the CFG_STYPE instance itself (as
827  * this is the way it is defined by the lexer), this function only frees its
828  * contents.
829  */
830 void
cfg_lexer_free_token(CFG_STYPE * token)831 cfg_lexer_free_token(CFG_STYPE *token)
832 {
833   if (token->type == LL_STRING || token->type == LL_IDENTIFIER || token->type == LL_BLOCK)
834     free(token->cptr);
835 }
836 
837 static int
_invoke__cfg_lexer_lex(CfgLexer * self,CFG_STYPE * yylval,CFG_LTYPE * yylloc)838 _invoke__cfg_lexer_lex(CfgLexer *self, CFG_STYPE *yylval, CFG_LTYPE *yylloc)
839 {
840   if (setjmp(self->fatal_error))
841     {
842       CFG_LTYPE *cur_lloc = &self->include_stack[self->include_depth].lloc;
843 
844       *yylloc = *cur_lloc;
845       return LL_ERROR;
846     }
847   return _cfg_lexer_lex(yylval, yylloc, self->state);
848 }
849 
850 static gboolean
cfg_lexer_consume_next_injected_token(CfgLexer * self,gint * tok,CFG_STYPE * yylval,CFG_LTYPE * yylloc)851 cfg_lexer_consume_next_injected_token(CfgLexer *self, gint *tok, CFG_STYPE *yylval, CFG_LTYPE *yylloc)
852 {
853   CfgTokenBlock *block;
854   CFG_STYPE *token;
855 
856   while (self->token_blocks)
857     {
858       block = self->token_blocks->data;
859       token = cfg_token_block_get_token(block);
860 
861       if (token)
862         {
863           *yylval = *token;
864           *yylloc = self->include_stack[self->include_depth].lloc;
865 
866           if (token->type == LL_TOKEN)
867             *tok = token->token;
868           else
869             *tok = token->type;
870 
871           return TRUE;
872         }
873       else
874         {
875           self->token_blocks = g_list_delete_link(self->token_blocks, self->token_blocks);
876           cfg_token_block_free(block);
877         }
878     }
879 
880   return FALSE;
881 }
882 
883 static gint
cfg_lexer_lex_next_token(CfgLexer * self,CFG_STYPE * yylval,CFG_LTYPE * yylloc)884 cfg_lexer_lex_next_token(CfgLexer *self, CFG_STYPE *yylval, CFG_LTYPE *yylloc)
885 {
886   yylval->type = 0;
887 
888   g_string_truncate(self->token_text, 0);
889   g_string_truncate(self->token_pretext, 0);
890 
891   gint tok = _invoke__cfg_lexer_lex(self, yylval, yylloc);
892   if (yylval->type == 0)
893     yylval->type = tok;
894 
895   return tok;
896 }
897 
898 static void
cfg_lexer_append_preprocessed_output(CfgLexer * self,const gchar * token_text)899 cfg_lexer_append_preprocessed_output(CfgLexer *self, const gchar *token_text)
900 {
901   if (self->preprocess_output)
902     g_string_append_printf(self->preprocess_output, "%s", token_text);
903 }
904 
905 static gboolean
cfg_lexer_parse_and_run_block_generator(CfgLexer * self,Plugin * p,CFG_STYPE * yylval)906 cfg_lexer_parse_and_run_block_generator(CfgLexer *self, Plugin *p, CFG_STYPE *yylval)
907 {
908   gpointer *args = NULL;
909   CfgIncludeLevel *level = &self->include_stack[self->include_depth];
910   CfgBlockGenerator *gen = plugin_construct(p);
911   gboolean success = TRUE;
912 
913   self->preprocess_suppress_tokens++;
914 
915   gint saved_line = level->lloc.first_line;
916   gint saved_column = level->lloc.first_column;
917   CfgParser *gen_parser = p->parser;
918   if (gen_parser && !cfg_parser_parse(gen_parser, self, (gpointer *) &args, NULL))
919     {
920       cfg_parser_cleanup(gen_parser, args);
921 
922       level->lloc.first_line = saved_line;
923       level->lloc.first_column = saved_column;
924       free(yylval->cptr);
925       self->preprocess_suppress_tokens--;
926 
927       success = FALSE;
928       goto exit;
929     }
930 
931   GString *result = g_string_sized_new(256);
932   gchar buf[256];
933   level->lloc.first_line = saved_line;
934   level->lloc.first_column = saved_column;
935   self->preprocess_suppress_tokens--;
936   success = cfg_block_generator_generate(gen, self->cfg, args, result,
937                                          cfg_lexer_format_location(self, &level->lloc, buf, sizeof(buf)));
938 
939   free(yylval->cptr);
940   cfg_parser_cleanup(gen_parser, args);
941 
942   if (!success)
943     {
944       g_string_free(result, TRUE);
945 
946       success = FALSE;
947       goto exit;
948     }
949 
950   cfg_block_generator_format_name(gen, buf, sizeof(buf));
951 
952   if (gen->suppress_backticks)
953     success = cfg_lexer_include_buffer_without_backtick_substitution(self, buf, result->str, result->len);
954   else
955     success = cfg_lexer_include_buffer(self, buf, result->str, result->len);
956   g_string_free(result, TRUE);
957 
958 exit:
959   cfg_block_generator_unref(gen);
960   return success;
961 }
962 
963 static gboolean
cfg_lexer_parse_pragma(CfgLexer * self)964 cfg_lexer_parse_pragma(CfgLexer *self)
965 {
966   gpointer dummy;
967   CfgIncludeLevel *level = &self->include_stack[self->include_depth];
968 
969   cfg_lexer_append_preprocessed_output(self, "@");
970 
971   gint saved_line = level->lloc.first_line;
972   gint saved_column = level->lloc.first_column;
973 
974   if (!cfg_parser_parse(&pragma_parser, self, &dummy, NULL))
975     {
976       level->lloc.first_line = saved_line;
977       level->lloc.first_column = saved_column;
978       return FALSE;
979     }
980 
981   return TRUE;
982 }
983 
984 static CfgLexerPreprocessResult
cfg_lexer_preprocess(CfgLexer * self,gint tok,CFG_STYPE * yylval,CFG_LTYPE * yylloc)985 cfg_lexer_preprocess(CfgLexer *self, gint tok, CFG_STYPE *yylval, CFG_LTYPE *yylloc)
986 {
987   /*
988    * NOTE:
989    *
990    * This code is deeply coupled with GlobalConfig and most of it does
991    * not make sense to execute if self->cfg is NULL.  Thus, some of the
992    * conditionals contain an explicit self->cfg check, in other cases it is
993    * implicitly checked by the first conditional of a series of if-then-else
994    * statements.
995    *
996    */
997 
998   Plugin *p;
999 
1000   if (tok == LL_IDENTIFIER &&
1001       self->cfg &&
1002       (p = cfg_lexer_find_generator_plugin(self, self->cfg, cfg_lexer_get_context_type(self), yylval->cptr)))
1003     {
1004       if (!cfg_lexer_parse_and_run_block_generator(self, p, yylval))
1005         return CLPR_ERROR;
1006 
1007       return CLPR_LEX_AGAIN;
1008     }
1009 
1010   if (self->ignore_pragma || self->cfg == NULL)
1011     {
1012       /* only process @pragma/@include tokens in case pragma allowed is set
1013        * and the associated configuration is not NULL */
1014       ;
1015     }
1016   else if (tok == LL_PRAGMA)
1017     {
1018       if (!cfg_lexer_parse_pragma(self))
1019         return CLPR_ERROR;
1020 
1021       return CLPR_LEX_AGAIN;
1022     }
1023   else if (cfg_lexer_get_context_type(self) != LL_CONTEXT_PRAGMA && !self->non_pragma_seen)
1024     {
1025       /* first non-pragma token */
1026 
1027       if (self->cfg->user_version == 0)
1028         {
1029           msg_error("ERROR: configuration files without a version number has become unsupported in " VERSION_3_13
1030                     ", please specify a version number using @version and update your configuration accordingly");
1031           return CLPR_ERROR;
1032         }
1033 
1034       cfg_discover_candidate_modules(self->cfg);
1035 
1036       cfg_load_forced_modules(self->cfg);
1037 
1038       self->non_pragma_seen = TRUE;
1039     }
1040 
1041   return CLPR_OK;
1042 }
1043 
1044 int
cfg_lexer_lex(CfgLexer * self,CFG_STYPE * yylval,CFG_LTYPE * yylloc)1045 cfg_lexer_lex(CfgLexer *self, CFG_STYPE *yylval, CFG_LTYPE *yylloc)
1046 {
1047   /*
1048    * NOTE:
1049    *
1050    * String tokens are allocated by malloc/free and not
1051    * g_malloc/g_free, this is significant.  The grammar contains the free()
1052    * call, so getting rid of that would require a lot of changes to the
1053    * grammar. (on Windows glib, malloc/g_malloc are NOT equivalent)
1054    *
1055    */
1056 
1057   gint tok;
1058   gboolean is_token_injected;
1059   CfgLexerPreprocessResult preprocess_result;
1060 
1061   do
1062     {
1063       is_token_injected = cfg_lexer_consume_next_injected_token(self, &tok, yylval, yylloc);
1064 
1065       if (!is_token_injected)
1066         {
1067           if (cfg_lexer_get_context_type(self) == LL_CONTEXT_BLOCK_CONTENT)
1068             cfg_lexer_start_block_state(self, "{}");
1069           else if (cfg_lexer_get_context_type(self) == LL_CONTEXT_BLOCK_ARG)
1070             cfg_lexer_start_block_state(self, "()");
1071 
1072           tok = cfg_lexer_lex_next_token(self, yylval, yylloc);
1073           cfg_lexer_append_preprocessed_output(self, self->token_pretext->str);
1074         }
1075 
1076       preprocess_result = cfg_lexer_preprocess(self, tok, yylval, yylloc);
1077       if (preprocess_result == CLPR_ERROR)
1078         return LL_ERROR;
1079     }
1080   while (preprocess_result == CLPR_LEX_AGAIN);
1081 
1082   if (!is_token_injected && self->preprocess_suppress_tokens == 0)
1083     cfg_lexer_append_preprocessed_output(self, self->token_text->str);
1084 
1085   return tok;
1086 }
1087 
1088 static void
cfg_lexer_init(CfgLexer * self,GlobalConfig * cfg)1089 cfg_lexer_init(CfgLexer *self, GlobalConfig *cfg)
1090 {
1091   CfgIncludeLevel *level;
1092 
1093   _cfg_lexer_lex_init_extra(self, &self->state);
1094   self->string_buffer = g_string_sized_new(32);
1095   self->token_text = g_string_sized_new(32);
1096   self->token_pretext = g_string_sized_new(32);
1097   self->cfg = cfg;
1098 
1099   level = &self->include_stack[0];
1100   level->lloc.first_line = level->lloc.last_line = 1;
1101   level->lloc.first_column = level->lloc.last_column = 1;
1102   level->lloc.level = level;
1103 }
1104 
1105 
1106 /* NOTE: cfg might be NULL in some call sites, but in those cases the lexer
1107  * should remain operational, obviously skipping cases where it would be
1108  * using the configuration instance.  The lexer and the configuration stuff
1109  * should be one-way dependent, right now it is a circular dependency. */
1110 CfgLexer *
cfg_lexer_new(GlobalConfig * cfg,FILE * file,const gchar * filename,GString * preprocess_output)1111 cfg_lexer_new(GlobalConfig *cfg, FILE *file, const gchar *filename, GString *preprocess_output)
1112 {
1113   CfgLexer *self;
1114   CfgIncludeLevel *level;
1115 
1116   self = g_new0(CfgLexer, 1);
1117   cfg_lexer_init(self, cfg);
1118   self->preprocess_output = preprocess_output;
1119 
1120   level = &self->include_stack[0];
1121   level->include_type = CFGI_FILE;
1122   level->name = g_strdup(filename);
1123   level->yybuf = _cfg_lexer__create_buffer(file, YY_BUF_SIZE, self->state);
1124   _cfg_lexer__switch_to_buffer(level->yybuf, self->state);
1125 
1126   return self;
1127 }
1128 
1129 CfgLexer *
cfg_lexer_new_buffer(GlobalConfig * cfg,const gchar * buffer,gsize length)1130 cfg_lexer_new_buffer(GlobalConfig *cfg, const gchar *buffer, gsize length)
1131 {
1132   CfgLexer *self;
1133   CfgIncludeLevel *level;
1134 
1135   self = g_new0(CfgLexer, 1);
1136   cfg_lexer_init(self, cfg);
1137   self->ignore_pragma = TRUE;
1138 
1139   level = &self->include_stack[0];
1140   level->include_type = CFGI_BUFFER;
1141   level->buffer.original_content = g_strdup(buffer);
1142   level->buffer.content = g_malloc(length + 2);
1143   memcpy(level->buffer.content, buffer, length);
1144   level->buffer.content[length] = 0;
1145   level->buffer.content[length + 1] = 0;
1146   level->buffer.content_length = length + 2;
1147   level->name = g_strdup("<string>");
1148   level->yybuf = _cfg_lexer__scan_buffer(level->buffer.content, level->buffer.content_length, self->state);
1149   _cfg_lexer__switch_to_buffer(level->yybuf, self->state);
1150 
1151   return self;
1152 }
1153 
1154 void
cfg_lexer_free(CfgLexer * self)1155 cfg_lexer_free(CfgLexer *self)
1156 {
1157   gint i;
1158 
1159   for (i = 0; i <= self->include_depth; i++)
1160     cfg_lexer_clear_include_level(self, &self->include_stack[i]);
1161 
1162   self->include_depth = 0;
1163   _cfg_lexer_lex_destroy(self->state);
1164   g_string_free(self->string_buffer, TRUE);
1165   if (self->token_text)
1166     g_string_free(self->token_text, TRUE);
1167   if (self->token_pretext)
1168     g_string_free(self->token_pretext, TRUE);
1169 
1170   while (self->context_stack)
1171     cfg_lexer_pop_context(self);
1172   g_list_foreach(self->token_blocks, (GFunc) cfg_token_block_free, NULL);
1173   g_list_free(self->token_blocks);
1174   g_free(self);
1175 }
1176 
1177 static const gchar *lexer_contexts[] =
1178 {
1179   [LL_CONTEXT_ROOT] = "root",
1180   [LL_CONTEXT_DESTINATION] = "destination",
1181   [LL_CONTEXT_SOURCE] = "source",
1182   [LL_CONTEXT_PARSER] = "parser",
1183   [LL_CONTEXT_REWRITE] = "rewrite",
1184   [LL_CONTEXT_FILTER] = "filter",
1185   [LL_CONTEXT_LOG] = "log",
1186   [LL_CONTEXT_BLOCK_DEF] = "block-def",
1187   [LL_CONTEXT_BLOCK_ARG] = "block-arg",
1188   [LL_CONTEXT_BLOCK_REF] = "block-ref",
1189   [LL_CONTEXT_BLOCK_CONTENT] = "block-content",
1190   [LL_CONTEXT_PRAGMA] = "pragma",
1191   [LL_CONTEXT_FORMAT] = "format",
1192   [LL_CONTEXT_TEMPLATE_FUNC] = "template-func",
1193   [LL_CONTEXT_INNER_DEST] = "inner-dest",
1194   [LL_CONTEXT_INNER_SRC] = "inner-src",
1195   [LL_CONTEXT_CLIENT_PROTO] = "client-proto",
1196   [LL_CONTEXT_SERVER_PROTO] = "server-proto",
1197   [LL_CONTEXT_OPTIONS] = "options",
1198   [LL_CONTEXT_CONFIG] = "config",
1199 };
1200 
1201 gint
cfg_lexer_lookup_context_type_by_name(const gchar * name)1202 cfg_lexer_lookup_context_type_by_name(const gchar *name)
1203 {
1204   gint i;
1205 
1206   for (i = 0; i < G_N_ELEMENTS(lexer_contexts); i++)
1207     {
1208       if (lexer_contexts[i] && strcmp(lexer_contexts[i], name) == 0)
1209         return i;
1210     }
1211   return 0;
1212 }
1213 
1214 const gchar *
cfg_lexer_lookup_context_name_by_type(gint type)1215 cfg_lexer_lookup_context_name_by_type(gint type)
1216 {
1217   type &= ~LL_CONTEXT_FLAGS;
1218   g_assert(type < G_N_ELEMENTS(lexer_contexts));
1219   return lexer_contexts[type];
1220 }
1221 
1222 /* token blocks */
1223 
1224 void
cfg_token_block_add_and_consume_token(CfgTokenBlock * self,CFG_STYPE * token)1225 cfg_token_block_add_and_consume_token(CfgTokenBlock *self, CFG_STYPE *token)
1226 {
1227   g_assert(self->pos == 0);
1228   g_array_append_val(self->tokens, *token);
1229 }
1230 
1231 void
cfg_token_block_add_token(CfgTokenBlock * self,CFG_STYPE * token)1232 cfg_token_block_add_token(CfgTokenBlock *self, CFG_STYPE *token)
1233 {
1234   CFG_STYPE copied_token = cfg_lexer_copy_token(token);
1235   cfg_token_block_add_and_consume_token(self, &copied_token);
1236 }
1237 
1238 CFG_STYPE *
cfg_token_block_get_token(CfgTokenBlock * self)1239 cfg_token_block_get_token(CfgTokenBlock *self)
1240 {
1241   if (self->pos < self->tokens->len)
1242     {
1243       CFG_STYPE *result;
1244 
1245       result = &g_array_index(self->tokens, CFG_STYPE, self->pos);
1246       self->pos++;
1247       return result;
1248     }
1249   return NULL;
1250 }
1251 
1252 CfgTokenBlock *
cfg_token_block_new(void)1253 cfg_token_block_new(void)
1254 {
1255   CfgTokenBlock *self = g_new0(CfgTokenBlock, 1);
1256 
1257   self->tokens = g_array_new(FALSE, TRUE, sizeof(CFG_STYPE));
1258   return self;
1259 }
1260 
1261 void
cfg_token_block_free(CfgTokenBlock * self)1262 cfg_token_block_free(CfgTokenBlock *self)
1263 {
1264   if (self->pos < self->tokens->len)
1265     {
1266       for (gint i = self->pos; i < self->tokens->len; i++)
1267         {
1268           CFG_STYPE *token = &g_array_index(self->tokens, CFG_STYPE, i);
1269 
1270           cfg_lexer_free_token(token);
1271         }
1272     }
1273 
1274   g_array_free(self->tokens, TRUE);
1275   g_free(self);
1276 }
1277 
1278 GQuark
cfg_lexer_error_quark(void)1279 cfg_lexer_error_quark(void)
1280 {
1281   return g_quark_from_static_string("cfg-lexer-error-quark");
1282 }
1283