1 /*
2  * Copyright (c) 2002-2017 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-parser.h"
26 #include "cfg-grammar.h"
27 
28 #include <string.h>
29 #include <stdlib.h>
30 
31 extern int main_debug;
32 
33 /* defined in the parser */
34 int main_parse(CfgLexer *lexer, gpointer *dummy, gpointer arg);
35 
36 /*
37  * PLEASE: if at all possible avoid adding keywords here, as these
38  * keywords might collide with user-defined identifiers (e.g. source,
39  * destination & block names).
40  *
41  * Add the keyword & the token to the parser of the module in question
42  * instead, even if there are multiple modules using the same
43  * keywords.
44  *
45  * Tokens (e.g. KW_xxx) associated with core keywords (e.g. those
46  * listed here) should be declared only once and only in cfg-grammar.y
47  * and NOT in external modules.
48  */
49 static CfgLexerKeyword main_keywords[] =
50 {
51   /* statements */
52   { "source",             KW_SOURCE },
53   { "filter",             KW_FILTER },
54   { "parser",             KW_PARSER },
55   { "rewrite",            KW_REWRITE },
56   { "destination",        KW_DESTINATION },
57   { "log",                KW_LOG },
58   { "junction",           KW_JUNCTION },
59   { "channel",            KW_CHANNEL },
60   { "options",            KW_OPTIONS },
61   { "include",            KW_INCLUDE, },
62   { "block",              KW_BLOCK },
63   { "if",                 KW_IF },
64   { "else",               KW_ELSE },
65   { "elif",               KW_ELIF },
66 
67   /* source or destination items */
68   { "internal",           KW_INTERNAL },
69 
70   /* value pairs */
71   { "value_pairs",        KW_VALUE_PAIRS },
72   { "exclude",            KW_EXCLUDE },
73   { "pair",               KW_PAIR },
74   { "key",                KW_KEY },
75   { "scope",              KW_SCOPE },
76   { "rekey",              KW_REKEY },
77   { "shift",              KW_SHIFT },
78   { "shift_levels",       KW_SHIFT_LEVELS },
79   { "add_prefix",         KW_ADD_PREFIX },
80   { "replace",            KW_REPLACE_PREFIX, KWS_OBSOLETE, "replace_prefix" },
81   { "replace_prefix",     KW_REPLACE_PREFIX },
82 
83   /* option items */
84   { "flags",              KW_FLAGS },
85   { "pad_size",           KW_PAD_SIZE },
86   { "truncate_size",      KW_TRUNCATE_SIZE },
87   { "mark_freq",          KW_MARK_FREQ },
88   { "mark",               KW_MARK_FREQ, KWS_OBSOLETE, "mark_freq" },
89   { "mark_mode",          KW_MARK_MODE },
90   { "stats_freq",         KW_STATS_FREQ },
91   { "stats_lifetime",     KW_STATS_LIFETIME },
92   { "stats_level",        KW_STATS_LEVEL },
93   { "stats",              KW_STATS_FREQ, KWS_OBSOLETE, "stats_freq" },
94   { "stats_max_dynamics", KW_STATS_MAX_DYNAMIC },
95   { "min_iw_size_per_reader", KW_MIN_IW_SIZE_PER_READER },
96   { "flush_lines",        KW_FLUSH_LINES },
97   { "flush_timeout",      KW_FLUSH_TIMEOUT, KWS_OBSOLETE, "Some drivers support batch-timeout() instead that you can specify at the destination level." },
98   { "suppress",           KW_SUPPRESS },
99   { "sync_freq",          KW_FLUSH_LINES, KWS_OBSOLETE, "flush_lines" },
100   { "sync",               KW_FLUSH_LINES, KWS_OBSOLETE, "flush_lines" },
101   { "long_hostnames",     KW_CHAIN_HOSTNAMES, KWS_OBSOLETE, "chain_hostnames" },
102   { "chain_hostnames",    KW_CHAIN_HOSTNAMES },
103   { "normalize_hostnames", KW_NORMALIZE_HOSTNAMES },
104   { "keep_hostname",      KW_KEEP_HOSTNAME },
105   { "check_hostname",     KW_CHECK_HOSTNAME },
106   { "bad_hostname",       KW_BAD_HOSTNAME },
107   { "custom_domain",      KW_CUSTOM_DOMAIN },
108   { "keep_timestamp",     KW_KEEP_TIMESTAMP },
109   { "encoding",           KW_ENCODING },
110   { "ts_format",          KW_TS_FORMAT },
111   { "frac_digits",        KW_FRAC_DIGITS },
112   { "time_zone",          KW_TIME_ZONE },
113   { "recv_time_zone",     KW_RECV_TIME_ZONE },
114   { "send_time_zone",     KW_SEND_TIME_ZONE },
115   { "local_time_zone",    KW_LOCAL_TIME_ZONE },
116   { "format",             KW_FORMAT },
117   { "use_time_recvd",     KW_USE_TIME_RECVD, KWS_OBSOLETE, "Use R_ or S_ prefixed macros in templates or keep_timestamp(no)" },
118   { "use_fqdn",           KW_USE_FQDN },
119   { "use_dns",            KW_USE_DNS },
120   { "time_reopen",        KW_TIME_REOPEN },
121   { "time_reap",          KW_TIME_REAP },
122   { "time_sleep",         KW_TIME_SLEEP, KWS_OBSOLETE, "time_sleep() has been deprecated" },
123   { "file_template",      KW_FILE_TEMPLATE },
124   { "proto_template",     KW_PROTO_TEMPLATE },
125   { "default_level",      KW_DEFAULT_SEVERITY },
126   { "default_priority",   KW_DEFAULT_SEVERITY },
127   { "default_severity",   KW_DEFAULT_SEVERITY },
128   { "default_facility",   KW_DEFAULT_FACILITY },
129   { "threaded",           KW_THREADED },
130   { "use_rcptid",         KW_USE_RCPTID, KWS_OBSOLETE, "This has been deprecated, try use_uniqid() instead" },
131   { "use_uniqid",         KW_USE_UNIQID },
132 
133   { "log_fifo_size",      KW_LOG_FIFO_SIZE },
134   { "log_fetch_limit",    KW_LOG_FETCH_LIMIT },
135   { "log_iw_size",        KW_LOG_IW_SIZE },
136   { "log_msg_size",       KW_LOG_MSG_SIZE },
137   { "trim_large_messages", KW_TRIM_LARGE_MESSAGES },
138   { "log_prefix",         KW_LOG_PREFIX, KWS_OBSOLETE, "program_override" },
139   { "program_override",   KW_PROGRAM_OVERRIDE },
140   { "host_override",      KW_HOST_OVERRIDE },
141   { "throttle",           KW_THROTTLE },
142 
143   { "create_dirs",        KW_CREATE_DIRS },
144   { "optional",           KW_OPTIONAL },
145 
146   { "owner",              KW_OWNER },
147   { "group",              KW_GROUP },
148   { "perm",               KW_PERM },
149   { "dir_owner",          KW_DIR_OWNER },
150   { "dir_group",          KW_DIR_GROUP },
151   { "dir_perm",           KW_DIR_PERM },
152   { "template",           KW_TEMPLATE },
153   { "template_escape",    KW_TEMPLATE_ESCAPE },
154   { "template_function",  KW_TEMPLATE_FUNCTION },
155   { "on_error",           KW_ON_ERROR },
156   { "persist_only",       KW_PERSIST_ONLY },
157   { "dns_cache_hosts",    KW_DNS_CACHE_HOSTS },
158   { "dns_cache",          KW_DNS_CACHE },
159   { "dns_cache_size",     KW_DNS_CACHE_SIZE },
160   { "dns_cache_expire",   KW_DNS_CACHE_EXPIRE },
161   { "dns_cache_expire_failed", KW_DNS_CACHE_EXPIRE_FAILED },
162   {
163     "pass_unix_credentials",   KW_PASS_UNIX_CREDENTIALS, KWS_OBSOLETE,
164     "The use of pass-unix-credentials() has been deprecated in " VERSION_3_35 " in favour of "
165     "the 'so-passcred()' source option or the 'ignore-aux-data' source flag"
166   },
167 
168   { "persist_name",            KW_PERSIST_NAME, VERSION_VALUE_3_8 },
169 
170   { "retries",            KW_RETRIES },
171   { "workers",            KW_WORKERS },
172   { "batch_lines",        KW_BATCH_LINES },
173   { "batch_timeout",      KW_BATCH_TIMEOUT },
174 
175   { "read_old_records",   KW_READ_OLD_RECORDS},
176   { "use_syslogng_pid",   KW_USE_SYSLOGNG_PID },
177   { "fetch_no_data_delay", KW_FETCH_NO_DATA_DELAY},
178   /* filter items */
179   { "type",               KW_TYPE },
180   { "tags",               KW_TAGS },
181 
182   /* on/off switches */
183   { "yes",                KW_YES },
184   { "on",                 KW_YES },
185   { "no",                 KW_NO },
186   { "off",                KW_NO },
187   /* rewrite rules */
188   { "condition",          KW_CONDITION },
189   { "value",              KW_VALUE },
190 
191   { NULL, 0 }
192 };
193 
194 
195 CfgParser main_parser =
196 {
197 #if SYSLOG_NG_ENABLE_DEBUG
198   .debug_flag = &main_debug,
199 #endif
200   .name = "config",
201   .context = LL_CONTEXT_ROOT,
202   .keywords = main_keywords,
203   .parse = main_parse,
204 };
205 
CFG_PARSER_IMPLEMENT_LEXER_BINDING(main_,MAIN_,gpointer *)206 CFG_PARSER_IMPLEMENT_LEXER_BINDING(main_, MAIN_, gpointer *)
207 
208 /* display CONTEXT lines before and after the offending line */
209 #define CONTEXT 5
210 
211 static void
212 _format_source_prefix(gchar *line_prefix, gsize line_prefix_len, gint lineno, gboolean error_location)
213 {
214   g_snprintf(line_prefix, line_prefix_len, "%d", lineno);
215   if (error_location)
216     {
217       for (gint i = strlen(line_prefix); i < 6; i++)
218         g_strlcat(line_prefix, "-", line_prefix_len);
219 
220       g_strlcat(line_prefix, ">", line_prefix_len);
221     }
222 }
223 
224 static void
_print_underline(const gchar * line,gint whitespace_before,gint number_of_carets)225 _print_underline(const gchar *line, gint whitespace_before, gint number_of_carets)
226 {
227   for (gint i = 0; line[i] && i < whitespace_before; i++)
228     {
229       fprintf(stderr, "%c", line[i] == '\t' ? '\t' : ' ');
230     }
231 
232   /* NOTE: sometimes the yylloc has zero characters, print a caret even in
233    * this case, that's why i == 0 is there */
234 
235   for (gint i = 0; i == 0 || i < number_of_carets; i++)
236     fprintf(stderr, "^");
237   fprintf(stderr, "\n");
238 }
239 
240 static void
_print_underlined_source_block(const CFG_LTYPE * yylloc,gchar ** lines,gint error_index)241 _print_underlined_source_block(const CFG_LTYPE *yylloc, gchar **lines, gint error_index)
242 {
243   gint line_ndx;
244   gchar line_prefix[12];
245   gint error_length = yylloc->last_line - yylloc->first_line + 1;
246 
247   for (line_ndx = 0; lines[line_ndx]; line_ndx++)
248     {
249       gint lineno = yylloc->first_line + line_ndx - error_index;
250       const gchar *line = lines[line_ndx];
251       gint line_len = strlen(line);
252       gboolean line_ends_with_newline = line_len > 0 && line[line_len - 1] == '\n';
253 
254       _format_source_prefix(line_prefix, sizeof(line_prefix), lineno,
255                             line_ndx >= error_index && line_ndx < error_index + error_length);
256 
257       fprintf(stderr, "%-8s%s%s", line_prefix, line, line_ends_with_newline ? "" : "\n");
258 
259       if (line_ndx == error_index)
260         {
261           /* print the underline right below the source line we just printed */
262           fprintf(stderr, "%-8s", line_prefix);
263 
264           gboolean multi_line = yylloc->first_line != yylloc->last_line;
265 
266           _print_underline(line, yylloc->first_column - 1,
267                            multi_line ? strlen(&line[yylloc->first_column]) + 1
268                            : yylloc->last_column - yylloc->first_column);
269         }
270       else if (line_ndx >= error_index + CONTEXT)
271         break;
272     }
273 }
274 
275 static void
_report_file_location(const gchar * filename,const CFG_LTYPE * yylloc)276 _report_file_location(const gchar *filename, const CFG_LTYPE *yylloc)
277 {
278   FILE *f;
279   gint lineno = 0;
280   gsize buflen = 65520;
281   gchar *buf = g_malloc(buflen);
282   GPtrArray *context = g_ptr_array_new();
283   gint error_index = 0;
284 
285   f = fopen(filename, "r");
286   if (f)
287     {
288       while (fgets(buf, buflen, f))
289         {
290           lineno++;
291           if (lineno > (gint) yylloc->first_line + CONTEXT)
292             break;
293           else if (lineno < (gint) yylloc->first_line - CONTEXT)
294             continue;
295           else if (lineno == yylloc->first_line)
296             error_index = context->len;
297           g_ptr_array_add(context, g_strdup(buf));
298         }
299       /* NOTE: do we have the appropriate number of lines? */
300       if (lineno <= yylloc->first_line)
301         goto exit;
302       g_ptr_array_add(context, NULL);
303       fclose(f);
304     }
305   _print_underlined_source_block(yylloc, (gchar **) context->pdata, error_index);
306 
307 exit:
308   g_free(buf);
309   g_ptr_array_foreach(context, (GFunc) g_free, NULL);
310   g_ptr_array_free(context, TRUE);
311 }
312 
313 static void
_report_buffer_location(const gchar * buffer_content,const CFG_LTYPE * yylloc)314 _report_buffer_location(const gchar *buffer_content, const CFG_LTYPE *yylloc)
315 {
316   gchar **lines = g_strsplit(buffer_content, "\n", yylloc->first_line + CONTEXT + 1);
317   gint num_lines = g_strv_length(lines);
318 
319   if (num_lines <= yylloc->first_line)
320     goto exit;
321 
322   gint start = yylloc->first_line - 1 - CONTEXT;
323   gint error_index = CONTEXT;
324   if (start < 0)
325     {
326       error_index += start;
327       start = 0;
328     }
329   _print_underlined_source_block(yylloc, &lines[start], error_index);
330 
331 exit:
332   g_strfreev(lines);
333 }
334 
335 void
report_syntax_error(CfgLexer * lexer,const CFG_LTYPE * yylloc,const char * what,const char * msg,gboolean in_main_grammar)336 report_syntax_error(CfgLexer *lexer, const CFG_LTYPE *yylloc, const char *what, const char *msg,
337                     gboolean in_main_grammar)
338 {
339   CfgIncludeLevel *level = yylloc->level, *from;
340 
341   for (from = level; from >= lexer->include_stack; from--)
342     {
343       const CFG_LTYPE *from_lloc;
344 
345       if (from == level)
346         {
347           /* the location on the initial level is the one we get as
348            * argument, instead of what we have in the lexer's state.  This
349            * is because the lexer might be one token in advance (because of
350            * LALR) and the grammar is kind enough to pass us the original
351            * location.  */
352 
353           from_lloc = yylloc;
354           fprintf(stderr, "Error parsing %s, %s in %s:%d:%d-%d:%d:\n",
355                   what,
356                   msg,
357                   from_lloc->level->name,
358                   from_lloc->first_line,
359                   from_lloc->first_column,
360                   from_lloc->last_line,
361                   from_lloc->last_column);
362         }
363       else
364         {
365           from_lloc = &from->lloc;
366           fprintf(stderr, "Included from %s:%d:%d-%d:%d:\n", from->name,
367                   from_lloc->first_line,
368                   from_lloc->first_column,
369                   from_lloc->last_line,
370                   from_lloc->last_column);
371         }
372       if (from->include_type == CFGI_FILE)
373         {
374           _report_file_location(from->name, from_lloc);
375         }
376       else if (from->include_type == CFGI_BUFFER)
377         {
378           _report_buffer_location(from->buffer.original_content, from_lloc);
379         }
380       fprintf(stderr, "\n");
381     }
382 
383   if (in_main_grammar)
384     fprintf(stderr, "\nsyslog-ng documentation: %s\n"
385             "contact: %s\n", PRODUCT_DOCUMENTATION, PRODUCT_CONTACT);
386 
387 }
388 
389 /* the debug flag for the main parser will be used for all parsers */
390 extern int cfg_parser_debug;
391 
392 
393 gboolean
cfg_parser_parse(CfgParser * self,CfgLexer * lexer,gpointer * instance,gpointer arg)394 cfg_parser_parse(CfgParser *self, CfgLexer *lexer, gpointer *instance, gpointer arg)
395 {
396   enum { OK, ERROR, MEMORY_EXHAUSTED } parse_result;
397   gboolean success;
398 
399   if (cfg_parser_debug)
400     {
401       fprintf(stderr, "\n\nStarting parser %s\n", self->name);
402     }
403   if (self->debug_flag)
404     (*self->debug_flag) = cfg_parser_debug;
405   cfg_lexer_push_context(lexer, self->context, self->keywords, self->name);
406   parse_result = self->parse(lexer, instance, arg);
407   success = (parse_result == OK);
408   cfg_lexer_pop_context(lexer);
409   if (cfg_parser_debug)
410     {
411       fprintf(stderr, "\nStopping parser %s, result: %d\n", self->name, parse_result);
412     }
413   if (parse_result == MEMORY_EXHAUSTED)
414     {
415       fprintf(stderr,
416               "\nToo many tokens found during parsing, consider increasing YYMAXDEPTH in lib/cfg-grammar.y and recompiling.\n");
417     }
418   return success;
419 }
420 
421 void
cfg_parser_cleanup(CfgParser * self,gpointer instance)422 cfg_parser_cleanup(CfgParser *self, gpointer instance)
423 {
424   if (instance && self->cleanup)
425     self->cleanup(instance);
426 }
427 
428 
429 /*
430  * This function can be used to parse flags in a flags(...) option. It
431  * makes it quite easy to write a flags parser by specifying the
432  * operations to be performed in a getopt-like array.
433  */
434 gboolean
cfg_process_flag(CfgFlagHandler * handlers,gpointer base,const gchar * flag_)435 cfg_process_flag(CfgFlagHandler *handlers, gpointer base, const gchar *flag_)
436 {
437   gint h;
438   gchar flag[32];
439 
440   for (h = 0; flag_[h] && h < sizeof(flag); h++)
441     {
442       if (flag_[h] == '_')
443         flag[h] = '-';
444       else
445         flag[h] = flag_[h];
446     }
447   flag[h] = 0;
448 
449   for (h = 0; handlers[h].name; h++)
450     {
451       CfgFlagHandler *handler = &handlers[h];
452 
453       if (strcmp(handlers[h].name, flag) == 0)
454         {
455           guint32 *field = ((guint32 *) (((gchar *) base) + handler->ofs));
456           switch (handler->op)
457             {
458             case CFH_SET:
459               /* this works if handler->mask is unset and handler->param is a single bit only */
460 
461               if (handler->mask)
462                 *field = ((*field) & ~handler->mask) | handler->param;
463               else
464                 *field = (*field) | handler->param;
465               return TRUE;
466             case CFH_CLEAR:
467               /* set the bitfield to zero */
468 
469               if (handler->mask)
470                 *field = (*field) & ~handler->mask;
471               else
472                 *field = (*field) & ~handler->param;
473               return TRUE;
474             default:
475               g_assert_not_reached();
476               break;
477             }
478         }
479     }
480   return FALSE;
481 }
482 
483 gboolean
cfg_process_yesno(const gchar * yesno)484 cfg_process_yesno(const gchar *yesno)
485 {
486   if (strcasecmp(yesno, "yes") == 0 || atoi(yesno) > 0)
487     return TRUE;
488   return FALSE;
489 }
490