new upstream release (3.3.0); modify package compatibility for Stretch
[ossec-hids.git] / src / os_dbd / db_op.c
1 /* Copyright (C) 2009 Trend Micro Inc.
2  * All rights reserved.
3  *
4  * This program is a free software; you can redistribute it
5  * and/or modify it under the terms of the GNU General Public
6  * License (version 2) as published by the FSF - Free Software
7  * Foundation.
8  */
9
10 /* Common lib for dealing with databases */
11
12 #include "dbd.h"
13
14 /* Prototypes */
15 void *(*osdb_connect)(const char *host, const char *user, const char *pass, const char *db, unsigned int port, const char *sock);
16 int (* osdb_query_insert)(void *db_conn, const char *query);
17 int (* osdb_query_select)(void *db_conn, const char *query);
18 void *(*osdb_close)(void *db_conn);
19 const unsigned char insert_map[256] = {
20     0, 0, 0, 0, 0, 0, 0, 0,
21     0, 0, 1, 0, 0, 0, 0, 0,
22     0, 0, 0, 0, 0, 0, 0, 0,
23     0, 0, 0, 0, 0, 0, 0, 0,
24     1, 1, 1, 1, 1, 1, 1, 0,
25     1, 1, 1, 1, 1, 1, 1, 1,
26     1, 1, 1, 1, 1, 1, 1, 1,
27     1, 1, 1, 1, 1, 1, 1, 1,
28     1, 1, 1, 1, 1, 1, 1, 1,
29     1, 1, 1, 1, 1, 1, 1, 1,
30     1, 1, 1, 1, 1, 1, 1, 1,
31     1, 1, 1, 1, 1, 1, 1, 1,
32     1, 1, 1, 1, 1, 1, 1, 1,
33     1, 1, 1, 1, 1, 1, 1, 1,
34     1, 1, 1, 1, 1, 1, 1, 1,
35     1, 1, 1, 1, 1, 1, 1, 0,
36     0, 0, 0, 0, 0, 0, 0, 0,
37     0, 0, 0, 0, 0, 0, 0, 0,
38     0, 0, 0, 0, 0, 0, 0, 0,
39     0, 0, 0, 0, 0, 0, 0, 0,
40     0, 0, 0, 0, 0, 0, 0, 0,
41     0, 0, 0, 0, 0, 0, 0, 0,
42     0, 0, 0, 0, 0, 0, 0, 0,
43     0, 0, 0, 0, 0, 0, 0, 0,
44     0, 0, 0, 0, 0, 0, 0, 0,
45     0, 0, 0, 0, 0, 0, 0, 0,
46     0, 0, 0, 0, 0, 0, 0, 0,
47     0, 0, 0, 0, 0, 0, 0, 0,
48     0, 0, 0, 0, 0, 0, 0, 0,
49     0, 0, 0, 0, 0, 0, 0, 0,
50     0, 0, 0, 0, 0, 0, 0, 0,
51     0, 0, 0, 0, 0, 0, 0, 0,
52 };
53
54 #ifdef MYSQL_DATABASE_ENABLED
55 #include <mysql.h>
56 #endif
57
58 #ifdef PGSQL_DATABASE_ENABLED
59 #include <libpq-fe.h>
60 #endif
61
62 #if defined(MYSQL_DATABASE_ENABLED) || defined(PGSQL_DATABASE_ENABLED)
63 static void osdb_checkerror(void);
64 static void osdb_seterror(void);
65 #endif
66
67 /* Config pointer */
68 static DBConfig *db_config_pt = NULL;
69
70
71 /* Escapes a null terminated string before inserting into the database
72  * We built a allow list of allowed characters at insert_map. Everything
73  * not allowed will become a space.
74  */
75 void osdb_escapestr(char *str)
76 {
77     if (!str) {
78         return;
79     }
80
81     while (*str) {
82         if (*str == '\'') {
83             *str = '`';
84         } else if (*str == '\\') {
85             *str = '/';
86         } else if (insert_map[(unsigned char)*str] != '\001') {
87             *str = ' ';
88         }
89         str++;
90     }
91
92     /* It can not end with \\ */
93     if (*(str - 1) == '\\') {
94         *(str - 1) = '\0';
95     }
96 }
97
98 #if defined(MYSQL_DATABASE_ENABLED) || defined(PGSQL_DATABASE_ENABLED)
99
100 /* Check for errors and handle them appropriately */
101 static void osdb_checkerror()
102 {
103     if (!db_config_pt || db_config_pt->error_count > 20) {
104         ErrorExit(DB_MAINERROR, ARGV0);
105     }
106
107     /* If error count is too large, we try to reconnect */
108     if (db_config_pt->error_count > 0) {
109         unsigned int i = 0, sleep_time = 2;
110
111         if (db_config_pt->conn) {
112             osdb_close(db_config_pt->conn);
113             db_config_pt->conn = NULL;
114         }
115
116         while (i <= db_config_pt->maxreconnect) {
117             merror(DB_ATTEMPT, ARGV0);
118             db_config_pt->conn = osdb_connect(db_config_pt->host,
119                                               db_config_pt->user,
120                                               db_config_pt->pass,
121                                               db_config_pt->db,
122                                               db_config_pt->port,
123                                               db_config_pt->sock);
124
125             /* If we were able to reconnect, keep going */
126             if (db_config_pt->conn) {
127                 break;
128             }
129             sleep(sleep_time);
130             sleep_time *= 2;
131             i++;
132         }
133
134         /* If we weren't able to connect, exit */
135         if (!db_config_pt->conn) {
136             ErrorExit(DB_MAINERROR, ARGV0);
137         }
138
139         verbose("%s: Connected to database '%s' at '%s'.",
140                 ARGV0, db_config_pt->db, db_config_pt->host);
141     }
142 }
143
144 /* Set the error counter */
145 static void osdb_seterror()
146 {
147     db_config_pt->error_count++;
148     osdb_checkerror();
149 }
150
151 #endif
152
153
154 /* Create an internal pointer to the db configuration */
155 void osdb_setconfig(DBConfig *db_config)
156 {
157     db_config_pt = db_config;
158 }
159
160 /** MySQL calls **/
161 #ifdef MYSQL_DATABASE_ENABLED
162
163 /* Create the database connection
164  * Returns NULL on error
165  */
166 void *mysql_osdb_connect(const char *host, const char *user, const char *pass, const char *db,
167                          unsigned int port, const char *sock)
168 {
169     MYSQL *conn;
170     conn = mysql_init(NULL);
171     if (conn == NULL) {
172         merror(DBINIT_ERROR, ARGV0);
173         return (NULL);
174     }
175
176     /* If host is 127.0.0.1 or localhost, use TCP socket */
177     if ((strcmp(host, "127.0.0.1") == 0) ||
178             (strcmp(host, "::1") == 0) ||
179             (strcmp(host, "localhost") == 0)) {
180         if (sock != NULL) {
181             mysql_options(conn, MYSQL_OPT_NAMED_PIPE, NULL);
182         } else {
183             unsigned int p_type = MYSQL_PROTOCOL_TCP;
184             mysql_options(conn, MYSQL_OPT_PROTOCOL, (char *)&p_type);
185         }
186     }
187     if (mysql_real_connect(conn, host, user, pass, db,
188                            port, sock, 0) == NULL) {
189         merror(DBCONN_ERROR, ARGV0, host, db, mysql_error(conn));
190         mysql_close(conn);
191         return (NULL);
192     }
193
194     return (conn);
195 }
196
197 /* Close the database connection */
198 void *mysql_osdb_close(void *db_conn)
199 {
200     merror(DB_CLOSING, ARGV0);
201     mysql_close(db_conn);
202     return (NULL);
203 }
204
205 /* Sends insert query to database */
206 int mysql_osdb_query_insert(void *db_conn, const char *query)
207 {
208     if (mysql_query(db_conn, query) != 0) {
209         /* failure; report error */
210         merror(DBQUERY_ERROR, ARGV0, query, mysql_error(db_conn));
211         osdb_seterror();
212         return (0);
213     }
214
215     return (1);
216 }
217
218 /* Sends a select query to database. Returns the value of it.
219  * Returns 0 on error (not found).
220  */
221 int mysql_osdb_query_select(void *db_conn, const char *query)
222 {
223     int result_int = 0;
224     MYSQL_RES *result_data;
225     MYSQL_ROW result_row;
226
227     /* Send the query. It can not fail. */
228     if (mysql_query(db_conn, query) != 0) {
229         /* Failure: report error */
230         merror(DBQUERY_ERROR, ARGV0, query, mysql_error(db_conn));
231         osdb_seterror();
232         return (0);
233     }
234
235     /* Get result */
236     result_data = mysql_use_result(db_conn);
237     if (result_data == NULL) {
238         /* Failure: report error */
239         merror(DBQUERY_ERROR, ARGV0, query, mysql_error(db_conn));
240         osdb_seterror();
241         return (0);
242     }
243
244     /* Get row. We only care about the first result. */
245     result_row = mysql_fetch_row(result_data);
246     if (result_row && (result_row[0] != NULL)) {
247         result_int = atoi(result_row[0]);
248     }
249
250     mysql_free_result(result_data);
251
252     return (result_int);
253 }
254 #endif
255 /** End of MySQL calls **/
256
257 /** PostgreSQL Calls **/
258 #ifdef PGSQL_DATABASE_ENABLED
259
260 /* Create the PostgreSQL database connection
261  * Returns NULL on error
262  */
263 void *postgresql_osdb_connect(const char *host, const char *user, const char *pass, const char *db,
264                               unsigned int port, __attribute__((unused)) const char *sock)
265 {
266     PGconn *conn;
267     char portAsString[6];
268
269     if (port > 0) {
270         snprintf(portAsString, 6, "%u", port);
271     } else {
272         snprintf(portAsString, 6, "");
273     }
274
275     conn = PQsetdbLogin(host, portAsString, NULL, NULL, db, user, pass);
276
277     if (PQstatus(conn) == CONNECTION_BAD) {
278         merror(DBCONN_ERROR, ARGV0, host, db, PQerrorMessage(conn));
279         PQfinish(conn);
280         return (NULL);
281     }
282
283     return (conn);
284 }
285
286 /* Terminates db connection */
287 void *postgresql_osdb_close(void *db_conn)
288 {
289     merror(DB_CLOSING, ARGV0);
290     PQfinish(db_conn);
291     return (NULL);
292 }
293
294 /* Send insert query to database */
295 int postgresql_osdb_query_insert(void *db_conn, const char *query)
296 {
297     PGresult *result;
298
299     result = PQexec(db_conn, query);
300     if (!result) {
301         merror(DBQUERY_ERROR, ARGV0, query, PQerrorMessage(db_conn));
302         osdb_seterror();
303         return (0);
304     }
305
306     if (PQresultStatus(result) != PGRES_COMMAND_OK) {
307         merror(DBQUERY_ERROR, ARGV0, query, PQerrorMessage(db_conn));
308         PQclear(result);
309         osdb_seterror();
310         return (0);
311     }
312
313     PQclear(result);
314
315     return (1);
316 }
317
318 /* Send a select query to database. Returns the value of it.
319  * Returns 0 on error (not found).
320  */
321 int postgresql_osdb_query_select(void *db_conn, const char *query)
322 {
323     int result_int = 0;
324     PGresult *result;
325
326     result = PQexec(db_conn, query);
327     if (!result) {
328         merror(DBQUERY_ERROR, ARGV0, query, PQerrorMessage(db_conn));
329         osdb_seterror();
330         return (0);
331     }
332
333     if ((PQresultStatus(result) == PGRES_TUPLES_OK)) {
334         if (PQntuples(result) == 1) {
335             result_int = atoi(PQgetvalue(result, 0, 0));
336         }
337     } else {
338         merror(DBQUERY_ERROR, ARGV0, query, PQerrorMessage(db_conn));
339         osdb_seterror();
340         return (0);
341     }
342
343     /* Clear result */
344     PQclear(result);
345
346     return (result_int);
347 }
348 /** End of PostgreSQL calls **/
349 #endif
350
351 /* Everything else when db is not defined */
352 #if !defined(PGSQL_DATABASE_ENABLED) && !defined(MYSQL_DATABASE_ENABLED)
353
354 void *none_osdb_connect(__attribute__((unused)) const char *host, __attribute__((unused)) const char *user,
355                         __attribute__((unused)) const char *pass, __attribute__((unused)) const char *db,
356                         __attribute__((unused)) unsigned int port, __attribute__((unused)) const char *sock)
357 {
358     merror("%s: ERROR: Database support not enabled. Exiting.", ARGV0);
359     return (NULL);
360 }
361 void *none_osdb_close(__attribute__((unused)) void *db_conn)
362 {
363     merror("%s: ERROR: Database support not enabled. Exiting.", ARGV0);
364     return (NULL);
365 }
366 int none_osdb_query_insert(__attribute__((unused)) void *db_conn, __attribute__((unused)) const char *query)
367 {
368     merror("%s: ERROR: Database support not enabled. Exiting.", ARGV0);
369     return (0);
370 }
371 int none_osdb_query_select(__attribute__((unused)) void *db_conn, __attribute__((unused)) const char *query)
372 {
373     merror("%s: ERROR: Database support not enabled. Exiting.", ARGV0);
374     return (0);
375 }
376
377 #endif
378