source: trunk/src/sh_log_parse_apache.c@ 184

Last change on this file since 184 was 183, checked in by katerina, 16 years ago

Support for logfile monitoring (ticket #122). Also improved some configure error messages.

File size: 8.6 KB
Line 
1/**************************************
2 **
3 ** PARSER RULES
4 **
5 ** (a) must set record->host
6 ** (eventually to dummy value)
7 **
8 ** (b) must set record->prefix
9 ** (itoa(status))
10 **
11 **
12 **************************************/
13
14/* for strptime */
15#define _XOPEN_SOURCE 500
16
17#include "config_xor.h"
18
19#include <stdio.h>
20#include <stdlib.h>
21#include <string.h>
22#include <sys/types.h>
23#include <time.h>
24
25#ifdef USE_LOGFILE_MONITOR
26
27#undef FIL__
28#define FIL__ _("sh_log_parse_apache.c")
29
30/* Debian/Ubuntu: libpcre3-dev */
31#include <pcre.h>
32
33#include "samhain.h"
34#include "sh_pthread.h"
35#include "sh_log_check.h"
36#include "sh_utils.h"
37#include "sh_string.h"
38
39extern int flag_err_debug;
40
41struct sh_fileinfo_apache {
42 pcre * line_regex;
43 int * line_ovector; /* captured substrings */
44 int line_ovecnum; /* how many captured */
45
46 int pos_host;
47 int pos_status;
48 int pos_time;
49};
50
51static const char lf_common0[] = N_("%h %l %u %t \"%r\" %>s %b");
52static const char lf_combined0[] = N_("%h %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\"");
53
54/* This variable is not used anywhere. It only exist
55 * to assign &new to them, which keeps gcc from
56 * putting it into a register, and avoids the 'clobbered
57 * by longjmp' warning. And no, 'volatile' proved insufficient.
58 */
59static void * sh_dummy_new = NULL;
60
61void * sh_eval_fileinfo_apache(char * str)
62{
63 struct sh_fileinfo_apache * result = NULL;
64 unsigned int i, quotes;
65 unsigned int nfields = 64;
66 size_t lengths[64];
67 char * new = NULL;
68 char ** splits;
69 char * token;
70 sh_string * re_string;
71 char * p;
72 volatile int p_host = -1;
73 volatile int p_status = -1;
74 volatile int p_time = -1;
75 const char * error;
76 int erroffset;
77
78 /* Take the address to keep gcc from putting them into registers.
79 * Avoids the 'clobbered by longjmp' warning.
80 */
81 sh_dummy_new = (void*) &new;
82
83 if (0 == strncmp("common", str, 6))
84 {
85 new = sh_util_strdup(_(lf_common0));
86 }
87 else if (0 == strncmp("combined", str, 8))
88 {
89 new = sh_util_strdup(_(lf_combined0));
90 }
91 else
92 {
93 new = sh_util_strdup(str);
94 }
95
96 if (flag_err_debug == SL_TRUE)
97 {
98 SH_MUTEX_LOCK(mutex_thread_nolog);
99 sh_error_handle(SH_ERR_ALL, FIL__, __LINE__, 0, MSG_E_SUBGEN,
100 new,
101 _("eval_fileinfo"));
102 SH_MUTEX_UNLOCK(mutex_thread_nolog);
103 }
104
105 splits = split_array_ws(new, &nfields, lengths);
106
107 if (nfields < 1)
108 {
109 SH_FREE(splits);
110 SH_FREE(new);
111 return NULL;
112 }
113
114 /* Build the regex string re_string
115 */
116 re_string = sh_string_new(0);
117 sh_string_add_from_char(re_string, "^");
118
119 for (i = 0; i < nfields; ++i)
120 {
121
122 if (i > 0)
123 sh_string_add_from_char(re_string, " ");
124
125 if (splits[i][0] != '"')
126 quotes = 0;
127 else
128 quotes = 1;
129
130 if (quotes && lengths[i] > 1 && splits[i][lengths[i]-1] == '"')
131 {
132 splits[i][lengths[i]-1] = '\0'; /* cut trailing quote */
133 token = &(splits[i][1]);
134 } else {
135 token = splits[i];
136 }
137
138 if(quotes)
139 {
140 if(strcmp(token, "%r") == 0 ||
141 strstr(token, _("{Referer}")) == 0 ||
142 strstr(token, _("{User-Agent}")) == 0)
143 {
144 p = _("\"([^\"\\\\]*(?:\\\\.[^\"\\\\]*)*)\"");
145 sh_string_add_from_char(re_string, p);
146 }
147 else
148 {
149 p = _("(\\S+)");
150 sh_string_add_from_char(re_string, p);
151 }
152 }
153 else if (token[0] == '%' && token[strlen(token)-1] == 't')
154 {
155 p = _("\\[([^\\]]+)\\]");
156 sh_string_add_from_char(re_string, p);
157 p_time = i+1;
158 }
159 else
160 {
161 p = _("(\\S+)");
162 sh_string_add_from_char(re_string, p);
163 if (token[0] == '%' && token[strlen(token)-1] == 's')
164 p_status = i+1;
165 else if (token[0] == '%' && token[strlen(token)-1] == 'v')
166 p_host = i+1;
167 }
168 }
169 sh_string_add_from_char(re_string, "$");
170
171 if (flag_err_debug == SL_TRUE)
172 {
173 SH_MUTEX_LOCK(mutex_thread_nolog);
174 sh_error_handle(SH_ERR_ALL, FIL__, __LINE__, 0, MSG_E_SUBGEN,
175 sh_string_str(re_string),
176 _("eval_fileinfo"));
177 SH_MUTEX_UNLOCK(mutex_thread_nolog);
178 }
179
180 result = SH_ALLOC(sizeof(struct sh_fileinfo_apache));
181 result->line_regex = pcre_compile(sh_string_str(re_string), 0,
182 &error, &erroffset, NULL);
183 if (!(result->line_regex))
184 {
185 sh_string * msg = sh_string_new(0);
186 sh_string_add_from_char(msg, _("Bad regex: "));
187 sh_string_add_from_char(msg, sh_string_str(re_string));
188
189 SH_MUTEX_LOCK(mutex_thread_nolog);
190 sh_error_handle(SH_ERR_ERR, FIL__, __LINE__, 0, MSG_E_SUBGEN,
191 sh_string_str(msg),
192 _("eval_fileinfo"));
193 SH_MUTEX_UNLOCK(mutex_thread_nolog);
194
195 SH_FREE(result);
196 SH_FREE(splits);
197 SH_FREE(new);
198 sh_string_destroy(&msg);
199 sh_string_destroy(&re_string);
200
201 return NULL;
202 }
203 sh_string_destroy(&re_string);
204
205 result->line_ovector = SH_ALLOC(sizeof(int) * (nfields+1) * 3);
206 result->line_ovecnum = nfields;
207 result->pos_host = p_host;
208 result->pos_status = p_status;
209 result->pos_time = p_time;
210
211 SH_FREE(splits);
212 SH_FREE(new);
213 return (void*)result;
214}
215
216struct sh_logrecord * sh_parse_apache (sh_string * logline, void * fileinfo)
217{
218 static struct tm old_tm;
219 static time_t old_time;
220
221 char tstr[128];
222 char sstr[128];
223 char * hstr;
224 int res;
225 const char **hstr_addr = (const char **) &hstr;
226
227 struct sh_fileinfo_apache * info = (struct sh_fileinfo_apache *) fileinfo;
228
229 if (sh_string_len(logline) > 0 && flag_err_debug == SL_TRUE)
230 {
231 SH_MUTEX_LOCK(mutex_thread_nolog);
232 sh_error_handle(SH_ERR_ALL, FIL__, __LINE__, 0, MSG_E_SUBGEN,
233 sh_string_str(logline),
234 _("sh_parse_apache"));
235 SH_MUTEX_UNLOCK(mutex_thread_nolog);
236 }
237
238 if (logline == NULL || info == NULL)
239 {
240 return NULL;
241 }
242
243 res = pcre_exec(info->line_regex, NULL,
244 sh_string_str(logline), (int)sh_string_len(logline), 0,
245 0, info->line_ovector, (3*(1+info->line_ovecnum)));
246
247 if (res == (1+info->line_ovecnum))
248 {
249 struct sh_logrecord * record;
250 time_t timestamp = 0;
251
252 if (info->pos_time > 0)
253 {
254 res = pcre_copy_substring(sh_string_str(logline),
255 info->line_ovector, res,
256 info->pos_time, tstr, sizeof(tstr));
257 if (res <= 0)
258 goto corrupt;
259 }
260 else
261 {
262 res = 0;
263 timestamp = 0;
264 sl_strlcpy(tstr, _("01/Jan/1970:00:00:00"), sizeof(tstr));
265 }
266
267 if (res > 0)
268 {
269 struct tm btime;
270 char * ptr, * q;
271
272 /* example: 01/Jun/2008:07:55:28 +0200 */
273
274 q = strchr(tstr, ' ');
275 if (q) *q = '\0';
276
277 ptr = /*@i@*/strptime(tstr, "%d/%b/%Y:%T", &btime);
278 if (ptr && *ptr == '\0')
279 {
280 timestamp = conv_timestamp(&btime, &old_tm, &old_time);
281 }
282 else
283 goto corrupt;
284 }
285
286 if (info->pos_status > 0)
287 {
288 res = pcre_copy_substring(sh_string_str(logline),
289 info->line_ovector, res,
290 info->pos_status, sstr, sizeof(sstr));
291 if (res <= 0)
292 goto corrupt;
293 }
294 else
295 {
296 sl_strlcpy(sstr, _("000"), sizeof(sstr)); /* FIXME API */
297 }
298
299 if (info->pos_host > 0)
300 {
301 res = pcre_get_substring(sh_string_str(logline),
302 info->line_ovector, res,
303 info->pos_host, hstr_addr);
304 if (res <= 0)
305 goto corrupt;
306 }
307 else
308 {
309 hstr = NULL;
310 }
311
312 record = SH_ALLOC(sizeof(struct sh_logrecord));
313
314 record->timestamp = timestamp;
315 record->timestr = sh_string_new_from_lchar(tstr, strlen(tstr));
316 record->prefix = sh_string_new_from_lchar(sstr, strlen(sstr));
317
318 if (hstr)
319 record->host = sh_string_new_from_lchar(hstr, strlen(hstr));
320 else
321 record->host = sh_string_new_from_lchar(sh.host.name, strlen(sh.host.name));
322
323 record->message = sh_string_new_from_lchar(sh_string_str(logline),
324 sh_string_len(logline));
325 record->pid = 0;
326
327 pcre_free(hstr);
328 return record;
329 }
330 else
331 {
332 char msg[128];
333 sl_snprintf(msg, sizeof(msg), _("Incorrect number of captured subexpressions: %d vs %d"),
334 res, info->line_ovecnum);
335
336 SH_MUTEX_LOCK(mutex_thread_nolog);
337 sh_error_handle(SH_ERR_ERR, FIL__, __LINE__, 0, MSG_E_SUBGEN,
338 msg,
339 _("sh_parse_apache"));
340 SH_MUTEX_UNLOCK(mutex_thread_nolog);
341 }
342
343 /* Corrupted logline */
344 corrupt:
345
346 {
347 sh_string * msg = sh_string_new(0);
348 sh_string_add_from_char(msg, _("Corrupt logline: "));
349 sh_string_add_from_char(msg, sh_string_str(logline));
350
351 SH_MUTEX_LOCK(mutex_thread_nolog);
352 sh_error_handle(SH_ERR_ERR, FIL__, __LINE__, 0, MSG_E_SUBGEN,
353 sh_string_str(msg),
354 _("sh_parse_apache"));
355 SH_MUTEX_UNLOCK(mutex_thread_nolog);
356 sh_string_destroy(&msg);
357 }
358 return NULL;
359}
360
361/* USE_LOGFILE_MONITOR */
362#endif
Note: See TracBrowser for help on using the repository browser.