The FreeRADIUS server  $Id: 15bac2a4c627c01d1aa2047687b3418955ac7f00 $
rlm_sql_unixodbc.c
Go to the documentation of this file.
1 /*
2  * sql_unixodbc.c unixODBC rlm_sql driver
3  *
4  * This program is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation; either version 2 of the License, or
7  * (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software
16  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
17  *
18  * @copyright 2000,2006 The FreeRADIUS server project
19  * @copyright 2000 Dmitri Ageev (d_ageev@ortcc.ru)
20  */
21 RCSID("$Id: 30ed97d6a3aa220bd7203269ba3b959a41e38592 $")
23 
24 #define LOG_PREFIX "sql - unixodbc"
25 
26 #include <freeradius-devel/server/base.h>
27 #include <freeradius-devel/util/debug.h>
28 
29 #include <sqltypes.h>
30 #include "rlm_sql.h"
31 
32 typedef struct {
33  SQLHENV env;
34  SQLHDBC dbc;
35  SQLHSTMT stmt;
37  void *conn;
39 
41 #include <sql.h>
42 #include <sqlext.h>
43 
44 /* Forward declarations */
45 static int sql_check_error(long err_handle, rlm_sql_handle_t *handle, rlm_sql_config_t const *config);
47 static int sql_affected_rows(rlm_sql_handle_t *handle, rlm_sql_config_t const *config);
48 static int sql_num_fields(rlm_sql_handle_t *handle, rlm_sql_config_t const *config);
49 
51 {
52  DEBUG2("Socket destructor called, closing socket");
53 
54  if (conn->stmt) SQLFreeStmt(conn->stmt, SQL_DROP);
55 
56  if (conn->dbc) {
57  SQLDisconnect(conn->dbc);
58  SQLFreeConnect(conn->dbc);
59  }
60 
61  if (conn->env) SQLFreeEnv(conn->env);
62 
63  return 0;
64 }
65 
68 {
70  long err_handle;
72 
73  MEM(conn = handle->conn = talloc_zero(handle, rlm_sql_unixodbc_conn_t));
74  talloc_set_destructor(conn, _sql_socket_destructor);
75 
76  /* 1. Allocate environment handle and register version */
77  err_handle = SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE, &conn->env);
78  if (sql_check_error(err_handle, handle, config)) {
79  ERROR("Can't allocate environment handle");
80  return RLM_SQL_ERROR;
81  }
82 
83  err_handle = SQLSetEnvAttr(conn->env, SQL_ATTR_ODBC_VERSION, (void*)SQL_OV_ODBC3, 0);
84  if (sql_check_error(err_handle, handle, config)) {
85  ERROR("Can't register ODBC version");
86  return RLM_SQL_ERROR;
87  }
88 
89  /* 2. Allocate connection handle */
90  err_handle = SQLAllocHandle(SQL_HANDLE_DBC, conn->env, &conn->dbc);
91  if (sql_check_error(err_handle, handle, config)) {
92  ERROR("Can't allocate connection handle");
93  return RLM_SQL_ERROR;
94  }
95 
96  /* Set the connection timeout */
97  SQLSetConnectAttr(conn->dbc, SQL_ATTR_LOGIN_TIMEOUT, &timeout_ms, SQL_IS_UINTEGER);
98 
99  /* 3. Connect to the datasource */
100  err_handle = SQLConnect(conn->dbc,
101  UNCONST(SQLCHAR *, config->sql_server), strlen(config->sql_server),
102  UNCONST(SQLCHAR *, config->sql_login), strlen(config->sql_login),
103  UNCONST(SQLCHAR *, config->sql_password), strlen(config->sql_password));
104 
105  if (sql_check_error(err_handle, handle, config)) {
106  ERROR("Connection failed");
107  return RLM_SQL_ERROR;
108  }
109 
110  /* 4. Allocate the stmt */
111  err_handle = SQLAllocHandle(SQL_HANDLE_STMT, conn->dbc, &conn->stmt);
112  if (sql_check_error(err_handle, handle, config)) {
113  ERROR("Can't allocate the stmt");
114  return RLM_SQL_ERROR;
115  }
116 
117  return RLM_SQL_OK;
118 }
119 
120 static sql_rcode_t sql_query(rlm_sql_handle_t *handle, rlm_sql_config_t const *config, char const *query)
121 {
122  rlm_sql_unixodbc_conn_t *conn = handle->conn;
123  long err_handle;
124  int state;
125 
126  /* Executing query */
127  err_handle = SQLExecDirect(conn->stmt, UNCONST(SQLCHAR *, query), strlen(query));
128  if ((state = sql_check_error(err_handle, handle, config))) {
129  if(state == RLM_SQL_RECONNECT) {
130  DEBUG("rlm_sql will attempt to reconnect");
131  }
132  return state;
133  }
134  return 0;
135 }
136 
137 static sql_rcode_t sql_select_query(rlm_sql_handle_t *handle, rlm_sql_config_t const *config, char const *query)
138 {
139  rlm_sql_unixodbc_conn_t *conn = handle->conn;
140  SQLINTEGER i;
141  SQLLEN len;
142  int colcount;
143  int state;
144 
145  /* Only state = 0 means success */
146  if ((state = sql_query(handle, config, query))) {
147  return state;
148  }
149 
150  colcount = sql_num_fields(handle, config);
151  if (colcount < 0) {
152  return RLM_SQL_ERROR;
153  }
154 
155  /* Reserving memory for result */
156  conn->row = talloc_zero_array(conn, char *, colcount + 1); /* Space for pointers */
157 
158  for (i = 1; i <= colcount; i++) {
159  len = 0;
160  SQLColAttributes(conn->stmt, ((SQLUSMALLINT) i), SQL_DESC_LENGTH, NULL, 0, NULL, &len);
161  conn->row[i - 1] = talloc_array(conn->row, char, ++len);
162  SQLBindCol(conn->stmt, i, SQL_C_CHAR, (SQLCHAR *)conn->row[i - 1], len, NULL);
163  }
164 
165  return RLM_SQL_OK;
166 }
167 
169 {
170  rlm_sql_unixodbc_conn_t *conn = handle->conn;
171  long err_handle;
172  SQLSMALLINT num_fields = 0;
173 
174  err_handle = SQLNumResultCols(conn->stmt,&num_fields);
175  if (sql_check_error(err_handle, handle, config)) return -1;
176 
177  return num_fields;
178 }
179 
180 static sql_rcode_t sql_fields(char const **out[], rlm_sql_handle_t *handle, UNUSED rlm_sql_config_t const *config)
181 {
182  rlm_sql_unixodbc_conn_t *conn = handle->conn;
183 
184  SQLSMALLINT fields, len, i;
185 
186  char const **names;
187  char field[128];
188 
189  SQLNumResultCols(conn->stmt, &fields);
190  if (fields == 0) return RLM_SQL_ERROR;
191 
192  MEM(names = talloc_array(handle, char const *, fields));
193 
194  for (i = 0; i < fields; i++) {
195  char *p;
196 
197  switch (SQLColAttribute(conn->stmt, i, SQL_DESC_BASE_COLUMN_NAME,
198  field, sizeof(field), &len, NULL)) {
199  case SQL_INVALID_HANDLE:
200  case SQL_ERROR:
201  ERROR("Failed retrieving field name at index %i", i);
203  return RLM_SQL_ERROR;
204 
205  default:
206  break;
207  }
208 
209  MEM(p = talloc_array(names, char, (size_t)len + 1));
210  strlcpy(p, field, (size_t)len + 1);
211  names[i] = p;
212  }
213  *out = names;
214 
215  return RLM_SQL_OK;
216 }
217 
219 {
220  rlm_sql_unixodbc_conn_t *conn = handle->conn;
221  long err_handle;
222  int state;
223 
224  *out = NULL;
225 
226  handle->row = NULL;
227 
228  err_handle = SQLFetch(conn->stmt);
229  if (err_handle == SQL_NO_DATA_FOUND) return RLM_SQL_NO_MORE_ROWS;
230 
231  if ((state = sql_check_error(err_handle, handle, config))) return state;
232 
233  *out = handle->row = conn->row;
234 
235  return RLM_SQL_OK;
236 }
237 
239 {
240  rlm_sql_unixodbc_conn_t *conn = handle->conn;
241 
242  sql_free_result(handle, config);
243 
244  /*
245  * SQL_CLOSE - The cursor (if any) associated with the statement
246  * handle (StatementHandle) is closed and all pending results are
247  * discarded. The application can reopen the cursor by calling
248  * SQLExecute() with the same or different values in the
249  * application variables (if any) that are bound to StatementHandle.
250  * If no cursor has been associated with the statement handle,
251  * this option has no effect (no warning or error is generated).
252  *
253  * So, this call does NOT free the statement at all, it merely
254  * resets it for the next call. This is terrible terrible naming.
255  */
256  SQLFreeStmt(conn->stmt, SQL_CLOSE);
257 
258  return 0;
259 }
260 
262 {
263  rlm_sql_unixodbc_conn_t *conn = handle->conn;
264 
265  SQLFreeStmt(conn->stmt, SQL_CLOSE);
266 
267  return 0;
268 }
269 
271 {
272  rlm_sql_unixodbc_conn_t *conn = handle->conn;
273 
274  TALLOC_FREE(conn->row);
275 
276  return 0;
277 }
278 
279 /** Retrieves any errors associated with the connection handle
280  *
281  * @note Caller will free any memory allocated in ctx.
282  *
283  * @param ctx to allocate temporary error buffers in.
284  * @param out Array of #sql_log_entry_t to fill.
285  * @param outlen Length of out array.
286  * @param handle rlm_sql connection handle.
287  * @param config rlm_sql config.
288  * @return number of errors written to the #sql_log_entry_t array.
289  */
290 static size_t sql_error(TALLOC_CTX *ctx, sql_log_entry_t out[], NDEBUG_UNUSED size_t outlen,
292 {
293  rlm_sql_unixodbc_conn_t *conn = handle->conn;
294  SQLCHAR state[256];
295  SQLCHAR errbuff[256];
296  SQLINTEGER errnum = 0;
297  SQLSMALLINT length = 255;
298 
299  fr_assert(outlen > 0);
300 
301  errbuff[0] = state[0] = '\0';
302  SQLError(conn->env, conn->dbc, conn->stmt, state, &errnum,
303  errbuff, sizeof(errbuff), &length);
304  if (errnum == 0) return 0;
305 
306  out[0].type = L_ERR;
307  out[0].msg = talloc_typed_asprintf(ctx, "%s: %s", state, errbuff);
308 
309  return 1;
310 }
311 
312 /** Checks the error code to determine if the connection needs to be re-esttablished
313  *
314  * @param error_handle Return code from a failed unixodbc call.
315  * @param handle rlm_sql connection handle.
316  * @param config rlm_sql config.
317  * @return
318  * - #RLM_SQL_OK on success.
319  * - #RLM_SQL_RECONNECT if reconnect is needed.
320  * - #RLM_SQL_ERROR on error.
321  */
322 static sql_rcode_t sql_check_error(long error_handle, rlm_sql_handle_t *handle, UNUSED rlm_sql_config_t const *config)
323 {
324  SQLCHAR state[256];
325  SQLCHAR error[256];
326  SQLINTEGER errornum = 0;
327  SQLSMALLINT length = 255;
328  int res = -1;
329 
330  rlm_sql_unixodbc_conn_t *conn = handle->conn;
331 
332  if (SQL_SUCCEEDED(error_handle)) return 0; /* on success, just return 0 */
333 
334  error[0] = state[0] = '\0';
335 
336  SQLError(conn->env, conn->dbc, conn->stmt, state, &errornum,
337  error, sizeof(error), &length);
338 
339  if (state[0] == '0') {
340  switch (state[1]) {
341  /* SQLSTATE 01 class contains info and warning messages */
342  case '1':
343  INFO("%s %s", state, error);
344  FALL_THROUGH;
345  case '0': /* SQLSTATE 00 class means success */
346  res = RLM_SQL_OK;
347  break;
348 
349  /* SQLSTATE 08 class describes various connection errors */
350  case '8':
351  ERROR("SQL down %s %s", state, error);
352  res = RLM_SQL_RECONNECT;
353  break;
354 
355  /* any other SQLSTATE means error */
356  default:
357  ERROR("%s %s", state, error);
358  res = RLM_SQL_ERROR;
359  break;
360  }
361  } else {
362  ERROR("%s %s", state, error);
363  }
364 
365  return res;
366 }
367 
368 /*************************************************************************
369  *
370  * Function: sql_affected_rows
371  *
372  * Purpose: Return the number of rows affected by the query (update,
373  * or insert)
374  *
375  *************************************************************************/
377 {
378  rlm_sql_unixodbc_conn_t *conn = handle->conn;
379  long error_handle;
380  SQLLEN affected_rows;
381 
382  error_handle = SQLRowCount(conn->stmt, &affected_rows);
383  if (sql_check_error(error_handle, handle, config)) return -1;
384 
385  return affected_rows;
386 }
387 
388 
389 /* Exported to rlm_sql */
392  .common = {
393  .magic = MODULE_MAGIC_INIT,
394  .name = "sql_unixodbc"
395  },
396  .number = 6,
397  .sql_socket_init = sql_socket_init,
398  .sql_query = sql_query,
399  .sql_select_query = sql_select_query,
400  .sql_num_fields = sql_num_fields,
401  .sql_affected_rows = sql_affected_rows,
402  .sql_fields = sql_fields,
403  .sql_fetch_row = sql_fetch_row,
404  .sql_free_result = sql_free_result,
405  .sql_error = sql_error,
406  .sql_finish_query = sql_finish_query,
407  .sql_finish_select_query = sql_finish_select_query
408 };
#define UNCONST(_type, _ptr)
Remove const qualification from a pointer.
Definition: build.h:165
#define USES_APPLE_DEPRECATED_API
Definition: build.h:431
#define RCSID(id)
Definition: build.h:444
#define NDEBUG_UNUSED
Definition: build.h:324
#define FALL_THROUGH
clang 10 doesn't recognised the FALL-THROUGH comment anymore
Definition: build.h:320
#define UNUSED
Definition: build.h:313
#define ERROR(fmt,...)
Definition: dhcpclient.c:41
#define DEBUG(fmt,...)
Definition: dhcpclient.c:39
static fr_time_delta_t timeout
Definition: dhcpclient.c:54
#define MODULE_MAGIC_INIT
Stop people using different module/library/server versions together.
Definition: dl_module.h:65
talloc_free(reap)
@ 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:188
#define DEBUG2(fmt,...)
Definition: radclient.h:43
#define INFO(fmt,...)
Definition: radict.c:54
Prototypes and functions for the SQL module.
void * conn
Database specific connection handle.
Definition: rlm_sql.h:101
sql_rcode_t
Action to take at end of an SQL query.
Definition: rlm_sql.h:42
@ RLM_SQL_RECONNECT
Stale connection, should reconnect.
Definition: rlm_sql.h:46
@ RLM_SQL_ERROR
General connection/server error.
Definition: rlm_sql.h:44
@ RLM_SQL_OK
Success.
Definition: rlm_sql.h:45
@ RLM_SQL_NO_MORE_ROWS
No more rows available.
Definition: rlm_sql.h:48
char ** rlm_sql_row_t
Definition: rlm_sql.h:57
rlm_sql_row_t row
Row data from the last query.
Definition: rlm_sql.h:102
Definition: rlm_sql.h:59
static int affected_rows(PGresult *result)
Return the number of affected rows of the result as an int instead of the string that postgresql prov...
static sql_rcode_t sql_socket_init(rlm_sql_handle_t *handle, rlm_sql_config_t const *config, fr_time_delta_t timeout)
rlm_sql_driver_t rlm_sql_unixodbc
static sql_rcode_t sql_fetch_row(rlm_sql_row_t *out, rlm_sql_handle_t *handle, rlm_sql_config_t const *config)
static sql_rcode_t sql_free_result(rlm_sql_handle_t *handle, rlm_sql_config_t const *config)
static int _sql_socket_destructor(rlm_sql_unixodbc_conn_t *conn)
static sql_rcode_t sql_finish_query(rlm_sql_handle_t *handle, UNUSED rlm_sql_config_t const *config)
static int sql_num_fields(rlm_sql_handle_t *handle, rlm_sql_config_t const *config)
static size_t sql_error(TALLOC_CTX *ctx, sql_log_entry_t out[], NDEBUG_UNUSED size_t outlen, rlm_sql_handle_t *handle, UNUSED rlm_sql_config_t const *config)
Retrieves any errors associated with the connection handle.
static int sql_affected_rows(rlm_sql_handle_t *handle, rlm_sql_config_t const *config)
static USES_APPLE_DEPRECATED_API int sql_check_error(long err_handle, rlm_sql_handle_t *handle, rlm_sql_config_t const *config)
static sql_rcode_t sql_select_query(rlm_sql_handle_t *handle, rlm_sql_config_t const *config, char const *query)
static sql_rcode_t sql_finish_select_query(rlm_sql_handle_t *handle, rlm_sql_config_t const *config)
static sql_rcode_t sql_query(rlm_sql_handle_t *handle, rlm_sql_config_t const *config, char const *query)
static sql_rcode_t sql_fields(char const **out[], rlm_sql_handle_t *handle, UNUSED rlm_sql_config_t const *config)
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:148
char * talloc_typed_asprintf(TALLOC_CTX *ctx, char const *fmt,...)
Call talloc vasprintf, setting the type on the new chunk correctly.
Definition: talloc.c:380
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:635
A time delta, a difference in time measured in nanoseconds.
Definition: time.h:80
static size_t char ** out
Definition: value.h:984