/* * Replace %<whatever> in the query. */ static int sql_xlat_query(rlm_sql_log_t *inst, REQUEST *request, const char *query, char *xlat_query, size_t len) { char sqlusername[MAX_STRING_LEN]; /* If query is not defined, we stop here */ if (query[0] == '\0') return RLM_MODULE_NOOP; /* Add attribute 'SQL-User-Name' */ if (sql_set_user(inst, request, sqlusername, NULL) <0) { radlog_request(L_ERR, 0, request, "Couldn't add SQL-User-Name attribute"); return RLM_MODULE_FAIL; } /* Expand variables in the query */ xlat_query[0] = '\0'; if (inst->utf8) radius_xlat(xlat_query, len, query, request, sql_utf8_escape_func); else radius_xlat(xlat_query, len, query, request, sql_escape_func); if (xlat_query[0] == '\0') { radlog_request(L_ERR, 0, request, "Couldn't xlat the query %s", query); return RLM_MODULE_FAIL; } return RLM_MODULE_OK; }
static int sql_groupcmp(void *instance, REQUEST *request, UNUSED VALUE_PAIR *request_vp, VALUE_PAIR *check, UNUSED VALUE_PAIR *check_pairs, UNUSED VALUE_PAIR **reply_pairs) { rlm_sql_handle_t *handle; rlm_sql_t *inst = instance; rlm_sql_grouplist_t *head, *entry; RDEBUG("sql_groupcmp"); if (!check || !check->length){ RDEBUG("sql_groupcmp: Illegal group name"); return 1; } if (!request){ RDEBUG("sql_groupcmp: NULL request"); return 1; } /* * Set, escape, and check the user attr here */ if (sql_set_user(inst, request, NULL) < 0) return 1; /* * Get a socket for this lookup */ handle = sql_get_socket(inst); if (!handle) { return 1; } /* * Get the list of groups this user is a member of */ if (sql_get_grouplist(inst, handle, request, &head) < 0) { REDEBUG("Error getting group membership"); sql_release_socket(inst, handle); return 1; } for (entry = head; entry != NULL; entry = entry->next) { if (strcmp(entry->name, check->vp_strvalue) == 0){ RDEBUG("sql_groupcmp finished: User is a member of group %s", check->vp_strvalue); talloc_free(head); sql_release_socket(inst, handle); return 0; } } /* Free the grouplist */ talloc_free(head); sql_release_socket(inst,handle); RDEBUG("sql_groupcmp finished: User is NOT a member of group %s", check->vp_strvalue); return 1; }
/* * 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; }
/* * SQL xlat function * * For selects the first value of the first column will be returned, * for inserts, updates and deletes the number of rows afftected will be * returned instead. */ static size_t sql_xlat(void *instance, REQUEST *request, const char *fmt, char *out, size_t freespace) { rlm_sql_handle_t *handle; rlm_sql_row_t row; rlm_sql_t *inst = instance; char querystr[MAX_QUERY_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, NULL); /* * Do an xlat on the provided string (nice recursive operation). */ if (!radius_xlat(querystr, sizeof(querystr), fmt, request, sql_escape_func, inst)) { radlog(L_ERR, "rlm_sql (%s): xlat failed.", inst->config->xlat_name); return 0; } handle = sql_get_socket(inst); if (handle == NULL) return 0; rlm_sql_query_log(inst, request, NULL, querystr); /* * 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(&handle,inst,querystr)) { sql_release_socket(inst,handle); return 0; } numaffected = (inst->module->sql_affected_rows)(handle, 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)(handle, inst->config); sql_release_socket(inst,handle); return 0; } memcpy(out, buffer, ret + 1); /* we did bounds checking above */ (inst->module->sql_finish_query)(handle, inst->config); sql_release_socket(inst,handle); return ret; } /* else it's a SELECT statement */ if (rlm_sql_select_query(&handle,inst,querystr)){ sql_release_socket(inst,handle); return 0; } ret = rlm_sql_fetch_row(&handle, inst); if (ret) { RDEBUG("SQL query did not succeed"); (inst->module->sql_finish_select_query)(handle, inst->config); sql_release_socket(inst,handle); return 0; } row = handle->row; if (row == NULL) { RDEBUG("SQL query did not return any results"); (inst->module->sql_finish_select_query)(handle, inst->config); sql_release_socket(inst,handle); return 0; } if (row[0] == NULL){ RDEBUG("Null value in first column"); (inst->module->sql_finish_select_query)(handle, inst->config); sql_release_socket(inst,handle); return 0; } ret = strlen(row[0]); if (ret >= freespace){ RDEBUG("Insufficient string space"); (inst->module->sql_finish_select_query)(handle, inst->config); sql_release_socket(inst,handle); return 0; } strlcpy(out,row[0],freespace); RDEBUG("sql_xlat finished"); (inst->module->sql_finish_select_query)(handle, inst->config); sql_release_socket(inst,handle); return ret; }
/* * Generic function for failing between a bunch of queries. * * Uses the same principle as rlm_linelog, expanding the 'reference' config * item using xlat to figure out what query it should execute. * * If the reference matches multiple config items, and a query fails or * doesn't update any rows, the next matching config item is used. * */ static int acct_redundant(rlm_sql_t *inst, REQUEST *request, sql_acct_section_t *section) { int ret = RLM_MODULE_OK; rlm_sql_handle_t *handle = NULL; int sql_ret; int numaffected = 0; CONF_ITEM *item; CONF_PAIR *pair; const char *attr = NULL; const char *value; char path[MAX_STRING_LEN]; char querystr[MAX_QUERY_LEN]; char *p = path; rad_assert(section); if (section->reference[0] != '.') *p++ = '.'; if (!radius_xlat(p, (sizeof(path) - (p - path)) - 1, section->reference, request, NULL, NULL)) return RLM_MODULE_FAIL; item = cf_reference_item(NULL, section->cs, path); if (!item) return RLM_MODULE_FAIL; if (cf_item_is_section(item)){ radlog(L_ERR, "Sections are not supported as references"); return RLM_MODULE_FAIL; } pair = cf_itemtopair(item); attr = cf_pair_attr(pair); RDEBUG2("Using query template '%s'", attr); handle = sql_get_socket(inst); if (handle == NULL) return RLM_MODULE_FAIL; sql_set_user(inst, request, NULL); while (TRUE) { value = cf_pair_value(pair); if (!value) { RDEBUG("Ignoring null query"); ret = RLM_MODULE_NOOP; goto release; } radius_xlat(querystr, sizeof(querystr), value, request, sql_escape_func, inst); if (!*querystr) { RDEBUG("Ignoring null query"); ret = RLM_MODULE_NOOP; goto release; } rlm_sql_query_log(inst, request, section, querystr); /* * If rlm_sql_query cannot use the socket it'll try and * reconnect. Reconnecting will automatically release * the current socket, and try to select a new one. * * If we get SQL_DOWN it means all connections in the pool * were exhausted, and we couldn't create a new connection, * so we do not need to call sql_release_socket. */ sql_ret = rlm_sql_query(&handle, inst, querystr); if (sql_ret == SQL_DOWN) return RLM_MODULE_FAIL; rad_assert(handle); /* * Assume all other errors are incidental, and just meant our * operation failed and its not a client or SQL syntax error. */ if (sql_ret == 0) { numaffected = (inst->module->sql_affected_rows) (handle, inst->config); if (numaffected > 0) break; RDEBUG("No records updated"); } (inst->module->sql_finish_query)(handle, inst->config); /* * We assume all entries with the same name form a redundant * set of queries. */ pair = cf_pair_find_next(section->cs, pair, attr); if (!pair) { RDEBUG("No additional queries configured"); ret = RLM_MODULE_NOOP; goto release; } RDEBUG("Trying next query..."); } (inst->module->sql_finish_query)(handle, inst->config); release: sql_release_socket(inst, handle); return ret; }
/* * 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; }
/* * Generic function for failing between a bunch of queries. * * Uses the same principle as rlm_linelog, expanding the 'reference' config * item using xlat to figure out what query it should execute. * * If the reference matches multiple config items, and a query fails or * doesn't update any rows, the next matching config item is used. * */ static int acct_redundant(rlm_sql_t *inst, REQUEST *request, sql_acct_section_t *section) { rlm_rcode_t rcode = RLM_MODULE_OK; rlm_sql_handle_t *handle = NULL; int sql_ret; int numaffected = 0; CONF_ITEM *item; CONF_PAIR *pair; char const *attr = NULL; char const *value; char path[MAX_STRING_LEN]; char *p = path; char *expanded = NULL; rad_assert(section); if (section->reference[0] != '.') { *p++ = '.'; } if (radius_xlat(p, sizeof(path) - (p - path), request, section->reference, NULL, NULL) < 0) { rcode = RLM_MODULE_FAIL; goto finish; } /* * If we can't find a matching config item we do * nothing so return RLM_MODULE_NOOP. */ item = cf_reference_item(NULL, section->cs, path); if (!item) { RWDEBUG("No such configuration item %s", path); rcode = RLM_MODULE_NOOP; goto finish; } if (cf_item_is_section(item)){ RWDEBUG("Sections are not supported as references"); rcode = RLM_MODULE_NOOP; goto finish; } pair = cf_item_to_pair(item); attr = cf_pair_attr(pair); RDEBUG2("Using query template '%s'", attr); handle = fr_connection_get(inst->pool); if (!handle) { rcode = RLM_MODULE_FAIL; goto finish; } sql_set_user(inst, request, NULL); while (true) { value = cf_pair_value(pair); if (!value) { RDEBUG("Ignoring null query"); rcode = RLM_MODULE_NOOP; goto finish; } if (radius_axlat(&expanded, request, value, inst->sql_escape_func, handle) < 0) { rcode = RLM_MODULE_FAIL; goto finish; } if (!*expanded) { RDEBUG("Ignoring null query"); rcode = RLM_MODULE_NOOP; talloc_free(expanded); goto finish; } rlm_sql_query_log(inst, request, section, expanded); sql_ret = rlm_sql_query(inst, request, &handle, expanded); TALLOC_FREE(expanded); RDEBUG("SQL query returned: %s", fr_int2str(sql_rcode_table, sql_ret, "<INVALID>")); switch (sql_ret) { /* * Query was a success! Now we just need to check if it did anything. */ case RLM_SQL_OK: break; /* * A general, unrecoverable server fault. */ case RLM_SQL_ERROR: /* * If we get RLM_SQL_RECONNECT it means all connections in the pool * were exhausted, and we couldn't create a new connection, * so we do not need to call fr_connection_release. */ case RLM_SQL_RECONNECT: rcode = RLM_MODULE_FAIL; goto finish; /* * Query was invalid, this is a terminal error, but we still need * to do cleanup, as the connection handle is still valid. */ case RLM_SQL_QUERY_INVALID: rcode = RLM_MODULE_INVALID; goto finish; /* * Driver found an error (like a unique key constraint violation) * that hinted it might be a good idea to try an alternative query. */ case RLM_SQL_ALT_QUERY: goto next; } rad_assert(handle); /* * We need to have updated something for the query to have been * counted as successful. */ numaffected = (inst->module->sql_affected_rows)(handle, inst->config); (inst->module->sql_finish_query)(handle, inst->config); RDEBUG("%i record(s) updated", numaffected); if (numaffected > 0) break; /* A query succeeded, were done! */ next: /* * We assume all entries with the same name form a redundant * set of queries. */ pair = cf_pair_find_next(section->cs, pair, attr); if (!pair) { RDEBUG("No additional queries configured"); rcode = RLM_MODULE_NOOP; goto finish; } RDEBUG("Trying next query..."); } finish: talloc_free(expanded); fr_connection_release(inst->pool, handle); sql_unset_user(inst, request); return rcode; }
static rlm_rcode_t mod_checksimul(void *instance, REQUEST * request) { rlm_rcode_t rcode = RLM_MODULE_OK; rlm_sql_handle_t *handle = NULL; rlm_sql_t *inst = instance; rlm_sql_row_t row; int check = 0; uint32_t ipno = 0; char const *call_num = NULL; VALUE_PAIR *vp; int ret; uint32_t nas_addr = 0; int nas_port = 0; char *expanded = NULL; /* If simul_count_query is not defined, we don't do any checking */ if (!inst->config->simul_count_query || (inst->config->simul_count_query[0] == '\0')) { return RLM_MODULE_NOOP; } if((!request->username) || (request->username->length == '\0')) { REDEBUG("Zero Length username not permitted"); return RLM_MODULE_INVALID; } if(sql_set_user(inst, request, NULL) < 0) { return RLM_MODULE_FAIL; } if (radius_axlat(&expanded, request, inst->config->simul_count_query, sql_escape_func, inst) < 0) { return RLM_MODULE_FAIL; } /* initialize the sql socket */ handle = sql_get_socket(inst); if (!handle) { talloc_free(expanded); return RLM_MODULE_FAIL; } if (rlm_sql_select_query(&handle, inst, expanded)) { rcode = RLM_MODULE_FAIL; goto finish; } ret = rlm_sql_fetch_row(&handle, inst); if (ret != 0) { rcode = RLM_MODULE_FAIL; goto finish; } row = handle->row; if (!row) { rcode = RLM_MODULE_FAIL; goto finish; } request->simul_count = atoi(row[0]); (inst->module->sql_finish_select_query)(handle, inst->config); TALLOC_FREE(expanded); if(request->simul_count < request->simul_max) { rcode = RLM_MODULE_OK; goto finish; } /* * Looks like too many sessions, so let's start verifying * them, unless told to rely on count query only. */ if (!inst->config->simul_verify_query || (inst->config->simul_verify_query[0] == '\0')) { rcode = RLM_MODULE_OK; goto finish; } if (radius_axlat(&expanded, request, inst->config->simul_verify_query, sql_escape_func, inst) < 0) { rcode = RLM_MODULE_FAIL; goto finish; } if(rlm_sql_select_query(&handle, inst, expanded)) { goto finish; } /* * Setup some stuff, like for MPP detection. */ request->simul_count = 0; if ((vp = pairfind(request->packet->vps, PW_FRAMED_IP_ADDRESS, 0, TAG_ANY)) != NULL) { ipno = vp->vp_ipaddr; } if ((vp = pairfind(request->packet->vps, PW_CALLING_STATION_ID, 0, TAG_ANY)) != NULL) { call_num = vp->vp_strvalue; } while (rlm_sql_fetch_row(&handle, inst) == 0) { row = handle->row; if (!row) { break; } if (!row[2]){ RDEBUG("Cannot zap stale entry. No username present in entry"); rcode = RLM_MODULE_FAIL; goto finish; } if (!row[1]){ RDEBUG("Cannot zap stale entry. No session id in entry"); rcode = RLM_MODULE_FAIL; goto finish; } if (row[3]) { nas_addr = inet_addr(row[3]); } if (row[4]) { nas_port = atoi(row[4]); } check = rad_check_ts(nas_addr, nas_port, row[2], row[1]); if (check == 0) { /* * Stale record - zap it. */ if (inst->config->deletestalesessions == true) { uint32_t framed_addr = 0; char proto = 0; int sess_time = 0; if (row[5]) framed_addr = inet_addr(row[5]); if (row[7]){ if (strcmp(row[7], "PPP") == 0) proto = 'P'; else if (strcmp(row[7], "SLIP") == 0) proto = 'S'; } if (row[8]) sess_time = atoi(row[8]); session_zap(request, nas_addr, nas_port, row[2], row[1], framed_addr, proto, sess_time); } } else if (check == 1) { /* * User is still logged in. */ ++request->simul_count; /* * Does it look like a MPP attempt? */ if (row[5] && ipno && inet_addr(row[5]) == ipno) { request->simul_mpp = 2; } else if (row[6] && call_num && !strncmp(row[6],call_num,16)) { request->simul_mpp = 2; } } else { /* * Failed to check the terminal server for * duplicate logins: return an error. */ REDEBUG("Failed to check the terminal server for user '%s'.", row[2]); rcode = RLM_MODULE_FAIL; goto finish; } } finish: (inst->module->sql_finish_select_query)(handle, inst->config); sql_release_socket(inst, handle); talloc_free(expanded); /* * The Auth module apparently looks at request->simul_count, * not the return value of this module when deciding to deny * a call for too many sessions. */ return rcode; }
static int rlm_sql_checksimul(void *instance, REQUEST * request) { SQLSOCK *sqlsocket; SQL_INST *inst = instance; SQL_ROW row; char querystr[MAX_QUERY_LEN]; char sqlusername[MAX_STRING_LEN]; int check = 0; uint32_t ipno = 0; char *call_num = NULL; VALUE_PAIR *vp; int ret; uint32_t nas_addr = 0; int nas_port = 0; /* If simul_count_query is not defined, we don't do any checking */ if (inst->config->simul_count_query[0] == 0) { return RLM_MODULE_NOOP; } if((request->username == NULL) || (request->username->length == 0)) { radlog(L_ERR, "rlm_sql (%s): Zero Length username not permitted\n", inst->config->xlat_name); return RLM_MODULE_INVALID; } if(sql_set_user(inst, request, sqlusername, NULL) < 0) return RLM_MODULE_FAIL; radius_xlat(querystr, sizeof(querystr), inst->config->simul_count_query, request, sql_escape_func); /* initialize the sql socket */ sqlsocket = sql_get_socket(inst); if(sqlsocket == NULL) return RLM_MODULE_FAIL; if(rlm_sql_select_query(sqlsocket, inst, querystr)) { radlog(L_ERR, "rlm_sql (%s) sql_checksimul: Database query failed", inst->config->xlat_name); sql_release_socket(inst, sqlsocket); return RLM_MODULE_FAIL; } ret = rlm_sql_fetch_row(sqlsocket, inst); if (ret != 0) { (inst->module->sql_finish_select_query)(sqlsocket, inst->config); sql_release_socket(inst, sqlsocket); return RLM_MODULE_FAIL; } row = sqlsocket->row; if (row == NULL) { (inst->module->sql_finish_select_query)(sqlsocket, inst->config); sql_release_socket(inst, sqlsocket); return RLM_MODULE_FAIL; } request->simul_count = atoi(row[0]); (inst->module->sql_finish_select_query)(sqlsocket, inst->config); if(request->simul_count < request->simul_max) { sql_release_socket(inst, sqlsocket); return RLM_MODULE_OK; } /* Looks like too many sessions, so lets start verifying them */ if (inst->config->simul_verify_query[0] == 0) { /* No verify query defined, so skip verify step and rely on count query only */ sql_release_socket(inst, sqlsocket); return RLM_MODULE_OK; } radius_xlat(querystr, sizeof(querystr), inst->config->simul_verify_query, request, sql_escape_func); if(rlm_sql_select_query(sqlsocket, inst, querystr)) { radlog(L_ERR, "rlm_sql (%s): sql_checksimul: Database query error", inst->config->xlat_name); sql_release_socket(inst, sqlsocket); return RLM_MODULE_FAIL; } /* * Setup some stuff, like for MPP detection. */ request->simul_count = 0; if ((vp = pairfind(request->packet->vps, PW_FRAMED_IP_ADDRESS)) != NULL) ipno = vp->lvalue; if ((vp = pairfind(request->packet->vps, PW_CALLING_STATION_ID)) != NULL) call_num = vp->strvalue; while (rlm_sql_fetch_row(sqlsocket, inst) == 0) { row = sqlsocket->row; if (row == NULL) break; if (!row[2]){ (inst->module->sql_finish_select_query)(sqlsocket, inst->config); sql_release_socket(inst, sqlsocket); DEBUG("rlm_sql (%s): Cannot zap stale entry. No username present in entry.", inst->config->xlat_name); return RLM_MODULE_FAIL; } if (!row[1]){ (inst->module->sql_finish_select_query)(sqlsocket, inst->config); sql_release_socket(inst, sqlsocket); DEBUG("rlm_sql (%s): Cannot zap stale entry. No session id in entry.", inst->config->xlat_name); return RLM_MODULE_FAIL; } if (row[3]) nas_addr = inet_addr(row[3]); if (row[4]) nas_port = atoi(row[4]); check = rad_check_ts(nas_addr, nas_port, row[2], row[1]); /* * Failed to check the terminal server for * duplicate logins: Return an error. */ if (check < 0) { (inst->module->sql_finish_select_query)(sqlsocket, inst->config); sql_release_socket(inst, sqlsocket); DEBUG("rlm_sql (%s) rad_check_ts() failed.", inst->config->xlat_name); return RLM_MODULE_FAIL; } if(check == 1) { ++request->simul_count; /* * Does it look like a MPP attempt? */ if (row[5] && ipno && inet_addr(row[5]) == ipno) request->simul_mpp = 2; else if (row[6] && call_num && !strncmp(row[6],call_num,16)) request->simul_mpp = 2; } else { /* * Stale record - zap it. */ uint32_t framed_addr = 0; char proto = 'P'; if (row[5]) framed_addr = inet_addr(row[5]); if (row[7]) if (strcmp(row[7],"SLIP") == 0) proto = 'S'; session_zap(request, nas_addr,nas_port,row[2],row[1], framed_addr, proto); } } (inst->module->sql_finish_select_query)(sqlsocket, inst->config); sql_release_socket(inst, sqlsocket); /* The Auth module apparently looks at request->simul_count, not the return value of this module when deciding to deny a call for too many sessions */ return RLM_MODULE_OK; }
static rlm_rcode_t CC_HINT(nonnull) mod_authorize(void *instance, REQUEST *request) { rlm_rcode_t rcode = RLM_MODULE_NOOP; rlm_sql_t *inst = instance; rlm_sql_handle_t *handle; VALUE_PAIR *check_tmp = NULL; VALUE_PAIR *reply_tmp = NULL; VALUE_PAIR *user_profile = NULL; bool user_found = false; sql_fall_through_t do_fall_through = FALL_THROUGH_DEFAULT; int rows; char *expanded = NULL; rad_assert(request->packet != NULL); rad_assert(request->reply != NULL); if (!inst->config->authorize_check_query && !inst->config->authorize_reply_query && !inst->config->read_groups && !inst->config->read_profiles) { RWDEBUG("No authorization checks configured, returning noop"); return RLM_MODULE_NOOP; } /* * Set, escape, and check the user attr here */ if (sql_set_user(inst, request, NULL) < 0) { return RLM_MODULE_FAIL; } /* * Reserve a socket * * After this point use goto error or goto release to cleanup socket temporary pairlists and * temporary attributes. */ handle = sql_get_socket(inst); if (!handle) { rcode = RLM_MODULE_FAIL; goto error; } /* * Query the check table to find any conditions associated with this user/realm/whatever... */ if (inst->config->authorize_check_query) { vp_cursor_t cursor; VALUE_PAIR *vp; if (radius_axlat(&expanded, request, inst->config->authorize_check_query, sql_escape_func, inst) < 0) { REDEBUG("Error generating query"); rcode = RLM_MODULE_FAIL; goto error; } rows = sql_getvpdata(request, inst, &handle, &check_tmp, expanded); TALLOC_FREE(expanded); if (rows < 0) { REDEBUG("SQL query error"); rcode = RLM_MODULE_FAIL; goto error; } if (rows == 0) goto skipreply; /* Don't need to free VPs we don't have */ /* * Only do this if *some* check pairs were returned */ RDEBUG2("User found in radcheck table"); user_found = true; if (paircompare(request, request->packet->vps, check_tmp, &request->reply->vps) != 0) { pairfree(&check_tmp); check_tmp = NULL; goto skipreply; } RDEBUG2("Conditional check items matched, merging assignment check items"); RINDENT(); for (vp = fr_cursor_init(&cursor, &check_tmp); vp; vp = fr_cursor_next(&cursor)) { if (!fr_assignment_op[vp->op]) continue; rdebug_pair(2, request, vp); } REXDENT(); radius_pairmove(request, &request->config_items, check_tmp, true); rcode = RLM_MODULE_OK; check_tmp = NULL; } if (inst->config->authorize_reply_query) { /* * Now get the reply pairs since the paircompare matched */ if (radius_axlat(&expanded, request, inst->config->authorize_reply_query, sql_escape_func, inst) < 0) { REDEBUG("Error generating query"); rcode = RLM_MODULE_FAIL; goto error; } rows = sql_getvpdata(request->reply, inst, &handle, &reply_tmp, expanded); TALLOC_FREE(expanded); if (rows < 0) { REDEBUG("SQL query error"); rcode = RLM_MODULE_FAIL; goto error; } if (rows == 0) goto skipreply; do_fall_through = fall_through(reply_tmp); RDEBUG2("User found in radreply table, merging reply items"); user_found = true; rdebug_pair_list(L_DBG_LVL_2, request, reply_tmp); radius_pairmove(request, &request->reply->vps, reply_tmp, true); rcode = RLM_MODULE_OK; reply_tmp = NULL; } skipreply: if ((do_fall_through == FALL_THROUGH_YES) || (inst->config->read_groups && (do_fall_through == FALL_THROUGH_DEFAULT))) { rlm_rcode_t ret; RDEBUG3("... falling-through to group processing"); ret = rlm_sql_process_groups(inst, request, &handle, &do_fall_through); switch (ret) { /* * Nothing bad happened, continue... */ case RLM_MODULE_UPDATED: rcode = RLM_MODULE_UPDATED; /* FALL-THROUGH */ case RLM_MODULE_OK: if (rcode != RLM_MODULE_UPDATED) { rcode = RLM_MODULE_OK; } /* FALL-THROUGH */ case RLM_MODULE_NOOP: user_found = true; break; case RLM_MODULE_NOTFOUND: break; default: rcode = ret; goto release; } } /* * Repeat the above process with the default profile or User-Profile */ if ((do_fall_through == FALL_THROUGH_YES) || (inst->config->read_profiles && (do_fall_through == FALL_THROUGH_DEFAULT))) { rlm_rcode_t ret; /* * Check for a default_profile or for a User-Profile. */ RDEBUG3("... falling-through to profile processing"); user_profile = pairfind(request->config_items, PW_USER_PROFILE, 0, TAG_ANY); char const *profile = user_profile ? user_profile->vp_strvalue : inst->config->default_profile; if (!profile || !*profile) { goto release; } RDEBUG2("Checking profile %s", profile); if (sql_set_user(inst, request, profile) < 0) { REDEBUG("Error setting profile"); rcode = RLM_MODULE_FAIL; goto error; } ret = rlm_sql_process_groups(inst, request, &handle, &do_fall_through); switch (ret) { /* * Nothing bad happened, continue... */ case RLM_MODULE_UPDATED: rcode = RLM_MODULE_UPDATED; /* FALL-THROUGH */ case RLM_MODULE_OK: if (rcode != RLM_MODULE_UPDATED) { rcode = RLM_MODULE_OK; } /* FALL-THROUGH */ case RLM_MODULE_NOOP: user_found = true; break; case RLM_MODULE_NOTFOUND: break; default: rcode = ret; goto release; } } /* * At this point the key (user) hasn't be found in the check table, the reply table * or the group mapping table, and there was no matching profile. */ release: if (!user_found) { rcode = RLM_MODULE_NOTFOUND; } sql_release_socket(inst, handle); sql_unset_user(inst, request); return rcode; error: pairfree(&check_tmp); pairfree(&reply_tmp); sql_unset_user(inst, request); sql_release_socket(inst, handle); return rcode; }
static int rlm_sql_authorize(void *instance, REQUEST * request) { VALUE_PAIR *check_tmp = NULL; VALUE_PAIR *reply_tmp = NULL; VALUE_PAIR *user_profile = NULL; int found = 0; int dofallthrough = 1; int rows; SQLSOCK *sqlsocket; SQL_INST *inst = instance; char querystr[MAX_QUERY_LEN]; char sqlusername[MAX_STRING_LEN]; /* * the profile username is used as the sqlusername during * profile checking so that we don't overwrite the orignal * sqlusername string */ char profileusername[MAX_STRING_LEN]; /* * Set, escape, and check the user attr here */ if (sql_set_user(inst, request, sqlusername, NULL) < 0) return RLM_MODULE_FAIL; /* * reserve a socket */ sqlsocket = sql_get_socket(inst); if (sqlsocket == NULL) { /* Remove the username we (maybe) added above */ pairdelete(&request->packet->vps, PW_SQL_USER_NAME, 0); return RLM_MODULE_FAIL; } /* * After this point, ALL 'return's MUST release the SQL socket! */ /* * Alright, start by getting the specific entry for the user */ if (!radius_xlat(querystr, sizeof(querystr), inst->config->authorize_check_query, request, sql_escape_func)) { radlog_request(L_ERR, 0, request, "Error generating query; rejecting user"); sql_release_socket(inst, sqlsocket); /* Remove the username we (maybe) added above */ pairdelete(&request->packet->vps, PW_SQL_USER_NAME, 0); return RLM_MODULE_FAIL; } rows = sql_getvpdata(inst, sqlsocket, &check_tmp, querystr); if (rows < 0) { radlog_request(L_ERR, 0, request, "SQL query error; rejecting user"); sql_release_socket(inst, sqlsocket); /* Remove the username we (maybe) added above */ pairdelete(&request->packet->vps, PW_SQL_USER_NAME, 0); pairfree(&check_tmp); return RLM_MODULE_FAIL; } else if (rows > 0) { /* * Only do this if *some* check pairs were returned */ if (paircompare(request, request->packet->vps, check_tmp, &request->reply->vps) == 0) { found = 1; RDEBUG2("User found in radcheck table"); if (inst->config->authorize_reply_query && *inst->config->authorize_reply_query) { /* * Now get the reply pairs since the paircompare matched */ if (!radius_xlat(querystr, sizeof(querystr), inst->config->authorize_reply_query, request, sql_escape_func)) { radlog_request(L_ERR, 0, request, "Error generating query; rejecting user"); sql_release_socket(inst, sqlsocket); /* Remove the username we (maybe) added above */ pairdelete(&request->packet->vps, PW_SQL_USER_NAME, 0); pairfree(&check_tmp); return RLM_MODULE_FAIL; } if (sql_getvpdata(inst, sqlsocket, &reply_tmp, querystr) < 0) { radlog_request(L_ERR, 0, request, "SQL query error; rejecting user"); sql_release_socket(inst, sqlsocket); /* Remove the username we (maybe) added above */ pairdelete(&request->packet->vps, PW_SQL_USER_NAME, 0); pairfree(&check_tmp); pairfree(&reply_tmp); return RLM_MODULE_FAIL; } if (!inst->config->read_groups) dofallthrough = fallthrough(reply_tmp); pairxlatmove(request, &request->reply->vps, &reply_tmp); } pairxlatmove(request, &request->config_items, &check_tmp); } } /* * Clear out the pairlists */ pairfree(&check_tmp); pairfree(&reply_tmp); /* * dofallthrough is set to 1 by default so that if the user information * is not found, we will still process groups. If the user information, * however, *is* found, Fall-Through must be set in order to process * the groups as well */ if (dofallthrough) { rows = rlm_sql_process_groups(inst, request, sqlsocket, &dofallthrough); if (rows < 0) { radlog_request(L_ERR, 0, request, "Error processing groups; rejecting user"); sql_release_socket(inst, sqlsocket); /* Remove the username we (maybe) added above */ pairdelete(&request->packet->vps, PW_SQL_USER_NAME, 0); return RLM_MODULE_FAIL; } else if (rows > 0) { found = 1; } } /* * repeat the above process with the default profile or User-Profile */ if (dofallthrough) { int profile_found = 0; /* * Check for a default_profile or for a User-Profile. */ user_profile = pairfind(request->config_items, PW_USER_PROFILE, 0); if (inst->config->default_profile[0] != 0 || user_profile != NULL){ char *profile = inst->config->default_profile; if (user_profile != NULL) profile = user_profile->vp_strvalue; if (profile && strlen(profile)){ RDEBUG("Checking profile %s", profile); if (sql_set_user(inst, request, profileusername, profile) < 0) { radlog_request(L_ERR, 0, request, "Error setting profile; rejecting user"); sql_release_socket(inst, sqlsocket); /* Remove the username we (maybe) added above */ pairdelete(&request->packet->vps, PW_SQL_USER_NAME, 0); return RLM_MODULE_FAIL; } else { profile_found = 1; } } } if (profile_found) { rows = rlm_sql_process_groups(inst, request, sqlsocket, &dofallthrough); if (rows < 0) { radlog_request(L_ERR, 0, request, "Error processing profile groups; rejecting user"); sql_release_socket(inst, sqlsocket); /* Remove the username we (maybe) added above */ pairdelete(&request->packet->vps, PW_SQL_USER_NAME, 0); return RLM_MODULE_FAIL; } else if (rows > 0) { found = 1; } } } /* Remove the username we (maybe) added above */ pairdelete(&request->packet->vps, PW_SQL_USER_NAME, 0); sql_release_socket(inst, sqlsocket); if (!found) { RDEBUG("User %s not found", sqlusername); return RLM_MODULE_NOTFOUND; } else { return RLM_MODULE_OK; } }
static int sql_groupcmp(void *instance, REQUEST *request, VALUE_PAIR *request_vp, VALUE_PAIR *check, VALUE_PAIR *check_pairs, VALUE_PAIR **reply_pairs) { SQLSOCK *sqlsocket; SQL_INST *inst = instance; char sqlusername[MAX_STRING_LEN]; SQL_GROUPLIST *group_list, *group_list_tmp; check_pairs = check_pairs; reply_pairs = reply_pairs; request_vp = request_vp; RDEBUG("sql_groupcmp"); if (!check || !check->vp_strvalue || !check->length){ RDEBUG("sql_groupcmp: Illegal group name"); return 1; } if (!request){ RDEBUG("sql_groupcmp: NULL request"); return 1; } /* * Set, escape, and check the user attr here */ if (sql_set_user(inst, request, sqlusername, NULL) < 0) return 1; /* * Get a socket for this lookup */ sqlsocket = sql_get_socket(inst); if (sqlsocket == NULL) { /* Remove the username we (maybe) added above */ pairdelete(&request->packet->vps, PW_SQL_USER_NAME, 0); return 1; } /* * Get the list of groups this user is a member of */ if (sql_get_grouplist(inst, sqlsocket, request, &group_list) < 0) { radlog_request(L_ERR, 0, request, "Error getting group membership"); /* Remove the username we (maybe) added above */ pairdelete(&request->packet->vps, PW_SQL_USER_NAME, 0); sql_release_socket(inst, sqlsocket); return 1; } for (group_list_tmp = group_list; group_list_tmp != NULL; group_list_tmp = group_list_tmp->next) { if (strcmp(group_list_tmp->groupname, check->vp_strvalue) == 0){ RDEBUG("sql_groupcmp finished: User is a member of group %s", check->vp_strvalue); /* Free the grouplist */ sql_grouplist_free(&group_list); /* Remove the username we (maybe) added above */ pairdelete(&request->packet->vps, PW_SQL_USER_NAME, 0); sql_release_socket(inst, sqlsocket); return 0; } } /* Free the grouplist */ sql_grouplist_free(&group_list); /* Remove the username we (maybe) added above */ pairdelete(&request->packet->vps, PW_SQL_USER_NAME, 0); sql_release_socket(inst,sqlsocket); RDEBUG("sql_groupcmp finished: User is NOT a member of group %s", check->vp_strvalue); return 1; }
static int sql_groupcmp(void *instance, REQUEST *request, UNUSED VALUE_PAIR *request_vp, VALUE_PAIR *check, UNUSED VALUE_PAIR *check_pairs, UNUSED VALUE_PAIR **reply_pairs) { rlm_sql_handle_t *handle; rlm_sql_t *inst = instance; rlm_sql_grouplist_t *head, *entry; /* * No group queries, don't do group comparisons. */ if (!inst->config->groupmemb_query) { RWARN("Cannot do group comparison when group_membership_query is not set"); return 1; } RDEBUG("sql_groupcmp"); if (check->vp_length == 0){ RDEBUG("sql_groupcmp: Illegal group name"); return 1; } /* * Set, escape, and check the user attr here */ if (sql_set_user(inst, request, NULL) < 0) return 1; /* * Get a socket for this lookup */ handle = fr_connection_get(inst->pool); if (!handle) { return 1; } /* * Get the list of groups this user is a member of */ if (sql_get_grouplist(inst, &handle, request, &head) < 0) { REDEBUG("Error getting group membership"); fr_connection_release(inst->pool, handle); return 1; } for (entry = head; entry != NULL; entry = entry->next) { if (strcmp(entry->name, check->vp_strvalue) == 0){ RDEBUG("sql_groupcmp finished: User is a member of group %s", check->vp_strvalue); talloc_free(head); fr_connection_release(inst->pool, handle); return 0; } } /* Free the grouplist */ talloc_free(head); fr_connection_release(inst->pool, handle); RDEBUG("sql_groupcmp finished: User is NOT a member of group %s", check->vp_strvalue); return 1; }
/** Executes a SELECT query and maps the result to server attributes * * @param mod_inst #rlm_sql_t instance. * @param proc_inst Instance data for this specific mod_proc call (unused). * @param request The current request. * @param query string to execute. * @param maps Head of the map list. * @return * - #RLM_MODULE_NOOP no rows were returned or columns matched. * - #RLM_MODULE_UPDATED if one or more #VALUE_PAIR were added to the #REQUEST. * - #RLM_MODULE_FAIL if a fault occurred. */ static rlm_rcode_t mod_map_proc(void *mod_inst, UNUSED void *proc_inst, REQUEST *request, char const *query, vp_map_t const *maps) { rlm_sql_t *inst = talloc_get_type_abort(mod_inst, rlm_sql_t); rlm_sql_handle_t *handle = NULL; int i, j; rlm_rcode_t rcode = RLM_MODULE_UPDATED; sql_rcode_t ret; vp_map_t const *map; rlm_sql_row_t row; int rows; int field_cnt; char const **fields = NULL, *map_rhs; char map_rhs_buff[128]; #define MAX_SQL_FIELD_INDEX (64) int field_index[MAX_SQL_FIELD_INDEX]; bool found_field = false; /* Did we find any matching fields in the result set ? */ rad_assert(inst->module->sql_fields); /* Should have been caught during validation... */ for (i = 0; i < MAX_SQL_FIELD_INDEX; i++) field_index[i] = -1; /* * 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, NULL); handle = fr_connection_get(inst->pool); /* connection pool should produce error */ if (!handle) return 0; rlm_sql_query_log(inst, request, NULL, query); ret = rlm_sql_select_query(inst, request, &handle, query); if (ret != RLM_SQL_OK) { RERROR("SQL query failed: %s", fr_int2str(sql_rcode_table, ret, "<INVALID>")); rcode = RLM_MODULE_FAIL; goto finish; } ret = (inst->module->sql_fields)(&fields, handle, inst->config); if (ret != RLM_SQL_OK) { RERROR("Failed retrieving field names: %s", fr_int2str(sql_rcode_table, ret, "<INVALID>")); error: rcode = RLM_MODULE_FAIL; (inst->module->sql_finish_select_query)(handle, inst->config); goto finish; } rad_assert(fields); field_cnt = talloc_array_length(fields); if (RDEBUG_ENABLED3) for (j = 0; j < field_cnt; j++) RDEBUG3("Got field: %s", fields[j]); /* * Iterate over the maps, it's O(N2)ish but probably * faster than building a radix tree each time the * map set is evaluated (map->rhs can be dynamic). */ for (map = maps, i = 0; map && (i < MAX_SQL_FIELD_INDEX); map = map->next, i++) { /* * Expand the RHS to get the name of the SQL field */ if (tmpl_expand(&map_rhs, map_rhs_buff, sizeof(map_rhs_buff), request, map->rhs, NULL, NULL) < 0) { RERROR("Failed getting field name: %s", fr_strerror()); goto error; } for (j = 0; j < field_cnt; j++) { if (strcmp(fields[j], map_rhs) != 0) continue; field_index[i] = j; found_field = true; } } /* * Couldn't resolve any map RHS values to fields * in the result set. */ if (!found_field) { RDEBUG("No fields matching map found in query result"); rcode = RLM_MODULE_NOOP; (inst->module->sql_finish_select_query)(handle, inst->config); goto finish; } /* * We've resolved all the maps to result indexes, now convert * the values at those indexes into VALUE_PAIRs. * * Note: Not all SQL client libraries provide a row count, * so we have to do the count here. */ for (ret = rlm_sql_fetch_row(&row, inst, request, &handle), rows = 0; (ret == RLM_SQL_OK) && row; ret = rlm_sql_fetch_row(&row, inst, request, &handle), rows++) { for (map = maps, j = 0; map && (j < MAX_SQL_FIELD_INDEX); map = map->next, j++) { if (field_index[j] < 0) continue; /* We didn't find the map RHS in the field set */ if (map_to_request(request, map, _sql_map_proc_get_value, row[field_index[j]]) < 0) goto error; } } if (ret == RLM_SQL_ERROR) goto error; if (!rows) { RDEBUG("SQL query returned no results"); rcode = RLM_MODULE_NOOP; } (inst->module->sql_finish_select_query)(handle, inst->config); finish: talloc_free(fields); fr_connection_release(inst->pool, handle); return rcode; }
static int sql_groupcmp(void *instance, REQUEST *request, VALUE_PAIR *request_vp, VALUE_PAIR *check, VALUE_PAIR *check_pairs, VALUE_PAIR **reply_pairs) { rlm_sql_handle_t *handle; rlm_sql_t *inst = instance; rlm_sql_grouplist_t *group_list, *group_list_tmp; check_pairs = check_pairs; reply_pairs = reply_pairs; request_vp = request_vp; RDEBUG("sql_groupcmp"); if (!check || !check->length){ RDEBUG("sql_groupcmp: Illegal group name"); return 1; } if (!request){ RDEBUG("sql_groupcmp: NULL request"); return 1; } /* * Set, escape, and check the user attr here */ if (sql_set_user(inst, request, NULL) < 0) return 1; /* * Get a socket for this lookup */ handle = sql_get_socket(inst); if (handle == NULL) { return 1; } /* * Get the list of groups this user is a member of */ if (sql_get_grouplist(inst, handle, request, &group_list) < 0) { radlog_request(L_ERR, 0, request, "Error getting group membership"); sql_release_socket(inst, handle); return 1; } for (group_list_tmp = group_list; group_list_tmp != NULL; group_list_tmp = group_list_tmp->next) { if (strcmp(group_list_tmp->groupname, check->vp_strvalue) == 0){ RDEBUG("sql_groupcmp finished: User is a member of group %s", check->vp_strvalue); /* Free the grouplist */ sql_grouplist_free(&group_list); sql_release_socket(inst, handle); return 0; } } /* Free the grouplist */ sql_grouplist_free(&group_list); sql_release_socket(inst,handle); RDEBUG("sql_groupcmp finished: User is NOT a member of group %s", check->vp_strvalue); return 1; }
static rlm_rcode_t rlm_sql_authorize(void *instance, REQUEST * request) { int ret = RLM_MODULE_NOTFOUND; rlm_sql_t *inst = instance; rlm_sql_handle_t *handle; VALUE_PAIR *check_tmp = NULL; VALUE_PAIR *reply_tmp = NULL; VALUE_PAIR *user_profile = NULL; int dofallthrough = 1; int rows; char querystr[MAX_QUERY_LEN]; /* * Set, escape, and check the user attr here */ if (sql_set_user(inst, request, NULL) < 0) return RLM_MODULE_FAIL; /* * Reserve a socket * * After this point use goto error or goto release to cleanup sockets * temporary pairlists and temporary attributes. */ handle = sql_get_socket(inst); if (handle == NULL) goto error; /* * Query the check table to find any conditions associated with * this user/realm/whatever... */ if (inst->config->authorize_check_query && *inst->config->authorize_check_query) { if (!radius_xlat(querystr, sizeof(querystr), inst->config->authorize_check_query, request, sql_escape_func, inst)) { radlog_request(L_ERR, 0, request, "Error generating query; rejecting user"); goto error; } rows = sql_getvpdata(inst, &handle, &check_tmp, querystr); if (rows < 0) { radlog_request(L_ERR, 0, request, "SQL query error; rejecting user"); goto error; } /* * Only do this if *some* check pairs were returned */ if ((rows > 0) && (paircompare(request, request->packet->vps, check_tmp, &request->reply->vps) == 0)) { RDEBUG2("User found in radcheck table"); radius_xlat_move(request, &request->config_items, &check_tmp); ret = RLM_MODULE_OK; } /* * We only process reply table items if check conditions * were verified */ else goto skipreply; } if (inst->config->authorize_reply_query && *inst->config->authorize_reply_query) { /* * Now get the reply pairs since the paircompare matched */ if (!radius_xlat(querystr, sizeof(querystr), inst->config->authorize_reply_query, request, sql_escape_func, inst)) { radlog_request(L_ERR, 0, request, "Error generating query; rejecting user"); goto error; } rows = sql_getvpdata(inst, &handle, &reply_tmp, querystr); if (rows < 0) { radlog_request(L_ERR, 0, request, "SQL query error; rejecting user"); goto error; } if (rows > 0) { if (!inst->config->read_groups) { dofallthrough = fallthrough(reply_tmp); } RDEBUG2("User found in radreply table"); radius_xlat_move(request, &request->reply->vps, &reply_tmp); ret = RLM_MODULE_OK; } } skipreply: /* * Clear out the pairlists */ pairfree(&check_tmp); pairfree(&reply_tmp); /* * dofallthrough is set to 1 by default so that if the user information * is not found, we will still process groups. If the user information, * however, *is* found, Fall-Through must be set in order to process * the groups as well. */ if (dofallthrough) { rows = rlm_sql_process_groups(inst, request, handle, &dofallthrough); if (rows < 0) { radlog_request(L_ERR, 0, request, "Error processing groups; rejecting user"); goto error; } if (rows > 0) ret = RLM_MODULE_OK; } /* * Repeat the above process with the default profile or User-Profile */ if (dofallthrough) { /* * Check for a default_profile or for a User-Profile. */ user_profile = pairfind(request->config_items, PW_USER_PROFILE, 0, TAG_ANY); const char *profile = user_profile ? user_profile->vp_strvalue : inst->config->default_profile; if (!profile || !*profile) goto release; RDEBUG("Checking profile %s", profile); if (sql_set_user(inst, request, profile) < 0) { radlog_request(L_ERR, 0, request, "Error setting profile; rejecting user"); goto error; } rows = rlm_sql_process_groups(inst, request, handle, &dofallthrough); if (rows < 0) { radlog_request(L_ERR, 0, request, "Error processing profile groups; rejecting user"); goto error; } if (rows > 0) ret = RLM_MODULE_OK; } goto release; error: ret = RLM_MODULE_FAIL; release: sql_release_socket(inst, handle); pairfree(&check_tmp); pairfree(&reply_tmp); 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; }
/* * Generic function for failing between a bunch of queries. * * Uses the same principle as rlm_linelog, expanding the 'reference' config * item using xlat to figure out what query it should execute. * * If the reference matches multiple config items, and a query fails or * doesn't update any rows, the next matching config item is used. * */ static int acct_redundant(rlm_sql_t *inst, REQUEST *request, sql_acct_section_t *section) { rlm_rcode_t rcode = RLM_MODULE_OK; rlm_sql_handle_t *handle = NULL; int sql_ret; int numaffected = 0; CONF_ITEM *item; CONF_PAIR *pair; char const *attr = NULL; char const *value; char path[MAX_STRING_LEN]; char *p = path; char *expanded = NULL; rad_assert(section); if (section->reference[0] != '.') { *p++ = '.'; } if (radius_xlat(p, sizeof(path) - (p - path), request, section->reference, NULL, NULL) < 0) { rcode = RLM_MODULE_FAIL; goto finish; } item = cf_reference_item(NULL, section->cs, path); if (!item) { rcode = RLM_MODULE_FAIL; goto finish; } if (cf_item_is_section(item)){ REDEBUG("Sections are not supported as references"); rcode = RLM_MODULE_FAIL; goto finish; } pair = cf_itemtopair(item); attr = cf_pair_attr(pair); RDEBUG2("Using query template '%s'", attr); handle = sql_get_socket(inst); if (!handle) { rcode = RLM_MODULE_FAIL; goto finish; } sql_set_user(inst, request, NULL); while (true) { value = cf_pair_value(pair); if (!value) { RDEBUG("Ignoring null query"); rcode = RLM_MODULE_NOOP; goto finish; } if (radius_axlat(&expanded, request, value, sql_escape_func, inst) < 0) { rcode = RLM_MODULE_FAIL; goto finish; } if (!*expanded) { RDEBUG("Ignoring null query"); rcode = RLM_MODULE_NOOP; talloc_free(expanded); goto finish; } rlm_sql_query_log(inst, request, section, expanded); /* * If rlm_sql_query cannot use the socket it'll try and * reconnect. Reconnecting will automatically release * the current socket, and try to select a new one. * * If we get RLM_SQL_RECONNECT it means all connections in the pool * were exhausted, and we couldn't create a new connection, * so we do not need to call sql_release_socket. */ sql_ret = rlm_sql_query(&handle, inst, expanded); TALLOC_FREE(expanded); if (sql_ret == RLM_SQL_RECONNECT) { rcode = RLM_MODULE_FAIL; goto finish; } rad_assert(handle); /* * Assume all other errors are incidental, and just meant our * operation failed and its not a client or SQL syntax error. * * @fixme We should actually be able to distinguish between key * constraint violations (which we expect) and other errors. */ if (sql_ret == RLM_SQL_OK) { numaffected = (inst->module->sql_affected_rows)(handle, inst->config); if (numaffected > 0) { break; /* A query succeeded, were done! */ } RDEBUG("No records updated"); } (inst->module->sql_finish_query)(handle, inst->config); /* * We assume all entries with the same name form a redundant * set of queries. */ pair = cf_pair_find_next(section->cs, pair, attr); if (!pair) { RDEBUG("No additional queries configured"); rcode = RLM_MODULE_NOOP; goto finish; } RDEBUG("Trying next query..."); } (inst->module->sql_finish_query)(handle, inst->config); finish: talloc_free(expanded); sql_release_socket(inst, handle); return rcode; }
static int sql_groupcmp(void *instance, REQUEST *req, VALUE_PAIR *request, VALUE_PAIR *check, VALUE_PAIR *check_pairs, VALUE_PAIR **reply_pairs) { SQLSOCK *sqlsocket; SQL_ROW row; SQL_INST *inst = instance; char querystr[MAX_QUERY_LEN]; char sqlusername[MAX_STRING_LEN]; check_pairs = check_pairs; reply_pairs = reply_pairs; DEBUG("rlm_sql (%s): - sql_groupcmp", inst->config->xlat_name); if (!check || !check->strvalue || !check->length){ DEBUG("rlm_sql (%s): sql_groupcmp: Illegal group name", inst->config->xlat_name); return 1; } if (req == NULL){ DEBUG("rlm_sql (%s): sql_groupcmp: NULL request", inst->config->xlat_name); return 1; } if (inst->config->groupmemb_query[0] == 0) return 1; /* * Set, escape, and check the user attr here */ if (sql_set_user(inst, req, sqlusername, NULL) < 0) return 1; if (!radius_xlat(querystr, sizeof(querystr), inst->config->groupmemb_query, req, sql_escape_func)){ radlog(L_ERR, "rlm_sql (%s): xlat failed.", inst->config->xlat_name); /* Remove the username we (maybe) added above */ pairdelete(&req->packet->vps, PW_SQL_USER_NAME); return 1; } /* Remove the username we (maybe) added above */ pairdelete(&req->packet->vps, PW_SQL_USER_NAME); sqlsocket = sql_get_socket(inst); if (sqlsocket == NULL) return 1; if ((inst->module->sql_select_query)(sqlsocket,inst->config,querystr) <0){ 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 1; } while (rlm_sql_fetch_row(sqlsocket, inst) == 0) { row = sqlsocket->row; if (row == NULL) break; 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 1; } if (strcmp(row[0],check->strvalue) == 0){ DEBUG("rlm_sql (%s): - sql_groupcmp finished: User belongs in group %s", inst->config->xlat_name, (char *)check->strvalue); (inst->module->sql_finish_select_query)(sqlsocket, inst->config); sql_release_socket(inst, sqlsocket); return 0; } } (inst->module->sql_finish_select_query)(sqlsocket, inst->config); sql_release_socket(inst,sqlsocket); DEBUG("rlm_sql (%s): - sql_groupcmp finished: User does not belong in group %s", inst->config->xlat_name, (char *)check->strvalue); return 1; }
/* * SQL xlat function * * For selects the first value of the first column will be returned, * for inserts, updates and deletes the number of rows afftected will be * returned instead. */ static ssize_t sql_xlat(void *instance, REQUEST *request, char const *query, char *out, size_t freespace) { rlm_sql_handle_t *handle = NULL; rlm_sql_row_t row; rlm_sql_t *inst = instance; ssize_t ret = 0; size_t len = 0; /* * 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, NULL); handle = sql_get_socket(inst); if (!handle) { return 0; } rlm_sql_query_log(inst, request, NULL, query); /* * If the query starts with any of the following prefixes, * then return the number of rows affected */ if ((strncasecmp(query, "insert", 6) == 0) || (strncasecmp(query, "update", 6) == 0) || (strncasecmp(query, "delete", 6) == 0)) { int numaffected; char buffer[21]; /* 64bit max is 20 decimal chars + null byte */ if (rlm_sql_query(&handle, inst, query)) { char const *error = (inst->module->sql_error)(handle, inst->config); REDEBUG("SQL query failed: %s", error); ret = -1; goto finish; } numaffected = (inst->module->sql_affected_rows)(handle, inst->config); if (numaffected < 1) { RDEBUG("SQL query affected no rows"); goto finish; } /* * 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); len = strlen(buffer); if (len >= freespace){ RDEBUG("rlm_sql (%s): Can't write result, insufficient string space", inst->config->xlat_name); (inst->module->sql_finish_query)(handle, inst->config); ret = -1; goto finish; } memcpy(out, buffer, len + 1); /* we did bounds checking above */ ret = len; (inst->module->sql_finish_query)(handle, inst->config); goto finish; } /* else it's a SELECT statement */ if (rlm_sql_select_query(&handle, inst, query)){ char const *error = (inst->module->sql_error)(handle, inst->config); REDEBUG("SQL query failed: %s", error); ret = -1; goto finish; } ret = rlm_sql_fetch_row(&handle, inst); if (ret) { REDEBUG("SQL query failed"); (inst->module->sql_finish_select_query)(handle, inst->config); ret = -1; goto finish; } row = handle->row; if (!row) { RDEBUG("SQL query returned no results"); (inst->module->sql_finish_select_query)(handle, inst->config); ret = -1; goto finish; } if (!row[0]){ RDEBUG("NULL value in first column of result"); (inst->module->sql_finish_select_query)(handle, inst->config); ret = -1; goto finish; } len = strlen(row[0]); if (len >= freespace){ RDEBUG("Insufficient string space"); (inst->module->sql_finish_select_query)(handle, inst->config); ret = -1; goto finish; } strlcpy(out, row[0], freespace); ret = len; (inst->module->sql_finish_select_query)(handle, inst->config); finish: sql_release_socket(inst, handle); return ret; }
static int rlm_sql_authorize(void *instance, REQUEST * request) { VALUE_PAIR *check_tmp = NULL; VALUE_PAIR *reply_tmp = NULL; VALUE_PAIR *user_profile = NULL; int found = 0; SQLSOCK *sqlsocket; SQL_INST *inst = instance; char querystr[MAX_QUERY_LEN]; char sqlusername[MAX_STRING_LEN]; /* * They MUST have a user name to do SQL authorization. */ if ((request->username == NULL) || (request->username->length == 0)) { radlog(L_ERR, "rlm_sql (%s): zero length username not permitted\n", inst->config->xlat_name); return RLM_MODULE_INVALID; } /* * Set, escape, and check the user attr here. */ if (sql_set_user(inst, request, sqlusername, NULL) < 0) return RLM_MODULE_FAIL; radius_xlat(querystr, sizeof(querystr), inst->config->authorize_check_query, request, sql_escape_func); sqlsocket = sql_get_socket(inst); if (sqlsocket == NULL) { /* Remove the username we (maybe) added above */ pairdelete(&request->packet->vps, PW_SQL_USER_NAME); return RLM_MODULE_FAIL; } /* * After this point, ALL 'return's MUST release the SQL socket! */ found = sql_getvpdata(inst, sqlsocket, &check_tmp, querystr, PW_VP_USERDATA); /* * Find the entry for the user. */ if (found > 0) { radius_xlat(querystr, sizeof(querystr), inst->config->authorize_group_check_query, request, sql_escape_func); sql_getvpdata(inst, sqlsocket, &check_tmp, querystr, PW_VP_GROUPDATA); radius_xlat(querystr, sizeof(querystr), inst->config->authorize_reply_query, request, sql_escape_func); sql_getvpdata(inst, sqlsocket, &reply_tmp, querystr, PW_VP_USERDATA); radius_xlat(querystr, sizeof(querystr), inst->config->authorize_group_reply_query, request, sql_escape_func); sql_getvpdata(inst, sqlsocket, &reply_tmp, querystr, PW_VP_GROUPDATA); } else if (found < 0) { radlog(L_ERR, "rlm_sql (%s): SQL query error; rejecting user", inst->config->xlat_name); sql_release_socket(inst, sqlsocket); /* Remove the username we (maybe) added above */ pairdelete(&request->packet->vps, PW_SQL_USER_NAME); pairfree(&check_tmp); return RLM_MODULE_FAIL; } else { radlog(L_DBG, "rlm_sql (%s): User %s not found in radcheck", inst->config->xlat_name, sqlusername); /* * We didn't find the user in radcheck, so we try looking * for radgroupcheck entry */ radius_xlat(querystr, sizeof(querystr), inst->config->authorize_group_check_query, request, sql_escape_func); found = sql_getvpdata(inst, sqlsocket, &check_tmp, querystr, PW_VP_GROUPDATA); radius_xlat(querystr, sizeof(querystr), inst->config->authorize_group_reply_query, request, sql_escape_func); sql_getvpdata(inst, sqlsocket, &reply_tmp, querystr, PW_VP_GROUPDATA); } if (!found) radlog(L_DBG, "rlm_sql (%s): User %s not found in radgroupcheck", inst->config->xlat_name, sqlusername); if (found || (!found && inst->config->query_on_not_found)){ int def_found = 0; /* * Check for a default_profile or for a User-Profile. */ user_profile = pairfind(request->config_items, PW_USER_PROFILE); if (inst->config->default_profile[0] != 0 || user_profile != NULL){ char *profile = inst->config->default_profile; if (user_profile != NULL) profile = user_profile->strvalue; if (profile && strlen(profile)){ radlog(L_DBG, "rlm_sql (%s): Checking profile %s", inst->config->xlat_name, profile); if (sql_set_user(inst, request, sqlusername, profile) < 0) { sql_release_socket(inst, sqlsocket); pairfree(&reply_tmp); pairfree(&check_tmp); return RLM_MODULE_FAIL; } radius_xlat(querystr, sizeof(querystr), inst->config->authorize_group_check_query, request, sql_escape_func); def_found = sql_getvpdata(inst, sqlsocket, &check_tmp, querystr, PW_VP_GROUPDATA); if (def_found) found = 1; radius_xlat(querystr, sizeof(querystr), inst->config->authorize_group_reply_query, request, sql_escape_func); sql_getvpdata(inst, sqlsocket, &reply_tmp, querystr, PW_VP_GROUPDATA); } } } /* * We don't need the SQL socket anymore. */ sql_release_socket(inst, sqlsocket); if (!found) { radlog(L_DBG, "rlm_sql (%s): User not found", inst->config->xlat_name); /* Remove the username we (maybe) added above */ pairdelete(&request->packet->vps, PW_SQL_USER_NAME); pairfree(&reply_tmp); pairfree(&check_tmp); return RLM_MODULE_NOTFOUND; } /* * Uncomment these lines for debugging * Recompile, and run 'radiusd -X' */ /* DEBUG2("rlm_sql: check items"); vp_listdebug(check_tmp); DEBUG2("rlm_sql: reply items"); vp_listdebug(reply_tmp); */ if (paircmp(request, request->packet->vps, check_tmp, &reply_tmp) != 0) { radlog(L_INFO, "rlm_sql (%s): No matching entry in the database for request from user [%s]", inst->config->xlat_name, sqlusername); /* Remove the username we (maybe) added above */ pairdelete(&request->packet->vps, PW_SQL_USER_NAME); pairfree(&reply_tmp); pairfree(&check_tmp); return RLM_MODULE_NOTFOUND; } pairxlatmove(request, &request->reply->vps, &reply_tmp); pairxlatmove(request, &request->config_items, &check_tmp); pairfree(&reply_tmp); pairfree(&check_tmp); /* Remove the username we (maybe) added above */ pairdelete(&request->packet->vps, PW_SQL_USER_NAME); return RLM_MODULE_OK; }
static rlm_rcode_t mod_authorize(void *instance, REQUEST * request) { rlm_rcode_t rcode = RLM_MODULE_NOOP; rlm_sql_t *inst = instance; rlm_sql_handle_t *handle; VALUE_PAIR *check_tmp = NULL; VALUE_PAIR *reply_tmp = NULL; VALUE_PAIR *user_profile = NULL; bool user_found = false; bool dofallthrough = true; int rows; char *expanded = NULL; rad_assert(request != NULL); rad_assert(request->packet != NULL); rad_assert(request->reply != NULL); /* * Set, escape, and check the user attr here */ if (sql_set_user(inst, request, NULL) < 0) { return RLM_MODULE_FAIL; } /* * Reserve a socket * * After this point use goto error or goto release to cleanup socket temporary pairlists and * temporary attributes. */ handle = sql_get_socket(inst); if (!handle) { rcode = RLM_MODULE_FAIL; goto error; } /* * Query the check table to find any conditions associated with this user/realm/whatever... */ if (inst->config->authorize_check_query && (inst->config->authorize_check_query[0] != '\0')) { if (radius_axlat(&expanded, request, inst->config->authorize_check_query, sql_escape_func, inst) < 0) { REDEBUG("Error generating query"); rcode = RLM_MODULE_FAIL; goto error; } rows = sql_getvpdata(inst, &handle, request, &check_tmp, expanded); TALLOC_FREE(expanded); if (rows < 0) { REDEBUG("SQL query error"); rcode = RLM_MODULE_FAIL; goto error; } if (rows == 0) { goto skipreply; } /* * Only do this if *some* check pairs were returned */ RDEBUG2("User found in radcheck table"); user_found = true; if (paircompare(request, request->packet->vps, check_tmp, &request->reply->vps) != 0) { goto skipreply; } RDEBUG2("Check items matched"); radius_pairmove(request, &request->config_items, check_tmp, true); rcode = RLM_MODULE_OK; } if (inst->config->authorize_reply_query && (inst->config->authorize_reply_query[0] != '\0')) { /* * Now get the reply pairs since the paircompare matched */ if (radius_axlat(&expanded, request, inst->config->authorize_reply_query, sql_escape_func, inst) < 0) { REDEBUG("Error generating query"); rcode = RLM_MODULE_FAIL; goto error; } rows = sql_getvpdata(inst, &handle, request->reply, &reply_tmp, expanded); TALLOC_FREE(expanded); if (rows < 0) { REDEBUG("SQL query error"); rcode = RLM_MODULE_FAIL; goto error; } if (rows == 0) { goto skipreply; } if (!inst->config->read_groups) { dofallthrough = fallthrough(reply_tmp); } RDEBUG2("User found in radreply table"); user_found = true; radius_pairmove(request, &request->reply->vps, reply_tmp, true); rcode = RLM_MODULE_OK; } skipreply: /* * Clear out the pairlists */ pairfree(&check_tmp); pairfree(&reply_tmp); /* * dofallthrough is set to 1 by default so that if the user information * is not found, we will still process groups. If the user information, * however, *is* found, Fall-Through must be set in order to process * the groups as well. */ if (dofallthrough) { rlm_rcode_t ret; RDEBUG3("... falling-through to group processing"); ret = rlm_sql_process_groups(inst, request, handle, &dofallthrough); switch (ret) { /* * Nothing bad happened, continue... */ case RLM_MODULE_UPDATED: rcode = RLM_MODULE_UPDATED; /* FALL-THROUGH */ case RLM_MODULE_OK: if (rcode != RLM_MODULE_UPDATED) { rcode = RLM_MODULE_OK; } /* FALL-THROUGH */ case RLM_MODULE_NOOP: user_found = true; break; case RLM_MODULE_NOTFOUND: break; default: rcode = ret; goto release; } } /* * Repeat the above process with the default profile or User-Profile */ if (dofallthrough) { rlm_rcode_t ret; /* * Check for a default_profile or for a User-Profile. */ RDEBUG3("... falling-through to profile processing"); user_profile = pairfind(request->config_items, PW_USER_PROFILE, 0, TAG_ANY); char const *profile = user_profile ? user_profile->vp_strvalue : inst->config->default_profile; if (!profile || !*profile) { goto release; } RDEBUG2("Checking profile %s", profile); if (sql_set_user(inst, request, profile) < 0) { REDEBUG("Error setting profile"); rcode = RLM_MODULE_FAIL; goto error; } ret = rlm_sql_process_groups(inst, request, handle, &dofallthrough); switch (ret) { /* * Nothing bad happened, continue... */ case RLM_MODULE_UPDATED: rcode = RLM_MODULE_UPDATED; /* FALL-THROUGH */ case RLM_MODULE_OK: if (rcode != RLM_MODULE_UPDATED) { rcode = RLM_MODULE_OK; } /* FALL-THROUGH */ case RLM_MODULE_NOOP: user_found = true; break; case RLM_MODULE_NOTFOUND: break; default: rcode = ret; goto release; } } /* * At this point the key (user) hasn't be found in the check table, the reply table * or the group mapping table, and there was no matching profile. */ release: if (!user_found) { rcode = RLM_MODULE_NOTFOUND; } error: sql_release_socket(inst, handle); pairfree(&check_tmp); pairfree(&reply_tmp); return rcode; }
/** Execute an arbitrary SQL query * * For selects the first value of the first column will be returned, * for inserts, updates and deletes the number of rows affected will be * returned instead. */ static ssize_t sql_xlat(char **out, UNUSED size_t outlen, void const *mod_inst, UNUSED void const *xlat_inst, REQUEST *request, char const *fmt) { rlm_sql_handle_t *handle = NULL; rlm_sql_row_t row; rlm_sql_t const *inst = mod_inst; sql_rcode_t rcode; ssize_t ret = 0; /* * 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, NULL); handle = fr_connection_get(inst->pool); /* connection pool should produce error */ if (!handle) return 0; rlm_sql_query_log(inst, request, NULL, fmt); /* * If the query starts with any of the following prefixes, * then return the number of rows affected */ if ((strncasecmp(fmt, "insert", 6) == 0) || (strncasecmp(fmt, "update", 6) == 0) || (strncasecmp(fmt, "delete", 6) == 0)) { int numaffected; rcode = rlm_sql_query(inst, request, &handle, fmt); if (rcode != RLM_SQL_OK) { query_error: RERROR("SQL query failed: %s", fr_int2str(sql_rcode_table, rcode, "<INVALID>")); ret = -1; goto finish; } numaffected = (inst->module->sql_affected_rows)(handle, inst->config); if (numaffected < 1) { RDEBUG("SQL query affected no rows"); goto finish; } MEM(*out = talloc_asprintf(request, "%d", numaffected)); ret = talloc_array_length(*out) - 1; (inst->module->sql_finish_query)(handle, inst->config); goto finish; } /* else it's a SELECT statement */ rcode = rlm_sql_select_query(inst, request, &handle, fmt); if (rcode != RLM_SQL_OK) goto query_error; rcode = rlm_sql_fetch_row(&row, inst, request, &handle); if (rcode) { (inst->module->sql_finish_select_query)(handle, inst->config); goto query_error; } if (!row) { RDEBUG("SQL query returned no results"); (inst->module->sql_finish_select_query)(handle, inst->config); ret = -1; goto finish; } if (!row[0]){ RDEBUG("NULL value in first column of result"); (inst->module->sql_finish_select_query)(handle, inst->config); ret = -1; goto finish; } *out = talloc_bstrndup(request, row[0], strlen(row[0])); ret = talloc_array_length(*out) - 1; (inst->module->sql_finish_select_query)(handle, inst->config); finish: fr_connection_release(inst->pool, handle); return ret; }