static void check_pair(REQUEST *request, VALUE_PAIR *check_item, VALUE_PAIR *reply_item, int *pass, int *fail) { int compare; if (check_item->op == T_OP_SET) return; compare = paircmp(check_item, reply_item); if (compare < 0) { REDEBUG("Comparison failed: %s", fr_strerror()); } if (compare == 1) { ++*(pass); } else { ++*(fail); } if (RDEBUG_ENABLED3) { char rule[1024], pair[1024]; vp_prints(rule, sizeof(rule), check_item); vp_prints(pair, sizeof(pair), reply_item); RDEBUG3("%s %s %s", pair, compare == 1 ? "allowed by" : "disallowed by", rule); } return; }
static int filter_packet(RADIUS_PACKET *packet) { VALUE_PAIR *check_item; VALUE_PAIR *vp; unsigned int pass, fail; int compare; pass = fail = 0; for (vp = packet->vps; vp != NULL; vp = vp->next) { for (check_item = filter_vps; check_item != NULL; check_item = check_item->next) if ((check_item->attribute == vp->attribute) && (check_item->operator != T_OP_SET)) { compare = paircmp(check_item, vp); if (compare == 1) pass++; else fail++; } } if (fail == 0 && pass != 0) { return 0; } return 1; }
/** Uses paircmp to verify all VALUE_PAIRs in list match the filter defined by check * * @note will sort both filter and list in place. * * @param failed pointer to an array to write the pointers of the filter/list attributes that didn't match. * May be NULL. * @param filter attributes to check list against. * @param list attributes, probably a request or reply */ bool pairvalidate(VALUE_PAIR const *failed[2], VALUE_PAIR *filter, VALUE_PAIR *list) { vp_cursor_t filter_cursor; vp_cursor_t list_cursor; VALUE_PAIR *check, *match; if (!filter && !list) { return true; } /* * This allows us to verify the sets of validate and reply are equal * i.e. we have a validate rule which matches every reply attribute. * * @todo this should be removed one we have sets and lists */ pairsort(&filter, attrtagcmp); pairsort(&list, attrtagcmp); check = fr_cursor_init(&filter_cursor, &filter); match = fr_cursor_init(&list_cursor, &list); while (match || check) { /* * Lists are of different lengths */ if (!match || !check) goto mismatch; /* * The lists are sorted, so if the first * attributes aren't of the same type, then we're * done. */ if (!ATTRIBUTE_EQ(check, match)) goto mismatch; /* * They're of the same type, but don't have the * same values. This is a problem. * * Note that the RFCs say that for attributes of * the same type, order is important. */ if (paircmp(check, match) != 1) goto mismatch; check = fr_cursor_next(&filter_cursor); match = fr_cursor_next(&list_cursor); } return true; mismatch: if (failed) { failed[0] = check; failed[1] = match; } return false; }
static void check_pair(VALUE_PAIR *check_item, VALUE_PAIR *reply_item, int *pass, int *fail) { int compare; if (check_item->op == T_OP_SET) return; compare = paircmp(check_item, reply_item); if (compare == 1) { ++*(pass); } else { ++*(fail); } return; }
/* * *presult is "did comparison match or not" */ static int radius_do_cmp(REQUEST *request, int *presult, FR_TOKEN lt, const char *pleft, FR_TOKEN token, FR_TOKEN rt, const char *pright, int cflags, int modreturn) { int result; uint32_t lint, rint; VALUE_PAIR *vp = NULL; #ifdef HAVE_REGEX_H char buffer[8192]; #else cflags = cflags; /* -Wunused */ #endif rt = rt; /* -Wunused */ if (lt == T_BARE_WORD) { /* * Maybe check the last return code. */ if (token == T_OP_CMP_TRUE) { int isreturn; /* * Looks like a return code, treat is as such. */ isreturn = fr_str2int(modreturn_table, pleft, -1); if (isreturn != -1) { *presult = (modreturn == isreturn); return TRUE; } } /* * Bare words on the left can be attribute names. */ if (radius_get_vp(request, pleft, &vp)) { VALUE_PAIR myvp; /* * VP exists, and that's all we're looking for. */ if (token == T_OP_CMP_TRUE) { *presult = (vp != NULL); return TRUE; } if (!vp) { DICT_ATTR *da; /* * The attribute on the LHS may * have been a dynamically * registered callback. i.e. it * doesn't exist as a VALUE_PAIR. * If so, try looking for it. */ da = dict_attrbyname(pleft); if (da && (da->vendor == 0) && radius_find_compare(da->attr)) { VALUE_PAIR *check = pairmake(pleft, pright, token); *presult = (radius_callback_compare(request, NULL, check, NULL, NULL) == 0); RDEBUG3(" Callback returns %d", *presult); pairfree(&check); return TRUE; } RDEBUG2(" (Attribute %s was not found)", pleft); *presult = 0; return TRUE; } #ifdef HAVE_REGEX_H /* * Regex comparisons treat everything as * strings. */ if ((token == T_OP_REG_EQ) || (token == T_OP_REG_NE)) { vp_prints_value(buffer, sizeof(buffer), vp, 0); pleft = buffer; goto do_checks; } #endif memcpy(&myvp, vp, sizeof(myvp)); if (!pairparsevalue(&myvp, pright)) { RDEBUG2("Failed parsing \"%s\": %s", pright, fr_strerror()); return FALSE; } myvp.operator = token; *presult = paircmp(&myvp, vp); RDEBUG3(" paircmp -> %d", *presult); return TRUE; } /* else it's not a VP in a list */ } #ifdef HAVE_REGEX_H do_checks: #endif switch (token) { case T_OP_GE: case T_OP_GT: case T_OP_LE: case T_OP_LT: if (!all_digits(pright)) { RDEBUG2(" (Right field is not a number at: %s)", pright); return FALSE; } rint = strtoul(pright, NULL, 0); if (!all_digits(pleft)) { RDEBUG2(" (Left field is not a number at: %s)", pleft); return FALSE; } lint = strtoul(pleft, NULL, 0); break; default: lint = rint = 0; /* quiet the compiler */ break; } switch (token) { case T_OP_CMP_TRUE: /* * Check for truth or falsehood. */ if (all_digits(pleft)) { lint = strtoul(pleft, NULL, 0); result = (lint != 0); } else { result = (*pleft != '\0'); } break; case T_OP_CMP_EQ: result = (strcmp(pleft, pright) == 0); break; case T_OP_NE: result = (strcmp(pleft, pright) != 0); break; case T_OP_GE: result = (lint >= rint); break; case T_OP_GT: result = (lint > rint); break; case T_OP_LE: result = (lint <= rint); break; case T_OP_LT: result = (lint < rint); break; #ifdef HAVE_REGEX_H case T_OP_REG_EQ: { int i, compare; regex_t reg; regmatch_t rxmatch[REQUEST_MAX_REGEX + 1]; /* * Include substring matches. */ compare = regcomp(®, pright, cflags); if (compare != 0) { if (debug_flag) { char errbuf[128]; regerror(compare, ®, errbuf, sizeof(errbuf)); DEBUG("ERROR: Failed compiling regular expression: %s", errbuf); } return FALSE; } compare = regexec(®, pleft, REQUEST_MAX_REGEX + 1, rxmatch, 0); regfree(®); /* * Add new %{0}, %{1}, etc. */ if (compare == 0) for (i = 0; i <= REQUEST_MAX_REGEX; i++) { char *r; free(request_data_get(request, request, REQUEST_DATA_REGEX | i)); /* * No %{i}, skip it. * We MAY have %{2} without %{1}. */ if (rxmatch[i].rm_so == -1) continue; /* * Copy substring into allocated buffer */ r = rad_malloc(rxmatch[i].rm_eo -rxmatch[i].rm_so + 1); memcpy(r, pleft + rxmatch[i].rm_so, rxmatch[i].rm_eo - rxmatch[i].rm_so); r[rxmatch[i].rm_eo - rxmatch[i].rm_so] = '\0'; request_data_add(request, request, REQUEST_DATA_REGEX | i, r, free); } result = (compare == 0); } break; case T_OP_REG_NE: { int compare; regex_t reg; regmatch_t rxmatch[REQUEST_MAX_REGEX + 1]; /* * Include substring matches. */ compare = regcomp(®, pright, cflags); if (compare != 0) { if (debug_flag) { char errbuf[128]; regerror(compare, ®, errbuf, sizeof(errbuf)); DEBUG("ERROR: Failed compiling regular expression: %s", errbuf); } return FALSE; } compare = regexec(®, pleft, REQUEST_MAX_REGEX + 1, rxmatch, 0); regfree(®); result = (compare != 0); } break; #endif default: DEBUG("ERROR: Comparison operator %s is not supported", fr_token_name(token)); result = FALSE; break; } *presult = result; return TRUE; }
static int filter_packet(RADIUS_PACKET *packet) { VALUE_PAIR *check_item; VALUE_PAIR *vp; unsigned int pass, fail; int compare; pass = fail = 0; for (vp = packet->vps; vp != NULL; vp = vp->next) { for (check_item = filter_vps; check_item != NULL; check_item = check_item->next) if ((check_item->da == vp->da) && (check_item->op != T_OP_SET)) { compare = paircmp(check_item, vp); if (compare == 1) pass++; else fail++; } } if (fail == 0 && pass != 0) { /* * Cache authentication requests, as the replies * may not match the RADIUS filter. */ if ((packet->code == PW_AUTHENTICATION_REQUEST) || (packet->code == PW_ACCOUNTING_REQUEST)) { rbtree_deletebydata(filter_tree, packet); if (!rbtree_insert(filter_tree, packet)) { oom: fprintf(stderr, "radsniff: Out of memory\n"); exit(1); } } return 0; /* matched */ } /* * Don't create erroneous matches. */ if ((packet->code == PW_AUTHENTICATION_REQUEST) || (packet->code == PW_ACCOUNTING_REQUEST)) { rbtree_deletebydata(filter_tree, packet); return 1; } /* * Else see if a previous Access-Request * matched. If so, also print out the * matching accept, reject, or challenge. */ if ((packet->code == PW_AUTHENTICATION_ACK) || (packet->code == PW_AUTHENTICATION_REJECT) || (packet->code == PW_ACCESS_CHALLENGE) || (packet->code == PW_ACCOUNTING_RESPONSE)) { RADIUS_PACKET *reply; /* * This swaps the various fields. */ reply = rad_alloc_reply(NULL, packet); if (!reply) goto oom; compare = 1; if (rbtree_finddata(filter_tree, reply)) { compare = 0; } rad_free(&reply); return compare; } return 1; }
/** Uses paircmp to verify all VALUE_PAIRs in list match the filter defined by check * * @note will sort both filter and list in place. * * @param failed pointer to an array to write the pointers of the filter/list attributes that didn't match. * May be NULL. * @param filter attributes to check list against. * @param list attributes, probably a request or reply */ bool pairvalidate_relaxed(VALUE_PAIR const *failed[2], VALUE_PAIR *filter, VALUE_PAIR *list) { vp_cursor_t filter_cursor; vp_cursor_t list_cursor; VALUE_PAIR *check, *last_check = NULL, *match = NULL; if (!filter && !list) { return true; } /* * This allows us to verify the sets of validate and reply are equal * i.e. we have a validate rule which matches every reply attribute. * * @todo this should be removed one we have sets and lists */ pairsort(&filter, attrtagcmp); pairsort(&list, attrtagcmp); fr_cursor_init(&list_cursor, &list); for (check = fr_cursor_init(&filter_cursor, &filter); check; check = fr_cursor_next(&filter_cursor)) { /* * Were processing check attributes of a new type. */ if (!ATTRIBUTE_EQ(last_check, check)) { /* * Record the start of the matching attributes in the pair list * For every other operator we require the match to be present */ match = fr_cursor_next_by_da(&list_cursor, check->da, check->tag); if (!match) { if (check->op == T_OP_CMP_FALSE) continue; goto mismatch; } fr_cursor_init(&list_cursor, &match); last_check = check; } /* * Now iterate over all attributes of the same type. */ for (match = fr_cursor_first(&list_cursor); ATTRIBUTE_EQ(match, check); match = fr_cursor_next(&list_cursor)) { /* * This attribute passed the filter */ if (!paircmp(check, match)) goto mismatch; } } return true; mismatch: if (failed) { failed[0] = check; failed[1] = match; } return false; }
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; }
/* * Common code called by everything below. */ static rlm_rcode_t file_common(rlm_files_t const *inst, REQUEST *request, char const *filename, rbtree_t *tree, RADIUS_PACKET *request_packet, RADIUS_PACKET *reply_packet) { char const *name; VALUE_PAIR *check_tmp = NULL; VALUE_PAIR *reply_tmp = NULL; PAIR_LIST const *user_pl, *default_pl; bool found = false; PAIR_LIST my_pl; char buffer[256]; if (!inst->key) { VALUE_PAIR *namepair; namepair = request->username; name = namepair ? namepair->vp_strvalue : "NONE"; } else { int len; len = xlat_eval(buffer, sizeof(buffer), request, inst->key, NULL, NULL); if (len < 0) { return RLM_MODULE_FAIL; } name = len ? buffer : "NONE"; } if (!tree) return RLM_MODULE_NOOP; my_pl.name = name; user_pl = rbtree_finddata(tree, &my_pl); my_pl.name = "DEFAULT"; default_pl = rbtree_finddata(tree, &my_pl); /* * Find the entry for the user. */ while (user_pl || default_pl) { fr_cursor_t cursor; VALUE_PAIR *vp; PAIR_LIST const *pl; /* * Figure out which entry to match on. */ if (!default_pl && user_pl) { pl = user_pl; user_pl = user_pl->next; } else if (!user_pl && default_pl) { pl = default_pl; default_pl = default_pl->next; } else if (user_pl->order < default_pl->order) { pl = user_pl; user_pl = user_pl->next; } else { pl = default_pl; default_pl = default_pl->next; } MEM(fr_pair_list_copy(request, &check_tmp, pl->check) >= 0); for (vp = fr_cursor_init(&cursor, &check_tmp); vp; vp = fr_cursor_next(&cursor)) { if (xlat_eval_pair(request, vp) < 0) { RWARN("Failed parsing expanded value for check item, skipping entry: %s", fr_strerror()); fr_pair_list_free(&check_tmp); continue; } } if (paircmp(request, request_packet->vps, check_tmp, &reply_packet->vps) == 0) { RDEBUG2("Found match \"%s\" one line %d of %s", pl->name, pl->lineno, filename); found = true; /* ctx may be reply or proxy */ MEM(fr_pair_list_copy(reply_packet, &reply_tmp, pl->reply) >= 0); radius_pairmove(request, &reply_packet->vps, reply_tmp, true); fr_pair_list_move(&request->control, &check_tmp); reply_tmp = NULL; /* radius_pairmove() frees input attributes */ fr_pair_list_free(&check_tmp); /* * Fallthrough? */ if (!fall_through(pl->reply)) break; } } /* * Remove server internal parameters. */ fr_pair_delete_by_da(&reply_packet->vps, attr_fall_through); /* * See if we succeeded. */ if (!found) return RLM_MODULE_NOOP; /* on to the next module */ return RLM_MODULE_OK; }