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 "msg-format.h"
26 #include "cfg.h"
27 #include "plugin.h"
28 #include "plugin-types.h"
29 #include "find-crlf.h"
30 #include "scratch-buffers.h"
31 
32 static gsize
_rstripped_message_length(const guchar * data,gsize length)33 _rstripped_message_length(const guchar *data, gsize length)
34 {
35   while (length > 0 && (data[length - 1] == '\n' || data[length - 1] == '\0'))
36     length--;
37   return length;
38 }
39 
40 static void
msg_format_inject_parse_error(LogMessage * msg,const guchar * data,gsize length,gint problem_position)41 msg_format_inject_parse_error(LogMessage *msg, const guchar *data, gsize length, gint problem_position)
42 {
43   GString *buf = scratch_buffers_alloc();
44 
45   log_msg_clear(msg);
46 
47   msg->timestamps[LM_TS_STAMP] = msg->timestamps[LM_TS_RECVD];
48   log_msg_set_value(msg, LM_V_HOST, "", 0);
49 
50   if (problem_position > 0)
51     g_string_printf(buf, "Error processing log message: %.*s>@<%.*s", (gint) problem_position-1,
52                     data, (gint) (length-problem_position+1), data+problem_position-1);
53   else
54     g_string_printf(buf, "Error processing log message: %.*s", (gint) length, data);
55 
56   log_msg_set_value(msg, LM_V_MESSAGE, buf->str, buf->len);
57   log_msg_set_value(msg, LM_V_PROGRAM, "syslog-ng", 9);
58   g_string_printf(buf, "%d", (int) getpid());
59   log_msg_set_value(msg, LM_V_PID, buf->str, buf->len);
60 
61   msg->flags |= LF_LOCAL;
62   msg->pri = LOG_SYSLOG | LOG_ERR;
63 }
64 
65 static void
msg_format_preprocess_message(MsgFormatOptions * options,LogMessage * msg,const guchar * data,gsize length)66 msg_format_preprocess_message(MsgFormatOptions *options, LogMessage *msg,
67                               const guchar *data, gsize length)
68 {
69   if (options->flags & LP_STORE_RAW_MESSAGE)
70     {
71       log_msg_set_value(msg, LOG_MSG_GET_VALUE_HANDLE_STATIC("RAWMSG"),
72                         (gchar *) data, _rstripped_message_length(data, length));
73     }
74 }
75 
76 static void
msg_format_postprocess_message(MsgFormatOptions * options,LogMessage * msg,const guchar * data,gsize length)77 msg_format_postprocess_message(MsgFormatOptions *options, LogMessage *msg,
78                                const guchar *data, gsize length)
79 {
80   if (options->flags & LP_NO_PARSE_DATE)
81     {
82       msg->timestamps[LM_TS_STAMP] = msg->timestamps[LM_TS_RECVD];
83       unix_time_set_timezone(&msg->timestamps[LM_TS_STAMP],
84                              time_zone_info_get_offset(options->recv_time_zone_info,
85                                                        msg->timestamps[LM_TS_RECVD].ut_sec));
86     }
87   if (G_UNLIKELY(options->flags & LP_NO_MULTI_LINE))
88     {
89       gssize msg_len;
90       gchar *msg_text;
91       gchar *p;
92 
93       p = msg_text = (gchar *) log_msg_get_value(msg, LM_V_MESSAGE, &msg_len);
94       while ((p = find_cr_or_lf(p, msg_text + msg_len - p)))
95         {
96           *p = ' ';
97           p++;
98         }
99     }
100   if (options->flags & LP_LOCAL)
101     msg->flags |= LF_LOCAL;
102   if (options->flags & LP_ASSUME_UTF8)
103     msg->flags |= LF_UTF8;
104 }
105 
106 static gboolean
msg_format_process_message(MsgFormatOptions * options,LogMessage * msg,const guchar * data,gsize length,gsize * problem_position)107 msg_format_process_message(MsgFormatOptions *options, LogMessage *msg,
108                            const guchar *data, gsize length,
109                            gsize *problem_position)
110 {
111   if ((options->flags & LP_NOPARSE) == 0)
112     {
113       return options->format_handler->parse(options, msg, data, length, problem_position);
114     }
115   else
116     {
117       log_msg_set_value(msg, LM_V_MESSAGE, (gchar *) data, _rstripped_message_length(data, length));
118       msg->pri = options->default_pri;
119       return TRUE;
120     }
121 }
122 
123 gboolean
msg_format_parse_conditional(MsgFormatOptions * options,LogMessage * msg,const guchar * data,gsize length,gsize * problem_position)124 msg_format_parse_conditional(MsgFormatOptions *options, LogMessage *msg,
125                              const guchar *data, gsize length,
126                              gsize *problem_position)
127 {
128   if (G_UNLIKELY(!options->format_handler))
129     {
130       gchar buf[256];
131 
132       g_snprintf(buf, sizeof(buf), "Error parsing message, format module %s is not loaded", options->format);
133       log_msg_set_value(msg, LM_V_MESSAGE, buf, -1);
134       return FALSE;
135     }
136 
137   msg_format_preprocess_message(options, msg, data, length);
138 
139   if (!msg_format_process_message(options, msg, data, length, problem_position))
140     return FALSE;
141 
142   msg_format_postprocess_message(options, msg, data, length);
143   return TRUE;
144 }
145 
146 void
msg_format_parse(MsgFormatOptions * options,LogMessage * msg,const guchar * data,gsize length)147 msg_format_parse(MsgFormatOptions *options, LogMessage *msg,
148                  const guchar *data, gsize length)
149 {
150   gsize problem_position = 0;
151 
152   if (!msg_format_parse_conditional(options, msg, data, length, &problem_position))
153     {
154       msg_format_inject_parse_error(msg, data, _rstripped_message_length(data, length), problem_position);
155 
156       /* the injected error message needs to be postprocessed too */
157       msg_format_postprocess_message(options, msg, data, length);
158     }
159 }
160 
161 void
msg_format_options_defaults(MsgFormatOptions * options)162 msg_format_options_defaults(MsgFormatOptions *options)
163 {
164   options->flags = LP_EXPECT_HOSTNAME | LP_STORE_LEGACY_MSGHDR;
165   options->recv_time_zone = NULL;
166   options->recv_time_zone_info = NULL;
167   options->bad_hostname = NULL;
168   options->default_pri = 0xFFFF;
169   options->sdata_param_value_max = 65535;
170 }
171 
172 /* NOTE: _init needs to be idempotent when called multiple times w/o invoking _destroy */
173 void
msg_format_options_init(MsgFormatOptions * options,GlobalConfig * cfg)174 msg_format_options_init(MsgFormatOptions *options, GlobalConfig *cfg)
175 {
176   Plugin *p;
177 
178   if (options->initialized)
179     return;
180 
181   if (cfg->bad_hostname_compiled)
182     options->bad_hostname = &cfg->bad_hostname;
183   if (options->recv_time_zone == NULL)
184     options->recv_time_zone = g_strdup(cfg->recv_time_zone);
185   if (options->recv_time_zone_info == NULL)
186     options->recv_time_zone_info = time_zone_info_new(options->recv_time_zone);
187 
188   if (!options->format)
189     options->format = g_strdup("syslog");
190 
191   p = cfg_find_plugin(cfg, LL_CONTEXT_FORMAT, options->format);
192   if (p)
193     options->format_handler = plugin_construct(p);
194   options->initialized = TRUE;
195 }
196 
197 void
msg_format_options_copy(MsgFormatOptions * options,const MsgFormatOptions * source)198 msg_format_options_copy(MsgFormatOptions *options, const MsgFormatOptions *source)
199 {
200   g_assert(!options->initialized);
201 
202   options->format = g_strdup(source->format);
203   options->flags = source->flags;
204   options->default_pri = source->default_pri;
205   options->recv_time_zone = g_strdup(source->recv_time_zone);
206   options->sdata_param_value_max = source->sdata_param_value_max;
207 }
208 
209 void
msg_format_options_destroy(MsgFormatOptions * options)210 msg_format_options_destroy(MsgFormatOptions *options)
211 {
212   if (options->format)
213     {
214       g_free(options->format);
215       options->format = NULL;
216     }
217   if (options->recv_time_zone)
218     {
219       g_free(options->recv_time_zone);
220       options->recv_time_zone = NULL;
221     }
222   if (options->recv_time_zone_info)
223     {
224       time_zone_info_free(options->recv_time_zone_info);
225       options->recv_time_zone_info = NULL;
226     }
227   options->initialized = FALSE;
228 }
229 
230 CfgFlagHandler msg_format_flag_handlers[] =
231 {
232   { "no-parse",                   CFH_SET, offsetof(MsgFormatOptions, flags), LP_NOPARSE },
233   { "check-hostname",             CFH_SET, offsetof(MsgFormatOptions, flags), LP_CHECK_HOSTNAME },
234   { "syslog-protocol",            CFH_SET, offsetof(MsgFormatOptions, flags), LP_SYSLOG_PROTOCOL },
235   { "assume-utf8",                CFH_SET, offsetof(MsgFormatOptions, flags), LP_ASSUME_UTF8 },
236   { "validate-utf8",              CFH_SET, offsetof(MsgFormatOptions, flags), LP_VALIDATE_UTF8 },
237   { "sanitize-utf8",              CFH_SET, offsetof(MsgFormatOptions, flags), LP_SANITIZE_UTF8 },
238   { "no-multi-line",              CFH_SET, offsetof(MsgFormatOptions, flags), LP_NO_MULTI_LINE },
239   { "store-legacy-msghdr",        CFH_SET, offsetof(MsgFormatOptions, flags), LP_STORE_LEGACY_MSGHDR },
240   { "store-raw-message",          CFH_SET, offsetof(MsgFormatOptions, flags), LP_STORE_RAW_MESSAGE },
241   { "dont-store-legacy-msghdr", CFH_CLEAR, offsetof(MsgFormatOptions, flags), LP_STORE_LEGACY_MSGHDR },
242   { "expect-hostname",            CFH_SET, offsetof(MsgFormatOptions, flags), LP_EXPECT_HOSTNAME },
243   { "no-hostname",              CFH_CLEAR, offsetof(MsgFormatOptions, flags), LP_EXPECT_HOSTNAME },
244   { "guess-timezone",             CFH_SET, offsetof(MsgFormatOptions, flags), LP_GUESS_TIMEZONE },
245   { "no-header",                  CFH_SET, offsetof(MsgFormatOptions, flags), LP_NO_HEADER },
246 
247   { NULL },
248 };
249 
250 gboolean
msg_format_options_process_flag(MsgFormatOptions * options,const gchar * flag)251 msg_format_options_process_flag(MsgFormatOptions *options, const gchar *flag)
252 {
253   return cfg_process_flag(msg_format_flag_handlers, options, flag);
254 }
255