1 /*
2 * Copyright (c) 2017 Balabit
3 *
4 * This program is free software; you can redistribute it and/or modify it
5 * under the terms of the GNU General Public License version 2 as published
6 * by the Free Software Foundation, or (at your option) any later version.
7 *
8 * This program is distributed in the hope that it will be useful,
9 * but WITHOUT ANY WARRANTY; without even the implied warranty of
10 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 * GNU General Public License for more details.
12 *
13 * You should have received a copy of the GNU General Public License
14 * along with this program; if not, write to the Free Software
15 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
16 *
17 * As an additional exemption you are allowed to compile & link against the
18 * OpenSSL libraries as published by the OpenSSL project. See the file
19 * COPYING for details.
20 *
21 */
22
23 #include "maxminddb-helper.h"
24 #include "scratch-buffers.h"
25 #include <logmsg/logmsg.h>
26 #include <messages.h>
27
28 #define return_and_set_error_if(predicate, status) \
29 if (predicate) \
30 { \
31 *status = MMDB_INVALID_DATA_ERROR; \
32 return NULL; \
33 }
34
35 gchar *
mmdb_default_database(void)36 mmdb_default_database(void)
37 {
38 static const gchar *possible_paths[] =
39 {
40 "/usr/share/GeoIP/GeoLite2-City.mmdb", //centos, arch, fedora
41 "/var/lib/GeoIP/GeoLite2-City.mmdb", //ubuntu
42 };
43
44 for (gint i = 0; i < G_N_ELEMENTS(possible_paths); ++i)
45 {
46 if (g_file_test(possible_paths[i], G_FILE_TEST_EXISTS))
47 return g_strdup(possible_paths[i]);
48 }
49
50 return NULL;
51 }
52
53 gboolean
mmdb_open_database(const gchar * path,MMDB_s * database)54 mmdb_open_database(const gchar *path, MMDB_s *database)
55 {
56 int mmdb_status = MMDB_open(path, MMDB_MODE_MMAP, database);
57 if (mmdb_status != MMDB_SUCCESS)
58 {
59 msg_error("MMDB_open",
60 evt_tag_str("error", MMDB_strerror(mmdb_status)));
61 return FALSE;
62 }
63 return TRUE;
64 }
65
66 void
append_mmdb_entry_data_to_gstring(GString * target,MMDB_entry_data_s * entry_data)67 append_mmdb_entry_data_to_gstring(GString *target, MMDB_entry_data_s *entry_data)
68 {
69
70 switch (entry_data->type)
71 {
72 case MMDB_DATA_TYPE_UTF8_STRING:
73 g_string_append_printf(target, "%.*s", entry_data->data_size, entry_data->utf8_string);
74 break;
75 case MMDB_DATA_TYPE_DOUBLE:
76 g_string_append_printf(target, "%f", entry_data->double_value);
77 break;
78 case MMDB_DATA_TYPE_FLOAT:
79 g_string_append_printf(target, "%f", (double)entry_data->float_value);
80 break;
81 case MMDB_DATA_TYPE_UINT16:
82 g_string_append_printf(target, "%u", entry_data->uint16);
83 break;
84 case MMDB_DATA_TYPE_UINT32:
85 g_string_append_printf(target, "%u", entry_data->uint32);
86 break;
87 case MMDB_DATA_TYPE_INT32:
88 g_string_append_printf(target, "%d", entry_data->int32);
89 break;
90 case MMDB_DATA_TYPE_UINT64:
91 g_string_append_printf(target, "%"G_GUINT64_FORMAT, entry_data->uint64);
92 break;
93 case MMDB_DATA_TYPE_BOOLEAN:
94 g_string_append_printf(target, "%s", entry_data->boolean ? "true" : "false");
95 break;
96 case MMDB_DATA_TYPE_UINT128:
97 case MMDB_DATA_TYPE_MAP:
98 case MMDB_DATA_TYPE_BYTES:
99 case MMDB_DATA_TYPE_ARRAY:
100 g_assert_not_reached();
101 default:
102 g_assert_not_reached();
103 }
104 }
105
106 static MMDB_entry_data_list_s *
107 mmdb_skip_tree(MMDB_entry_data_list_s *entry_data_list, gint *status);
108
109 static MMDB_entry_data_list_s *
mmdb_skip_map(MMDB_entry_data_list_s * entry_data_list,gint * status)110 mmdb_skip_map(MMDB_entry_data_list_s *entry_data_list, gint *status)
111 {
112 guint32 size = entry_data_list->entry_data.data_size;
113
114 for (entry_data_list = entry_data_list->next;
115 size && entry_data_list; size--)
116 {
117
118 return_and_set_error_if(MMDB_DATA_TYPE_UTF8_STRING != entry_data_list->entry_data.type, status);
119
120 entry_data_list = entry_data_list->next;
121 entry_data_list = mmdb_skip_tree(entry_data_list, status);
122 if (MMDB_SUCCESS != *status)
123 {
124 return NULL;
125 }
126 }
127
128 return entry_data_list;
129 }
130
131 static MMDB_entry_data_list_s *
mmdb_skip_array(MMDB_entry_data_list_s * entry_data_list,gint * status)132 mmdb_skip_array(MMDB_entry_data_list_s *entry_data_list, gint *status)
133 {
134 uint32_t size = entry_data_list->entry_data.data_size;
135
136 for (entry_data_list = entry_data_list->next;
137 size && entry_data_list; size--)
138 {
139 entry_data_list = mmdb_skip_tree(entry_data_list, status);
140
141 if (MMDB_SUCCESS != *status)
142 {
143 return NULL;
144 }
145 }
146 return entry_data_list;
147 }
148
149 static MMDB_entry_data_list_s *
mmdb_skip_tree(MMDB_entry_data_list_s * entry_data_list,gint * status)150 mmdb_skip_tree(MMDB_entry_data_list_s *entry_data_list, gint *status)
151 {
152
153 switch (entry_data_list->entry_data.type)
154 {
155 case MMDB_DATA_TYPE_MAP:
156 entry_data_list = mmdb_skip_map(entry_data_list, status);
157 break;
158 case MMDB_DATA_TYPE_ARRAY:
159 entry_data_list = mmdb_skip_array(entry_data_list, status);
160 break;
161 case MMDB_DATA_TYPE_BYTES:
162 case MMDB_DATA_TYPE_BOOLEAN:
163 case MMDB_DATA_TYPE_UINT128:
164 case MMDB_DATA_TYPE_UTF8_STRING:
165 case MMDB_DATA_TYPE_DOUBLE:
166 case MMDB_DATA_TYPE_FLOAT:
167 case MMDB_DATA_TYPE_UINT16:
168 case MMDB_DATA_TYPE_UINT32:
169 case MMDB_DATA_TYPE_UINT64:
170 case MMDB_DATA_TYPE_INT32:
171 entry_data_list = entry_data_list->next;
172 break;
173
174 default:
175 *status = MMDB_INVALID_DATA_ERROR;
176 return NULL;
177 }
178
179 *status = MMDB_SUCCESS;
180 return entry_data_list;
181 }
182
183 static void
_geoip_log_msg_add_value(LogMessage * msg,GArray * path,GString * value)184 _geoip_log_msg_add_value(LogMessage *msg, GArray *path, GString *value)
185 {
186 gchar *path_string = g_strjoinv(".", (gchar **)path->data);
187 log_msg_set_value_by_name(msg, path_string, value->str, value->len);
188 g_free(path_string);
189 }
190
191 static void
_print_preferred_string_for_lang(LogMessage * msg,MMDB_entry_data_s * entry_data,GArray * path,gchar * preferred_language)192 _print_preferred_string_for_lang(LogMessage *msg, MMDB_entry_data_s *entry_data, GArray *path,
193 gchar *preferred_language)
194 {
195 g_array_append_val(path, preferred_language);
196
197 GString *value = scratch_buffers_alloc();
198 g_string_printf(value, "%.*s",
199 entry_data->data_size,
200 entry_data->utf8_string);
201 _geoip_log_msg_add_value(msg, path, value);
202 g_array_remove_index(path, path->len-1);
203 }
204
205 static MMDB_entry_data_list_s *
check_language_and_maybe_insert(GString * key,gchar * preferred_language,LogMessage * msg,MMDB_entry_data_list_s * entry_data_list,GArray * path,gint * status)206 check_language_and_maybe_insert(GString *key, gchar *preferred_language, LogMessage *msg,
207 MMDB_entry_data_list_s *entry_data_list, GArray *path, gint *status)
208 {
209 if (!strcmp(key->str, preferred_language))
210 {
211 return_and_set_error_if(entry_data_list->entry_data.type != MMDB_DATA_TYPE_UTF8_STRING, status);
212
213 _print_preferred_string_for_lang(msg, &entry_data_list->entry_data, path, preferred_language);
214 entry_data_list = entry_data_list->next;
215 }
216 else
217 {
218 entry_data_list = mmdb_skip_tree(entry_data_list, status);
219 if (MMDB_SUCCESS != *status)
220 return NULL;
221 }
222
223 return entry_data_list;
224 }
225
226 static MMDB_entry_data_list_s *
select_language(gchar * preferred_language,LogMessage * msg,MMDB_entry_data_list_s * entry_data_list,GArray * path,gint * status)227 select_language(gchar *preferred_language, LogMessage *msg,
228 MMDB_entry_data_list_s *entry_data_list, GArray *path, gint *status)
229 {
230
231 return_and_set_error_if(entry_data_list->entry_data.type != MMDB_DATA_TYPE_MAP, status)
232
233 guint32 size = entry_data_list->entry_data.data_size;
234
235 for (entry_data_list = entry_data_list->next;
236 size && entry_data_list; size--)
237 {
238 return_and_set_error_if(entry_data_list->entry_data.type != MMDB_DATA_TYPE_UTF8_STRING, status);
239
240 GString *key = scratch_buffers_alloc();
241 g_string_printf(key, "%.*s",
242 entry_data_list->entry_data.data_size,
243 entry_data_list->entry_data.utf8_string);
244
245 entry_data_list = entry_data_list->next;
246 entry_data_list = check_language_and_maybe_insert(key, preferred_language, msg,
247 entry_data_list, path, status);
248 if (MMDB_SUCCESS != *status)
249 return NULL;
250
251 }
252 return entry_data_list;
253 }
254
255 static void
_index_array_in_path(GArray * path,guint32 _index,GString * indexer)256 _index_array_in_path(GArray *path, guint32 _index, GString *indexer)
257 {
258 g_string_printf(indexer, "%d", _index);
259 ((gchar **)path->data)[path->len-1] = indexer->str;
260 }
261
262 MMDB_entry_data_list_s *
dump_geodata_into_msg_map(LogMessage * msg,MMDB_entry_data_list_s * entry_data_list,GArray * path,gint * status)263 dump_geodata_into_msg_map(LogMessage *msg, MMDB_entry_data_list_s *entry_data_list, GArray *path, gint *status)
264 {
265 guint32 size = entry_data_list->entry_data.data_size;
266
267 for (entry_data_list = entry_data_list->next;
268 size && entry_data_list; size--)
269 {
270 return_and_set_error_if(MMDB_DATA_TYPE_UTF8_STRING != entry_data_list->entry_data.type, status);
271
272 GString *key = scratch_buffers_alloc();
273 g_string_printf(key, "%.*s",
274 entry_data_list->entry_data.data_size,
275 entry_data_list->entry_data.utf8_string);
276
277 g_array_append_val(path, key->str);
278 entry_data_list = entry_data_list->next;
279
280 if (!strcmp(key->str, "names"))
281 entry_data_list = select_language("en", msg, entry_data_list, path, status);
282 else
283 entry_data_list = dump_geodata_into_msg(msg, entry_data_list, path, status);
284
285 if (MMDB_SUCCESS != *status)
286 return NULL;
287
288 g_array_remove_index(path, path->len-1);
289 }
290
291 return entry_data_list;
292 }
293
294 MMDB_entry_data_list_s *
dump_geodata_into_msg_array(LogMessage * msg,MMDB_entry_data_list_s * entry_data_list,GArray * path,gint * status)295 dump_geodata_into_msg_array(LogMessage *msg, MMDB_entry_data_list_s *entry_data_list, GArray *path, gint *status)
296 {
297 guint32 size = entry_data_list->entry_data.data_size;
298 guint32 _index = 0;
299 GString *indexer = scratch_buffers_alloc();
300 g_array_append_val(path, indexer->str);
301
302 for (entry_data_list = entry_data_list->next;
303 (_index < size) && entry_data_list;
304 _index++)
305 {
306 _index_array_in_path(path, _index, indexer);
307 entry_data_list = dump_geodata_into_msg(msg, entry_data_list, path, status);
308
309 if (MMDB_SUCCESS != *status)
310 return NULL;
311 }
312 g_array_remove_index(path, path->len-1);
313
314 return entry_data_list;
315 }
316
317 static void G_GNUC_PRINTF(3, 4)
dump_geodata_into_msg_data(LogMessage * msg,GArray * path,gchar * fmt,...)318 dump_geodata_into_msg_data(LogMessage *msg, GArray *path, gchar *fmt, ...)
319 {
320 GString *value = scratch_buffers_alloc();
321 va_list va;
322
323 va_start(va, fmt);
324 g_string_vprintf(value, fmt, va);
325 va_end(va);
326
327 _geoip_log_msg_add_value(msg, path, value);
328 }
329
330 MMDB_entry_data_list_s *
dump_geodata_into_msg(LogMessage * msg,MMDB_entry_data_list_s * entry_data_list,GArray * path,gint * status)331 dump_geodata_into_msg(LogMessage *msg, MMDB_entry_data_list_s *entry_data_list, GArray *path, gint *status)
332 {
333 switch (entry_data_list->entry_data.type)
334 {
335 case MMDB_DATA_TYPE_MAP:
336 entry_data_list = dump_geodata_into_msg_map(msg, entry_data_list, path, status);
337 if (MMDB_SUCCESS != *status)
338 return NULL;
339 break;
340
341 case MMDB_DATA_TYPE_BYTES:
342 case MMDB_DATA_TYPE_UINT128:
343 g_assert_not_reached();
344
345 case MMDB_DATA_TYPE_ARRAY:
346 entry_data_list = dump_geodata_into_msg_array(msg, entry_data_list, path, status);
347 if (MMDB_SUCCESS != *status)
348 return NULL;
349 break;
350 case MMDB_DATA_TYPE_UTF8_STRING:
351 dump_geodata_into_msg_data(msg, path, "%.*s", entry_data_list->entry_data.data_size,
352 entry_data_list->entry_data.utf8_string);
353 entry_data_list = entry_data_list->next;
354 break;
355
356 case MMDB_DATA_TYPE_DOUBLE:
357 dump_geodata_into_msg_data(msg, path, "%f", entry_data_list->entry_data.double_value);
358 entry_data_list = entry_data_list->next;
359 break;
360
361 case MMDB_DATA_TYPE_FLOAT:
362 dump_geodata_into_msg_data(msg, path, "%f", (double)entry_data_list->entry_data.float_value);
363 entry_data_list = entry_data_list->next;
364 break;
365
366 case MMDB_DATA_TYPE_UINT16:
367 dump_geodata_into_msg_data(msg, path, "%u", entry_data_list->entry_data.uint16);
368 entry_data_list = entry_data_list->next;
369 break;
370
371 case MMDB_DATA_TYPE_UINT32:
372 dump_geodata_into_msg_data(msg, path, "%u", entry_data_list->entry_data.uint32);
373 entry_data_list = entry_data_list->next;
374 break;
375
376 case MMDB_DATA_TYPE_UINT64:
377 dump_geodata_into_msg_data(msg, path, "%" PRIu64, entry_data_list->entry_data.uint64);
378 entry_data_list = entry_data_list->next;
379 break;
380
381 case MMDB_DATA_TYPE_INT32:
382 dump_geodata_into_msg_data(msg, path, "%d", entry_data_list->entry_data.int32);
383 entry_data_list = entry_data_list->next;
384 break;
385 case MMDB_DATA_TYPE_BOOLEAN:
386 dump_geodata_into_msg_data(msg, path, "%s", entry_data_list->entry_data.boolean ? "true" : "false");
387 entry_data_list = entry_data_list->next;
388 break;
389 default:
390 *status = MMDB_INVALID_DATA_ERROR;
391 return NULL;
392 }
393
394 *status = MMDB_SUCCESS;
395 return entry_data_list;
396 }
397