3 * Main SQL module file. Most ICRADIUS code is located in sql.c
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
21 * Copyright 2000,2006 The FreeRADIUS server project
22 * Copyright 2000 Mike Machado <mike@innercite.com>
23 * Copyright 2000 Alan DeKok <aland@ox.org>
26 #include <freeradius-devel/ident.h>
29 #include <freeradius-devel/radiusd.h>
30 #include <freeradius-devel/modules.h>
31 #include <freeradius-devel/rad_assert.h>
37 static char *allowed_chars = NULL;
39 static const CONF_PARSER module_config[] = {
40 {"driver",PW_TYPE_STRING_PTR,
41 offsetof(SQL_CONFIG,sql_driver), NULL, "mysql"},
42 {"server",PW_TYPE_STRING_PTR,
43 offsetof(SQL_CONFIG,sql_server), NULL, "localhost"},
44 {"port",PW_TYPE_STRING_PTR,
45 offsetof(SQL_CONFIG,sql_port), NULL, ""},
46 {"login", PW_TYPE_STRING_PTR,
47 offsetof(SQL_CONFIG,sql_login), NULL, ""},
48 {"password", PW_TYPE_STRING_PTR,
49 offsetof(SQL_CONFIG,sql_password), NULL, ""},
50 {"radius_db", PW_TYPE_STRING_PTR,
51 offsetof(SQL_CONFIG,sql_db), NULL, "radius"},
52 {"read_groups", PW_TYPE_BOOLEAN,
53 offsetof(SQL_CONFIG,read_groups), NULL, "yes"},
54 {"sqltrace", PW_TYPE_BOOLEAN,
55 offsetof(SQL_CONFIG,sqltrace), NULL, "no"},
56 {"sqltracefile", PW_TYPE_STRING_PTR,
57 offsetof(SQL_CONFIG,tracefile), NULL, SQLTRACEFILE},
58 {"readclients", PW_TYPE_BOOLEAN,
59 offsetof(SQL_CONFIG,do_clients), NULL, "no"},
60 {"deletestalesessions", PW_TYPE_BOOLEAN,
61 offsetof(SQL_CONFIG,deletestalesessions), NULL, "yes"},
62 {"num_sql_socks", PW_TYPE_INTEGER,
63 offsetof(SQL_CONFIG,num_sql_socks), NULL, "5"},
64 {"lifetime", PW_TYPE_INTEGER,
65 offsetof(SQL_CONFIG,lifetime), NULL, "0"},
66 {"max_queries", PW_TYPE_INTEGER,
67 offsetof(SQL_CONFIG,max_queries), NULL, "0"},
68 {"sql_user_name", PW_TYPE_STRING_PTR,
69 offsetof(SQL_CONFIG,query_user), NULL, ""},
70 {"default_user_profile", PW_TYPE_STRING_PTR,
71 offsetof(SQL_CONFIG,default_profile), NULL, ""},
72 {"nas_query", PW_TYPE_STRING_PTR,
73 offsetof(SQL_CONFIG,nas_query), NULL, "SELECT id,nasname,shortname,type,secret FROM nas"},
74 {"authorize_check_query", PW_TYPE_STRING_PTR,
75 offsetof(SQL_CONFIG,authorize_check_query), NULL, ""},
76 {"authorize_reply_query", PW_TYPE_STRING_PTR,
77 offsetof(SQL_CONFIG,authorize_reply_query), NULL, NULL},
78 {"authorize_group_check_query", PW_TYPE_STRING_PTR,
79 offsetof(SQL_CONFIG,authorize_group_check_query), NULL, ""},
80 {"authorize_group_reply_query", PW_TYPE_STRING_PTR,
81 offsetof(SQL_CONFIG,authorize_group_reply_query), NULL, ""},
82 {"accounting_onoff_query", PW_TYPE_STRING_PTR,
83 offsetof(SQL_CONFIG,accounting_onoff_query), NULL, ""},
84 {"accounting_update_query", PW_TYPE_STRING_PTR,
85 offsetof(SQL_CONFIG,accounting_update_query), NULL, ""},
86 {"accounting_update_query_alt", PW_TYPE_STRING_PTR,
87 offsetof(SQL_CONFIG,accounting_update_query_alt), NULL, ""},
88 {"accounting_start_query", PW_TYPE_STRING_PTR,
89 offsetof(SQL_CONFIG,accounting_start_query), NULL, ""},
90 {"accounting_start_query_alt", PW_TYPE_STRING_PTR,
91 offsetof(SQL_CONFIG,accounting_start_query_alt), NULL, ""},
92 {"accounting_stop_query", PW_TYPE_STRING_PTR,
93 offsetof(SQL_CONFIG,accounting_stop_query), NULL, ""},
94 {"accounting_stop_query_alt", PW_TYPE_STRING_PTR,
95 offsetof(SQL_CONFIG,accounting_stop_query_alt), NULL, ""},
96 {"group_membership_query", PW_TYPE_STRING_PTR,
97 offsetof(SQL_CONFIG,groupmemb_query), NULL, NULL},
98 {"connect_failure_retry_delay", PW_TYPE_INTEGER,
99 offsetof(SQL_CONFIG,connect_failure_retry_delay), NULL, "60"},
100 {"simul_count_query", PW_TYPE_STRING_PTR,
101 offsetof(SQL_CONFIG,simul_count_query), NULL, ""},
102 {"simul_verify_query", PW_TYPE_STRING_PTR,
103 offsetof(SQL_CONFIG,simul_verify_query), NULL, ""},
104 {"postauth_query", PW_TYPE_STRING_PTR,
105 offsetof(SQL_CONFIG,postauth_query), NULL, ""},
106 {"safe-characters", PW_TYPE_STRING_PTR,
107 offsetof(SQL_CONFIG,allowed_chars), NULL,
108 "@abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789.-_: /"},
111 * This only works for a few drivers.
113 {"query_timeout", PW_TYPE_INTEGER,
114 offsetof(SQL_CONFIG,query_timeout), NULL, NULL},
116 {NULL, -1, 0, NULL, NULL}
120 * Fall-Through checking function from rlm_files.c
122 static int fallthrough(VALUE_PAIR *vp)
125 tmp = pairfind(vp, PW_FALL_THROUGH, 0);
127 return tmp ? tmp->vp_integer : 0;
135 static int generate_sql_clients(SQL_INST *inst);
136 static size_t sql_escape_func(char *out, size_t outlen, const char *in);
139 * sql xlat function. Right now only SELECTs are supported. Only
140 * the first element of the SELECT result will be used.
142 * For other statements (insert, update, delete, etc.), the
143 * number of affected rows will be returned.
145 static int sql_xlat(void *instance, REQUEST *request,
146 char *fmt, char *out, size_t freespace,
147 UNUSED RADIUS_ESCAPE_STRING func)
151 SQL_INST *inst = instance;
152 char querystr[MAX_QUERY_LEN];
153 char sqlusername[MAX_STRING_LEN];
159 * Add SQL-User-Name attribute just in case it is needed
160 * We could search the string fmt for SQL-User-Name to see if this is
163 sql_set_user(inst, request, sqlusername, NULL);
165 * Do an xlat on the provided string (nice recursive operation).
167 if (!radius_xlat(querystr, sizeof(querystr), fmt, request, sql_escape_func)) {
168 radlog(L_ERR, "rlm_sql (%s): xlat failed.",
169 inst->config->xlat_name);
173 query_log(request, inst,querystr);
174 sqlsocket = sql_get_socket(inst);
175 if (sqlsocket == NULL)
179 * If the query starts with any of the following prefixes,
180 * then return the number of rows affected
182 if ((strncasecmp(querystr, "insert", 6) == 0) ||
183 (strncasecmp(querystr, "update", 6) == 0) ||
184 (strncasecmp(querystr, "delete", 6) == 0)) {
186 char buffer[21]; /* 64bit max is 20 decimal chars + null byte */
188 if (rlm_sql_query(sqlsocket,inst,querystr)) {
189 radlog(L_ERR, "rlm_sql (%s): database query error, %s: %s",
190 inst->config->xlat_name, querystr,
191 (inst->module->sql_error)(sqlsocket,
193 sql_release_socket(inst,sqlsocket);
197 numaffected = (inst->module->sql_affected_rows)(sqlsocket,
199 if (numaffected < 1) {
200 RDEBUG("rlm_sql (%s): SQL query affected no rows",
201 inst->config->xlat_name);
205 * Don't chop the returned number if freespace is
206 * too small. This hack is necessary because
207 * some implementations of snprintf return the
208 * size of the written data, and others return
209 * the size of the data they *would* have written
210 * if the output buffer was large enough.
212 snprintf(buffer, sizeof(buffer), "%d", numaffected);
213 ret = strlen(buffer);
214 if (ret >= freespace){
215 RDEBUG("rlm_sql (%s): Can't write result, insufficient string space",
216 inst->config->xlat_name);
217 (inst->module->sql_finish_query)(sqlsocket,
219 sql_release_socket(inst,sqlsocket);
223 memcpy(out, buffer, ret + 1); /* we did bounds checking above */
225 (inst->module->sql_finish_query)(sqlsocket, inst->config);
226 sql_release_socket(inst,sqlsocket);
228 } /* else it's a SELECT statement */
230 if (rlm_sql_select_query(sqlsocket,inst,querystr)){
231 radlog(L_ERR, "rlm_sql (%s): database query error, %s: %s",
232 inst->config->xlat_name,querystr,
233 (inst->module->sql_error)(sqlsocket, inst->config));
234 sql_release_socket(inst,sqlsocket);
238 ret = rlm_sql_fetch_row(sqlsocket, inst);
241 RDEBUG("SQL query did not succeed");
242 (inst->module->sql_finish_select_query)(sqlsocket, inst->config);
243 sql_release_socket(inst,sqlsocket);
247 row = sqlsocket->row;
249 RDEBUG("SQL query did not return any results");
250 (inst->module->sql_finish_select_query)(sqlsocket, inst->config);
251 sql_release_socket(inst,sqlsocket);
256 RDEBUG("row[0] returned NULL");
257 (inst->module->sql_finish_select_query)(sqlsocket, inst->config);
258 sql_release_socket(inst,sqlsocket);
261 ret = strlen(row[0]);
262 if (ret >= freespace){
263 RDEBUG("Insufficient string space");
264 (inst->module->sql_finish_select_query)(sqlsocket, inst->config);
265 sql_release_socket(inst,sqlsocket);
269 strlcpy(out,row[0],freespace);
271 RDEBUG("sql_xlat finished");
273 (inst->module->sql_finish_select_query)(sqlsocket, inst->config);
274 sql_release_socket(inst,sqlsocket);
278 static int generate_sql_clients(SQL_INST *inst)
282 char querystr[MAX_QUERY_LEN];
284 char *prefix_ptr = NULL;
288 DEBUG("rlm_sql (%s): Processing generate_sql_clients",
289 inst->config->xlat_name);
291 /* NAS query isn't xlat'ed */
292 strlcpy(querystr, inst->config->nas_query, sizeof(querystr));
293 DEBUG("rlm_sql (%s) in generate_sql_clients: query is %s",
294 inst->config->xlat_name, querystr);
296 sqlsocket = sql_get_socket(inst);
297 if (sqlsocket == NULL)
299 if (rlm_sql_select_query(sqlsocket,inst,querystr)){
300 radlog(L_ERR, "rlm_sql (%s): database query error, %s: %s",
301 inst->config->xlat_name,querystr,
302 (inst->module->sql_error)(sqlsocket, inst->config));
303 sql_release_socket(inst,sqlsocket);
307 while(rlm_sql_fetch_row(sqlsocket, inst) == 0) {
309 row = sqlsocket->row;
313 * The return data for each row MUST be in the following order:
315 * 0. Row ID (currently unused)
316 * 1. Name (or IP address)
320 * 5. Virtual Server (optional)
323 radlog(L_ERR, "rlm_sql (%s): No row id found on pass %d",inst->config->xlat_name,i);
327 radlog(L_ERR, "rlm_sql (%s): No nasname found for row %s",inst->config->xlat_name,row[0]);
331 radlog(L_ERR, "rlm_sql (%s): No short name found for row %s",inst->config->xlat_name,row[0]);
335 radlog(L_ERR, "rlm_sql (%s): No secret found for row %s",inst->config->xlat_name,row[0]);
339 DEBUG("rlm_sql (%s): Read entry nasname=%s,shortname=%s,secret=%s",inst->config->xlat_name,
340 row[1],row[2],row[4]);
342 c = rad_malloc(sizeof(*c));
343 memset(c, 0, sizeof(*c));
345 #ifdef WITH_DYNAMIC_CLIENTS
353 prefix_ptr = strchr(row[1], '/');
355 c->prefix = atoi(prefix_ptr + 1);
356 if ((c->prefix < 0) || (c->prefix > 128)) {
357 radlog(L_ERR, "rlm_sql (%s): Invalid Prefix value '%s' for IP.",
358 inst->config->xlat_name, prefix_ptr + 1);
362 /* Replace '/' with '\0' */
367 * Always get the numeric representation of IP
369 if (ip_hton(row[1], AF_UNSPEC, &c->ipaddr) < 0) {
370 radlog(L_CONS|L_ERR, "rlm_sql (%s): Failed to look up hostname %s: %s",
371 inst->config->xlat_name,
372 row[1], fr_strerror());
377 ip_ntoh(&c->ipaddr, buffer, sizeof(buffer));
378 c->longname = strdup(buffer);
381 if (c->prefix < 0) switch (c->ipaddr.af) {
393 * Other values (secret, shortname, nastype, virtual_server)
395 c->secret = strdup(row[4]);
396 c->shortname = strdup(row[2]);
398 c->nastype = strdup(row[3]);
400 numf = (inst->module->sql_num_fields)(sqlsocket, inst->config);
401 if ((numf > 5) && (row[5] != NULL) && *row[5]) c->server = strdup(row[5]);
403 DEBUG("rlm_sql (%s): Adding client %s (%s, server=%s) to clients list",
404 inst->config->xlat_name,
405 c->longname,c->shortname, c->server ? c->server : "<none>");
406 if (!client_add(NULL, c)) {
407 DEBUG("rlm_sql (%s): Failed to add client %s (%s) to clients list. Maybe there's a duplicate?",
408 inst->config->xlat_name,
409 c->longname,c->shortname);
414 (inst->module->sql_finish_select_query)(sqlsocket, inst->config);
415 sql_release_socket(inst, sqlsocket);
422 * Translate the SQL queries.
424 static size_t sql_escape_func(char *out, size_t outlen, const char *in)
430 * Non-printable characters get replaced with their
431 * mime-encoded equivalents.
434 strchr(allowed_chars, *in) == NULL) {
436 * Only 3 or less bytes available.
442 snprintf(out, outlen, "=%02X", (unsigned char) in[0]);
451 * Only one byte left.
471 * Set the SQL user name.
473 * We don't call the escape function here. The resulting string
474 * will be escaped later in the queries xlat so we don't need to
475 * escape it twice. (it will make things wrong if we have an
476 * escape candidate character in the username)
478 int sql_set_user(SQL_INST *inst, REQUEST *request, char *sqlusername, const char *username)
481 char tmpuser[MAX_STRING_LEN];
484 sqlusername[0]= '\0';
486 /* Remove any user attr we added previously */
487 pairdelete(&request->packet->vps, PW_SQL_USER_NAME, 0);
489 if (username != NULL) {
490 strlcpy(tmpuser, username, sizeof(tmpuser));
491 } else if (strlen(inst->config->query_user)) {
492 radius_xlat(tmpuser, sizeof(tmpuser), inst->config->query_user, request, NULL);
497 strlcpy(sqlusername, tmpuser, MAX_STRING_LEN);
498 RDEBUG2("sql_set_user escaped user --> '%s'", sqlusername);
499 vp = radius_pairmake(request, &request->packet->vps,
500 "SQL-User-Name", NULL, 0);
502 radlog(L_ERR, "%s", fr_strerror());
506 strlcpy(vp->vp_strvalue, tmpuser, sizeof(vp->vp_strvalue));
507 vp->length = strlen(vp->vp_strvalue);
514 static void sql_grouplist_free (SQL_GROUPLIST **group_list)
520 *group_list = (*group_list)->next;
526 static int sql_get_grouplist (SQL_INST *inst, SQLSOCK *sqlsocket, REQUEST *request, SQL_GROUPLIST **group_list)
528 char querystr[MAX_QUERY_LEN];
531 SQL_GROUPLIST *group_list_tmp;
533 /* NOTE: sql_set_user should have been run before calling this function */
535 group_list_tmp = *group_list = NULL;
537 if (!inst->config->groupmemb_query ||
538 (inst->config->groupmemb_query[0] == 0))
541 if (!radius_xlat(querystr, sizeof(querystr), inst->config->groupmemb_query, request, sql_escape_func)) {
542 radlog_request(L_ERR, 0, request, "xlat \"%s\" failed.",
543 inst->config->groupmemb_query);
547 if (rlm_sql_select_query(sqlsocket, inst, querystr) < 0) {
548 radlog_request(L_ERR, 0, request,
549 "database query error, %s: %s",
551 (inst->module->sql_error)(sqlsocket,inst->config));
554 while (rlm_sql_fetch_row(sqlsocket, inst) == 0) {
555 row = sqlsocket->row;
559 RDEBUG("row[0] returned NULL");
560 (inst->module->sql_finish_select_query)(sqlsocket, inst->config);
561 sql_grouplist_free(group_list);
564 if (*group_list == NULL) {
565 *group_list = rad_malloc(sizeof(SQL_GROUPLIST));
566 group_list_tmp = *group_list;
568 rad_assert(group_list_tmp != NULL);
569 group_list_tmp->next = rad_malloc(sizeof(SQL_GROUPLIST));
570 group_list_tmp = group_list_tmp->next;
572 group_list_tmp->next = NULL;
573 strlcpy(group_list_tmp->groupname, row[0], MAX_STRING_LEN);
576 (inst->module->sql_finish_select_query)(sqlsocket, inst->config);
583 * sql groupcmp function. That way we can do group comparisons (in the users file for example)
584 * with the group memberships reciding in sql
585 * The group membership query should only return one element which is the username. The returned
586 * username will then be checked with the passed check string.
589 static int sql_groupcmp(void *instance, REQUEST *request, VALUE_PAIR *request_vp, VALUE_PAIR *check,
590 VALUE_PAIR *check_pairs, VALUE_PAIR **reply_pairs)
593 SQL_INST *inst = instance;
594 char sqlusername[MAX_STRING_LEN];
595 SQL_GROUPLIST *group_list, *group_list_tmp;
597 check_pairs = check_pairs;
598 reply_pairs = reply_pairs;
599 request_vp = request_vp;
601 RDEBUG("sql_groupcmp");
602 if (!check || !check->vp_strvalue || !check->length){
603 RDEBUG("sql_groupcmp: Illegal group name");
607 RDEBUG("sql_groupcmp: NULL request");
611 * Set, escape, and check the user attr here
613 if (sql_set_user(inst, request, sqlusername, NULL) < 0)
617 * Get a socket for this lookup
619 sqlsocket = sql_get_socket(inst);
620 if (sqlsocket == NULL) {
621 /* Remove the username we (maybe) added above */
622 pairdelete(&request->packet->vps, PW_SQL_USER_NAME, 0);
627 * Get the list of groups this user is a member of
629 if (sql_get_grouplist(inst, sqlsocket, request, &group_list) < 0) {
630 radlog_request(L_ERR, 0, request,
631 "Error getting group membership");
632 /* Remove the username we (maybe) added above */
633 pairdelete(&request->packet->vps, PW_SQL_USER_NAME, 0);
634 sql_release_socket(inst, sqlsocket);
638 for (group_list_tmp = group_list; group_list_tmp != NULL; group_list_tmp = group_list_tmp->next) {
639 if (strcmp(group_list_tmp->groupname, check->vp_strvalue) == 0){
640 RDEBUG("sql_groupcmp finished: User is a member of group %s",
642 /* Free the grouplist */
643 sql_grouplist_free(&group_list);
644 /* Remove the username we (maybe) added above */
645 pairdelete(&request->packet->vps, PW_SQL_USER_NAME, 0);
646 sql_release_socket(inst, sqlsocket);
651 /* Free the grouplist */
652 sql_grouplist_free(&group_list);
653 /* Remove the username we (maybe) added above */
654 pairdelete(&request->packet->vps, PW_SQL_USER_NAME, 0);
655 sql_release_socket(inst,sqlsocket);
657 RDEBUG("sql_groupcmp finished: User is NOT a member of group %s",
665 static int rlm_sql_process_groups(SQL_INST *inst, REQUEST *request, SQLSOCK *sqlsocket, int *dofallthrough)
667 VALUE_PAIR *check_tmp = NULL;
668 VALUE_PAIR *reply_tmp = NULL;
669 SQL_GROUPLIST *group_list, *group_list_tmp;
670 VALUE_PAIR *sql_group = NULL;
671 char querystr[MAX_QUERY_LEN];
676 * Get the list of groups this user is a member of
678 if (sql_get_grouplist(inst, sqlsocket, request, &group_list) < 0) {
679 radlog_request(L_ERR, 0, request, "Error retrieving group list");
683 for (group_list_tmp = group_list; group_list_tmp != NULL && *dofallthrough != 0; group_list_tmp = group_list_tmp->next) {
685 * Add the Sql-Group attribute to the request list so we know
686 * which group we're retrieving attributes for
688 sql_group = pairmake("Sql-Group", group_list_tmp->groupname, T_OP_EQ);
690 radlog_request(L_ERR, 0, request,
691 "Error creating Sql-Group attribute");
694 pairadd(&request->packet->vps, sql_group);
695 if (!radius_xlat(querystr, sizeof(querystr), inst->config->authorize_group_check_query, request, sql_escape_func)) {
696 radlog_request(L_ERR, 0, request,
697 "Error generating query; rejecting user");
698 /* Remove the grouup we added above */
699 pairdelete(&request->packet->vps, PW_SQL_GROUP, 0);
702 rows = sql_getvpdata(inst, sqlsocket, &check_tmp, querystr);
704 radlog_request(L_ERR, 0, request, "Error retrieving check pairs for group %s",
705 group_list_tmp->groupname);
706 /* Remove the grouup we added above */
707 pairdelete(&request->packet->vps, PW_SQL_GROUP, 0);
708 pairfree(&check_tmp);
710 } else if (rows > 0) {
712 * Only do this if *some* check pairs were returned
714 if (paircompare(request, request->packet->vps, check_tmp, &request->reply->vps) == 0) {
716 RDEBUG2("User found in group %s",
717 group_list_tmp->groupname);
719 * Now get the reply pairs since the paircompare matched
721 if (!radius_xlat(querystr, sizeof(querystr), inst->config->authorize_group_reply_query, request, sql_escape_func)) {
722 radlog_request(L_ERR, 0, request, "Error generating query; rejecting user");
723 /* Remove the grouup we added above */
724 pairdelete(&request->packet->vps, PW_SQL_GROUP, 0);
725 pairfree(&check_tmp);
728 if (sql_getvpdata(inst, sqlsocket, &reply_tmp, querystr) < 0) {
729 radlog_request(L_ERR, 0, request, "Error retrieving reply pairs for group %s",
730 group_list_tmp->groupname);
731 /* Remove the grouup we added above */
732 pairdelete(&request->packet->vps, PW_SQL_GROUP, 0);
733 pairfree(&check_tmp);
734 pairfree(&reply_tmp);
737 *dofallthrough = fallthrough(reply_tmp);
738 pairxlatmove(request, &request->reply->vps, &reply_tmp);
739 pairxlatmove(request, &request->config_items, &check_tmp);
743 * rows == 0. This is like having the username on a line
744 * in the user's file with no check vp's. As such, we treat
745 * it as found and add the reply attributes, so that we
746 * match expected behavior
749 RDEBUG2("User found in group %s",
750 group_list_tmp->groupname);
752 * Now get the reply pairs since the paircompare matched
754 if (!radius_xlat(querystr, sizeof(querystr), inst->config->authorize_group_reply_query, request, sql_escape_func)) {
755 radlog_request(L_ERR, 0, request, "Error generating query; rejecting user");
756 /* Remove the grouup we added above */
757 pairdelete(&request->packet->vps, PW_SQL_GROUP, 0);
758 pairfree(&check_tmp);
761 if (sql_getvpdata(inst, sqlsocket, &reply_tmp, querystr) < 0) {
762 radlog_request(L_ERR, 0, request, "Error retrieving reply pairs for group %s",
763 group_list_tmp->groupname);
764 /* Remove the grouup we added above */
765 pairdelete(&request->packet->vps, PW_SQL_GROUP, 0);
766 pairfree(&check_tmp);
767 pairfree(&reply_tmp);
770 *dofallthrough = fallthrough(reply_tmp);
771 pairxlatmove(request, &request->reply->vps, &reply_tmp);
772 pairxlatmove(request, &request->config_items, &check_tmp);
776 * Delete the Sql-Group we added above
777 * And clear out the pairlists
779 pairdelete(&request->packet->vps, PW_SQL_GROUP, 0);
780 pairfree(&check_tmp);
781 pairfree(&reply_tmp);
784 sql_grouplist_free(&group_list);
789 static int rlm_sql_detach(void *instance)
791 SQL_INST *inst = instance;
793 paircompare_unregister(PW_SQL_GROUP, sql_groupcmp);
802 if (inst->config->xlat_name) {
803 xlat_unregister(inst->config->xlat_name,(RAD_XLAT_FUNC)sql_xlat);
804 free(inst->config->xlat_name);
808 * Free up dynamically allocated string pointers.
810 for (i = 0; module_config[i].name != NULL; i++) {
812 if (module_config[i].type != PW_TYPE_STRING_PTR) {
817 * Treat 'config' as an opaque array of bytes,
818 * and take the offset into it. There's a
819 * (char*) pointer at that offset, and we want
822 p = (char **) (((char *)inst->config) + module_config[i].offset);
823 if (!*p) { /* nothing allocated */
830 * Catch multiple instances of the module.
832 if (allowed_chars == inst->config->allowed_chars) {
833 allowed_chars = NULL;
842 * FIXME: Call the modules 'destroy' function?
844 lt_dlclose(inst->handle); /* ignore any errors */
851 static int rlm_sql_instantiate(CONF_SECTION * conf, void **instance)
854 const char *xlat_name;
856 inst = rad_malloc(sizeof(SQL_INST));
857 memset(inst, 0, sizeof(SQL_INST));
859 inst->config = rad_malloc(sizeof(SQL_CONFIG));
860 memset(inst->config, 0, sizeof(SQL_CONFIG));
863 * Export these methods, too. This avoids RTDL_GLOBAL.
865 inst->sql_set_user = sql_set_user;
866 inst->sql_get_socket = sql_get_socket;
867 inst->sql_release_socket = sql_release_socket;
868 inst->sql_escape_func = sql_escape_func;
869 inst->sql_query = rlm_sql_query;
870 inst->sql_select_query = rlm_sql_select_query;
871 inst->sql_fetch_row = rlm_sql_fetch_row;
874 * If the configuration parameters can't be parsed, then
877 if (cf_section_parse(conf, inst->config, module_config) < 0) {
878 rlm_sql_detach(inst);
882 xlat_name = cf_section_name2(conf);
883 if (xlat_name == NULL)
884 xlat_name = cf_section_name1(conf);
886 inst->config->xlat_name = strdup(xlat_name);
887 xlat_register(xlat_name, (RAD_XLAT_FUNC)sql_xlat, inst);
890 if (inst->config->num_sql_socks > MAX_SQL_SOCKS) {
891 radlog(L_ERR, "rlm_sql (%s): sql_instantiate: number of sqlsockets cannot exceed MAX_SQL_SOCKS, %d",
892 inst->config->xlat_name, MAX_SQL_SOCKS);
893 rlm_sql_detach(inst);
898 * Sanity check for crazy people.
900 if (strncmp(inst->config->sql_driver, "rlm_sql_", 8) != 0) {
901 radlog(L_ERR, "\"%s\" is NOT an SQL driver!",
902 inst->config->sql_driver);
903 rlm_sql_detach(inst);
907 inst->handle = lt_dlopenext(inst->config->sql_driver);
908 if (inst->handle == NULL) {
909 radlog(L_ERR, "Could not link driver %s: %s",
910 inst->config->sql_driver,
912 radlog(L_ERR, "Make sure it (and all its dependent libraries!) are in the search path of your system's ld.");
913 rlm_sql_detach(inst);
917 inst->module = (rlm_sql_module_t *) lt_dlsym(inst->handle, inst->config->sql_driver);
919 radlog(L_ERR, "Could not link symbol %s: %s",
920 inst->config->sql_driver,
922 rlm_sql_detach(inst);
926 radlog(L_INFO, "rlm_sql (%s): Driver %s (module %s) loaded and linked",
927 inst->config->xlat_name, inst->config->sql_driver,
929 radlog(L_INFO, "rlm_sql (%s): Attempting to connect to %s@%s:%s/%s",
930 inst->config->xlat_name, inst->config->sql_login,
931 inst->config->sql_server, inst->config->sql_port,
932 inst->config->sql_db);
934 if (sql_init_socketpool(inst) < 0) {
935 rlm_sql_detach(inst);
939 paircompare_register(PW_SQL_GROUP, PW_USER_NAME, sql_groupcmp, inst);
941 if (inst->config->do_clients){
942 if (generate_sql_clients(inst) == -1){
943 radlog(L_ERR, "Failed to load clients from SQL.");
944 rlm_sql_detach(inst);
948 allowed_chars = inst->config->allowed_chars;
952 return RLM_MODULE_OK;
956 static int rlm_sql_authorize(void *instance, REQUEST * request)
958 VALUE_PAIR *check_tmp = NULL;
959 VALUE_PAIR *reply_tmp = NULL;
960 VALUE_PAIR *user_profile = NULL;
962 int dofallthrough = 1;
965 SQL_INST *inst = instance;
966 char querystr[MAX_QUERY_LEN];
967 char sqlusername[MAX_STRING_LEN];
969 * the profile username is used as the sqlusername during
970 * profile checking so that we don't overwrite the orignal
973 char profileusername[MAX_STRING_LEN];
976 * Set, escape, and check the user attr here
978 if (sql_set_user(inst, request, sqlusername, NULL) < 0)
979 return RLM_MODULE_FAIL;
985 sqlsocket = sql_get_socket(inst);
986 if (sqlsocket == NULL) {
987 /* Remove the username we (maybe) added above */
988 pairdelete(&request->packet->vps, PW_SQL_USER_NAME, 0);
989 return RLM_MODULE_FAIL;
994 * After this point, ALL 'return's MUST release the SQL socket!
998 * Alright, start by getting the specific entry for the user
1000 if (!radius_xlat(querystr, sizeof(querystr), inst->config->authorize_check_query, request, sql_escape_func)) {
1001 radlog_request(L_ERR, 0, request, "Error generating query; rejecting user");
1002 sql_release_socket(inst, sqlsocket);
1003 /* Remove the username we (maybe) added above */
1004 pairdelete(&request->packet->vps, PW_SQL_USER_NAME, 0);
1005 return RLM_MODULE_FAIL;
1007 rows = sql_getvpdata(inst, sqlsocket, &check_tmp, querystr);
1009 radlog_request(L_ERR, 0, request, "SQL query error; rejecting user");
1010 sql_release_socket(inst, sqlsocket);
1011 /* Remove the username we (maybe) added above */
1012 pairdelete(&request->packet->vps, PW_SQL_USER_NAME, 0);
1013 pairfree(&check_tmp);
1014 return RLM_MODULE_FAIL;
1015 } else if (rows > 0) {
1017 * Only do this if *some* check pairs were returned
1019 if (paircompare(request, request->packet->vps, check_tmp, &request->reply->vps) == 0) {
1021 RDEBUG2("User found in radcheck table");
1023 if (inst->config->authorize_reply_query &&
1024 *inst->config->authorize_reply_query) {
1027 * Now get the reply pairs since the paircompare matched
1029 if (!radius_xlat(querystr, sizeof(querystr), inst->config->authorize_reply_query, request, sql_escape_func)) {
1030 radlog_request(L_ERR, 0, request, "Error generating query; rejecting user");
1031 sql_release_socket(inst, sqlsocket);
1032 /* Remove the username we (maybe) added above */
1033 pairdelete(&request->packet->vps, PW_SQL_USER_NAME, 0);
1034 pairfree(&check_tmp);
1035 return RLM_MODULE_FAIL;
1037 if (sql_getvpdata(inst, sqlsocket, &reply_tmp, querystr) < 0) {
1038 radlog_request(L_ERR, 0, request, "SQL query error; rejecting user");
1039 sql_release_socket(inst, sqlsocket);
1040 /* Remove the username we (maybe) added above */
1041 pairdelete(&request->packet->vps, PW_SQL_USER_NAME, 0);
1042 pairfree(&check_tmp);
1043 pairfree(&reply_tmp);
1044 return RLM_MODULE_FAIL;
1047 if (!inst->config->read_groups)
1048 dofallthrough = fallthrough(reply_tmp);
1049 pairxlatmove(request, &request->reply->vps, &reply_tmp);
1051 pairxlatmove(request, &request->config_items, &check_tmp);
1056 * Clear out the pairlists
1058 pairfree(&check_tmp);
1059 pairfree(&reply_tmp);
1062 * dofallthrough is set to 1 by default so that if the user information
1063 * is not found, we will still process groups. If the user information,
1064 * however, *is* found, Fall-Through must be set in order to process
1065 * the groups as well
1067 if (dofallthrough) {
1068 rows = rlm_sql_process_groups(inst, request, sqlsocket, &dofallthrough);
1070 radlog_request(L_ERR, 0, request, "Error processing groups; rejecting user");
1071 sql_release_socket(inst, sqlsocket);
1072 /* Remove the username we (maybe) added above */
1073 pairdelete(&request->packet->vps, PW_SQL_USER_NAME, 0);
1074 return RLM_MODULE_FAIL;
1075 } else if (rows > 0) {
1081 * repeat the above process with the default profile or User-Profile
1083 if (dofallthrough) {
1084 int profile_found = 0;
1086 * Check for a default_profile or for a User-Profile.
1088 user_profile = pairfind(request->config_items, PW_USER_PROFILE, 0);
1089 if (inst->config->default_profile[0] != 0 || user_profile != NULL){
1090 char *profile = inst->config->default_profile;
1092 if (user_profile != NULL)
1093 profile = user_profile->vp_strvalue;
1094 if (profile && strlen(profile)){
1095 RDEBUG("Checking profile %s", profile);
1096 if (sql_set_user(inst, request, profileusername, profile) < 0) {
1097 radlog_request(L_ERR, 0, request, "Error setting profile; rejecting user");
1098 sql_release_socket(inst, sqlsocket);
1099 /* Remove the username we (maybe) added above */
1100 pairdelete(&request->packet->vps, PW_SQL_USER_NAME, 0);
1101 return RLM_MODULE_FAIL;
1108 if (profile_found) {
1109 rows = rlm_sql_process_groups(inst, request, sqlsocket, &dofallthrough);
1111 radlog_request(L_ERR, 0, request, "Error processing profile groups; rejecting user");
1112 sql_release_socket(inst, sqlsocket);
1113 /* Remove the username we (maybe) added above */
1114 pairdelete(&request->packet->vps, PW_SQL_USER_NAME, 0);
1115 return RLM_MODULE_FAIL;
1116 } else if (rows > 0) {
1122 /* Remove the username we (maybe) added above */
1123 pairdelete(&request->packet->vps, PW_SQL_USER_NAME, 0);
1124 sql_release_socket(inst, sqlsocket);
1127 RDEBUG("User %s not found", sqlusername);
1128 return RLM_MODULE_NOTFOUND;
1130 return RLM_MODULE_OK;
1135 * Accounting: save the account data to our sql table
1137 static int rlm_sql_accounting(void *instance, REQUEST * request) {
1139 SQLSOCK *sqlsocket = NULL;
1141 SQL_INST *inst = instance;
1142 int ret = RLM_MODULE_OK;
1143 int numaffected = 0;
1144 int acctstatustype = 0;
1145 char querystr[MAX_QUERY_LEN];
1146 char logstr[MAX_QUERY_LEN];
1147 char sqlusername[MAX_STRING_LEN];
1149 #ifdef CISCO_ACCOUNTING_HACK
1150 int acctsessiontime = 0;
1153 memset(querystr, 0, MAX_QUERY_LEN);
1156 * Find the Acct Status Type
1158 if ((pair = pairfind(request->packet->vps, PW_ACCT_STATUS_TYPE, 0)) != NULL) {
1159 acctstatustype = pair->vp_integer;
1161 radius_xlat(logstr, sizeof(logstr), "packet has no accounting status type. [user '%{User-Name}', nas '%{NAS-IP-Address}']", request, NULL);
1162 radlog_request(L_ERR, 0, request, "%s", logstr);
1163 return RLM_MODULE_INVALID;
1166 switch (acctstatustype) {
1168 * The Terminal server informed us that it was rebooted
1169 * STOP all records from this NAS
1171 case PW_STATUS_ACCOUNTING_ON:
1172 case PW_STATUS_ACCOUNTING_OFF:
1173 RDEBUG("Received Acct On/Off packet");
1174 radius_xlat(querystr, sizeof(querystr), inst->config->accounting_onoff_query, request, sql_escape_func);
1175 query_log(request, inst, querystr);
1177 sqlsocket = sql_get_socket(inst);
1178 if (sqlsocket == NULL)
1179 return(RLM_MODULE_FAIL);
1180 if (*querystr) { /* non-empty query */
1181 if (rlm_sql_query(sqlsocket, inst, querystr)) {
1182 radlog_request(L_ERR, 0, request, "Couldn't update SQL accounting for Acct On/Off packet - %s",
1183 (inst->module->sql_error)(sqlsocket, inst->config));
1184 ret = RLM_MODULE_FAIL;
1186 (inst->module->sql_finish_query)(sqlsocket, inst->config);
1192 * Got an update accounting packet
1194 case PW_STATUS_ALIVE:
1197 * Set, escape, and check the user attr here
1199 sql_set_user(inst, request, sqlusername, NULL);
1201 radius_xlat(querystr, sizeof(querystr), inst->config->accounting_update_query, request, sql_escape_func);
1202 query_log(request, inst, querystr);
1204 sqlsocket = sql_get_socket(inst);
1205 if (sqlsocket == NULL)
1206 return(RLM_MODULE_FAIL);
1207 if (*querystr) { /* non-empty query */
1208 if (rlm_sql_query(sqlsocket, inst, querystr)) {
1209 radlog_request(L_ERR, 0, request, "Couldn't update SQL accounting ALIVE record - %s",
1210 (inst->module->sql_error)(sqlsocket, inst->config));
1211 ret = RLM_MODULE_FAIL;
1214 numaffected = (inst->module->sql_affected_rows)(sqlsocket, inst->config);
1215 if (numaffected < 1) {
1218 * If our update above didn't match anything
1219 * we assume it's because we haven't seen a
1220 * matching Start record. So we have to
1221 * insert this update rather than do an update
1223 radius_xlat(querystr, sizeof(querystr), inst->config->accounting_update_query_alt, request, sql_escape_func);
1224 query_log(request, inst, querystr);
1225 if (*querystr) { /* non-empty query */
1226 if (rlm_sql_query(sqlsocket, inst, querystr)) {
1227 radlog_request(L_ERR, 0, request, "Couldn't insert SQL accounting ALIVE record - %s",
1228 (inst->module->sql_error)(sqlsocket, inst->config));
1229 ret = RLM_MODULE_FAIL;
1231 (inst->module->sql_finish_query)(sqlsocket, inst->config);
1235 (inst->module->sql_finish_query)(sqlsocket, inst->config);
1240 * Got accounting start packet
1242 case PW_STATUS_START:
1245 * Set, escape, and check the user attr here
1247 sql_set_user(inst, request, sqlusername, NULL);
1249 radius_xlat(querystr, sizeof(querystr), inst->config->accounting_start_query, request, sql_escape_func);
1250 query_log(request, inst, querystr);
1252 sqlsocket = sql_get_socket(inst);
1253 if (sqlsocket == NULL)
1254 return(RLM_MODULE_FAIL);
1255 if (*querystr) { /* non-empty query */
1256 if (rlm_sql_query(sqlsocket, inst, querystr)) {
1257 radlog_request(L_ERR, 0, request, "Couldn't insert SQL accounting START record - %s",
1258 (inst->module->sql_error)(sqlsocket, inst->config));
1261 * We failed the insert above. It's probably because
1262 * the stop record came before the start. We try
1263 * our alternate query now (typically an UPDATE)
1265 radius_xlat(querystr, sizeof(querystr), inst->config->accounting_start_query_alt, request, sql_escape_func);
1266 query_log(request, inst, querystr);
1268 if (*querystr) { /* non-empty query */
1269 if (rlm_sql_query(sqlsocket, inst, querystr)) {
1270 radlog_request(L_ERR, 0, request, "Couldn't update SQL accounting START record - %s",
1271 (inst->module->sql_error)(sqlsocket, inst->config));
1272 ret = RLM_MODULE_FAIL;
1274 (inst->module->sql_finish_query)(sqlsocket, inst->config);
1277 (inst->module->sql_finish_query)(sqlsocket, inst->config);
1282 * Got accounting stop packet
1284 case PW_STATUS_STOP:
1287 * Set, escape, and check the user attr here
1289 sql_set_user(inst, request, sqlusername, NULL);
1291 radius_xlat(querystr, sizeof(querystr), inst->config->accounting_stop_query, request, sql_escape_func);
1292 query_log(request, inst, querystr);
1294 sqlsocket = sql_get_socket(inst);
1295 if (sqlsocket == NULL)
1296 return(RLM_MODULE_FAIL);
1297 if (*querystr) { /* non-empty query */
1298 if (rlm_sql_query(sqlsocket, inst, querystr)) {
1299 radlog_request(L_ERR, 0, request, "Couldn't update SQL accounting STOP record - %s",
1300 (inst->module->sql_error)(sqlsocket, inst->config));
1301 ret = RLM_MODULE_FAIL;
1304 numaffected = (inst->module->sql_affected_rows)(sqlsocket, inst->config);
1305 if (numaffected < 1) {
1307 * If our update above didn't match anything
1308 * we assume it's because we haven't seen a
1309 * matching Start record. So we have to
1310 * insert this stop rather than do an update
1312 #ifdef CISCO_ACCOUNTING_HACK
1314 * If stop but zero session length AND no previous
1315 * session found, drop it as in invalid packet
1316 * This is to fix CISCO's aaa from filling our
1317 * table with bogus crap
1319 if ((pair = pairfind(request->packet->vps, PW_ACCT_SESSION_TIME, 0)) != NULL)
1320 acctsessiontime = pair->vp_integer;
1322 if (acctsessiontime <= 0) {
1323 radius_xlat(logstr, sizeof(logstr), "stop packet with zero session length. [user '%{User-Name}', nas '%{NAS-IP-Address}']", request, NULL);
1324 radlog_request(L_DBG, 0, request, "%s", logstr);
1325 sql_release_socket(inst, sqlsocket);
1326 return RLM_MODULE_NOOP;
1330 radius_xlat(querystr, sizeof(querystr), inst->config->accounting_stop_query_alt, request, sql_escape_func);
1331 query_log(request, inst, querystr);
1333 if (*querystr) { /* non-empty query */
1334 if (rlm_sql_query(sqlsocket, inst, querystr)) {
1335 radlog_request(L_ERR, 0, request, "Couldn't insert SQL accounting STOP record - %s",
1337 (inst->module->sql_error)(sqlsocket, inst->config));
1338 ret = RLM_MODULE_FAIL;
1340 (inst->module->sql_finish_query)(sqlsocket, inst->config);
1344 (inst->module->sql_finish_query)(sqlsocket, inst->config);
1349 * Anything else is ignored.
1352 RDEBUG("Unsupported Acct-Status-Type = %d",
1354 return RLM_MODULE_NOOP;
1359 sql_release_socket(inst, sqlsocket);
1366 * See if a user is already logged in. Sets request->simul_count to the
1367 * current session count for this user.
1369 * Check twice. If on the first pass the user exceeds his
1370 * max. number of logins, do a second pass and validate all
1371 * logins by querying the terminal server (using eg. SNMP).
1374 static int rlm_sql_checksimul(void *instance, REQUEST * request) {
1376 SQL_INST *inst = instance;
1378 char querystr[MAX_QUERY_LEN];
1379 char sqlusername[MAX_STRING_LEN];
1382 char *call_num = NULL;
1385 uint32_t nas_addr = 0;
1388 /* If simul_count_query is not defined, we don't do any checking */
1389 if (!inst->config->simul_count_query ||
1390 (inst->config->simul_count_query[0] == 0)) {
1391 return RLM_MODULE_NOOP;
1394 if((request->username == NULL) || (request->username->length == 0)) {
1395 radlog_request(L_ERR, 0, request, "Zero Length username not permitted\n");
1396 return RLM_MODULE_INVALID;
1400 if(sql_set_user(inst, request, sqlusername, NULL) < 0)
1401 return RLM_MODULE_FAIL;
1403 radius_xlat(querystr, sizeof(querystr), inst->config->simul_count_query, request, sql_escape_func);
1405 /* initialize the sql socket */
1406 sqlsocket = sql_get_socket(inst);
1407 if(sqlsocket == NULL)
1408 return RLM_MODULE_FAIL;
1410 if(rlm_sql_select_query(sqlsocket, inst, querystr)) {
1411 radlog(L_ERR, "rlm_sql (%s) sql_checksimul: Database query failed", inst->config->xlat_name);
1412 sql_release_socket(inst, sqlsocket);
1413 return RLM_MODULE_FAIL;
1416 ret = rlm_sql_fetch_row(sqlsocket, inst);
1419 (inst->module->sql_finish_select_query)(sqlsocket, inst->config);
1420 sql_release_socket(inst, sqlsocket);
1421 return RLM_MODULE_FAIL;
1424 row = sqlsocket->row;
1426 (inst->module->sql_finish_select_query)(sqlsocket, inst->config);
1427 sql_release_socket(inst, sqlsocket);
1428 return RLM_MODULE_FAIL;
1431 request->simul_count = atoi(row[0]);
1432 (inst->module->sql_finish_select_query)(sqlsocket, inst->config);
1434 if(request->simul_count < request->simul_max) {
1435 sql_release_socket(inst, sqlsocket);
1436 return RLM_MODULE_OK;
1440 * Looks like too many sessions, so let's start verifying
1441 * them, unless told to rely on count query only.
1443 if (!inst->config->simul_verify_query ||
1444 (inst->config->simul_verify_query[0] == '\0')) {
1445 sql_release_socket(inst, sqlsocket);
1446 return RLM_MODULE_OK;
1449 radius_xlat(querystr, sizeof(querystr), inst->config->simul_verify_query, request, sql_escape_func);
1450 if(rlm_sql_select_query(sqlsocket, inst, querystr)) {
1451 radlog_request(L_ERR, 0, request, "Database query error");
1452 sql_release_socket(inst, sqlsocket);
1453 return RLM_MODULE_FAIL;
1457 * Setup some stuff, like for MPP detection.
1459 request->simul_count = 0;
1461 if ((vp = pairfind(request->packet->vps, PW_FRAMED_IP_ADDRESS, 0)) != NULL)
1462 ipno = vp->vp_ipaddr;
1463 if ((vp = pairfind(request->packet->vps, PW_CALLING_STATION_ID, 0)) != NULL)
1464 call_num = vp->vp_strvalue;
1467 while (rlm_sql_fetch_row(sqlsocket, inst) == 0) {
1468 row = sqlsocket->row;
1472 (inst->module->sql_finish_select_query)(sqlsocket, inst->config);
1473 sql_release_socket(inst, sqlsocket);
1474 RDEBUG("Cannot zap stale entry. No username present in entry.", inst->config->xlat_name);
1475 return RLM_MODULE_FAIL;
1478 (inst->module->sql_finish_select_query)(sqlsocket, inst->config);
1479 sql_release_socket(inst, sqlsocket);
1480 RDEBUG("Cannot zap stale entry. No session id in entry.", inst->config->xlat_name);
1481 return RLM_MODULE_FAIL;
1484 nas_addr = inet_addr(row[3]);
1486 nas_port = atoi(row[4]);
1488 check = rad_check_ts(nas_addr, nas_port, row[2], row[1]);
1492 * Stale record - zap it.
1494 if (inst->config->deletestalesessions == TRUE) {
1495 uint32_t framed_addr = 0;
1500 framed_addr = inet_addr(row[5]);
1502 if (strcmp(row[7], "PPP") == 0)
1504 else if (strcmp(row[7], "SLIP") == 0)
1508 sess_time = atoi(row[8]);
1509 session_zap(request, nas_addr, nas_port,
1510 row[2], row[1], framed_addr,
1514 else if (check == 1) {
1516 * User is still logged in.
1518 ++request->simul_count;
1521 * Does it look like a MPP attempt?
1523 if (row[5] && ipno && inet_addr(row[5]) == ipno)
1524 request->simul_mpp = 2;
1525 else if (row[6] && call_num &&
1526 !strncmp(row[6],call_num,16))
1527 request->simul_mpp = 2;
1531 * Failed to check the terminal server for
1532 * duplicate logins: return an error.
1534 (inst->module->sql_finish_select_query)(sqlsocket, inst->config);
1535 sql_release_socket(inst, sqlsocket);
1536 radlog_request(L_ERR, 0, request, "Failed to check the terminal server for user '%s'.", row[2]);
1537 return RLM_MODULE_FAIL;
1541 (inst->module->sql_finish_select_query)(sqlsocket, inst->config);
1542 sql_release_socket(inst, sqlsocket);
1545 * The Auth module apparently looks at request->simul_count,
1546 * not the return value of this module when deciding to deny
1547 * a call for too many sessions.
1549 return RLM_MODULE_OK;
1553 * Execute postauth_query after authentication
1555 static int rlm_sql_postauth(void *instance, REQUEST *request) {
1556 SQLSOCK *sqlsocket = NULL;
1557 SQL_INST *inst = instance;
1558 char querystr[MAX_QUERY_LEN];
1559 char sqlusername[MAX_STRING_LEN];
1561 if(sql_set_user(inst, request, sqlusername, NULL) < 0)
1562 return RLM_MODULE_FAIL;
1564 /* If postauth_query is not defined, we stop here */
1565 if (!inst->config->postauth_query ||
1566 (inst->config->postauth_query[0] == '\0'))
1567 return RLM_MODULE_NOOP;
1569 /* Expand variables in the query */
1570 memset(querystr, 0, MAX_QUERY_LEN);
1571 radius_xlat(querystr, sizeof(querystr), inst->config->postauth_query,
1572 request, sql_escape_func);
1573 query_log(request, inst, querystr);
1574 DEBUG2("rlm_sql (%s) in sql_postauth: query is %s",
1575 inst->config->xlat_name, querystr);
1577 /* Initialize the sql socket */
1578 sqlsocket = sql_get_socket(inst);
1579 if (sqlsocket == NULL)
1580 return RLM_MODULE_FAIL;
1582 /* Process the query */
1583 if (rlm_sql_query(sqlsocket, inst, querystr)) {
1584 radlog(L_ERR, "rlm_sql (%s) in sql_postauth: Database query error - %s",
1585 inst->config->xlat_name,
1586 (inst->module->sql_error)(sqlsocket, inst->config));
1587 sql_release_socket(inst, sqlsocket);
1588 return RLM_MODULE_FAIL;
1590 (inst->module->sql_finish_query)(sqlsocket, inst->config);
1592 sql_release_socket(inst, sqlsocket);
1593 return RLM_MODULE_OK;
1596 /* globally exported name */
1597 module_t rlm_sql = {
1600 RLM_TYPE_THREAD_SAFE, /* type: reserved */
1601 rlm_sql_instantiate, /* instantiation */
1602 rlm_sql_detach, /* detach */
1604 NULL, /* authentication */
1605 rlm_sql_authorize, /* authorization */
1606 NULL, /* preaccounting */
1607 rlm_sql_accounting, /* accounting */
1608 rlm_sql_checksimul, /* checksimul */
1609 NULL, /* pre-proxy */
1610 NULL, /* post-proxy */
1611 rlm_sql_postauth /* post-auth */