static int mod_instantiate(CONF_SECTION *conf, void *instance) { module_instance_t *modinst; rlm_rediswho_t *inst = instance; inst->xlat_name = cf_section_name2(conf); if (!inst->xlat_name) inst->xlat_name = cf_section_name1(conf); inst->cs = conf; modinst = module_instantiate(cf_section_find("modules"), inst->redis_instance_name); if (!modinst) { ERROR("rediswho: failed to find module instance \"%s\"", inst->redis_instance_name); return -1; } if (strcmp(modinst->entry->name, "rlm_redis") != 0) { ERROR("rediswho: Module \"%s\"" " is not an instance of the redis module", inst->redis_instance_name); return -1; } inst->redis_inst = (REDIS_INST *) modinst->insthandle; return 0; }
/* * Do any per-module initialization that is separate to each * configured instance of the module. e.g. set up connections * to external databases, read configuration files, set up * dictionary entries, etc. * * If configuration information is given in the config section * that must be referenced in later calls, store a handle to it * in *instance otherwise put a null pointer there. */ static int mod_instantiate(CONF_SECTION *conf, void *instance) { module_instance_t *sqlinst; rlm_sqlippool_t *inst = instance; char const *pool_name = NULL; pool_name = cf_section_name2(conf); if (pool_name != NULL) { inst->pool_name = talloc_typed_strdup(inst, pool_name); } else { inst->pool_name = talloc_typed_strdup(inst, "ippool"); } sqlinst = find_module_instance(cf_section_find("modules"), inst->sql_instance_name, 1); if (!sqlinst) { cf_log_err_cs(conf, "failed to find sql instance named %s", inst->sql_instance_name); return -1; } if (strcmp(sqlinst->entry->name, "rlm_sql") != 0) { cf_log_err_cs(conf, "Module \"%s\"" " is not an instance of the rlm_sql module", inst->sql_instance_name); return -1; } inst->sql_inst = (rlm_sql_t *) sqlinst->insthandle; return 0; }
static rlm_rcode_t arp_process(REQUEST *request) { CONF_SECTION *unlang; request->server_cs = request->listener->server_cs; unlang = cf_section_find(request->server_cs, "arp", NULL); request->component = "arp"; return unlang_interpret(request, unlang, RLM_MODULE_NOOP); }
static int rediswho_instantiate(CONF_SECTION * conf, void ** instance) { module_instance_t *modinst; rlm_rediswho_t *inst; /* * Set up a storage area for instance data */ inst = *instance = rad_malloc(sizeof (*inst)); memset(inst, 0, sizeof (*inst)); /* * If the configuration parameters can't be parsed, then * fail. */ if (cf_section_parse(conf, inst, module_config) < 0) { free(inst); return -1; } inst->xlat_name = cf_section_name2(conf); if (!inst->xlat_name) inst->xlat_name = cf_section_name1(conf); inst->xlat_name = strdup(inst->xlat_name); inst->cs = conf; modinst = find_module_instance(cf_section_find("modules"), inst->redis_instance_name, 1); if (!modinst) { radlog(L_ERR, "rediswho: failed to find module instance \"%s\"", inst->redis_instance_name); rediswho_detach(inst); return -1; } if (strcmp(modinst->entry->name, "rlm_redis") != 0) { radlog(L_ERR, "rediswho: Module \"%s\"" " is not an instance of the redis module", inst->redis_instance_name); rediswho_detach(inst); return -1; } inst->redis_inst = (REDIS_INST *) modinst->insthandle; return 0; }
/** Receive notification of present phase * * @note This is a callback for the sync_demux function. * * @param[in] conn the sync belongs to. * @param[in] config of the sync that entered the refresh present phase. * @param[in] sync_id of the sync that entered the refresh present phase. * @param[in] phase Refresh phase the sync was previously in. * @param[in] user_ctx The listener. * @return 0. */ static int _proto_ldap_present(fr_ldap_connection_t *conn, sync_config_t const *config, int sync_id, sync_phases_t phase, void *user_ctx) { rad_listen_t *listen = talloc_get_type_abort(user_ctx, rad_listen_t); if (!cf_section_find(listen->server_cs, "recv", "Present")) { DEBUG2("Present phase is not supported, reinitialising sync"); return _proto_ldap_refresh_required(conn, config, sync_id, phase, user_ctx); } return 0; }
/* standard foobar code */ static int sqlhpwippool_instantiate(CONF_SECTION *conf, void **instance) { rlm_sqlhpwippool_t *data; module_instance_t *modinst; /* set up a storage area for instance data */ data = rad_malloc(sizeof(*data)); if (!data) return -1; memset(data, 0, sizeof(*data)); /* so _detach will know what to free */ /* fail if the configuration parameters can't be parsed */ if (cf_section_parse(conf, data, module_config) < 0) { sqlhpwippool_detach(*instance); return -1; } /* save my name */ data->myname = cf_section_name2(conf); if (!data->myname) { data->myname = "(no name)"; } data->sincesync = 0; modinst = find_module_instance(cf_section_find("modules"), (data->sqlinst_name), 1 ); if (!modinst) { nvp_log(__LINE__, data, L_ERR, "sqlhpwippool_instantiate(): cannot find module instance " "named \"%s\"", data->sqlinst_name); return -1; } /* check if the given instance is really a rlm_sql instance */ if (strcmp(modinst->entry->name, "rlm_sql") != 0) { nvp_log(__LINE__, data, L_ERR, "sqlhpwippool_instantiate(): given instance (%s) is not " "an instance of the rlm_sql module", data->sqlinst_name); return -1; } /* save pointers to useful "objects" */ data->sqlinst = (SQL_INST *) modinst->insthandle; data->db = (rlm_sql_module_t *) data->sqlinst->module; /* everything went ok, cleanup pool */ *instance = data; return ((nvp_cleanup(data)) ? 0 : -1); }
/** Wrapper around dl_instance * * @param[in] ctx to allocate data in (instance of proto_radius). * @param[out] out Where to write a dl_instance_t containing the module handle and instance. * @param[in] parent Base structure address. * @param[in] ci #CONF_PAIR specifying the name of the type module. * @param[in] rule unused. * @return * - 0 on success. * - -1 on failure. */ static int transport_parse(TALLOC_CTX *ctx, void *out, UNUSED void *parent, CONF_ITEM *ci, UNUSED CONF_PARSER const *rule) { char const *name = cf_pair_value(cf_item_to_pair(ci)); dl_instance_t *parent_inst; CONF_SECTION *cs = cf_item_to_section(cf_parent(ci)); CONF_SECTION *transport_cs; transport_cs = cf_section_find(cs, name, NULL); /* * Allocate an empty section if one doesn't exist * this is so defaults get parsed. */ if (!transport_cs) transport_cs = cf_section_alloc(cs, cs, name, NULL); parent_inst = cf_data_value(cf_data_find(cs, dl_instance_t, "rlm_radius")); rad_assert(parent_inst); return dl_instance(ctx, out, transport_cs, parent_inst, name, DL_TYPE_SUBMODULE); }
/** Initialise a module specific exfile handle * * @see exfile_init * * @param[in] ctx to bind the lifetime of the exfile handle to. * @param[in] module section. * @param[in] max_entries Max file descriptors to cache, and manage locks for. * @param[in] max_idle Maximum time a file descriptor can be idle before it's closed. * @param[in] locking Whether or not to lock the files. * @param[in] trigger_prefix if NULL will be set automatically from the module CONF_SECTION. * @param[in] trigger_args to make available in any triggers executed by the connection pool. * @return * - New connection pool. * - NULL on error. */ exfile_t *module_exfile_init(TALLOC_CTX *ctx, CONF_SECTION *module, uint32_t max_entries, uint32_t max_idle, bool locking, char const *trigger_prefix, VALUE_PAIR *trigger_args) { char trigger_prefix_buff[128]; exfile_t *handle; if (!trigger_prefix) { snprintf(trigger_prefix_buff, sizeof(trigger_prefix_buff), "modules.%s.file", cf_section_name1(module)); trigger_prefix = trigger_prefix_buff; } handle = exfile_init(ctx, max_entries, max_idle, locking); if (!handle) return NULL; exfile_enable_triggers(handle, cf_section_find(module, "file", NULL), trigger_prefix, trigger_args); return handle; }
/* standard foobar code */ static int mod_instantiate(CONF_SECTION *conf, void *instance) { rlm_sqlhpwippool_t *inst = instance; module_instance_t *sqlinst; /* save my name */ inst->myname = cf_section_name2(conf); if (!inst->myname) { inst->myname = "(no name)"; } inst->sincesync = 0; sqlinst = find_module_instance(cf_section_find("modules"), (inst->sql_module_instance), 1 ); if (!sqlinst) { nvp_log(__LINE__, inst, L_ERR, "mod_instantiate(): cannot find module instance " "named \"%s\"", inst->sql_module_instance); return -1; } /* check if the given instance is really a rlm_sql instance */ if (strcmp(sqlinst->entry->name, "rlm_sql") != 0) { nvp_log(__LINE__, inst, L_ERR, "mod_instantiate(): given instance (%s) is not " "an instance of the rlm_sql module", inst->sql_module_instance); return -1; } /* save pointers to useful "objects" */ inst->sqlinst = (rlm_sql_t *) sqlinst->insthandle; inst->db = (rlm_sql_module_t *) inst->sqlinst->module; return ((nvp_cleanup(inst)) ? 0 : -1); }
/** Initialize the rlm_couchbase module * * Intialize the module and create the initial Couchbase connection pool. * * @param conf The module configuration. * @param instance The module instance. * @return * - 0 on success. * - -1 on failure. */ static int mod_instantiate(void *instance, CONF_SECTION *conf) { rlm_couchbase_t *inst = instance; /* our module instance */ { char *server, *p; size_t len, i; bool sep = false; len = talloc_array_length(inst->server_raw); server = p = talloc_array(inst, char, len); for (i = 0; i < len; i++) { switch (inst->server_raw[i]) { case '\t': case ' ': case ',': /* Consume multiple separators occurring in sequence */ if (sep == true) continue; sep = true; *p++ = ';'; break; default: sep = false; *p++ = inst->server_raw[i]; break; } } *p = '\0'; inst->server = server; } /* setup item map */ if (mod_build_attribute_element_map(conf, inst) != 0) { /* fail */ return -1; } /* initiate connection pool */ inst->pool = module_connection_pool_init(conf, inst, mod_conn_create, mod_conn_alive, NULL, NULL, NULL); /* check connection pool */ if (!inst->pool) { ERROR("failed to initiate connection pool"); /* fail */ return -1; } /* load clients if requested */ if (inst->read_clients) { CONF_SECTION *cs, *map, *tmpl; /* conf section */ /* attempt to find client section */ cs = cf_section_find(conf, "client", NULL); if (!cs) { ERROR("failed to find client section while loading clients"); /* fail */ return -1; } /* attempt to find attribute subsection */ map = cf_section_find(cs, "attribute", NULL); if (!map) { ERROR("failed to find attribute subsection while loading clients"); /* fail */ return -1; } tmpl = cf_section_find(cs, "template", NULL); /* debugging */ DEBUG("preparing to load client documents"); /* attempt to load clients */ if (mod_load_client_documents(inst, tmpl, map) != 0) { /* fail */ return -1; } } /* return okay */ return 0; }
/* * Find a module instance. */ module_instance_t *find_module_instance(const char *instname) { CONF_SECTION *cs, *inst_cs; const char *name1, *name2; module_instance_t *node, **last; char module_name[256]; /* * Look through the global module instance list for the * named module. */ last = &module_instance_list; for (node = module_instance_list; node != NULL; node = node->next) { /* * Found the named instance. Return it. */ if (strcmp(node->name, instname) == 0) return node; /* * Keep a pointer to the last entry to update... */ last = &node->next; } /* * Instance doesn't exist yet. Try to find the * corresponding configuration section and create it. */ /* * Look for the 'modules' configuration section. */ cs = cf_section_find("modules"); if (cs == NULL) { radlog(L_ERR|L_CONS, "ERROR: Cannot find a 'modules' section in the configuration file.\n"); return NULL; } /* * Module instances are declared in the modules{} block * and referenced later by their name, which is the * name2 from the config section, or name1 if there was * no name2. */ name1 = name2 = NULL; for(inst_cs=cf_subsection_find_next(cs, NULL, NULL); inst_cs != NULL; inst_cs=cf_subsection_find_next(cs, inst_cs, NULL)) { name1 = cf_section_name1(inst_cs); name2 = cf_section_name2(inst_cs); if ( (name2 && !strcmp(name2, instname)) || (!name2 && !strcmp(name1, instname)) ) break; } if (inst_cs == NULL) { radlog(L_ERR|L_CONS, "ERROR: Cannot find a configuration entry for module \"%s\".\n", instname); return NULL; } /* * Found the configuration entry. */ node = rad_malloc(sizeof(*node)); node->next = NULL; node->insthandle = NULL; /* * Link to the module by name: rlm_FOO-major.minor */ if (strncmp(name1, "rlm_", 4)) { #if 0 snprintf(module_name, sizeof(module_name), "rlm_%s-%d.%d", name1, RADIUSD_MAJOR_VERSION, RADIUSD_MINOR_VERSION); #else snprintf(module_name, sizeof(module_name), "rlm_%s", name1); #endif } else { strNcpy(module_name, name1, sizeof(module_name)); } /* * FIXME: "radiusd.conf" is wrong here; must find cf filename */ node->entry = linkto_module(module_name, "radiusd.conf", cf_section_lineno(inst_cs)); if (!node->entry) { free(node); /* linkto_module logs any errors */ return NULL; } /* * Call the module's instantiation routine. */ if ((node->entry->module->instantiate) && ((node->entry->module->instantiate)(inst_cs, &node->insthandle) < 0)) { radlog(L_ERR|L_CONS, "radiusd.conf[%d]: %s: Module instantiation failed.\n", cf_section_lineno(inst_cs), instname); free(node); return NULL; } /* * We're done. Fill in the rest of the data structure, * and link it to the module instance list. */ strNcpy(node->name, instname, sizeof(node->name)); #if HAVE_PTHREAD_H /* * If we're threaded, check if the module is thread-safe. * * If it isn't, we create a mutex. */ if ((node->entry->module->type & RLM_TYPE_THREAD_UNSAFE) != 0) { node->mutex = (pthread_mutex_t *) rad_malloc(sizeof(pthread_mutex_t)); /* * Initialize the mutex. */ pthread_mutex_init(node->mutex, NULL); } else { /* * The module is thread-safe. Don't give it a mutex. */ node->mutex = NULL; } #endif *last = node; DEBUG("Module: Instantiated %s (%s) ", name1, node->name); return node; }
/* * Parse the module config sections, and load * and call each module's init() function. * * Libtool makes your life a LOT easier, especially with libltdl. * see: http://www.gnu.org/software/libtool/ */ int setup_modules(void) { int comp; CONF_SECTION *cs; /* * FIXME: This should be pulled from somewhere else. */ const char *filename="radiusd.conf"; /* * No current list of modules: Go initialize libltdl. */ if (!module_list) { /* * Set the default list of preloaded symbols. * This is used to initialize libltdl's list of * preloaded modules. * * i.e. Static modules. */ LTDL_SET_PRELOADED_SYMBOLS(); if (lt_dlinit() != 0) { radlog(L_ERR|L_CONS, "Failed to initialize libraries: %s\n", lt_dlerror()); exit(1); /* FIXME */ } /* * Set the search path to ONLY our library directory. * This prevents the modules from being found from * any location on the disk. */ lt_dlsetsearchpath(radlib_dir); DEBUG2("Module: Library search path is %s", lt_dlgetsearchpath()); /* * Initialize the components. */ for (comp = 0; comp < RLM_COMPONENT_COUNT; comp++) { components[comp] = NULL; } } else { detach_modules(); } /* * Create any DICT_VALUE's for the types. See * 'doc/configurable_failover' for examples of 'authtype' * used to create new Auth-Type values. In order to * let the user create new names, we've got to look for * those names, and create DICT_VALUE's for them. */ for (comp = 0; section_type_value[comp].section != NULL; comp++) { const char *name2; DICT_ATTR *dattr; DICT_VALUE *dval; CONF_SECTION *sub, *next; CONF_PAIR *cp; /* * Big-time YUCK */ static int my_value = 32767; cs = cf_section_find(section_type_value[comp].section); if (!cs) continue; sub = NULL; do { /* * See if there's a sub-section by that * name. */ next = cf_subsection_find_next(cs, sub, section_type_value[comp].typename); /* * Allow some old names, too. */ if (!next && (comp <= 4)) { next = cf_subsection_find_next(cs, sub, old_section_type_value[comp].typename); } sub = next; /* * If so, look for it to define a new * value. */ name2 = cf_section_name2(sub); if (!name2) continue; /* * If the value already exists, don't * create it again. */ dval = dict_valbyname(section_type_value[comp].attr, name2); if (dval) continue; /* * Find the attribute for the value. */ dattr = dict_attrbyvalue(section_type_value[comp].attr); if (!dattr) continue; /* * Finally, create the new attribute. */ if (dict_addvalue(name2, dattr->name, my_value++) < 0) { radlog(L_ERR, "%s", librad_errstr); exit(1); } } while (sub != NULL); /* * Loop over the non-sub-sections, too. */ cp = NULL; do { /* * See if there's a conf-pair by that * name. */ cp = cf_pair_find_next(cs, cp, NULL); if (!cp) break; /* * If the value already exists, don't * create it again. */ name2 = cf_pair_attr(cp); dval = dict_valbyname(section_type_value[comp].attr, name2); if (dval) continue; /* * Find the attribute for the value. */ dattr = dict_attrbyvalue(section_type_value[comp].attr); if (!dattr) continue; /* * Finally, create the new attribute. */ if (dict_addvalue(name2, dattr->name, my_value++) < 0) { radlog(L_ERR, "%s", librad_errstr); exit(1); } } while (cp != NULL); } /* over the sections which can have redundent sub-sections */
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; }
* Finally, create the new attribute. */ if (dict_addvalue(name2, dattr->name, my_value++) < 0) { radlog(L_ERR, "%s", librad_errstr); exit(1); } } while (cp != NULL); } /* over the sections which can have redundent sub-sections */ /* * Look for the 'instantiate' section, which tells us * the instantiation order of the modules, and also allows * us to load modules with no authorize/authenticate/etc. * sections. */ cs = cf_section_find("instantiate"); if (cs != NULL) { CONF_ITEM *ci; CONF_PAIR *cp; module_instance_t *module; const char *name; /* * Loop over the items in the 'instantiate' section. */ for (ci=cf_item_find_next(cs, NULL); ci != NULL; ci=cf_item_find_next(cs, ci)) { if (cf_item_is_section(ci)) { radlog(L_ERR|L_CONS, "%s[%d] Subsection for module instantiate is not allowed\n", filename, cf_section_lineno(cf_itemtosection(ci)));
static fr_io_final_t mod_process(UNUSED void const *instance, REQUEST *request, fr_io_action_t action) { rlm_rcode_t rcode; CONF_SECTION *unlang; REQUEST_VERIFY(request); /* * Pass this through asynchronously to the module which * is waiting for something to happen. */ if (action != FR_IO_ACTION_RUN) { unlang_signal(request, (fr_state_signal_t) action); return FR_IO_DONE; } switch (request->request_state) { case REQUEST_INIT: request->component = "radius"; unlang = cf_section_find(request->server_cs, "new", "client"); if (!unlang) { RWDEBUG("Failed to find 'new client' section"); request->reply->code = FR_CODE_ACCESS_REJECT; goto send_reply; } RDEBUG("Running 'new client' from file %s", cf_filename(unlang)); unlang_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; } unlang = cf_section_find(request->server_cs, "add", "client"); if (!unlang) goto send_reply; rerun_nak: RDEBUG("Running '%s client' from file %s", cf_section_name1(unlang), cf_filename(unlang)); unlang_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) { RWDEBUG("Failed running 'add client', trying 'deny client'."); deny: request->reply->code = FR_CODE_ACCESS_REJECT; unlang = cf_section_find(request->server_cs, "deny", "client"); if (unlang) goto rerun_nak; RWDEBUG("Not running 'deny client' section as it does not exist"); } break; } if (request->reply->code == FR_CODE_ACCESS_ACCEPT) { VALUE_PAIR *vp; vp = fr_pair_find_by_da(request->control, attr_freeradius_client_ip_address, TAG_ANY); if (!vp) fr_pair_find_by_da(request->control, attr_freeradius_client_ipv6_address, TAG_ANY); if (!vp) fr_pair_find_by_da(request->control, attr_freeradius_client_ip_prefix, TAG_ANY); if (!vp) fr_pair_find_by_da(request->control, attr_freeradius_client_ipv6_prefix, TAG_ANY); if (!vp) { ERROR("The 'control' list MUST contain a FreeRADIUS-Client.. IP address attribute"); goto deny; } vp = fr_pair_find_by_da(request->control, attr_freeradius_client_secret, TAG_ANY); if (!vp) { ERROR("The 'control' list MUST contain a FreeRADIUS-Client-Secret attribute"); goto deny; } /* * Else we're flexible. */ } send_reply: /* * This is an internally generated request. Don't print IP addresses. */ if (request->reply->code == FR_CODE_ACCESS_ACCEPT) { RDEBUG("Adding client"); } else { RDEBUG("Denying client"); } break; default: return FR_IO_FAIL; } return FR_IO_REPLY; }
static rlm_rcode_t dhcp_process(REQUEST *request) { rlm_rcode_t rcode; unsigned int i; VALUE_PAIR *vp; dhcp_socket_t *sock; /* * If there's a giaddr, save it as the Relay-IP-Address * in the response. That way the later code knows where * to send the reply. */ vp = fr_pair_find_by_num(request->packet->vps, DHCP_MAGIC_VENDOR, 266, TAG_ANY); /* DHCP-Gateway-IP-Address */ if (vp && (vp->vp_ipv4addr != htonl(INADDR_ANY))) { VALUE_PAIR *relay; /* DHCP-Relay-IP-Address */ MEM(relay = fr_pair_afrom_num(request->reply, DHCP_MAGIC_VENDOR, 222)); relay->vp_ipv4addr = vp->vp_ipv4addr; fr_pair_add(&request->reply->vps, relay); } vp = fr_pair_find_by_num(request->packet->vps, DHCP_MAGIC_VENDOR, 53, TAG_ANY); /* DHCP-Message-Type */ if (vp) { fr_dict_enum_t *dv = fr_dict_enum_by_value(vp->da, &vp->data); if (dv) { CONF_SECTION *server, *unlang; RDEBUG("Trying sub-section dhcp %s {...}", dv->alias); server = cf_item_to_section(cf_parent(request->listener->cs)); unlang = cf_section_find(server, "dhcp", dv->alias); rcode = unlang_interpret(request, unlang, RLM_MODULE_NOOP); } else { REDEBUG("Unknown DHCP-Message-Type %d", vp->vp_uint8); rcode = RLM_MODULE_FAIL; } } else { REDEBUG("Failed to find DHCP-Message-Type in packet!"); rcode = RLM_MODULE_FAIL; } vp = fr_pair_find_by_num(request->reply->vps, DHCP_MAGIC_VENDOR, 53, TAG_ANY); /* DHCP-Message-Type */ if (vp) { request->reply->code = vp->vp_uint8; } else switch (rcode) { case RLM_MODULE_OK: case RLM_MODULE_UPDATED: if (request->packet->code == FR_DHCP_DISCOVER) { request->reply->code = FR_DHCP_OFFER; break; } else if (request->packet->code == FR_DHCP_REQUEST) { request->reply->code = FR_DHCP_ACK; break; } request->reply->code = FR_DHCP_NAK; break; default: case RLM_MODULE_REJECT: case RLM_MODULE_FAIL: case RLM_MODULE_INVALID: case RLM_MODULE_NOOP: case RLM_MODULE_NOTFOUND: if (request->packet->code == FR_DHCP_DISCOVER) { request->reply->code = 0; /* ignore the packet */ } else { request->reply->code = FR_DHCP_NAK; } break; case RLM_MODULE_HANDLED: request->reply->code = 0; /* ignore the packet */ break; } /* * TODO: Handle 'output' of RLM_MODULE when acting as a * DHCP relay We may want to not forward packets in * certain circumstances. */ /* * Handle requests when acting as a DHCP relay */ vp = fr_pair_find_by_num(request->packet->vps, DHCP_MAGIC_VENDOR, 256, TAG_ANY); /* DHCP-Opcode */ if (!vp) { RPEDEBUG("Someone deleted the DHCP-Opcode!"); return RLM_MODULE_FAIL; } /* BOOTREPLY received on port 67 (i.e. from a server) */ if (vp->vp_uint8 == 2) { return dhcprelay_process_server_reply(request); } /* Packet from client, and we have DHCP-Relay-To-IP-Address */ if (fr_pair_find_by_num(request->control, DHCP_MAGIC_VENDOR, 270, TAG_ANY)) { return dhcprelay_process_client_request(request); } /* else it's a packet from a client, without relaying */ rad_assert(vp->vp_uint8 == 1); /* BOOTREQUEST */ sock = request->listener->data; /* * Handle requests when acting as a DHCP server */ /* * Releases don't get replies. */ if (request->packet->code == FR_DHCP_RELEASE) { request->reply->code = 0; } if (request->reply->code == 0) { return RLM_MODULE_OK; } request->reply->sockfd = request->packet->sockfd; /* * Copy specific fields from packet to reply, if they * don't already exist */ for (i = 0; i < sizeof(attrnums) / sizeof(attrnums[0]); i++) { uint32_t attr = attrnums[i]; if (fr_pair_find_by_num(request->reply->vps, DHCP_MAGIC_VENDOR, attr, TAG_ANY)) continue; vp = fr_pair_find_by_num(request->packet->vps, DHCP_MAGIC_VENDOR, attr, TAG_ANY); if (vp) { fr_pair_add(&request->reply->vps, fr_pair_copy(request->reply, vp)); } } vp = fr_pair_find_by_num(request->reply->vps, DHCP_MAGIC_VENDOR, 256, TAG_ANY); /* DHCP-Opcode */ rad_assert(vp != NULL); vp->vp_uint8 = 2; /* BOOTREPLY */ /* * Allow NAKs to be delayed for a short period of time. */ if (request->reply->code == FR_DHCP_NAK) { vp = fr_pair_find_by_num(request->reply->vps, 0, FR_FREERADIUS_RESPONSE_DELAY, TAG_ANY); if (vp) { if (vp->vp_uint32 <= 10) { request->response_delay.tv_sec = vp->vp_uint32; request->response_delay.tv_usec = 0; } else { request->response_delay.tv_sec = 10; request->response_delay.tv_usec = 0; } } else { #ifndef USEC #define USEC 1000000 #endif vp = fr_pair_find_by_num(request->reply->vps, 0, FR_FREERADIUS_RESPONSE_DELAY_USEC, TAG_ANY); if (vp) { if (vp->vp_uint32 <= 10 * USEC) { request->response_delay.tv_sec = vp->vp_uint32 / USEC; request->response_delay.tv_usec = vp->vp_uint32 % USEC; } else { request->response_delay.tv_sec = 10; request->response_delay.tv_usec = 0; } } } } /* * Prepare the reply packet for sending through dhcp_socket_send() */ request->reply->dst_ipaddr.af = AF_INET; request->reply->src_ipaddr.af = AF_INET; request->reply->src_ipaddr.prefix = 32; /* * Packet-Src-IP-Address has highest precedence */ vp = fr_pair_find_by_num(request->reply->vps, 0, FR_PACKET_SRC_IP_ADDRESS, TAG_ANY); if (vp) { request->reply->if_index = 0; /* Must be 0, we don't know the outbound if_index */ request->reply->src_ipaddr.addr.v4.s_addr = vp->vp_ipv4addr; /* * The request was unicast (via a relay) */ } else if (request->packet->dst_ipaddr.addr.v4.s_addr != htonl(INADDR_BROADCAST) && request->packet->dst_ipaddr.addr.v4.s_addr != htonl(INADDR_ANY)) { request->reply->src_ipaddr.addr.v4.s_addr = request->packet->dst_ipaddr.addr.v4.s_addr; request->reply->if_index = request->packet->if_index; /* * The listener was bound to an IP address, or we determined * the address automatically, as it was the only address bound * to the interface, and we bound to the interface. */ } else if (sock->src_ipaddr.addr.v4.s_addr != htonl(INADDR_ANY)) { request->reply->src_ipaddr.addr.v4.s_addr = sock->src_ipaddr.addr.v4.s_addr; #ifdef WITH_IFINDEX_IPADDR_RESOLUTION /* * We built with udpfromto and have the if_index of the receiving * interface, which we can now resolve to an IP address. */ } else if (request->packet->if_index > 0) { fr_ipaddr_t primary; if (fr_ipaddr_from_ifindex(&primary, request->packet->sockfd, request->packet->dst_ipaddr.af, request->packet->if_index) < 0) { RPEDEBUG("Failed determining src_ipaddr from if_index"); return RLM_MODULE_FAIL; } request->reply->src_ipaddr.addr.v4.s_addr = primary.addr.v4.s_addr; #endif /* * There's a Server-Identification attribute */ } else if ((vp = fr_pair_find_by_num(request->reply->vps, DHCP_MAGIC_VENDOR, 54, TAG_ANY))) { request->reply->src_ipaddr.addr.v4.s_addr = vp->vp_ipv4addr; } else { REDEBUG("Unable to determine correct src_ipaddr for response"); return RLM_MODULE_FAIL; } request->reply->dst_port = request->packet->src_port; request->reply->src_port = request->packet->dst_port; /* * Answer to client's nearest DHCP relay. * * Which may be different than the giaddr given in the * packet to the client. i.e. the relay may have a * public IP, but the gateway a private one. */ vp = fr_pair_find_by_num(request->reply->vps, DHCP_MAGIC_VENDOR, 272, TAG_ANY); /* DHCP-Relay-IP-Address */ if (vp && (vp->vp_ipv4addr != ntohl(INADDR_ANY))) { RDEBUG2("Reply will be unicast to giaddr from original packet"); request->reply->dst_ipaddr.addr.v4.s_addr = vp->vp_ipv4addr; request->reply->dst_port = request->packet->dst_port; vp = fr_pair_find_by_num(request->reply->vps, 0, FR_PACKET_DST_PORT, TAG_ANY); if (vp) request->reply->dst_port = vp->vp_uint16; return RLM_MODULE_OK; } /* * Answer to client's nearest DHCP gateway. In this * case, the client can reach the gateway, as can the * server. * * We also use *our* source port as the destination port. * Gateways are servers, and listen on the server port, * not the client port. */ vp = fr_pair_find_by_num(request->reply->vps, DHCP_MAGIC_VENDOR, 266, TAG_ANY); /* DHCP-Gateway-IP-Address */ if (vp && (vp->vp_ipv4addr != htonl(INADDR_ANY))) { RDEBUG2("Reply will be unicast to giaddr"); request->reply->dst_ipaddr.addr.v4.s_addr = vp->vp_ipv4addr; request->reply->dst_port = request->packet->dst_port; return RLM_MODULE_OK; } /* * If it's a NAK, or the broadcast flag was set, ond * there's no client-ip-address, send a broadcast. */ if ((request->reply->code == FR_DHCP_NAK) || ((vp = fr_pair_find_by_num(request->reply->vps, DHCP_MAGIC_VENDOR, 262, TAG_ANY)) && /* DHCP-Flags */ (vp->vp_uint32 & 0x8000) && ((vp = fr_pair_find_by_num(request->reply->vps, DHCP_MAGIC_VENDOR, 263, TAG_ANY)) && /* DHCP-Client-IP-Address */ (vp->vp_ipv4addr == htonl(INADDR_ANY))))) { /* * RFC 2131, page 23 * * Broadcast on * - DHCPNAK * or * - Broadcast flag is set up and ciaddr == NULL */ RDEBUG2("Reply will be broadcast"); request->reply->dst_ipaddr.addr.v4.s_addr = htonl(INADDR_BROADCAST); return RLM_MODULE_OK; } /* * RFC 2131, page 23 * * Unicast to ciaddr if present, otherwise to yiaddr. */ if ((vp = fr_pair_find_by_num(request->reply->vps, DHCP_MAGIC_VENDOR, 263, TAG_ANY)) && /* DHCP-Client-IP-Address */ (vp->vp_ipv4addr != htonl(INADDR_ANY))) { RDEBUG2("Reply will be sent unicast to &DHCP-Client-IP-Address"); request->reply->dst_ipaddr.addr.v4.s_addr = vp->vp_ipv4addr; return RLM_MODULE_OK; } vp = fr_pair_find_by_num(request->reply->vps, DHCP_MAGIC_VENDOR, 264, TAG_ANY); /* DHCP-Your-IP-Address */ if (!vp) { REDEBUG("Can't assign address to client: Neither &reply:DHCP-Client-IP-Address nor " "&reply:DHCP-Your-IP-Address set"); /* * There is nowhere to send the response to, so don't bother. */ request->reply->code = 0; return RLM_MODULE_FAIL; } #ifdef SIOCSARP /* * The system is configured to listen for broadcast * packets, which means we'll need to send unicast * replies, to IPs which haven't yet been assigned. * Therefore, we need to update the ARP table. * * However, they haven't specified a interface. So we * can't update the ARP table. And we must send a * broadcast response. */ if (sock->lsock.broadcast && !sock->src_interface) { WARN("You MUST set \"interface\" if you have \"broadcast = yes\""); RDEBUG2("Reply will be broadcast as no interface was defined"); request->reply->dst_ipaddr.addr.v4.s_addr = htonl(INADDR_BROADCAST); return RLM_MODULE_OK; } RDEBUG2("Reply will be unicast to &DHCP-Your-IP-Address"); request->reply->dst_ipaddr.addr.v4.s_addr = vp->vp_ipv4addr; /* * When sending a DHCP_OFFER, make sure our ARP table * contains an entry for the client IP address. * Otherwise the packet may not be sent to the client, as * the OS has no ARP entry for it. * * This is a cute hack to avoid us having to create a raw * socket to send DHCP packets. */ if (request->reply->code == FR_DHCP_OFFER) { VALUE_PAIR *hwvp = fr_pair_find_by_num(request->reply->vps, DHCP_MAGIC_VENDOR, 267, TAG_ANY); /* DHCP-Client-Hardware-Address */ if (!hwvp) return RLM_MODULE_FAIL; if (fr_dhcpv4_udp_add_arp_entry(request->reply->sockfd, sock->src_interface, &vp->vp_ip, hwvp->vp_ether) < 0) { RPEDEBUG("Failed adding arp entry"); return RLM_MODULE_FAIL; } } #else if (request->packet->src_ipaddr.addr.v4.s_addr != ntohl(INADDR_NONE)) { RDEBUG2("Reply will be unicast to the unicast source IP address"); request->reply->dst_ipaddr.addr.v4.s_addr = request->packet->src_ipaddr.addr.v4.s_addr; } else { RDEBUG2("Reply will be broadcast as this system does not support ARP updates"); request->reply->dst_ipaddr.addr.v4.s_addr = htonl(INADDR_BROADCAST); } #endif return RLM_MODULE_OK; }
/** Resolve polymorphic item's from a module's #CONF_SECTION to a subsection in another module * * This allows certain module sections to reference module sections in other instances * of the same module and share #CONF_DATA associated with them. * * @verbatim example { data { ... } } example inst { data = example } * @endverbatim * * @param[out] out where to write the pointer to a module's config section. May be NULL on success, * indicating the config item was not found within the module #CONF_SECTION * or the chain of module references was followed and the module at the end of the chain * did not a subsection. * @param[in] module #CONF_SECTION. * @param[in] name of the polymorphic sub-section. * @return * - 0 on success with referenced section. * - 1 on success with local section. * - -1 on failure. */ int module_sibling_section_find(CONF_SECTION **out, CONF_SECTION *module, char const *name) { CONF_PAIR *cp; CONF_SECTION *cs; CONF_DATA const *cd; module_instance_t *mi; char const *inst_name; #define FIND_SIBLING_CF_KEY "find_sibling" *out = NULL; /* * Is a real section (not referencing sibling module). */ cs = cf_section_find(module, name, NULL); if (cs) { *out = cs; return 0; } /* * Item omitted completely from module config. */ cp = cf_pair_find(module, name); if (!cp) return 0; if (cf_data_find(module, CONF_SECTION, FIND_SIBLING_CF_KEY)) { cf_log_err(cp, "Module reference loop found"); return -1; } cd = cf_data_add(module, module, FIND_SIBLING_CF_KEY, false); /* * Item found, resolve it to a module instance. * This triggers module loading, so we don't have * instantiation order issues. */ inst_name = cf_pair_value(cp); mi = module_by_name(NULL, inst_name); if (!mi) { cf_log_err(cp, "Unknown module instance \"%s\"", inst_name); return -1; } if (!mi->instantiated) { CONF_SECTION *parent = module; /* * Find the root of the config... */ do { CONF_SECTION *tmp; tmp = cf_item_to_section(cf_parent(parent)); if (!tmp) break; parent = tmp; } while (true); _module_instantiate(module_by_name(NULL, inst_name), NULL); } /* * Remove the config data we added for loop * detection. */ cf_data_remove(module, cd); /* * Check the module instances are of the same type. */ if (strcmp(cf_section_name1(mi->dl_inst->conf), cf_section_name1(module)) != 0) { cf_log_err(cp, "Referenced module is a rlm_%s instance, must be a rlm_%s instance", cf_section_name1(mi->dl_inst->conf), cf_section_name1(module)); return -1; } *out = cf_section_find(mi->dl_inst->conf, name, NULL); return 1; }
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; }
/** Synchronously load cookie data * * FIXME: This should not be synchronous, but integrating it into the event loop * before the server has started processing requests makes my head hurt. * * @param[in] ctx to allocate cookie buffer in. * @param[out] cookie Where to write the cookie we loaded. * @param[in] listen structure encapsulating the LDAP * @param[in] config of the sync we're loading the cookie for. * @return * - -1 on failure. * - 0 on success. * - 1 no cookie returned. */ static int proto_ldap_cookie_load(TALLOC_CTX *ctx, uint8_t **cookie, rad_listen_t *listen, sync_config_t const *config) { proto_ldap_inst_t *inst = talloc_get_type_abort(listen->data, proto_ldap_inst_t); REQUEST *request; CONF_SECTION *unlang; int ret = 0; rlm_rcode_t rcode; request = proto_ldap_request_setup(listen, inst, 0); if (!request) return -1; proto_ldap_attributes_add(request, config); request->packet->code = LDAP_SYNC_CODE_COOKIE_STORE; unlang = cf_section_find(request->server_cs, "load", "Cookie"); if (!unlang) { RDEBUG2("Ignoring %s operation. Add \"load Cookie {}\" to virtual-server \"%s\"" " to handle", fr_int2str(ldap_sync_code_table, request->packet->code, "<INVALID>"), cf_section_name2(request->server_cs)); } *cookie = NULL; rcode = unlang_interpret_synchronous(request, unlang, RLM_MODULE_NOOP); switch (rcode) { case RLM_MODULE_OK: case RLM_MODULE_UPDATED: { VALUE_PAIR *vp; vp = fr_pair_find_by_da(request->reply->vps, attr_ldap_sync_cookie, TAG_ANY); if (!vp) { if (config->allow_refresh) RDEBUG2("No &reply:Cookie attribute found. All entries matching " "sync configuration will be returned"); ret = 1; goto finish; } /* * So the request pool doesn't hang around indefinitely. */ MEM(*cookie = talloc_memdup(ctx, vp->vp_octets, vp->vp_length)); ret = 0; } break; case RLM_MODULE_NOOP: if (config->allow_refresh) RDEBUG2("Section returned \"noop\". All entries matching sync " "configuration will be returned"); ret = 1; break; default: RERROR("Section must return \"ok\", \"updated\", or \"noop\" for listener instantiation to succeed"); ret = -1; break; } finish: talloc_free(request); return ret; }
/** Parse socket configuration * * @param[in] cs specifying the listener configuration. * @param[in] listen structure encapsulating the libldap socket. * @return * - 0 on success. * - -1 on error. */ static int proto_ldap_socket_parse(CONF_SECTION *cs, rad_listen_t *listen) { proto_ldap_inst_t *inst = listen->data; CONF_SECTION *sync_cs; size_t i; int ret; /* * Always cache the CONF_SECTION of the server. */ listen->server_cs = virtual_server_find(listen->server); if (!listen->server_cs) { cf_log_err(cs, "Failed to find virtual server '%s'", listen->server); return -1; } if (cf_section_rules_push(cs, module_config) < 0) return -1; ret = cf_section_parse(inst, inst, cs); if (ret < 0) return ret; talloc_set_type(inst, proto_ldap_inst_t); rad_assert(inst->handle_config.server_str[0]); inst->handle_config.name = talloc_typed_asprintf(inst, "proto_ldap_conn (%s)", listen->server); memcpy(&inst->handle_config.server, &inst->handle_config.server_str[0], sizeof(inst->handle_config.server)); /* * Convert scope strings to enumerated constants */ for (sync_cs = cf_section_find(cs, "sync", NULL), i = 0; sync_cs; sync_cs = cf_section_find_next(cs, sync_cs, "sync", NULL), i++) { int scope; void **tmp; CONF_SECTION *map_cs; talloc_set_type(inst->sync_config[i], sync_config_t); scope = fr_str2int(fr_ldap_scope, inst->sync_config[i]->scope_str, -1); if (scope < 0) { #ifdef LDAP_SCOPE_CHILDREN cf_log_err(cs, "Invalid 'user.scope' value \"%s\", expected 'sub', 'one'" ", 'base' or 'children'", inst->sync_config[i]->scope_str); #else cf_log_err(cs, "Invalid 'user.scope' value \"%s\", expected 'sub', 'one'" " or 'base'", inst->sync_config[i]->scope_str) #endif return -1; } inst->sync_config[i]->scope = scope; /* * Needs to be NULL terminated as that's what libldap needs */ if (inst->sync_config[i]->attrs) { memcpy(&tmp, &inst->sync_config[i]->attrs, sizeof(tmp)); tmp = talloc_array_null_terminate(tmp); memcpy(&inst->sync_config[i]->attrs, tmp, sizeof(inst->sync_config[i]->attrs)); } inst->sync_config[i]->persist = true; inst->sync_config[i]->user_ctx = listen; inst->sync_config[i]->cookie = _proto_ldap_cookie_store; inst->sync_config[i]->entry = _proto_ldap_entry; inst->sync_config[i]->refresh_required = _proto_ldap_refresh_required; inst->sync_config[i]->present = _proto_ldap_present; /* * Parse and validate any maps */ map_cs = cf_section_find(sync_cs, "update", NULL); if (map_cs && map_afrom_cs(inst, &inst->sync_config[i]->entry_map, map_cs, NULL, NULL, fr_ldap_map_verify, NULL, LDAP_MAX_ATTRMAP) < 0) { return -1; } }
/** 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_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; } }
/** Initialise a module specific connection pool * * @see fr_pool_init * * @param[in] module section. * @param[in] opaque data pointer to pass to callbacks. * @param[in] c Callback to create new connections. * @param[in] a Callback to check the status of connections. * @param[in] log_prefix override, if NULL will be set automatically from the module CONF_SECTION. * @param[in] trigger_prefix if NULL will be set automatically from the module CONF_SECTION. * @param[in] trigger_args to make available in any triggers executed by the connection pool. * @return * - New connection pool. * - NULL on error. */ fr_pool_t *module_connection_pool_init(CONF_SECTION *module, void *opaque, fr_pool_connection_create_t c, fr_pool_connection_alive_t a, char const *log_prefix, char const *trigger_prefix, VALUE_PAIR *trigger_args) { CONF_SECTION *cs, *mycs; char log_prefix_buff[128]; char trigger_prefix_buff[128]; fr_pool_t *pool; char const *cs_name1, *cs_name2; int ret; #define parent_name(_x) cf_section_name(cf_item_to_section(cf_parent(_x))) cs_name1 = cf_section_name1(module); cs_name2 = cf_section_name2(module); if (!cs_name2) cs_name2 = cs_name1; if (!trigger_prefix) { snprintf(trigger_prefix_buff, sizeof(trigger_prefix_buff), "modules.%s.pool", cs_name1); trigger_prefix = trigger_prefix_buff; } if (!log_prefix) { snprintf(log_prefix_buff, sizeof(log_prefix_buff), "rlm_%s (%s)", cs_name1, cs_name2); log_prefix = log_prefix_buff; } /* * Get sibling's pool config section */ ret = module_sibling_section_find(&cs, module, "pool"); switch (ret) { case -1: return NULL; case 1: DEBUG4("%s: Using pool section from \"%s\"", log_prefix, parent_name(cs)); break; case 0: DEBUG4("%s: Using local pool section", log_prefix); break; } /* * Get our pool config section */ mycs = cf_section_find(module, "pool", NULL); if (!mycs) { DEBUG4("%s: Adding pool section to config item \"%s\" to store pool references", log_prefix, cf_section_name(module)); mycs = cf_section_alloc(module, module, "pool", NULL); } /* * Sibling didn't have a pool config section * Use our own local pool. */ if (!cs) { DEBUG4("%s: \"%s.pool\" section not found, using \"%s.pool\"", log_prefix, parent_name(cs), parent_name(mycs)); cs = mycs; } /* * If fr_pool_init has already been called * for this config section, reuse the previous instance. * * This allows modules to pass in the config sections * they would like to use the connection pool from. */ pool = cf_data_value(cf_data_find(cs, fr_pool_t, NULL)); if (!pool) { DEBUG4("%s: No pool reference found for config item \"%s.pool\"", log_prefix, parent_name(cs)); pool = fr_pool_init(cs, cs, opaque, c, a, log_prefix); if (!pool) return NULL; fr_pool_enable_triggers(pool, trigger_prefix, trigger_args); if (fr_pool_start(pool) < 0) { ERROR("%s: Starting initial connections failed", log_prefix); return NULL; } DEBUG4("%s: Adding pool reference %p to config item \"%s.pool\"", log_prefix, pool, parent_name(cs)); cf_data_add(cs, pool, NULL, false); return pool; } fr_pool_ref(pool); DEBUG4("%s: Found pool reference %p in config item \"%s.pool\"", log_prefix, pool, parent_name(cs)); /* * We're reusing pool data add it to our local config * section. This allows other modules to transitively * re-use a pool through this module. */ if (mycs != cs) { DEBUG4("%s: Copying pool reference %p from config item \"%s.pool\" to config item \"%s.pool\"", log_prefix, pool, parent_name(cs), parent_name(mycs)); cf_data_add(mycs, pool, NULL, false); } return pool; }
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; REQUEST_VERIFY(request); /* * Pass this through asynchronously to the module which * is waiting for something to happen. */ if (action != FR_IO_ACTION_RUN) { unlang_signal(request, (fr_state_signal_t) action); return FR_IO_DONE; } switch (request->request_state) { case REQUEST_INIT: RDEBUG("Received %s ID %i", fr_dict_enum_alias_by_value(attr_packet_type, fr_box_uint32(request->reply->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", NULL); if (!unlang) { REDEBUG("Failed to find 'recv' section"); return FR_IO_FAIL; } RDEBUG("Running 'recv' from file %s", cf_filename(unlang)); unlang_push_section(request, unlang, RLM_MODULE_NOOP, UNLANG_TOP_FRAME); request->request_state = REQUEST_RECV; /* FALL-THROUGH */ case REQUEST_RECV: rcode = unlang_interpret_continue(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) { /* * The module has a number of OK return codes. */ case RLM_MODULE_OK: case RLM_MODULE_UPDATED: switch (request->packet->code) { case FR_CODE_ACCOUNTING_REQUEST: request->reply->code = FR_CODE_ACCOUNTING_RESPONSE; break; case FR_CODE_COA_REQUEST: request->reply->code = FR_CODE_COA_ACK; break; case FR_CODE_DISCONNECT_REQUEST: request->reply->code = FR_CODE_DISCONNECT_ACK; break; default: request->reply->code = 0; break; } break; case RLM_MODULE_HANDLED: break; /* * The module failed, or said the request is * invalid, therefore we stop here. */ case RLM_MODULE_NOOP: case RLM_MODULE_FAIL: case RLM_MODULE_INVALID: case RLM_MODULE_NOTFOUND: case RLM_MODULE_REJECT: case RLM_MODULE_USERLOCK: default: request->reply->code = 0; 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; if (request->reply->code == FR_CODE_DO_NOT_RESPOND) { RWARN("Ignoring 'do_not_respond' as it does not apply to detail files"); } unlang = cf_section_find(request->server_cs, "send", "ok"); if (!unlang) goto send_reply; RDEBUG("Running 'send %s { ... }' from file %s", cf_section_name2(unlang), cf_filename(unlang)); unlang_push_section(request, unlang, RLM_MODULE_NOOP, UNLANG_TOP_FRAME); request->request_state = REQUEST_SEND; /* FALL-THROUGH */ case REQUEST_SEND: rcode = unlang_interpret_continue(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: request->reply->code = 0; break; } send_reply: /* * Failed, but we still reply with a magic code, * so that the reader can retransmit. */ if (!request->reply->code) { REDEBUG("Failed ID %i", request->reply->id); } else { RDEBUG("Sent %s ID %i", fr_dict_enum_alias_by_value(attr_packet_type, fr_box_uint32(request->reply->code)), request->reply->id); } log_request_proto_pair_list(L_DBG_LVL_1, request, request->reply->vps, ""); break; default: return FR_IO_FAIL; } return FR_IO_REPLY; }
/* * Zap a user from the radutmp and radwtmp file. */ int main(int argc, char **argv) { CONF_SECTION *cs; NAS *nas; uint32_t ip = 0; uint32_t nas_port = ~0; char *user = NULL; char *s; char buf[256]; struct radutmp u; int argval; progname = argv[0]; radius_dir = strdup(RADIUS_DIR); /* Process the options. */ while ((argval = getopt(argc, argv, "d:p:r:")) != EOF) { switch(argval) { case 'd': if (radius_dir) free(radius_dir); radius_dir = strdup(optarg); break; case 'p': acct_port = atoi(optarg); break; case 'r': if ((radiusip = ip_getaddr(optarg)) == INADDR_NONE) { fprintf(stderr, "%s: %s: radius server unknown\n", progname, optarg); exit(1); } break; default: usage(); exit(1); } } if (argc == optind) { /* no terminal server specified */ usage(); exit(1); } if (argc > optind + 1) { /* NAS port given */ s = argv[optind+1]; if (*s == 's' || *s == 'S') s++; nas_port = strtoul(s, NULL, 10); } if (argc > optind + 2) { /* username (login) given */ user = argv[optind+2]; } /* * Find the IP address of the terminal server. */ if ((nas = nas_findbyname(argv[optind])) == NULL && argv[optind][0] != 0) { if ((ip = ip_getaddr(argv[optind])) == INADDR_NONE) { fprintf(stderr, "%s: host not found.\n", argv[optind]); exit(1); } } if (nas != NULL) ip = nas->ipaddr; /* * Ensure that the configuration is initialized. */ memset(&mainconfig, 0, sizeof(mainconfig)); /* Read radiusd.conf */ if (read_mainconfig(0) < 0) { fprintf(stderr, "%s: Error reading radiusd.conf.\n", argv[0]); exit(1); } /* Read the radutmp section of radiusd.conf */ cs = cf_section_sub_find(cf_section_find("modules"), "radutmp"); if(!cs) { fprintf(stderr, "%s: No configuration information in radutmp section of radiusd.conf!\n", argv[0]); exit(1); } cf_section_parse(cs, NULL, module_config); printf("%s: zapping termserver %s, port %u", progname, ip_hostname(buf, sizeof(buf), ip), nas_port); if (user != NULL) printf(", user %s", user); printf("\n"); if (nas_port == ~0) { return do_accton_packet(ip); } if (!radutmp_lookup(&u, ip, nas_port, user)) { fprintf(stderr, "Entry not found\n"); return 1; } return do_stop_packet(&u); }
static int rediswho_instantiate(CONF_SECTION * conf, void ** instance) { module_instance_t *modinst; rlm_rediswho_t *inst; /* * Set up a storage area for instance data */ inst = *instance = rad_malloc(sizeof (*inst)); memset(inst, 0, sizeof (*inst)); /* * If the configuration parameters can't be parsed, then * fail. */ if (cf_section_parse(conf, inst, module_config) < 0) { free(inst); return -1; } inst->xlat_name = cf_section_name2(conf); if (!inst->xlat_name) inst->xlat_name = cf_section_name1(conf); inst->xlat_name = strdup(inst->xlat_name); DEBUG("xlat name %s\n", inst->xlat_name); /* * Check that all the queries are in place */ if ((inst->start_insert == NULL) || !*inst->start_insert) { radlog(L_ERR, "rlm_rediswho: the 'start_insert' statement must be set."); rediswho_detach(inst); return -1; } if ((inst->start_trim == NULL) || !*inst->start_trim) { radlog(L_ERR, "rlm_rediswho: the 'start_trim' statement must be set."); rediswho_detach(inst); return -1; } if ((inst->start_expire == NULL) || !*inst->start_expire) { radlog(L_ERR, "rlm_rediswho: the 'start_expire' statement must be set."); rediswho_detach(inst); return -1; } if ((inst->alive_insert == NULL) || !*inst->alive_insert) { radlog(L_ERR, "rlm_rediswho: the 'alive_insert' statement must be set."); rediswho_detach(inst); return -1; } if ((inst->alive_trim == NULL) || !*inst->alive_trim) { radlog(L_ERR, "rlm_rediswho: the 'alive_trim' statement must be set."); rediswho_detach(inst); return -1; } if ((inst->alive_expire == NULL) || !*inst->alive_expire) { radlog(L_ERR, "rlm_rediswho: the 'alive_expire' statement must be set."); rediswho_detach(inst); return -1; } if ((inst->stop_insert == NULL) || !*inst->stop_insert) { radlog(L_ERR, "rlm_rediswho: the 'stop_insert' statement must be set."); rediswho_detach(inst); return -1; } if ((inst->stop_trim == NULL) || !*inst->stop_trim) { radlog(L_ERR, "rlm_rediswho: the 'stop_trim' statement must be set."); rediswho_detach(inst); return -1; } if ((inst->stop_expire == NULL) || !*inst->stop_expire) { radlog(L_ERR, "rlm_rediswho: the 'stop_expire' statement must be set."); rediswho_detach(inst); return -1; } modinst = find_module_instance(cf_section_find("modules"), inst->redis_instance_name, 1); if (!modinst) { radlog(L_ERR, "rediswho: failed to find module instance \"%s\"", inst->redis_instance_name); rediswho_detach(inst); return -1; } if (strcmp(modinst->entry->name, "rlm_redis") != 0) { radlog(L_ERR, "rediswho: Module \"%s\"" " is not an instance of the redis module", inst->redis_instance_name); rediswho_detach(inst); return -1; } inst->redis_inst = (REDIS_INST *) modinst->insthandle; return 0; }
/* * Do any per-module initialization that is separate to each * configured instance of the module. e.g. set up connections * to external databases, read configuration files, set up * dictionary entries, etc. * * If configuration information is given in the config section * that must be referenced in later calls, store a handle to it * in *instance otherwise put a null pointer there. */ static int sqlippool_instantiate(CONF_SECTION * conf, void ** instance) { module_instance_t *modinst; rlm_sqlippool_t * data; const char * pool_name = NULL; /* * Set up a storage area for instance data */ data = rad_malloc(sizeof(*data)); memset(data, 0, sizeof(*data)); /* * If the configuration parameters can't be parsed, then * fail. */ if (cf_section_parse(conf, data, module_config) < 0) { free(data); return -1; } if (IS_EMPTY(data->sql_instance_name)) { radlog(L_ERR, "rlm_sqlippool: the 'sql-instance-name' variable must be set."); sqlippool_detach(data); return -1; } /* * Check that all the queries are in place */ if (IS_EMPTY(data->allocate_clear)) { radlog(L_ERR, "rlm_sqlippool: the 'allocate-clear' statement must be set."); sqlippool_detach(data); return -1; } if (IS_EMPTY(data->allocate_find)) { radlog(L_ERR, "rlm_sqlippool: the 'allocate-find' statement must be set."); sqlippool_detach(data); return -1; } if (IS_EMPTY(data->allocate_update)) { radlog(L_ERR, "rlm_sqlippool: the 'allocate-update' statement must be set."); sqlippool_detach(data); return -1; } if (IS_EMPTY(data->start_update)) { radlog(L_ERR, "rlm_sqlippool: the 'start-update' statement must be set."); sqlippool_detach(data); return -1; } if (IS_EMPTY(data->alive_update)) { radlog(L_ERR, "rlm_sqlippool: the 'alive-update' statement must be set."); sqlippool_detach(data); return -1; } if (IS_EMPTY(data->stop_clear)) { radlog(L_ERR, "rlm_sqlippool: the 'stop-clear' statement must be set."); sqlippool_detach(data); return -1; } if (IS_EMPTY(data->on_clear)) { radlog(L_ERR, "rlm_sqlippool: the 'on-clear' statement must be set."); sqlippool_detach(data); return -1; } if (IS_EMPTY(data->off_clear)) { radlog(L_ERR, "rlm_sqlippool: the 'off-clear' statement must be set."); sqlippool_detach(data); return -1; } pool_name = cf_section_name2(conf); if (pool_name != NULL) data->pool_name = strdup(pool_name); else data->pool_name = strdup("ippool"); modinst = find_module_instance(cf_section_find("modules"), data->sql_instance_name, 1); if (!modinst) { radlog(L_ERR, "sqlippool_instantiate: failed to find sql instance named %s", data->sql_instance_name); sqlippool_detach(data); return -1; } if (strcmp(modinst->entry->name, "rlm_sql") != 0) { radlog(L_ERR, "sqlippool_instantiate: Module \"%s\"" " is not an instance of the rlm_sql module", data->sql_instance_name); sqlippool_detach(data); return -1; } data->sql_inst = (SQL_INST *) modinst->insthandle; *instance = data; return 0; }
/** Allocate a new IP address from a pool * */ static ippool_rcode_t redis_ippool_allocate(rlm_redis_ippool_t const *inst, REQUEST *request, uint8_t const *key_prefix, size_t key_prefix_len, uint8_t const *device_id, size_t device_id_len, uint8_t const *gateway_id, size_t gateway_id_len, uint32_t expires) { struct timeval now; redisReply *reply = NULL; fr_redis_rcode_t status; ippool_rcode_t ret = IPPOOL_RCODE_SUCCESS; rad_assert(key_prefix); rad_assert(device_id); gettimeofday(&now, NULL); /* * hiredis doesn't deal well with NULL string pointers */ if (!gateway_id) gateway_id = (uint8_t const *)""; status = ippool_script(&reply, request, inst->cluster, key_prefix, key_prefix_len, inst->wait_num, FR_TIMEVAL_TO_MS(&inst->wait_timeout), lua_alloc_digest, lua_alloc_cmd, "EVALSHA %s 1 %b %u %u %b %b", lua_alloc_digest, key_prefix, key_prefix_len, (unsigned int)now.tv_sec, expires, device_id, device_id_len, gateway_id, gateway_id_len); if (status != REDIS_RCODE_SUCCESS) { ret = IPPOOL_RCODE_FAIL; goto finish; } rad_assert(reply); if (reply->type != REDIS_REPLY_ARRAY) { REDEBUG("Expected result to be array got \"%s\"", fr_int2str(redis_reply_types, reply->type, "<UNKNOWN>")); ret = IPPOOL_RCODE_FAIL; goto finish; } if (reply->elements == 0) { REDEBUG("Got empty result array"); ret = IPPOOL_RCODE_FAIL; goto finish; } /* * Process return code */ if (reply->element[0]->type != REDIS_REPLY_INTEGER) { REDEBUG("Server returned unexpected type \"%s\" for rcode element (result[0])", fr_int2str(redis_reply_types, reply->type, "<UNKNOWN>")); ret = IPPOOL_RCODE_FAIL; goto finish; } ret = reply->element[0]->integer; if (ret < 0) goto finish; /* * Process IP address */ if (reply->elements > 1) { vp_tmpl_t ip_rhs = { .type = TMPL_TYPE_DATA, .tmpl_value_type = FR_TYPE_STRING }; vp_map_t ip_map = { .lhs = inst->allocated_address_attr, .op = T_OP_SET, .rhs = &ip_rhs }; switch (reply->element[1]->type) { /* * Destination attribute may not be IPv4, in which case * we want to pre-convert the integer value to an IPv4 * address before casting it once more to the type of * the destination attribute. */ case REDIS_REPLY_INTEGER: { if (ip_map.lhs->tmpl_da->type != FR_TYPE_IPV4_ADDR) { fr_value_box_t tmp; memset(&tmp, 0, sizeof(tmp)); tmp.vb_uint32 = ntohl((uint32_t)reply->element[1]->integer); tmp.type = FR_TYPE_UINT32; if (fr_value_box_cast(NULL, &ip_map.rhs->tmpl_value, FR_TYPE_IPV4_ADDR, NULL, &tmp)) { RPEDEBUG("Failed converting integer to IPv4 address"); ret = IPPOOL_RCODE_FAIL; goto finish; } } else { ip_map.rhs->tmpl_value.vb_uint32 = ntohl((uint32_t)reply->element[1]->integer); ip_map.rhs->tmpl_value_type = FR_TYPE_UINT32; } } goto do_ip_map; case REDIS_REPLY_STRING: ip_map.rhs->tmpl_value.vb_strvalue = reply->element[1]->str; ip_map.rhs->tmpl_value_length = reply->element[1]->len; ip_map.rhs->tmpl_value_type = FR_TYPE_STRING; do_ip_map: if (map_to_request(request, &ip_map, map_to_vp, NULL) < 0) { ret = IPPOOL_RCODE_FAIL; goto finish; } break; default: REDEBUG("Server returned unexpected type \"%s\" for IP element (result[1])", fr_int2str(redis_reply_types, reply->element[1]->type, "<UNKNOWN>")); ret = IPPOOL_RCODE_FAIL; goto finish; } } /* * Process Range identifier */ if (reply->elements > 2) { switch (reply->element[2]->type) { /* * Add range ID to request */ case REDIS_REPLY_STRING: { vp_tmpl_t range_rhs = { .name = "", .type = TMPL_TYPE_DATA, .tmpl_value_type = FR_TYPE_STRING, .quote = T_DOUBLE_QUOTED_STRING }; vp_map_t range_map = { .lhs = inst->range_attr, .op = T_OP_SET, .rhs = &range_rhs }; range_map.rhs->tmpl_value.vb_strvalue = reply->element[2]->str; range_map.rhs->tmpl_value_length = reply->element[2]->len; range_map.rhs->tmpl_value_type = FR_TYPE_STRING; if (map_to_request(request, &range_map, map_to_vp, NULL) < 0) { ret = IPPOOL_RCODE_FAIL; goto finish; } } break; case REDIS_REPLY_NIL: break; default: REDEBUG("Server returned unexpected type \"%s\" for range element (result[2])", fr_int2str(redis_reply_types, reply->element[2]->type, "<UNKNOWN>")); ret = IPPOOL_RCODE_FAIL; goto finish; } } /* * Process Expiry time */ if (inst->expiry_attr && (reply->elements > 3)) { vp_tmpl_t expiry_rhs = { .name = "", .type = TMPL_TYPE_DATA, .tmpl_value_type = FR_TYPE_STRING, .quote = T_DOUBLE_QUOTED_STRING }; vp_map_t expiry_map = { .lhs = inst->expiry_attr, .op = T_OP_SET, .rhs = &expiry_rhs }; if (reply->element[3]->type != REDIS_REPLY_INTEGER) { REDEBUG("Server returned unexpected type \"%s\" for expiry element (result[3])", fr_int2str(redis_reply_types, reply->element[3]->type, "<UNKNOWN>")); ret = IPPOOL_RCODE_FAIL; goto finish; } expiry_map.rhs->tmpl_value.vb_uint32 = reply->element[3]->integer; expiry_map.rhs->tmpl_value_type = FR_TYPE_UINT32; if (map_to_request(request, &expiry_map, map_to_vp, NULL) < 0) { ret = IPPOOL_RCODE_FAIL; goto finish; } } finish: fr_redis_reply_free(&reply); return ret; } /** Update an existing IP address in a pool * */ static ippool_rcode_t redis_ippool_update(rlm_redis_ippool_t const *inst, REQUEST *request, uint8_t const *key_prefix, size_t key_prefix_len, fr_ipaddr_t *ip, uint8_t const *device_id, size_t device_id_len, uint8_t const *gateway_id, size_t gateway_id_len, uint32_t expires) { struct timeval now; redisReply *reply = NULL; fr_redis_rcode_t status; ippool_rcode_t ret = IPPOOL_RCODE_SUCCESS; vp_tmpl_t range_rhs = { .name = "", .type = TMPL_TYPE_DATA, .tmpl_value_type = FR_TYPE_STRING, .quote = T_DOUBLE_QUOTED_STRING }; vp_map_t range_map = { .lhs = inst->range_attr, .op = T_OP_SET, .rhs = &range_rhs }; gettimeofday(&now, NULL); /* * hiredis doesn't deal well with NULL string pointers */ if (!device_id) device_id = (uint8_t const *)""; if (!gateway_id) gateway_id = (uint8_t const *)""; if ((ip->af == AF_INET) && inst->ipv4_integer) { status = ippool_script(&reply, request, inst->cluster, key_prefix, key_prefix_len, inst->wait_num, FR_TIMEVAL_TO_MS(&inst->wait_timeout), lua_update_digest, lua_update_cmd, "EVALSHA %s 1 %b %u %u %u %b %b", lua_update_digest, key_prefix, key_prefix_len, (unsigned int)now.tv_sec, expires, htonl(ip->addr.v4.s_addr), device_id, device_id_len, gateway_id, gateway_id_len); } else { char ip_buff[FR_IPADDR_PREFIX_STRLEN]; IPPOOL_SPRINT_IP(ip_buff, ip, ip->prefix); status = ippool_script(&reply, request, inst->cluster, key_prefix, key_prefix_len, inst->wait_num, FR_TIMEVAL_TO_MS(&inst->wait_timeout), lua_update_digest, lua_update_cmd, "EVALSHA %s 1 %b %u %u %s %b %b", lua_update_digest, key_prefix, key_prefix_len, (unsigned int)now.tv_sec, expires, ip_buff, device_id, device_id_len, gateway_id, gateway_id_len); } if (status != REDIS_RCODE_SUCCESS) { ret = IPPOOL_RCODE_FAIL; goto finish; } if (reply->type != REDIS_REPLY_ARRAY) { REDEBUG("Expected result to be array got \"%s\"", fr_int2str(redis_reply_types, reply->type, "<UNKNOWN>")); ret = IPPOOL_RCODE_FAIL; goto finish; } if (reply->elements == 0) { REDEBUG("Got empty result array"); ret = IPPOOL_RCODE_FAIL; goto finish; } /* * Process return code */ if (reply->element[0]->type != REDIS_REPLY_INTEGER) { REDEBUG("Server returned unexpected type \"%s\" for rcode element (result[0])", fr_int2str(redis_reply_types, reply->type, "<UNKNOWN>")); ret = IPPOOL_RCODE_FAIL; goto finish; } ret = reply->element[0]->integer; if (ret < 0) goto finish; /* * Process Range identifier */ if (reply->elements > 1) { switch (reply->element[1]->type) { /* * Add range ID to request */ case REDIS_REPLY_STRING: range_map.rhs->tmpl_value.vb_strvalue = reply->element[1]->str; range_map.rhs->tmpl_value_length = reply->element[1]->len; range_map.rhs->tmpl_value_type = FR_TYPE_STRING; if (map_to_request(request, &range_map, map_to_vp, NULL) < 0) { ret = IPPOOL_RCODE_FAIL; goto finish; } break; case REDIS_REPLY_NIL: break; default: REDEBUG("Server returned unexpected type \"%s\" for range element (result[1])", fr_int2str(redis_reply_types, reply->element[0]->type, "<UNKNOWN>")); ret = IPPOOL_RCODE_FAIL; goto finish; } } /* * Copy expiry time to expires attribute (if set) */ if (inst->expiry_attr) { vp_tmpl_t expiry_rhs = { .name = "", .type = TMPL_TYPE_DATA, .tmpl_value_type = FR_TYPE_STRING, .quote = T_DOUBLE_QUOTED_STRING }; vp_map_t expiry_map = { .lhs = inst->expiry_attr, .op = T_OP_SET, .rhs = &expiry_rhs }; expiry_map.rhs->tmpl_value.vb_uint32 = expires; expiry_map.rhs->tmpl_value_type = FR_TYPE_UINT32; if (map_to_request(request, &expiry_map, map_to_vp, NULL) < 0) { ret = IPPOOL_RCODE_FAIL; goto finish; } } finish: fr_redis_reply_free(&reply); return ret; } /** Release an existing IP address in a pool * */ static ippool_rcode_t redis_ippool_release(rlm_redis_ippool_t const *inst, REQUEST *request, uint8_t const *key_prefix, size_t key_prefix_len, fr_ipaddr_t *ip, uint8_t const *device_id, size_t device_id_len) { struct timeval now; redisReply *reply = NULL; fr_redis_rcode_t status; ippool_rcode_t ret = IPPOOL_RCODE_SUCCESS; gettimeofday(&now, NULL); /* * hiredis doesn't deal well with NULL string pointers */ if (!device_id) device_id = (uint8_t const *)""; if ((ip->af == AF_INET) && inst->ipv4_integer) { status = ippool_script(&reply, request, inst->cluster, key_prefix, key_prefix_len, inst->wait_num, FR_TIMEVAL_TO_MS(&inst->wait_timeout), lua_release_digest, lua_release_cmd, "EVALSHA %s 1 %b %u %u %b", lua_release_digest, key_prefix, key_prefix_len, (unsigned int)now.tv_sec, htonl(ip->addr.v4.s_addr), device_id, device_id_len); } else { char ip_buff[FR_IPADDR_PREFIX_STRLEN]; IPPOOL_SPRINT_IP(ip_buff, ip, ip->prefix); status = ippool_script(&reply, request, inst->cluster, key_prefix, key_prefix_len, inst->wait_num, FR_TIMEVAL_TO_MS(&inst->wait_timeout), lua_release_digest, lua_release_cmd, "EVALSHA %s 1 %b %u %s %b", lua_release_digest, key_prefix, key_prefix_len, (unsigned int)now.tv_sec, ip_buff, device_id, device_id_len); } if (status != REDIS_RCODE_SUCCESS) { ret = IPPOOL_RCODE_FAIL; goto finish; } if (reply->type != REDIS_REPLY_ARRAY) { REDEBUG("Expected result to be array got \"%s\"", fr_int2str(redis_reply_types, reply->type, "<UNKNOWN>")); ret = IPPOOL_RCODE_FAIL; goto finish; } if (reply->elements == 0) { REDEBUG("Got empty result array"); ret = IPPOOL_RCODE_FAIL; goto finish; } /* * Process return code */ if (reply->element[0]->type != REDIS_REPLY_INTEGER) { REDEBUG("Server returned unexpected type \"%s\" for rcode element (result[0])", fr_int2str(redis_reply_types, reply->type, "<UNKNOWN>")); ret = IPPOOL_RCODE_FAIL; goto finish; } ret = reply->element[0]->integer; if (ret < 0) goto finish; finish: fr_redis_reply_free(&reply); return ret; } /** Find the pool name we'll be allocating from * * @param[out] out Where to write the pool name. * @param[out] buff Where to write the pool name (in the case of an expansion). * @param[in] bufflen Size of the output buffer. * @param[in] inst This instance of the rlm_redis_ippool module. * @param[in] request The current request. * @return * - < 0 on error. * - 0 if no pool attribute exists, or the pool name is a zero length string. * - > 0 on success (length of data written to out). */ static inline ssize_t ippool_pool_name(uint8_t const **out, uint8_t buff[], size_t bufflen, rlm_redis_ippool_t const *inst, REQUEST *request) { ssize_t slen; slen = tmpl_expand(out, (char *)buff, bufflen, request, inst->pool_name, NULL, NULL); if (slen < 0) { if (inst->pool_name->type == TMPL_TYPE_ATTR) { RDEBUG2("Pool attribute not present in request. Doing nothing"); return 0; } REDEBUG("Failed expanding pool name"); return -1; } if (slen == 0) { RDEBUG2("Empty pool name. Doing nothing"); return 0; } if ((*out == buff) && is_truncated((size_t)slen, bufflen)) { REDEBUG("Pool name too long. Expected %zu bytes, got %zu bytes", bufflen, (size_t)slen); return -1; } return slen; } static rlm_rcode_t mod_action(rlm_redis_ippool_t const *inst, REQUEST *request, ippool_action_t action) { uint8_t key_prefix_buff[IPPOOL_MAX_KEY_PREFIX_SIZE], device_id_buff[256], gateway_id_buff[256]; uint8_t const *key_prefix, *device_id = NULL, *gateway_id = NULL; size_t key_prefix_len, device_id_len = 0, gateway_id_len = 0; ssize_t slen; fr_ipaddr_t ip; char expires_buff[20]; char const *expires_str; unsigned long expires = 0; char *q; slen = ippool_pool_name(&key_prefix, (uint8_t *)&key_prefix_buff, sizeof(key_prefix_len), inst, request); if (slen < 0) return RLM_MODULE_FAIL; if (slen == 0) return RLM_MODULE_NOOP; key_prefix_len = (size_t)slen; if (inst->device_id) { slen = tmpl_expand((char const **)&device_id, (char *)&device_id_buff, sizeof(device_id_buff), request, inst->device_id, NULL, NULL); if (slen < 0) { REDEBUG("Failed expanding device (%s)", inst->device_id->name); return RLM_MODULE_FAIL; } device_id_len = (size_t)slen; } if (inst->gateway_id) { slen = tmpl_expand((char const **)&gateway_id, (char *)&gateway_id_buff, sizeof(gateway_id_buff), request, inst->gateway_id, NULL, NULL); if (slen < 0) { REDEBUG("Failed expanding gateway (%s)", inst->gateway_id->name); return RLM_MODULE_FAIL; } gateway_id_len = (size_t)slen; } switch (action) { case POOL_ACTION_ALLOCATE: if (tmpl_expand(&expires_str, expires_buff, sizeof(expires_buff), request, inst->offer_time, NULL, NULL) < 0) { REDEBUG("Failed expanding offer_time (%s)", inst->offer_time->name); return RLM_MODULE_FAIL; } expires = strtoul(expires_str, &q, 10); if (q != (expires_str + strlen(expires_str))) { REDEBUG("Invalid offer_time. Must be an integer value"); return RLM_MODULE_FAIL; } ippool_action_print(request, action, L_DBG_LVL_2, key_prefix, key_prefix_len, NULL, device_id, device_id_len, gateway_id, gateway_id_len, expires); switch (redis_ippool_allocate(inst, request, key_prefix, key_prefix_len, device_id, device_id_len, gateway_id, gateway_id_len, (uint32_t)expires)) { case IPPOOL_RCODE_SUCCESS: RDEBUG2("IP address lease allocated"); return RLM_MODULE_UPDATED; case IPPOOL_RCODE_POOL_EMPTY: RWDEBUG("Pool contains no free addresses"); return RLM_MODULE_NOTFOUND; default: return RLM_MODULE_FAIL; } case POOL_ACTION_UPDATE: { char ip_buff[INET6_ADDRSTRLEN + 4]; char const *ip_str; if (tmpl_expand(&expires_str, expires_buff, sizeof(expires_buff), request, inst->lease_time, NULL, NULL) < 0) { REDEBUG("Failed expanding lease_time (%s)", inst->lease_time->name); return RLM_MODULE_FAIL; } expires = strtoul(expires_str, &q, 10); if (q != (expires_str + strlen(expires_str))) { REDEBUG("Invalid expires. Must be an integer value"); return RLM_MODULE_FAIL; } if (tmpl_expand(&ip_str, ip_buff, sizeof(ip_buff), request, inst->requested_address, NULL, NULL) < 0) { REDEBUG("Failed expanding requested_address (%s)", inst->requested_address->name); return RLM_MODULE_FAIL; } if (fr_inet_pton(&ip, ip_str, -1, AF_UNSPEC, false, true) < 0) { RPEDEBUG("Failed parsing address"); return RLM_MODULE_FAIL; } ippool_action_print(request, action, L_DBG_LVL_2, key_prefix, key_prefix_len, ip_str, device_id, device_id_len, gateway_id, gateway_id_len, expires); switch (redis_ippool_update(inst, request, key_prefix, key_prefix_len, &ip, device_id, device_id_len, gateway_id, gateway_id_len, (uint32_t)expires)) { case IPPOOL_RCODE_SUCCESS: RDEBUG2("Requested IP address' \"%s\" lease updated", ip_str); /* * Copy over the input IP address to the reply attribute */ if (inst->copy_on_update) { vp_tmpl_t ip_rhs = { .name = "", .type = TMPL_TYPE_DATA, .quote = T_BARE_WORD, }; vp_map_t ip_map = { .lhs = inst->allocated_address_attr, .op = T_OP_SET, .rhs = &ip_rhs }; ip_rhs.tmpl_value_length = strlen(ip_str); ip_rhs.tmpl_value.vb_strvalue = ip_str; ip_rhs.tmpl_value_type = FR_TYPE_STRING; if (map_to_request(request, &ip_map, map_to_vp, NULL) < 0) return RLM_MODULE_FAIL; } return RLM_MODULE_UPDATED; /* * It's useful to be able to identify the 'not found' case * as we can relay to a server where the IP address might * be found. This extremely useful for migrations. */ case IPPOOL_RCODE_NOT_FOUND: REDEBUG("Requested IP address \"%s\" is not a member of the specified pool", ip_str); return RLM_MODULE_NOTFOUND; case IPPOOL_RCODE_EXPIRED: REDEBUG("Requested IP address' \"%s\" lease already expired at time of renewal", ip_str); return RLM_MODULE_INVALID; case IPPOOL_RCODE_DEVICE_MISMATCH: REDEBUG("Requested IP address' \"%s\" lease allocated to another device", ip_str); return RLM_MODULE_INVALID; default: return RLM_MODULE_FAIL; } } case POOL_ACTION_RELEASE: { char ip_buff[INET6_ADDRSTRLEN + 4]; char const *ip_str; if (tmpl_expand(&ip_str, ip_buff, sizeof(ip_buff), request, inst->requested_address, NULL, NULL) < 0) { REDEBUG("Failed expanding requested_address (%s)", inst->requested_address->name); return RLM_MODULE_FAIL; } if (fr_inet_pton(&ip, ip_str, -1, AF_UNSPEC, false, true) < 0) { RPEDEBUG("Failed parsing address"); return RLM_MODULE_FAIL; } ippool_action_print(request, action, L_DBG_LVL_2, key_prefix, key_prefix_len, ip_str, device_id, device_id_len, gateway_id, gateway_id_len, 0); switch (redis_ippool_release(inst, request, key_prefix, key_prefix_len, &ip, device_id, device_id_len)) { case IPPOOL_RCODE_SUCCESS: RDEBUG2("IP address \"%s\" released", ip_str); return RLM_MODULE_UPDATED; /* * It's useful to be able to identify the 'not found' case * as we can relay to a server where the IP address might * be found. This extremely useful for migrations. */ case IPPOOL_RCODE_NOT_FOUND: REDEBUG("Requested IP address \"%s\" is not a member of the specified pool", ip_str); return RLM_MODULE_NOTFOUND; case IPPOOL_RCODE_DEVICE_MISMATCH: REDEBUG("Requested IP address' \"%s\" lease allocated to another device", ip_str); return RLM_MODULE_INVALID; default: return RLM_MODULE_FAIL; } } case POOL_ACTION_BULK_RELEASE: RDEBUG2("Bulk release not yet implemented"); return RLM_MODULE_NOOP; default: rad_assert(0); return RLM_MODULE_FAIL; } } static rlm_rcode_t mod_accounting(void *instance, UNUSED void *thread, REQUEST *request) CC_HINT(nonnull); static rlm_rcode_t mod_accounting(void *instance, UNUSED void *thread, REQUEST *request) { rlm_redis_ippool_t const *inst = instance; VALUE_PAIR *vp; /* * Pool-Action override */ vp = fr_pair_find_by_da(request->control, attr_pool_action, TAG_ANY); if (vp) return mod_action(inst, request, vp->vp_uint32); /* * Otherwise, guess the action by Acct-Status-Type */ vp = fr_pair_find_by_da(request->packet->vps, attr_acct_status_type, TAG_ANY); if (!vp) { RDEBUG2("Couldn't find &request:Acct-Status-Type or &control:Pool-Action, doing nothing..."); return RLM_MODULE_NOOP; } switch (vp->vp_uint32) { case FR_STATUS_START: case FR_STATUS_ALIVE: return mod_action(inst, request, POOL_ACTION_UPDATE); case FR_STATUS_STOP: return mod_action(inst, request, POOL_ACTION_RELEASE); case FR_STATUS_ACCOUNTING_OFF: case FR_STATUS_ACCOUNTING_ON: return mod_action(inst, request, POOL_ACTION_BULK_RELEASE); default: return RLM_MODULE_NOOP; } } static rlm_rcode_t mod_authorize(void *instance, UNUSED void *thread, REQUEST *request) CC_HINT(nonnull); static rlm_rcode_t mod_authorize(void *instance, UNUSED void *thread, REQUEST *request) { rlm_redis_ippool_t const *inst = instance; VALUE_PAIR *vp; /* * Unless it's overridden the default action is to allocate * when called in Post-Auth. */ vp = fr_pair_find_by_da(request->control, attr_pool_action, TAG_ANY); return mod_action(inst, request, vp ? vp->vp_uint32 : POOL_ACTION_ALLOCATE); } static rlm_rcode_t mod_post_auth(void *instance, UNUSED void *thread, REQUEST *request) CC_HINT(nonnull); static rlm_rcode_t mod_post_auth(void *instance, UNUSED void *thread, REQUEST *request) { rlm_redis_ippool_t const *inst = instance; VALUE_PAIR *vp; ippool_action_t action = POOL_ACTION_ALLOCATE; /* * Unless it's overridden the default action is to allocate * when called in Post-Auth. */ vp = fr_pair_find_by_da(request->control, attr_pool_action, TAG_ANY); if (vp) { if ((vp->vp_uint32 > 0) && (vp->vp_uint32 <= POOL_ACTION_BULK_RELEASE)) { action = vp->vp_uint32; } else { RWDEBUG("Ignoring invalid action %d", vp->vp_uint32); return RLM_MODULE_NOOP; } #ifdef WITH_DHCP } else if (request->dict == dict_dhcpv4) { vp = fr_pair_find_by_da(request->control, attr_message_type, TAG_ANY); if (!vp) goto run; if (vp->vp_uint8 == FR_DHCP_REQUEST) action = POOL_ACTION_UPDATE; #endif } run: return mod_action(inst, request, action); } static int mod_instantiate(void *instance, CONF_SECTION *conf) { static bool done_hash = false; CONF_SECTION *subcs = cf_section_find(conf, "redis", NULL); rlm_redis_ippool_t *inst = instance; rad_assert(inst->allocated_address_attr->type == TMPL_TYPE_ATTR); rad_assert(subcs); inst->cluster = fr_redis_cluster_alloc(inst, subcs, &inst->conf, true, NULL, NULL, NULL); if (!inst->cluster) return -1; if (!fr_redis_cluster_min_version(inst->cluster, "3.0.2")) { PERROR("Cluster error"); return -1; } /* * Pre-Compute the SHA1 hashes of the Lua scripts */ if (!done_hash) { fr_sha1_ctx sha1_ctx; uint8_t digest[SHA1_DIGEST_LENGTH]; fr_sha1_init(&sha1_ctx); fr_sha1_update(&sha1_ctx, (uint8_t const *)lua_alloc_cmd, sizeof(lua_alloc_cmd) - 1); fr_sha1_final(digest, &sha1_ctx); fr_bin2hex(lua_alloc_digest, digest, sizeof(digest)); fr_sha1_init(&sha1_ctx); fr_sha1_update(&sha1_ctx, (uint8_t const *)lua_update_cmd, sizeof(lua_update_cmd) - 1); fr_sha1_final(digest, &sha1_ctx); fr_bin2hex(lua_update_digest, digest, sizeof(digest)); fr_sha1_init(&sha1_ctx); fr_sha1_update(&sha1_ctx, (uint8_t const *)lua_release_cmd, sizeof(lua_release_cmd) - 1); fr_sha1_final(digest, &sha1_ctx); fr_bin2hex(lua_release_digest, digest, sizeof(digest)); } /* * If we don't have a separate time specifically for offers * just use the lease time. */ if (!inst->offer_time) inst->offer_time = inst->lease_time; return 0; } static int mod_load(void) { fr_redis_version_print(); return 0; } extern module_t rlm_redis_ippool; module_t rlm_redis_ippool = { .magic = RLM_MODULE_INIT, .name = "redis", .type = RLM_TYPE_THREAD_SAFE, .inst_size = sizeof(rlm_redis_ippool_t), .config = module_config, .onload = mod_load, .instantiate = mod_instantiate, .methods = { [MOD_ACCOUNTING] = mod_accounting, [MOD_AUTHORIZE] = mod_authorize, [MOD_POST_AUTH] = mod_post_auth, }, };
/* * Do any per-module initialization that is separate to each * configured instance of the module. e.g. set up connections * to external databases, read configuration files, set up * dictionary entries, etc. * * If configuration information is given in the config section * that must be referenced in later calls, store a handle to it * in *instance otherwise put a null pointer there. * * Setup a hashes wich we will use later * parse a module and give him a chance to live * */ static int mod_instantiate(void *instance, CONF_SECTION *conf) { rlm_perl_t *inst = instance; AV *end_AV; char const **embed_c; /* Stupid Perl and lack of const consistency */ char **embed; char **envp = NULL; int exitstatus = 0, argc=0; char arg[] = "0"; CONF_SECTION *cs; #ifdef USE_ITHREADS /* * Create pthread key. This key will be stored in instance */ pthread_mutex_init(&inst->clone_mutex, NULL); MEM(inst->thread_key = talloc_zero(inst, pthread_key_t)); rlm_perl_make_key(inst->thread_key); #endif /* * Setup the argument array we pass to the perl interpreter */ MEM(embed_c = talloc_zero_array(inst, char const *, 4)); memcpy(&embed, &embed_c, sizeof(embed)); embed_c[0] = NULL; if (inst->perl_flags) { embed_c[1] = inst->perl_flags; embed_c[2] = inst->module; embed_c[3] = arg; argc = 4; } else { embed_c[1] = inst->module; embed_c[2] = arg; argc = 3; } /* * Create tweak the server's environment to support * perl. Docs say only call this once... Oops. */ if (!perl_sys_init3_called) { PERL_SYS_INIT3(&argc, &embed, &envp); perl_sys_init3_called = 1; } /* * Allocate a new perl interpreter to do the parsing */ if ((inst->perl = perl_alloc()) == NULL) { ERROR("No memory for allocating new perl interpretor!"); return -1; } perl_construct(inst->perl); /* ...and initialise it */ #ifdef USE_ITHREADS PL_perl_destruct_level = 2; { dTHXa(inst->perl); } PERL_SET_CONTEXT(inst->perl); #endif #if PERL_REVISION >= 5 && PERL_VERSION >=8 PL_exit_flags |= PERL_EXIT_DESTRUCT_END; #endif exitstatus = perl_parse(inst->perl, xs_init, argc, embed, NULL); end_AV = PL_endav; PL_endav = (AV *)NULL; if (exitstatus) { ERROR("Perl_parse failed: %s not found or has syntax errors", inst->module); return -1; } /* parse perl configuration sub-section */ cs = cf_section_find(conf, "config", NULL); if (cs) { inst->rad_perlconf_hv = get_hv("RAD_PERLCONF", 1); perl_parse_config(cs, 0, inst->rad_perlconf_hv); } inst->perl_parsed = true; perl_run(inst->perl); PL_endav = end_AV; return 0; }
static fr_io_final_t mod_process(UNUSED void const *instance, REQUEST *request, UNUSED fr_io_action_t action) { rlm_rcode_t rcode; CONF_SECTION *unlang; fr_dict_enum_t const *dv; fr_dict_attr_t const *da = NULL; VALUE_PAIR *vp; REQUEST_VERIFY(request); rad_assert(request->packet->code > 0); rad_assert(request->packet->code <= FR_DHCP_INFORM); switch (request->request_state) { case REQUEST_INIT: RDEBUG("Received %s ID %08x", dhcp_message_types[request->packet->code], request->packet->id); log_request_proto_pair_list(L_DBG_LVL_1, request, request->packet->vps, ""); request->component = "dhcpv4"; dv = fr_dict_enum_by_value(attr_message_type, fr_box_uint8(request->packet->code)); if (!dv) { REDEBUG("Failed to find value for &request:DHCP-Message-Type"); return FR_IO_FAIL; } unlang = cf_section_find(request->server_cs, "recv", dv->alias); if (!unlang) { RWDEBUG("Failed to find 'recv %s' section", dv->alias); request->reply->code = FR_DHCP_MESSAGE_TYPE_VALUE_DHCP_DO_NOT_RESPOND; goto send_reply; } RDEBUG("Running 'recv %s' from file %s", cf_section_name2(unlang), cf_filename(unlang)); unlang_push_section(request, unlang, RLM_MODULE_NOOP, UNLANG_TOP_FRAME); request->request_state = REQUEST_RECV; /* FALL-THROUGH */ case REQUEST_RECV: rcode = unlang_interpret_continue(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); /* * Allow the admin to explicitly set the reply * type. */ vp = fr_pair_find_by_da(request->reply->vps, attr_message_type, TAG_ANY); if (vp) { request->reply->code = vp->vp_uint8; } else switch (rcode) { case RLM_MODULE_NOOP: case RLM_MODULE_OK: case RLM_MODULE_UPDATED: request->reply->code = reply_ok[request->packet->code]; break; default: case RLM_MODULE_REJECT: case RLM_MODULE_FAIL: request->reply->code = reply_fail[request->packet->code]; break; case RLM_MODULE_HANDLED: if (!request->reply->code) request->reply->code = FR_DHCP_MESSAGE_TYPE_VALUE_DHCP_DO_NOT_RESPOND; break; } /* * DHCP-Release / Decline doesn't send a reply, and doesn't run "send DHCP-Do-Not-Respond" */ if (!request->reply->code) { return FR_IO_DONE; } /* * Offer and ACK MUST have YIADDR. */ if ((request->reply->code == FR_DHCP_OFFER) || (request->reply->code == FR_DHCP_ACK)) { vp = fr_pair_find_by_da(request->reply->vps, attr_yiaddr, TAG_ANY); if (!vp) { REDEBUG("%s packet does not have YIADDR. The client will not receive an IP address.", dhcp_message_types[request->reply->code]); } } dv = fr_dict_enum_by_value(da, fr_box_uint8(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_push_section(request, unlang, RLM_MODULE_NOOP, UNLANG_TOP_FRAME); request->request_state = REQUEST_SEND; /* FALL-THROUGH */ case REQUEST_SEND: rcode = unlang_interpret_continue(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_DHCP_MESSAGE_TYPE_VALUE_DHCP_DO_NOT_RESPOND) { dv = fr_dict_enum_by_value(attr_message_type, fr_box_uint8(request->reply->code)); RWDEBUG("Failed running 'send %s', trying 'send Do-Not-Respond'", dv->alias); request->reply->code = FR_DHCP_MESSAGE_TYPE_VALUE_DHCP_DO_NOT_RESPOND; dv = fr_dict_enum_by_value(da, fr_box_uint8(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: /* * Check for "do not respond". */ if (request->reply->code == FR_DHCP_MESSAGE_TYPE_VALUE_DHCP_DO_NOT_RESPOND) { RDEBUG("Not sending reply to client"); return FR_IO_DONE; } if (RDEBUG_ENABLED) common_packet_debug(request, request->reply, false); break; default: return FR_IO_FAIL; } return FR_IO_REPLY; }