/* * Execute postauth_query after authentication */ static int rlm_sql_postauth(void *instance, REQUEST *request) { SQLSOCK *sqlsocket = NULL; SQL_INST *inst = instance; char querystr[MAX_QUERY_LEN]; char sqlusername[MAX_STRING_LEN]; DEBUG("rlm_sql (%s): Processing sql_postauth", inst->config->xlat_name); if(sql_set_user(inst, request, sqlusername, NULL) < 0) return RLM_MODULE_FAIL; /* If postauth_query is not defined, we stop here */ if (inst->config->postauth_query[0] == '\0') return RLM_MODULE_NOOP; /* Expand variables in the query */ memset(querystr, 0, MAX_QUERY_LEN); radius_xlat(querystr, sizeof(querystr), inst->config->postauth_query, request, sql_escape_func); query_log(request, inst, querystr); DEBUG2("rlm_sql (%s) in sql_postauth: query is %s", inst->config->xlat_name, querystr); /* Initialize the sql socket */ sqlsocket = sql_get_socket(inst); if (sqlsocket == NULL) return RLM_MODULE_FAIL; /* Process the query */ if (rlm_sql_query(sqlsocket, inst, querystr)) { radlog(L_ERR, "rlm_sql (%s) in sql_postauth: Database query error - %s", inst->config->xlat_name, (char *)(inst->module->sql_error)(sqlsocket, inst->config)); sql_release_socket(inst, sqlsocket); return RLM_MODULE_FAIL; } (inst->module->sql_finish_query)(sqlsocket, inst->config); sql_release_socket(inst, sqlsocket); return RLM_MODULE_OK; }
/* * Execute postauth_query after authentication */ static rlm_rcode_t rlm_redisn_postauth(void *instance, REQUEST *request) { REDISSOCK *redis_socket = NULL; REDIS_INST *inst = instance; char querystr[MAX_QUERY_LEN]; char redisnusername[MAX_STRING_LEN]; /* If postauth_query is not defined, we stop here */ if (!inst->postauth_query || (inst->postauth_query[0] == '\0')) return RLM_MODULE_NOOP; if(redisn_set_user(inst, request, redisnusername, NULL) < 0) return RLM_MODULE_FAIL; /* Expand variables in the query */ memset(querystr, 0, MAX_QUERY_LEN); radius_xlat(querystr, sizeof(querystr), inst->postauth_query, request, redisn_escape_func, inst); query_log(request, inst, querystr); DEBUG2("rlm_redisn (%s) in redisn_postauth: query is %s", inst->xlat_name, querystr); /* Initialize the redisn socket */ redis_socket = redisn_get_socket(inst); if (redis_socket == NULL) return RLM_MODULE_FAIL; /* Process the query */ if (rlm_redisn_query(inst, redis_socket, querystr)) { radlog(L_ERR, "rlm_redisn (%s) in redisn_postauth: Database query error - %s", inst->xlat_name, querystr); redisn_release_socket(inst, redis_socket); return RLM_MODULE_FAIL; } (inst->redisn_finish_query)(inst, redis_socket); redisn_release_socket(inst, redis_socket); return RLM_MODULE_OK; }
/* * Accounting: save the account data to our sql table */ static int rlm_sql_accounting(void *instance, REQUEST * request) { SQLSOCK *sqlsocket = NULL; VALUE_PAIR *pair; SQL_INST *inst = instance; int ret = RLM_MODULE_OK; int numaffected = 0; int acctstatustype = 0; char querystr[MAX_QUERY_LEN]; char logstr[MAX_QUERY_LEN]; char sqlusername[MAX_STRING_LEN]; #ifdef CISCO_ACCOUNTING_HACK int acctsessiontime = 0; #endif memset(querystr, 0, MAX_QUERY_LEN); /* * Find the Acct Status Type */ if ((pair = pairfind(request->packet->vps, PW_ACCT_STATUS_TYPE)) != NULL) { acctstatustype = pair->lvalue; } else { radius_xlat(logstr, sizeof(logstr), "packet has no accounting status type. [user '%{User-Name}', nas '%{NAS-IP-Address}']", request, NULL); radlog(L_ERR, "rlm_sql (%s) in sql_accounting: %s", inst->config->xlat_name, logstr); return RLM_MODULE_INVALID; } switch (acctstatustype) { /* * The Terminal server informed us that it was rebooted * STOP all records from this NAS */ case PW_STATUS_ACCOUNTING_ON: case PW_STATUS_ACCOUNTING_OFF: radlog(L_INFO, "rlm_sql (%s): received Acct On/Off packet", inst->config->xlat_name); radius_xlat(querystr, sizeof(querystr), inst->config->accounting_onoff_query, request, sql_escape_func); query_log(request, inst, querystr); sqlsocket = sql_get_socket(inst); if (sqlsocket == NULL) return(RLM_MODULE_FAIL); if (*querystr) { /* non-empty query */ if (rlm_sql_query(sqlsocket, inst, querystr)) { radlog(L_ERR, "rlm_sql (%s): Couldn't update SQL accounting for Acct On/Off packet - %s", inst->config->xlat_name, (char *)(inst->module->sql_error)(sqlsocket, inst->config)); ret = RLM_MODULE_FAIL; } (inst->module->sql_finish_query)(sqlsocket, inst->config); } break; /* * Got an update accounting packet */ case PW_STATUS_ALIVE: /* * Set, escape, and check the user attr here */ sql_set_user(inst, request, sqlusername, NULL); radius_xlat(querystr, sizeof(querystr), inst->config->accounting_update_query, request, sql_escape_func); query_log(request, inst, querystr); sqlsocket = sql_get_socket(inst); if (sqlsocket == NULL) return(RLM_MODULE_FAIL); if (*querystr) { /* non-empty query */ if (rlm_sql_query(sqlsocket, inst, querystr)) { radlog(L_ERR, "rlm_sql (%s): Couldn't update SQL accounting ALIVE record - %s", inst->config->xlat_name, (char *)(inst->module->sql_error)(sqlsocket, inst->config)); ret = RLM_MODULE_FAIL; } else { numaffected = (inst->module->sql_affected_rows)(sqlsocket, inst->config); if (numaffected < 1) { /* * If our update above didn't match anything * we assume it's because we haven't seen a * matching Start record. So we have to * insert this update rather than do an update */ radius_xlat(querystr, sizeof(querystr), inst->config->accounting_update_query_alt, request, sql_escape_func); query_log(request, inst, querystr); if (*querystr) { /* non-empty query */ if (rlm_sql_query(sqlsocket, inst, querystr)) { radlog(L_ERR, "rlm_sql (%s): Couldn't insert SQL accounting ALIVE record - %s", inst->config->xlat_name, (char *)(inst->module->sql_error)(sqlsocket, inst->config)); ret = RLM_MODULE_FAIL; } (inst->module->sql_finish_query)(sqlsocket, inst->config); } } } (inst->module->sql_finish_query)(sqlsocket, inst->config); } break; /* * Got accounting start packet */ case PW_STATUS_START: /* * Set, escape, and check the user attr here */ sql_set_user(inst, request, sqlusername, NULL); radius_xlat(querystr, sizeof(querystr), inst->config->accounting_start_query, request, sql_escape_func); query_log(request, inst, querystr); sqlsocket = sql_get_socket(inst); if (sqlsocket == NULL) return(RLM_MODULE_FAIL); if (*querystr) { /* non-empty query */ if (rlm_sql_query(sqlsocket, inst, querystr)) { radlog(L_ERR, "rlm_sql (%s): Couldn't insert SQL accounting START record - %s", inst->config->xlat_name, (char *)(inst->module->sql_error)(sqlsocket, inst->config)); /* * We failed the insert above. It's probably because * the stop record came before the start. We try * our alternate query now (typically an UPDATE) */ radius_xlat(querystr, sizeof(querystr), inst->config->accounting_start_query_alt, request, sql_escape_func); query_log(request, inst, querystr); if (*querystr) { /* non-empty query */ if (rlm_sql_query(sqlsocket, inst, querystr)) { radlog(L_ERR, "rlm_sql (%s): Couldn't update SQL accounting START record - %s", inst->config->xlat_name, (char *)(inst->module->sql_error)(sqlsocket, inst->config)); ret = RLM_MODULE_FAIL; } (inst->module->sql_finish_query)(sqlsocket, inst->config); } } (inst->module->sql_finish_query)(sqlsocket, inst->config); } break; /* * Got accounting stop packet */ case PW_STATUS_STOP: /* * Set, escape, and check the user attr here */ sql_set_user(inst, request, sqlusername, NULL); radius_xlat(querystr, sizeof(querystr), inst->config->accounting_stop_query, request, sql_escape_func); query_log(request, inst, querystr); sqlsocket = sql_get_socket(inst); if (sqlsocket == NULL) return(RLM_MODULE_FAIL); if (*querystr) { /* non-empty query */ if (rlm_sql_query(sqlsocket, inst, querystr)) { radlog(L_ERR, "rlm_sql (%s): Couldn't update SQL accounting STOP record - %s", inst->config->xlat_name, (char *)(inst->module->sql_error)(sqlsocket, inst->config)); ret = RLM_MODULE_FAIL; } else { numaffected = (inst->module->sql_affected_rows)(sqlsocket, inst->config); if (numaffected < 1) { /* * If our update above didn't match anything * we assume it's because we haven't seen a * matching Start record. So we have to * insert this stop rather than do an update */ #ifdef CISCO_ACCOUNTING_HACK /* * If stop but zero session length AND no previous * session found, drop it as in invalid packet * This is to fix CISCO's aaa from filling our * table with bogus crap */ if ((pair = pairfind(request->packet->vps, PW_ACCT_SESSION_TIME)) != NULL) acctsessiontime = pair->lvalue; if (acctsessiontime <= 0) { radius_xlat(logstr, sizeof(logstr), "stop packet with zero session length. [user '%{User-Name}', nas '%{NAS-IP-Address}']", request, NULL); radlog(L_ERR, "rlm_sql (%s) in sql_accounting: %s", inst->config->xlat_name, logstr); sql_release_socket(inst, sqlsocket); ret = RLM_MODULE_NOOP; } #endif radius_xlat(querystr, sizeof(querystr), inst->config->accounting_stop_query_alt, request, sql_escape_func); query_log(request, inst, querystr); if (*querystr) { /* non-empty query */ if (rlm_sql_query(sqlsocket, inst, querystr)) { radlog(L_ERR, "rlm_sql (%s): Couldn't insert SQL accounting STOP record - %s", inst->config->xlat_name, (char *)(inst->module->sql_error)(sqlsocket, inst->config)); ret = RLM_MODULE_FAIL; } (inst->module->sql_finish_query)(sqlsocket, inst->config); } } } (inst->module->sql_finish_query)(sqlsocket, inst->config); } break; /* * Anything else is ignored. */ default: radlog(L_INFO, "rlm_sql (%s): Unsupported Acct-Status-Type = %d", inst->config->xlat_name, acctstatustype); return RLM_MODULE_NOOP; break; } sql_release_socket(inst, sqlsocket); return ret; }
/* * sql xlat function. Right now only SELECTs are supported. Only * the first element of the SELECT result will be used. */ static int sql_xlat(void *instance, REQUEST *request, char *fmt, char *out, size_t freespace, RADIUS_ESCAPE_STRING func) { SQLSOCK *sqlsocket; SQL_ROW row; SQL_INST *inst = instance; char querystr[MAX_QUERY_LEN]; char sqlusername[MAX_STRING_LEN]; int ret = 0; DEBUG("rlm_sql (%s): - sql_xlat", inst->config->xlat_name); /* * Add SQL-User-Name attribute just in case it is needed * We could search the string fmt for SQL-User-Name to see if this is * needed or not */ sql_set_user(inst, request, sqlusername, NULL); /* * Do an xlat on the provided string (nice recursive operation). */ if (!radius_xlat(querystr, sizeof(querystr), fmt, request, sql_escape_func)) { radlog(L_ERR, "rlm_sql (%s): xlat failed.", inst->config->xlat_name); return 0; } query_log(request, inst,querystr); sqlsocket = sql_get_socket(inst); if (sqlsocket == NULL) return 0; if (rlm_sql_select_query(sqlsocket,inst,querystr)){ radlog(L_ERR, "rlm_sql (%s): database query error, %s: %s", inst->config->xlat_name,querystr, (char *)(inst->module->sql_error)(sqlsocket, inst->config)); sql_release_socket(inst,sqlsocket); return 0; } ret = rlm_sql_fetch_row(sqlsocket, inst); if (ret) { DEBUG("rlm_sql (%s): SQL query did not succeed", inst->config->xlat_name); (inst->module->sql_finish_select_query)(sqlsocket, inst->config); sql_release_socket(inst,sqlsocket); return 0; } row = sqlsocket->row; if (row == NULL) { DEBUG("rlm_sql (%s): SQL query did not return any results", inst->config->xlat_name); (inst->module->sql_finish_select_query)(sqlsocket, inst->config); sql_release_socket(inst,sqlsocket); return 0; } if (row[0] == NULL){ DEBUG("rlm_sql (%s): row[0] returned NULL", inst->config->xlat_name); (inst->module->sql_finish_select_query)(sqlsocket, inst->config); sql_release_socket(inst,sqlsocket); return 0; } ret = strlen(row[0]); if (ret > freespace){ DEBUG("rlm_sql (%s): sql_xlat:: Insufficient string space", inst->config->xlat_name); (inst->module->sql_finish_select_query)(sqlsocket, inst->config); sql_release_socket(inst,sqlsocket); return 0; } strncpy(out,row[0],ret); DEBUG("rlm_sql (%s): - sql_xlat finished", inst->config->xlat_name); (inst->module->sql_finish_select_query)(sqlsocket, inst->config); sql_release_socket(inst,sqlsocket); return ret; }
/* * redisn xlat function. Right now only xGET are supported. Only * the first element of the SELECT result will be used. */ static int redisn_xlat(void *instance, REQUEST *request, char *fmt, char *out, size_t freespace, UNUSED RADIUS_ESCAPE_STRING func) { REDISSOCK *redis_socket=NULL; REDIS_ROW row; REDIS_INST *inst = instance; char querystr[MAX_QUERY_LEN]; char redisnusername[MAX_STRING_LEN]; size_t ret = 0; RDEBUG("redisn_xlat"); /* * Add REDISN-User-Name attribute just in case it is needed * We could search the string fmt for REDISN-User-Name to see if this is * needed or not */ redisn_set_user(inst, request, redisnusername, NULL); /* * Do an xlat on the provided string (nice recursive operation). */ if (!radius_xlat(querystr, sizeof(querystr), fmt, request, redisn_escape_func, inst)) { radlog(L_ERR, "rlm_redisn (%s): xlat failed.", inst->xlat_name); return 0; } query_log(request, inst, querystr); redis_socket = redisn_get_socket(inst); if (redis_socket == NULL) return 0; if (rlm_redisn_query(inst, redis_socket, querystr)<0) { radlog(L_ERR, "rlm_redisn (%s): database query error, %s", inst->xlat_name,querystr); redisn_release_socket(inst,redis_socket); return 0; } ret = rlm_redisn_fetch_row(inst, redis_socket); if (ret) { RDEBUG("REDIS query did not succeed"); (inst->redisn_finish_query)(inst, redis_socket); redisn_release_socket(inst,redis_socket); return 0; } row = redis_socket->row; if (row == NULL) { RDEBUG("REDIS query did not return any results"); (inst->redisn_finish_query)(inst, redis_socket); redisn_release_socket(inst,redis_socket); return 0; } if (row[0] == NULL){ RDEBUG("row[0] returned NULL"); (inst->redisn_finish_query)(inst, redis_socket); redisn_release_socket(inst,redis_socket); return 0; } ret = strlen(row[0]); if (ret >= freespace){ RDEBUG("Insufficient string space"); (inst->redisn_finish_query)(inst, redis_socket); redisn_release_socket(inst,redis_socket); return 0; } strlcpy(out,row[0],freespace); RDEBUG("redisn_xlat finished"); (inst->redisn_finish_query)(inst,redis_socket); redisn_release_socket(inst,redis_socket); return ret; }
/* * Accounting: save the account data to our redisn table */ static rlm_rcode_t rlm_redisn_accounting(void *instance, REQUEST * request) { REDISSOCK *redis_socket = NULL; VALUE_PAIR *pair; REDIS_INST *inst = instance; int ret = RLM_MODULE_OK; int acctstatustype = 0; char logstr[MAX_QUERY_LEN]; char redisnusername[MAX_STRING_LEN]; char** queries=NULL; /* * Find the Acct Status Type */ if ((pair = pairfind(request->packet->vps, PW_ACCT_STATUS_TYPE, 0, TAG_ANY)) != NULL) { acctstatustype = pair->vp_integer; } else { radius_xlat(logstr, sizeof(logstr), "packet has no accounting status type. [user '%{User-Name}', nas '%{NAS-IP-Address}']", request, NULL, inst); radlog_request(L_ERR, 0, request, "%s", logstr); return RLM_MODULE_INVALID; } switch (acctstatustype) { /* * The Terminal server informed us that it was rebooted * STOP all records from this NAS */ case PW_STATUS_ACCOUNTING_ON: RDEBUG("Received Acct On packet"); queries=inst->accounting_on_queries; break; case PW_STATUS_ACCOUNTING_OFF: RDEBUG("Received Acct Off packet"); queries=inst->accounting_off_queries; break; case PW_STATUS_START: RDEBUG("Received Acct Start packet"); queries=inst->accounting_start_queries; break; case PW_STATUS_STOP: RDEBUG("Received Acct Stop packet"); #if 0 //#ifdef CISCO_ACCOUNTING_HACK { /* * If stop but zero session length AND no previous * session found, drop it as in invalid packet * This is to fix CISCO's aaa from filling our * table with bogus crap */ int acctsessiontime = 0; if ((pair = pairfind(request->packet->vps, PW_ACCT_SESSION_TIME, 0, TAG_ANY)) != NULL) acctsessiontime = pair->vp_integer; if (acctsessiontime <= 0) { radius_xlat(logstr, sizeof(logstr), "stop packet with zero session length. [user '%{User-Name}', nas '%{NAS-IP-Address}']", request, NULL, inst); radlog_request(L_DBG, 0, request, "%s", logstr); redisn_release_socket(inst, redis_socket); return RLM_MODULE_NOOP; } } #endif queries=inst->accounting_stop_queries; break; case PW_STATUS_ALIVE: RDEBUG("Received Acct Alive packet"); queries=inst->accounting_update_queries; break; /* * Anything else is ignored. */ default: RDEBUG("Unsupported Acct-Status-Type = %d", acctstatustype); return RLM_MODULE_NOOP; break; } if (queries==NULL ||*queries==NULL) ret=RLM_MODULE_NOOP; else { char querystr[MAX_QUERY_LEN]; redis_socket = redisn_get_socket(inst); if (redis_socket == NULL) return(RLM_MODULE_FAIL); redisn_set_user(inst, request, redisnusername, NULL); while(*queries) { char* query=*queries; memset(querystr, 0, MAX_QUERY_LEN); radius_xlat(querystr, sizeof(querystr), query, request, redisn_escape_func, inst); query_log(request, inst, querystr); if (rlm_redisn_query(inst, redis_socket, querystr)) { radlog_request(L_ERR, 0, request, "Accounting query failed: %s", querystr); ret = RLM_MODULE_FAIL; } (inst->redisn_finish_query)(inst, redis_socket); queries++; } redisn_release_socket(inst, redis_socket); } return ret; }
/* * sql xlat function. Right now only SELECTs are supported. Only * the first element of the SELECT result will be used. * * For other statements (insert, update, delete, etc.), the * number of affected rows will be returned. */ static int sql_xlat(void *instance, REQUEST *request, char *fmt, char *out, size_t freespace, UNUSED RADIUS_ESCAPE_STRING func) { SQLSOCK *sqlsocket; SQL_ROW row; SQL_INST *inst = instance; char querystr[MAX_QUERY_LEN]; char sqlusername[MAX_STRING_LEN]; size_t ret = 0; RDEBUG("sql_xlat"); /* * Add SQL-User-Name attribute just in case it is needed * We could search the string fmt for SQL-User-Name to see if this is * needed or not */ sql_set_user(inst, request, sqlusername, NULL); /* * Do an xlat on the provided string (nice recursive operation). */ if (!radius_xlat(querystr, sizeof(querystr), fmt, request, sql_escape_func)) { radlog(L_ERR, "rlm_sql (%s): xlat failed.", inst->config->xlat_name); return 0; } query_log(request, inst,querystr); sqlsocket = sql_get_socket(inst); if (sqlsocket == NULL) return 0; /* * If the query starts with any of the following prefixes, * then return the number of rows affected */ if ((strncasecmp(querystr, "insert", 6) == 0) || (strncasecmp(querystr, "update", 6) == 0) || (strncasecmp(querystr, "delete", 6) == 0)) { int numaffected; char buffer[21]; /* 64bit max is 20 decimal chars + null byte */ if (rlm_sql_query(sqlsocket,inst,querystr)) { radlog(L_ERR, "rlm_sql (%s): database query error, %s: %s", inst->config->xlat_name, querystr, (inst->module->sql_error)(sqlsocket, inst->config)); sql_release_socket(inst,sqlsocket); return 0; } numaffected = (inst->module->sql_affected_rows)(sqlsocket, inst->config); if (numaffected < 1) { RDEBUG("rlm_sql (%s): SQL query affected no rows", inst->config->xlat_name); } /* * Don't chop the returned number if freespace is * too small. This hack is necessary because * some implementations of snprintf return the * size of the written data, and others return * the size of the data they *would* have written * if the output buffer was large enough. */ snprintf(buffer, sizeof(buffer), "%d", numaffected); ret = strlen(buffer); if (ret >= freespace){ RDEBUG("rlm_sql (%s): Can't write result, insufficient string space", inst->config->xlat_name); (inst->module->sql_finish_query)(sqlsocket, inst->config); sql_release_socket(inst,sqlsocket); return 0; } memcpy(out, buffer, ret + 1); /* we did bounds checking above */ (inst->module->sql_finish_query)(sqlsocket, inst->config); sql_release_socket(inst,sqlsocket); return ret; } /* else it's a SELECT statement */ if (rlm_sql_select_query(sqlsocket,inst,querystr)){ radlog(L_ERR, "rlm_sql (%s): database query error, %s: %s", inst->config->xlat_name,querystr, (inst->module->sql_error)(sqlsocket, inst->config)); sql_release_socket(inst,sqlsocket); return 0; } ret = rlm_sql_fetch_row(sqlsocket, inst); if (ret) { RDEBUG("SQL query did not succeed"); (inst->module->sql_finish_select_query)(sqlsocket, inst->config); sql_release_socket(inst,sqlsocket); return 0; } row = sqlsocket->row; if (row == NULL) { RDEBUG("SQL query did not return any results"); (inst->module->sql_finish_select_query)(sqlsocket, inst->config); sql_release_socket(inst,sqlsocket); return 0; } if (row[0] == NULL){ RDEBUG("row[0] returned NULL"); (inst->module->sql_finish_select_query)(sqlsocket, inst->config); sql_release_socket(inst,sqlsocket); return 0; } ret = strlen(row[0]); if (ret >= freespace){ RDEBUG("Insufficient string space"); (inst->module->sql_finish_select_query)(sqlsocket, inst->config); sql_release_socket(inst,sqlsocket); return 0; } strlcpy(out,row[0],freespace); RDEBUG("sql_xlat finished"); (inst->module->sql_finish_select_query)(sqlsocket, inst->config); sql_release_socket(inst,sqlsocket); return ret; }