The FreeRADIUS server  $Id: 15bac2a4c627c01d1aa2047687b3418955ac7f00 $
rlm_sql_db2.c
Go to the documentation of this file.
1 /*
2  * sql_db2.c IBM DB2 rlm_sql driver
3  *
4  * Version: $Id: 6abbb5a397f4ab344256a97695cfbc22f1ae5595 $
5  *
6  * This program is free software; you can redistribute it and/or modify
7  * it under the terms of the GNU General Public License as published by
8  * the Free Software Foundation; either version 2 of the License, or
9  * (at your option) any later version.
10  *
11  * This program is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14  * GNU General Public License for more details.
15  *
16  * You should have received a copy of the GNU General Public License
17  * along with this program; if not, write to the Free Software
18  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
19  *
20  * @copyright 2000,2006 The FreeRADIUS server project
21  * @copyright 2000 Mike Machado (mike@innercite.com)
22  * @copyright 2000 Alan DeKok (aland@freeradius.org)
23  * @copyright 2001 Joerg Wendland (wendland@scan-plus.de)
24  */
25 
26 /*
27  * Modification of rlm_sql_db2 to handle IBM DB2 UDB V7
28  * by Joerg Wendland <wendland@scan-plus.de>
29  */
30 RCSID("$Id: 6abbb5a397f4ab344256a97695cfbc22f1ae5595 $")
31 
32 #define LOG_PREFIX "sql - db2"
33 
34 #include <freeradius-devel/server/base.h>
35 #include <freeradius-devel/util/debug.h>
36 
37 #include <sys/stat.h>
38 
39 #include <sqlcli1.h>
40 #include <sqlstate.h>
41 #include "rlm_sql.h"
42 #include "rlm_sql_trunk.h"
43 
44 typedef struct {
45  SQLHANDLE dbc_handle;
46  SQLHANDLE env_handle;
47  SQLHANDLE stmt;
49 
51 {
52  rlm_sql_db2_conn_t *conn = talloc_get_type_abort(h, rlm_sql_db2_conn_t);
53 
54  DEBUG2("Socket destructor called, closing socket");
55 
56  if (conn->dbc_handle) {
57  SQLDisconnect(conn->dbc_handle);
58  SQLFreeHandle(SQL_HANDLE_DBC, conn->dbc_handle);
59  }
60 
61  if (conn->env_handle) SQLFreeHandle(SQL_HANDLE_ENV, conn->env_handle);
62 
63  talloc_free(h);
64 }
65 
66 CC_NO_UBSAN(function) /* UBSAN: false positive - public vs private connection_t trips --fsanitize=function */
67 static connection_state_t _sql_connection_init(void **h, connection_t *conn, void *uctx)
68 {
71  rlm_sql_config_t const *config = &sql->config;
72  uint32_t timeout_ms = fr_time_delta_to_msec(config->trunk_conf.conn_conf->connection_timeout);
73  SQLRETURN ret;
74 
75  MEM(c = talloc_zero(conn, rlm_sql_db2_conn_t));
76 
77  /* Allocate handles */
78  SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE, &(c->env_handle));
79  SQLAllocHandle(SQL_HANDLE_DBC, c->env_handle, &(c->dbc_handle));
80 
81  /* Set the connection timeout */
82  SQLSetConnectAttr(c->dbc_handle, SQL_ATTR_LOGIN_TIMEOUT, &timeout_ms, SQL_IS_UINTEGER);
83 
84  /*
85  * We probably want to use SQLDriverConnect, which connects
86  * to a remote server.
87  *
88  * http://www.ibm.com/support/knowledgecenter/SSEPGG_10.5.0/com.ibm.db2.luw.apdv.cli.doc/doc/r0000584.html
89  * http://stackoverflow.com/questions/27167070/connection-string-to-a-remote-db2-db-in-another-server
90  *
91  * And probably synthesise the retarded connection string ourselves,
92  * probably via config file expansions:
93  *
94  * Driver={IBM DB2 ODBC Driver};Database=testDb;Hostname=remoteHostName.com;UID=username;PWD=mypasswd;PORT=50000
95  */
96  ret = SQLConnect(c->dbc_handle,
97  UNCONST(SQLCHAR *, config->sql_server), SQL_NTS,
98  UNCONST(SQLCHAR *, config->sql_login), SQL_NTS,
99  UNCONST(SQLCHAR *, config->sql_password), SQL_NTS);
100  if (ret != SQL_SUCCESS) {
101  ERROR("could not connect to DB2 server %s", config->sql_server);
102 
104  }
105 
106  *h = c;
108 }
109 
111 
113 
114 CC_NO_UBSAN(function) /* UBSAN: false positive - public vs private connection_t trips --fsanitize=function */
115 static void sql_trunk_request_mux(UNUSED fr_event_list_t *el, trunk_connection_t *tconn,
116  connection_t *conn, UNUSED void *uctx)
117 {
118  rlm_sql_db2_conn_t *sql_conn = talloc_get_type_abort(conn->h, rlm_sql_db2_conn_t);
119  trunk_request_t *treq;
120  request_t *request;
121  fr_sql_query_t *query_ctx;
122  SQLRETURN ret;
123  SQLCHAR *db2_query;
124 
125  if (trunk_connection_pop_request(&treq, tconn) != 0) return;
126  if (!treq) return;
127 
128  query_ctx = talloc_get_type_abort(treq->preq, fr_sql_query_t);
129  request = query_ctx->request;
130  query_ctx->tconn = tconn;
131 
132  ROPTIONAL(RDEBUG2, DEBUG2, "Executing query: %s", query_ctx->query_str);
133 
134  /* allocate handle for statement */
135  SQLAllocHandle(SQL_HANDLE_STMT, sql_conn->dbc_handle, &(sql_conn->stmt));
136 
137  /* execute query */
138  memcpy(&db2_query, &query_ctx->query_str, sizeof(query_ctx->query_str));
139 
140  ret = SQLExecDirect(sql_conn->stmt, db2_query, SQL_NTS);
141  if (ret != SQL_SUCCESS) {
142  SQLCHAR state[6];
143  SQLSMALLINT len;
144 
145  SQLGetDiagField(SQL_HANDLE_STMT, sql_conn->dbc_handle, 1, SQL_DIAG_SQLSTATE, state, sizeof(state), &len);
146 
147  if (strncmp((char *)state, SQL_CONSTR_INDEX_UNIQUE, 5)) {
148  query_ctx->rcode = RLM_SQL_ALT_QUERY;
149  goto finish;
150  }
151 
152  /* XXX Check if ret means we should return RLM_SQL_RECONNECT */
153  ERROR("Could not execute statement \"%s\"", query_ctx->query_str);
154  query_ctx->rcode = RLM_SQL_ERROR;
156  return;
157  }
158 
159  query_ctx->rcode = RLM_SQL_OK;
160 finish:
161  query_ctx->status = SQL_QUERY_RETURNED;
163  if (request) unlang_interpret_mark_runnable(request);
164 }
165 
166 static sql_rcode_t sql_fields(char const **out[], fr_sql_query_t *query_ctx, UNUSED rlm_sql_config_t const *config)
167 {
168  rlm_sql_db2_conn_t *conn = talloc_get_type_abort(query_ctx->tconn->conn->h, rlm_sql_db2_conn_t);
169 
170  SQLSMALLINT fields, len, i;
171 
172  char const **names;
173  char field[128];
174 
175  SQLNumResultCols(conn->stmt, &fields);
176  if (fields == 0) return RLM_SQL_ERROR;
177 
178  MEM(names = talloc_array(query_ctx, char const *, fields));
179 
180  for (i = 0; i < fields; i++) {
181  char *p;
182 
183  switch (SQLColAttribute(conn->stmt, i, SQL_DESC_BASE_COLUMN_NAME,
184  field, sizeof(field), &len, NULL)) {
185  case SQL_INVALID_HANDLE:
186  case SQL_ERROR:
187  ERROR("Failed retrieving field name at index %i", i);
189  return RLM_SQL_ERROR;
190 
191  default:
192  break;
193  }
194 
195  MEM(p = talloc_array(names, char, (size_t)len + 1));
196  strlcpy(p, field, (size_t)len + 1);
197  names[i] = p;
198  }
199  *out = names;
200 
201  return RLM_SQL_OK;
202 }
203 
204 static unlang_action_t sql_fetch_row(rlm_rcode_t *p_result, UNUSED int *priority, UNUSED request_t *request, void *uctx)
205 {
206  fr_sql_query_t *query_ctx = talloc_get_type_abort(uctx, fr_sql_query_t);
207  int i;
208  SQLINTEGER len, slen;
209  SQLSMALLINT c;
210  rlm_sql_row_t row;
211  rlm_sql_db2_conn_t *conn = talloc_get_type_abort(query_ctx->tconn->conn->h, rlm_sql_db2_conn_t);
212 
213  TALLOC_FREE(query_ctx->row);
214 
215  SQLNumResultCols(conn->stmt, &c);
216 
217  /* advance cursor */
218  if (SQLFetch(conn->stmt) == SQL_NO_DATA_FOUND) {
219  query_ctx->rcode = RLM_SQL_NO_MORE_ROWS;
221  }
222 
223  MEM(row = (rlm_sql_row_t)talloc_zero_array(query_ctx, char *, c + 1));
224  for (i = 0; i < c; i++) {
225  /* get column length */
226  SQLColAttribute(conn->stmt, i + 1, SQL_DESC_DISPLAY_SIZE, NULL, 0, NULL, &len);
227 
228  MEM(row[i] = talloc_array(row, char, len + 1));
229 
230  /* get the actual column */
231  SQLGetData(conn->stmt, i + 1, SQL_C_CHAR, row[i], len + 1, &slen);
232  if (slen == SQL_NULL_DATA) row[i][0] = '\0';
233  }
234 
235  query_ctx->row = row;
236 
237  query_ctx->rcode = RLM_SQL_OK;
239 }
240 
242 {
243  rlm_sql_db2_conn_t *conn;
244 
245  conn = query_ctx->handle->conn;
246  TALLOC_FREE(query_ctx->row);
247  SQLFreeHandle(SQL_HANDLE_STMT, conn->stmt);
248 
249  return RLM_SQL_OK;
250 }
251 
252 /** Retrieves any errors associated with the query context
253  *
254  * @note Caller will free any memory allocated in ctx.
255  *
256  * @param ctx to allocate temporary error buffers in.
257  * @param out Array of sql_log_entrys to fill.
258  * @param outlen Length of out array.
259  * @param query_ctx Query context to retrieve error for.
260  * @param config rlm_sql config.
261  * @return number of errors written to the #sql_log_entry_t array.
262  */
263 static size_t sql_error(TALLOC_CTX *ctx, sql_log_entry_t out[], NDEBUG_UNUSED size_t outlen,
264  fr_sql_query_t *query_ctx)
265 {
266  char state[6];
267  char errbuff[1024];
268  SQLINTEGER err;
269  SQLSMALLINT rl;
270  rlm_sql_db2_conn_t *conn = talloc_get_type_abort(query_ctx->tconn->conn->h, rlm_sql_db2_conn_t);
271 
272  fr_assert(conn);
273  fr_assert(outlen > 0);
274 
275  errbuff[0] = '\0';
276  SQLGetDiagRec(SQL_HANDLE_STMT, conn->stmt, 1, (SQLCHAR *) state, &err,
277  (SQLCHAR *) errbuff, sizeof(errbuff), &rl);
278  if (errbuff[0] == '\0') return 0;
279 
280  out[0].type = L_ERR;
281  out[0].msg = talloc_typed_asprintf(ctx, "%s: %s", state, errbuff);
282 
283  return 1;
284 }
285 
287 {
288  return RLM_SQL_OK;
289 }
290 
292 {
293  SQLINTEGER c;
294  rlm_sql_db2_conn_t *conn = talloc_get_type_abort(query_ctx->tconn->conn->h, rlm_sql_db2_conn_t);
295 
296  SQLRowCount(conn->stmt, &c);
297 
298  return c;
299 }
300 
301 static void sql_request_fail(request_t *request, void *preq, UNUSED void *rctx,
303 {
304  fr_sql_query_t *query_ctx = talloc_get_type_abort(preq, fr_sql_query_t);
305 
306  query_ctx->treq = NULL;
307  if (query_ctx->rcode == RLM_SQL_OK) query_ctx->rcode = RLM_SQL_ERROR;
308  if (request) unlang_interpret_mark_runnable(request);
309 }
310 
311 /* Exported to rlm_sql */
314  .common = {
315  .magic = MODULE_MAGIC_INIT,
316  .name = "sql_db2",
317  },
319  .sql_query_resume = sql_query_resume,
320  .sql_select_query_resume = sql_query_resume,
321  .sql_affected_rows = sql_affected_rows,
322  .sql_fields = sql_fields,
323  .sql_fetch_row = sql_fetch_row,
324  .sql_free_result = sql_free_result,
325  .sql_error = sql_error,
326  .sql_finish_query = sql_finish_query,
327  .sql_finish_select_query = sql_finish_query,
328  .uses_trunks = true,
329  .trunk_io_funcs = {
330  .connection_alloc = sql_trunk_connection_alloc,
331  .request_mux = sql_trunk_request_mux,
332  .request_fail = sql_request_fail,
333  }
334 };
unlang_action_t
Returned by unlang_op_t calls, determine the next action of the interpreter.
Definition: action.h:35
#define UNCONST(_type, _ptr)
Remove const qualification from a pointer.
Definition: build.h:165
#define RCSID(id)
Definition: build.h:481
#define NDEBUG_UNUSED
Definition: build.h:324
#define UNUSED
Definition: build.h:313
connection_state_t
Definition: connection.h:45
@ CONNECTION_STATE_FAILED
Connection has failed.
Definition: connection.h:54
@ CONNECTION_STATE_CONNECTED
File descriptor is open (ready for writing).
Definition: connection.h:52
fr_dcursor_eval_t void const * uctx
Definition: dcursor.h:546
#define ERROR(fmt,...)
Definition: dhcpclient.c:41
static fr_slen_t err
Definition: dict.h:821
#define MODULE_MAGIC_INIT
Stop people using different module/library/server versions together.
Definition: dl_module.h:63
void unlang_interpret_mark_runnable(request_t *request)
Mark a request as resumable.
Definition: interpret.c:1359
#define ROPTIONAL(_l_request, _l_global, _fmt,...)
Use different logging functions depending on whether request is NULL or not.
Definition: log.h:528
talloc_free(reap)
Stores all information relating to an event list.
Definition: event.c:411
@ L_ERR
Error message.
Definition: log.h:56
unsigned int uint32_t
Definition: merged_model.c:33
static const conf_parser_t config[]
Definition: base.c:183
#define RDEBUG2(fmt,...)
Definition: radclient.h:54
#define DEBUG2(fmt,...)
Definition: radclient.h:43
#define RETURN_MODULE_OK
Definition: rcode.h:57
rlm_rcode_t
Return codes indicating the result of the module call.
Definition: rcode.h:40
Prototypes and functions for the SQL module.
fr_sql_query_status_t status
Status of the query.
Definition: rlm_sql.h:146
trunk_connection_t * tconn
Trunk connection this query is being run on.
Definition: rlm_sql.h:142
void * conn
Database specific connection handle.
Definition: rlm_sql.h:114
char const * query_str
Query string to run.
Definition: rlm_sql.h:144
request_t * request
Request this query relates to.
Definition: rlm_sql.h:139
sql_rcode_t
Action to take at end of an SQL query.
Definition: rlm_sql.h:44
@ RLM_SQL_ALT_QUERY
Key constraint violation, use an alternative query.
Definition: rlm_sql.h:49
@ RLM_SQL_ERROR
General connection/server error.
Definition: rlm_sql.h:46
@ RLM_SQL_OK
Success.
Definition: rlm_sql.h:47
@ RLM_SQL_NO_MORE_ROWS
No more rows available.
Definition: rlm_sql.h:50
rlm_sql_handle_t * handle
Connection handle this query is being run on.
Definition: rlm_sql.h:140
#define RLM_SQL_RCODE_FLAGS_ALT_QUERY
Can distinguish between other errors and those.
Definition: rlm_sql.h:172
char ** rlm_sql_row_t
Definition: rlm_sql.h:59
rlm_sql_row_t row
Row data from the last query.
Definition: rlm_sql.h:148
sql_rcode_t rcode
Result code.
Definition: rlm_sql.h:147
trunk_request_t * treq
Trunk request for this query.
Definition: rlm_sql.h:143
@ SQL_QUERY_RETURNED
Query has executed.
Definition: rlm_sql.h:131
Definition: rlm_sql.h:61
rlm_sql_driver_t rlm_sql_db2
Definition: rlm_sql_db2.c:313
static sql_rcode_t sql_fields(char const **out[], fr_sql_query_t *query_ctx, UNUSED rlm_sql_config_t const *config)
Definition: rlm_sql_db2.c:166
static size_t sql_error(TALLOC_CTX *ctx, sql_log_entry_t out[], NDEBUG_UNUSED size_t outlen, fr_sql_query_t *query_ctx)
Retrieves any errors associated with the query context.
Definition: rlm_sql_db2.c:263
static unlang_action_t sql_fetch_row(rlm_rcode_t *p_result, UNUSED int *priority, UNUSED request_t *request, void *uctx)
Definition: rlm_sql_db2.c:204
static void _sql_connection_close(UNUSED fr_event_list_t *el, void *h, UNUSED void *uctx)
Definition: rlm_sql_db2.c:50
static sql_rcode_t sql_finish_query(UNUSED fr_sql_query_t *query_ctx, UNUSED rlm_sql_config_t const *config)
Definition: rlm_sql_db2.c:286
SQLHANDLE dbc_handle
Definition: rlm_sql_db2.c:45
static sql_rcode_t sql_free_result(fr_sql_query_t *query_ctx, UNUSED rlm_sql_config_t const *config)
Definition: rlm_sql_db2.c:241
CC_NO_UBSAN(function)
Definition: rlm_sql_db2.c:66
static int sql_affected_rows(fr_sql_query_t *query_ctx, UNUSED rlm_sql_config_t const *config)
Definition: rlm_sql_db2.c:291
static void sql_request_fail(request_t *request, void *preq, UNUSED void *rctx, UNUSED trunk_request_state_t state, UNUSED void *uctx)
Definition: rlm_sql_db2.c:301
SQLHANDLE env_handle
Definition: rlm_sql_db2.c:46
Macros to reduce boilerplate in trunk SQL drivers.
#define SQL_QUERY_RESUME
Definition: rlm_sql_trunk.h:56
#define SQL_TRUNK_CONNECTION_ALLOC
Allocate an SQL trunk connection.
Definition: rlm_sql_trunk.h:35
fr_assert(0)
MEM(pair_append_request(&vp, attr_eap_aka_sim_identity) >=0)
size_t strlcpy(char *dst, char const *src, size_t siz)
Definition: strlcpy.c:34
module_t common
Common fields for all loadable modules.
Definition: rlm_sql.h:204
rlm_sql_config_t config
Definition: rlm_sql.h:238
char * talloc_typed_asprintf(TALLOC_CTX *ctx, char const *fmt,...)
Call talloc vasprintf, setting the type on the new chunk correctly.
Definition: talloc.c:492
#define talloc_get_type_abort_const
Definition: talloc.h:282
static const char * names[8]
Definition: time.c:617
static int64_t fr_time_delta_to_msec(fr_time_delta_t delta)
Definition: time.h:637
void trunk_request_signal_fail(trunk_request_t *treq)
Signal that a trunk request failed.
Definition: trunk.c:2120
int trunk_connection_pop_request(trunk_request_t **treq_out, trunk_connection_t *tconn)
Pop a request off a connection's pending queue.
Definition: trunk.c:3861
void trunk_request_signal_reapable(trunk_request_t *treq)
Signal that the request was written to a connection successfully, but no response is expected.
Definition: trunk.c:2065
Associates request queues with a connection.
Definition: trunk.c:131
Wraps a normal request.
Definition: trunk.c:97
trunk_request_state_t
Used for sanity checks and to simplify freeing.
Definition: trunk.h:161
static fr_event_list_t * el
static size_t char ** out
Definition: value.h:997