static fr_io_final_t mod_process(UNUSED void const *instance, REQUEST *request) { rlm_rcode_t rcode; CONF_SECTION *unlang; fr_dict_enum_t const *dv; VALUE_PAIR *vp; REQUEST_VERIFY(request); switch (request->request_state) { case REQUEST_INIT: if (request->parent && RDEBUG_ENABLED) { RDEBUG("Received %s ID %i", fr_packet_codes[request->packet->code], request->packet->id); log_request_pair_list(L_DBG_LVL_1, request, request->packet->vps, ""); } request->component = "radius"; unlang = cf_section_find(request->server_cs, "recv", "Status-Server"); if (!unlang) { RWDEBUG("Failed to find 'recv Status-Server' section"); request->reply->code = FR_CODE_ACCESS_REJECT; goto send_reply; } RDEBUG("Running 'recv Status-Server' from file %s", cf_filename(unlang)); unlang_interpret_push_section(request, unlang, RLM_MODULE_NOOP, UNLANG_TOP_FRAME); request->request_state = REQUEST_RECV; /* FALL-THROUGH */ case REQUEST_RECV: rcode = unlang_interpret_resume(request); if (request->master_state == REQUEST_STOP_PROCESSING) return FR_IO_DONE; if (rcode == RLM_MODULE_YIELD) return FR_IO_YIELD; rad_assert(request->log.unlang_indent == 0); switch (rcode) { case RLM_MODULE_OK: case RLM_MODULE_UPDATED: request->reply->code = FR_CODE_ACCESS_ACCEPT; break; case RLM_MODULE_FAIL: case RLM_MODULE_HANDLED: request->reply->code = 0; /* don't reply */ break; default: case RLM_MODULE_REJECT: request->reply->code = FR_CODE_ACCESS_REJECT; break; } /* * Allow for over-ride of reply code. */ vp = fr_pair_find_by_da(request->reply->vps, attr_packet_type, TAG_ANY); if (vp) request->reply->code = vp->vp_uint32; dv = fr_dict_enum_by_value(attr_packet_type, fr_box_uint32(request->reply->code)); unlang = NULL; if (dv) unlang = cf_section_find(request->server_cs, "send", dv->alias); if (!unlang) goto send_reply; rerun_nak: RDEBUG("Running 'send %s' from file %s", cf_section_name2(unlang), cf_filename(unlang)); unlang_interpret_push_section(request, unlang, RLM_MODULE_NOOP, UNLANG_TOP_FRAME); request->request_state = REQUEST_SEND; /* FALL-THROUGH */ case REQUEST_SEND: rcode = unlang_interpret_resume(request); if (request->master_state == REQUEST_STOP_PROCESSING) return FR_IO_DONE; if (rcode == RLM_MODULE_YIELD) return FR_IO_YIELD; rad_assert(request->log.unlang_indent == 0); switch (rcode) { case RLM_MODULE_NOOP: case RLM_MODULE_OK: case RLM_MODULE_UPDATED: case RLM_MODULE_HANDLED: /* reply is already set */ break; default: /* * If we over-ride an ACK with a NAK, run * the NAK section. */ if (request->reply->code != FR_CODE_ACCESS_REJECT) { dv = fr_dict_enum_by_value(attr_packet_type, fr_box_uint32(request->reply->code)); RWDEBUG("Failed running 'send %s', trying 'send Access-Reject'", dv->alias); request->reply->code = FR_CODE_ACCESS_REJECT; dv = fr_dict_enum_by_value(attr_packet_type, fr_box_uint32(request->reply->code)); unlang = NULL; if (!dv) goto send_reply; unlang = cf_section_find(request->server_cs, "send", dv->alias); if (unlang) goto rerun_nak; RWDEBUG("Not running 'send %s' section as it does not exist", dv->alias); } break; } send_reply: gettimeofday(&request->reply->timestamp, NULL); /* * Check for "do not respond". */ if (request->reply->code == FR_CODE_DO_NOT_RESPOND) { RDEBUG("Not sending reply to client."); break; } if (request->parent && RDEBUG_ENABLED) { RDEBUG("Sending %s ID %i", fr_packet_codes[request->reply->code], request->reply->id); log_request_pair_list(L_DBG_LVL_1, request, request->reply->vps, ""); } break; default: return FR_IO_FAIL; } return FR_IO_REPLY; }
static fr_io_final_t mod_process(UNUSED void const *instance, REQUEST *request, fr_io_action_t action) { VALUE_PAIR *vp; rlm_rcode_t rcode; CONF_SECTION *unlang; fr_dict_enum_t const *dv; REQUEST_VERIFY(request); /* * Pass this through asynchronously to the module which * is waiting for something to happen. */ if (action != FR_IO_ACTION_RUN) { unlang_interpret_signal(request, (fr_state_signal_t) action); return FR_IO_DONE; } switch (request->request_state) { case REQUEST_INIT: if (request->parent && RDEBUG_ENABLED) { RDEBUG("Received %s ID %i", fr_packet_codes[request->packet->code], request->packet->id); log_request_pair_list(L_DBG_LVL_1, request, request->packet->vps, ""); } request->component = "radius"; /* * We can run CoA-Request or Disconnect-Request sections here */ dv = fr_dict_enum_by_value(attr_packet_type, fr_box_uint32(request->packet->code)); if (!dv) { REDEBUG("Failed to find value for &request:Packet-Type"); return FR_IO_FAIL; } unlang = cf_section_find(request->server_cs, "recv", dv->alias); if (!unlang) { REDEBUG("Failed to find 'recv %s' section", dv->alias); return FR_IO_FAIL; } RDEBUG("Running 'recv %s' from file %s", dv->alias, cf_filename(unlang)); unlang_interpret_push_section(request, unlang, RLM_MODULE_NOOP, UNLANG_TOP_FRAME); request->request_state = REQUEST_RECV; /* FALL-THROUGH */ case REQUEST_RECV: rcode = unlang_interpret_resume(request); if (request->master_state == REQUEST_STOP_PROCESSING) return FR_IO_DONE; if (rcode == RLM_MODULE_YIELD) return FR_IO_YIELD; rad_assert(request->log.unlang_indent == 0); switch (rcode) { case RLM_MODULE_NOOP: case RLM_MODULE_NOTFOUND: case RLM_MODULE_OK: case RLM_MODULE_UPDATED: request->reply->code = request->packet->code + 1; /* ACK */ break; case RLM_MODULE_HANDLED: break; case RLM_MODULE_FAIL: case RLM_MODULE_INVALID: case RLM_MODULE_REJECT: case RLM_MODULE_USERLOCK: default: request->reply->code = request->packet->code + 2; /* NAK */ break; } /* * Allow for over-ride of reply code. */ vp = fr_pair_find_by_da(request->reply->vps, attr_packet_type, TAG_ANY); if (vp) request->reply->code = vp->vp_uint32; dv = fr_dict_enum_by_value(attr_packet_type, fr_box_uint32(request->reply->code)); unlang = NULL; if (dv) unlang = cf_section_find(request->server_cs, "send", dv->alias); if (!unlang) goto send_reply; /* * Note that for NAKs, we do NOT use * reject_delay. This is because we're acting as * a NAS, and we want to respond to the RADIUS * server as quickly as possible. */ rerun_nak: RDEBUG("Running 'send %s' from file %s", cf_section_name2(unlang), cf_filename(unlang)); unlang_interpret_push_section(request, unlang, RLM_MODULE_NOOP, UNLANG_TOP_FRAME); rad_assert(request->log.unlang_indent == 0); request->request_state = REQUEST_SEND; /* FALL-THROUGH */ case REQUEST_SEND: rcode = unlang_interpret_resume(request); if (request->master_state == REQUEST_STOP_PROCESSING) return FR_IO_DONE; if (rcode == RLM_MODULE_YIELD) return FR_IO_YIELD; rad_assert(request->log.unlang_indent == 0); switch (rcode) { /* * We need to send CoA-NAK back if Service-Type * is Authorize-Only. Rely on the user's policy * to do that. We're not a real NAS, so this * restriction doesn't (ahem) apply to us. */ case RLM_MODULE_FAIL: case RLM_MODULE_INVALID: case RLM_MODULE_REJECT: case RLM_MODULE_USERLOCK: default: /* * If we over-ride an ACK with a NAK, run * the NAK section. */ if (request->reply->code == request->packet->code + 1) { dv = fr_dict_enum_by_value(attr_packet_type, fr_box_uint32(request->reply->code)); RWDEBUG("Failed running 'send %s', trying corresponding NAK section.", dv->alias); request->reply->code = request->packet->code + 2; dv = fr_dict_enum_by_value(attr_packet_type, fr_box_uint32(request->reply->code)); unlang = NULL; if (!dv) goto send_reply; unlang = cf_section_find(request->server_cs, "send", dv->alias); if (unlang) goto rerun_nak; RWDEBUG("Not running 'send %s' section as it does not exist", dv->alias); } /* * Else it was already a NAK or something else. */ break; case RLM_MODULE_HANDLED: case RLM_MODULE_NOOP: case RLM_MODULE_NOTFOUND: case RLM_MODULE_OK: case RLM_MODULE_UPDATED: /* reply code is already set */ break; } send_reply: gettimeofday(&request->reply->timestamp, NULL); /* * Check for "do not respond". */ if (request->reply->code == FR_CODE_DO_NOT_RESPOND) { RDEBUG("Not sending reply to client."); break; } if (request->parent && RDEBUG_ENABLED) { RDEBUG("Sending %s ID %i", fr_packet_codes[request->reply->code], request->reply->id); log_request_pair_list(L_DBG_LVL_1, request, request->reply->vps, ""); } break; default: return FR_IO_FAIL; } return FR_IO_REPLY; }
/** Very simple state machine to process requests * * Unlike normal protocol requests which may have multiple distinct states, * we really only have REQUEST_INIT and REQUEST_RECV phases. * * Conversion of LDAPMessage to VALUE_PAIR structs is done in the listener * because we cannot easily duplicate the LDAPMessage to send it across to * the worker for parsing. * * Most LDAP directories can only handle between 2000-5000 modifications a second * so we're unlikely to be I/O or CPU bound using this division of responsibilities. * * @param[in] request to process. * @param[in] action If something has signalled that the request should stop * being processed. */ static void request_running(REQUEST *request, fr_state_signal_t action) { CONF_SECTION *unlang; char const *verb; char const *state; rlm_rcode_t rcode = RLM_MODULE_FAIL; REQUEST_VERIFY(request); /* * Async (in the same thread, tho) signal to be done. */ if (action == FR_SIGNAL_CANCEL) goto done; /* * We ignore all other actions. */ if (action != FR_SIGNAL_RUN) return; switch (request->request_state) { case REQUEST_INIT: if (RDEBUG_ENABLED) proto_ldap_packet_debug(request, request->packet, true); log_request_proto_pair_list(L_DBG_LVL_1, request, request->packet->vps, ""); request->server_cs = request->listener->server_cs; request->component = "ldap"; switch (request->packet->code) { case LDAP_SYNC_CODE_PRESENT: verb = "recv"; state = "Present"; break; case LDAP_SYNC_CODE_ADD: verb = "recv"; state = "Add"; break; case LDAP_SYNC_CODE_MODIFY: verb = "recv"; state = "Modify"; break; case LDAP_SYNC_CODE_DELETE: verb = "recv"; state = "Delete"; break; case LDAP_SYNC_CODE_COOKIE_LOAD: verb = "load"; state = "Cookie"; break; case LDAP_SYNC_CODE_COOKIE_STORE: verb = "store"; state = "Cookie"; break; default: rad_assert(0); return; } unlang = cf_section_find(request->server_cs, verb, state); if (!unlang) unlang = cf_section_find(request->server_cs, "recv", "*"); if (!unlang) { RDEBUG2("Ignoring %s operation. Add \"%s %s {}\" to virtual-server \"%s\"" " to handle", fr_int2str(ldap_sync_code_table, request->packet->code, "<INVALID>"), verb, state, cf_section_name2(request->server_cs)); rcode = RLM_MODULE_NOOP; goto done; } RDEBUG("Running '%s %s' from file %s", cf_section_name1(unlang), cf_section_name2(unlang), cf_filename(unlang)); unlang_interpret_push_section(request, unlang, RLM_MODULE_NOOP, UNLANG_TOP_FRAME); request->request_state = REQUEST_RECV; /* FALL-THROUGH */ case REQUEST_RECV: rcode = unlang_interpret_resume(request); if (request->master_state == REQUEST_STOP_PROCESSING) goto done; if (rcode == RLM_MODULE_YIELD) return; /* FALL-THROUGH */ default: done: switch (rcode) { case RLM_MODULE_UPDATED: case RLM_MODULE_OK: { } default: break; } rad_assert(request->log.unlang_indent == 0); //request_delete(request); break; } }