/* * Xlat for %{client:foo} */ static ssize_t xlat_client(UNUSED void *instance, REQUEST *request, char const *fmt, char *out, size_t outlen) { char const *value = NULL; CONF_PAIR *cp; if (!fmt || !out || (outlen < 1)) return 0; if (!request->client) { RWDEBUG("No client associated with this request"); *out = '\0'; return 0; } cp = cf_pair_find(request->client->cs, fmt); if (!cp || !(value = cf_pair_value(cp))) { if (strcmp(fmt, "shortname") == 0 && request->client->shortname) { value = request->client->shortname; } else if (strcmp(fmt, "nas_type") == 0 && request->client->nas_type) { value = request->client->nas_type; } else { *out = '\0'; return 0; } } strlcpy(out, value, outlen); return strlen(out); }
/* * Xlat for %{listen:foo} */ static ssize_t xlat_listen(UNUSED void *instance, REQUEST *request, char const *fmt, char *out, size_t outlen) { char const *value = NULL; CONF_PAIR *cp; if (!fmt || !out || (outlen < 1)) return 0; if (!request->listener) { RWDEBUG("No listener associated with this request"); *out = '\0'; return 0; } cp = cf_pair_find(request->listener->cs, fmt); if (!cp || !(value = cf_pair_value(cp))) { RDEBUG("Listener does not contain config item \"%s\"", fmt); *out = '\0'; return 0; } strlcpy(out, value, outlen); return strlen(out); }
/* * Xlat for %{client:foo} */ static size_t xlat_client(UNUSED void *instance, REQUEST *request, char *fmt, char *out, size_t outlen, UNUSED RADIUS_ESCAPE_STRING func) { const char *value = NULL; CONF_PAIR *cp; if (!fmt || !out || (outlen < 1)) return 0; if (!request || !request->client) { *out = '\0'; return 0; } cp = cf_pair_find(request->client->cs, fmt); if (!cp || !(value = cf_pair_value(cp))) { *out = '\0'; return 0; } strlcpy(out, value, outlen); return strlen(out); }
/* * Xlat for %{client:foo} */ static ssize_t xlat_client(UNUSED void *instance, REQUEST *request, char const *fmt, char *out, size_t outlen) { char const *value = NULL; CONF_PAIR *cp; if (!fmt || !out || (outlen < 1)) return 0; if (!request->client) { RWDEBUG("No client associated with this request"); *out = '\0'; return 0; } cp = cf_pair_find(request->client->cs, fmt); if (!cp || !(value = cf_pair_value(cp))) { if (strcmp(fmt, "shortname") == 0) { strlcpy(out, request->client->shortname, outlen); return strlen(out); } RDEBUG("Client does not contain config item \"%s\"", fmt); *out = '\0'; return 0; } strlcpy(out, value, outlen); return strlen(out); }
/* * Find the client definition. */ static rlm_rcode_t mod_authorize(UNUSED void *instance, REQUEST *request) { size_t length; const char *value; CONF_PAIR *cp; RADCLIENT *c; char buffer[2048]; /* * Ensure we're only being called from the main thread, * with fake packets. */ if ((request->packet->src_port != 0) || (request->packet->vps != NULL) || (request->parent != NULL)) { RDEBUG("Improper configuration"); return RLM_MODULE_NOOP; } if (!request->client || !request->client->cs) { RDEBUG("Unknown client definition"); return RLM_MODULE_NOOP; } cp = cf_pair_find(request->client->cs, "directory"); if (!cp) { RDEBUG("No directory configuration in the client"); return RLM_MODULE_NOOP; } value = cf_pair_value(cp); if (!value) { RDEBUG("No value given for the directory entry in the client."); return RLM_MODULE_NOOP; } length = strlen(value); if (length > (sizeof(buffer) - 256)) { RDEBUG("Directory name too long"); return RLM_MODULE_NOOP; } memcpy(buffer, value, length + 1); ip_ntoh(&request->packet->src_ipaddr, buffer + length, sizeof(buffer) - length - 1); /* * Read the buffer and generate the client. */ c = client_read(buffer, (request->client->server != NULL), TRUE); if (!c) return RLM_MODULE_FAIL; /* * Replace the client. This is more than a bit of a * hack. */ request->client = c; return RLM_MODULE_OK; }
/* * Parse a configuration section, and populate a HV. * This function is recursively called (allows to have nested hashes.) */ static void perl_parse_config(CONF_SECTION *cs, int lvl, HV *rad_hv) { if (!cs || !rad_hv) return; int indent_section = (lvl + 1) * 4; int indent_item = (lvl + 2) * 4; DEBUG("%*s%s {", indent_section, " ", cf_section_name1(cs)); CONF_ITEM *ci = NULL; while ((ci = cf_item_next(cs, ci))) { /* * This is a section. * Create a new HV, store it as a reference in current HV, * Then recursively call perl_parse_config with this section and the new HV. */ if (cf_item_is_section(ci)) { CONF_SECTION *sub_cs = cf_item_to_section(ci); char const *key = cf_section_name1(sub_cs); /* hash key */ HV *sub_hv; SV *ref; if (!key) continue; if (hv_exists(rad_hv, key, strlen(key))) { WARN("Ignoring duplicate config section '%s'", key); continue; } sub_hv = newHV(); ref = newRV_inc((SV*) sub_hv); (void)hv_store(rad_hv, key, strlen(key), ref, 0); perl_parse_config(sub_cs, lvl + 1, sub_hv); } else if (cf_item_is_pair(ci)){ CONF_PAIR *cp = cf_item_to_pair(ci); char const *key = cf_pair_attr(cp); /* hash key */ char const *value = cf_pair_value(cp); /* hash value */ if (!key || !value) continue; /* * This is an item. * Store item attr / value in current HV. */ if (hv_exists(rad_hv, key, strlen(key))) { WARN("Ignoring duplicate config item '%s'", key); continue; } (void)hv_store(rad_hv, key, strlen(key), newSVpvn(value, strlen(value)), 0); DEBUG("%*s%s = %s", indent_item, " ", key, value); } } DEBUG("%*s}", indent_section, " "); }
static int rediswho_accounting(void * instance, REQUEST * request) { int rcode; VALUE_PAIR * vp; DICT_VALUE *dv; CONF_SECTION *cs; const char *insert, *trim, *expire; rlm_rediswho_t *inst = (rlm_rediswho_t *) instance; REDISSOCK *dissocket; vp = pairfind(request->packet->vps, PW_ACCT_STATUS_TYPE, 0, TAG_ANY); if (!vp) { RDEBUG("Could not find account status type in packet."); return RLM_MODULE_NOOP; } dv = dict_valbyattr(vp->attribute, vp->vendor, vp->vp_integer); if (!dv) { RDEBUG("Unknown Acct-Status-Type %u", vp->vp_integer); return RLM_MODULE_NOOP; } cs = cf_section_sub_find(inst->cs, dv->name); if (!cs) { RDEBUG("No subsection %s", dv->name); return RLM_MODULE_NOOP; } dissocket = fr_connection_get(inst->redis_inst->pool); if (!dissocket) { RDEBUG("cannot allocate redis connection"); return RLM_MODULE_FAIL; } insert = cf_pair_value(cf_pair_find(cs, "insert")); trim = cf_pair_value(cf_pair_find(cs, "trim")); expire = cf_pair_value(cf_pair_find(cs, "expire")); rcode = rediswho_accounting_all(&dissocket, inst, request, insert, trim, expire); if (dissocket) fr_connection_release(inst->redis_inst->pool, dissocket); return rcode; }
static int _map_proc_client_get_vp(TALLOC_CTX *ctx, VALUE_PAIR **out, REQUEST *request, vp_map_t const *map, void *uctx) { client_get_vp_ctx_t *client = uctx; VALUE_PAIR *head = NULL, *vp; fr_cursor_t cursor; fr_dict_attr_t const *da; CONF_PAIR const *cp; rad_assert(ctx != NULL); fr_cursor_init(&cursor, &head); /* * FIXME: allow multiple entries. */ if (map->lhs->type == TMPL_TYPE_ATTR) { da = map->lhs->tmpl_da; } else { char *attr; if (tmpl_aexpand(ctx, &attr, request, map->lhs, NULL, NULL) <= 0) { RWDEBUG("Failed expanding string"); return -1; } da = fr_dict_attr_by_name(request->dict, attr); if (!da) { RWDEBUG("No such attribute '%s'", attr); return -1; } talloc_free(attr); } for (cp = client->cp; cp; cp = cf_pair_find_next(client->cs, cp, client->field)) { char const *value = cf_pair_value(cp); MEM(vp = fr_pair_afrom_da(ctx, da)); if (fr_pair_value_from_str(vp, value, talloc_array_length(value) - 1, '\0', false) < 0) { RWDEBUG("Failed parsing value \"%pV\" for attribute %s: %s", fr_box_strvalue(value), map->lhs->tmpl_da->name, fr_strerror()); fr_pair_list_free(&head); talloc_free(vp); return -1; } vp->op = map->op; fr_cursor_append(&cursor, vp); if (map->op != T_OP_ADD) break; /* Create multiple attribute for multiple CONF_PAIRs */ } *out = head; return 0; }
/** Convert CONFIG_PAIR to VALUE_PAIR_MAP. * * Treats the left operand as a * @verbatim<request>.<list>.<attribute>@endverbatim reference and the right * operand as a module specific value. * * The left operand will be pre-parsed into request ref, dst list, and da, * the right operand will be left as a string. * * Return must be freed with radius_mapfree. * * @param[in] cp to convert to map. * @param[in] request_def The default request to insert unqualified * attributes into. * @param[in] list_def The default list to insert unqualified attributes into. * @return VALUE_PAIR_MAP if successful or NULL on error. */ VALUE_PAIR_MAP *radius_cp2map(CONF_PAIR *cp, request_refs_t request_def, pair_lists_t list_def) { VALUE_PAIR_MAP *map; const char *attr; const char *value; map = rad_malloc(sizeof(VALUE_PAIR_MAP)); memset(map, 0, sizeof(VALUE_PAIR_MAP)); attr = cf_pair_attr(cp); map->dst = radius_attr2tmpl(attr, request_def, list_def); if (!map->dst){ goto error; } value = cf_pair_value(cp); if (!value) { radlog(L_ERR, "Missing attribute name"); goto error; } map->src = radius_str2tmpl(value); if (!map->src) { goto error; } map->op_token = cf_pair_operator(cp); /* * Infer whether we need to expand the mapping values * The old style attribute map allowed the user to specify * whether the LDAP value should be expanded. * We can't really support that easily, but equivalent * functionality should be available with %{eval:} */ switch (cf_pair_value_type(cp)) { case T_BARE_WORD: case T_SINGLE_QUOTED_STRING: map->src->do_xlat = FALSE; break; case T_BACK_QUOTED_STRING: case T_DOUBLE_QUOTED_STRING: map->src->do_xlat = TRUE; break; default: rad_assert(0); goto error; } return map; error: radius_mapfree(&map); return NULL; }
/** Iterate over all client attribute pairs and create client pair data using JSON element names * * If we hit a CONF_SECTION we recurse and process its CONF_PAIRS as well to support nested * configurations sections. * * @param client The new client config section using the mapped names. * @param map The client attribute section from the module configuration. * @param json JSON object representation of a client document fetched from Couchbase. * @param docid Document id. * @return Returns 0 on success, -1 on error. */ int _mod_client_map_section(CONF_SECTION *client, CONF_SECTION const *map, json_object *json, char const *docid) { CONF_ITEM const *ci; for (ci = cf_item_find_next(map, NULL); ci != NULL; ci = cf_item_find_next(map, ci)) { CONF_PAIR const *cp; char const *attribute; char const *element; json_object *jval; /* * Recursively process map subsection */ if (cf_item_is_section(ci)) { CONF_SECTION *cs, *cc; /* local scoped for new section */ cs = cf_itemtosection(ci); cc = cf_section_alloc(client, cf_section_name1(cs), cf_section_name2(cs)); if (!cc) return -1; cf_section_add(client, cc); if (_mod_client_map_section(cc, cs, json, docid) != 0) { return -1; } /* continue on to the next item */ continue; } /* create pair from item and get attribute name and value */ cp = cf_itemtopair(ci); attribute = cf_pair_attr(cp); element = cf_pair_value(cp); /* attempt to find element in json object */ if (!json_object_object_get_ex(json, element, &jval)) { /* skip this item */ continue; } /* allocate config pair */ cp = cf_pair_alloc(client, attribute, json_object_get_string(jval), T_OP_SET, T_SINGLE_QUOTED_STRING); /* check pair */ if (!cp) { ERROR("rlm_couchbase: failed allocating config pair '%s' = '%s'", attribute, json_object_get_string(jval)); return -1; } /* add pair to section */ cf_item_add(client, cf_pairtoitem(cp)); } /* return success */ return 0; }
/** Build a JSON object map from the configuration "map" section * * Parse the "map" section from the module configuration file and store this * as a JSON object (key/value list) in the module instance. This map will be * used to lookup and map attributes for all incoming accounting requests. * * @param conf Configuration section. * @param instance The module instance. * @return Returns 0 on success, -1 on error. */ int mod_build_attribute_element_map(CONF_SECTION *conf, void *instance) { rlm_couchbase_t *inst = instance; /* our module instance */ CONF_SECTION *cs; /* module config section */ CONF_ITEM *ci; /* config item */ CONF_PAIR *cp; /* conig pair */ const char *attribute, *element; /* attribute and element names */ /* find map section */ cs = cf_section_sub_find(conf, "map"); /* check section */ if (!cs) { ERROR("rlm_couchbase: failed to find 'map' section in config"); /* fail */ return -1; } /* create attribute map object */ inst->map = json_object_new_object(); /* parse update section */ for (ci = cf_item_find_next(cs, NULL); ci != NULL; ci = cf_item_find_next(cs, ci)) { /* validate item */ if (!cf_item_is_pair(ci)) { ERROR("rlm_couchbase: failed to parse invalid item in 'map' section"); /* free map */ if (inst->map) { json_object_put(inst->map); } /* fail */ return -1; } /* get value pair from item */ cp = cf_itemtopair(ci); /* get pair name (element name) */ element = cf_pair_attr(cp); /* get pair value (attribute name) */ attribute = cf_pair_value(cp); /* add pair name and value */ json_object_object_add(inst->map, attribute, json_object_new_string(element)); /* debugging */ DEBUG("rlm_couchbase: added attribute '%s' to element '%s' map to object", attribute, element); } /* debugging */ DEBUG("rlm_couchbase: built attribute to element map %s", json_object_to_json_string(inst->map)); /* return */ return 0; }
static int rad_load_transforms(struct Protocol *prot, CONF_SECTION *cf) { CONF_PAIR *cp; int option_exists = 0; int i = 0; rad_assert(prot); rad_assert(cf); DEBUG(IKEv2_LOG_PREFIX "Begin load transforms"); while(config_transforms[i].name) { uint8_t id; uint16_t keylen; for(cp = cf_pair_find(cf,config_transforms[i].name); cp; cp = cf_pair_find_next(cf,cp,config_transforms[i].name)) { if (TransformFromName(cf_pair_value(cp),config_transforms[i].type,&id,&keylen)) { ERROR(IKEv2_LOG_PREFIX "Unsupported %s transform: %s ", config_transforms[i].name,cf_pair_value(cp)); return -1; } if (!AddTransform(prot,config_transforms[i].type,id,keylen)) { ERROR(IKEv2_LOG_PREFIX "Problem with transform %s:%s", config_transforms[i].name,cf_pair_value(cp)); return -1; } option_exists |= config_transforms[i].exist_flag; } i++; } if ((option_exists & OPT_NEEDED) != OPT_NEEDED) { ERROR(IKEv2_LOG_PREFIX "Not all mandatory transforms are set properly"); DEBUG(IKEv2_LOG_PREFIX "Option flags: 0x%02X",option_exists); return -1; } return 0; }
static int virtual_server_parse(UNUSED TALLOC_CTX *ctx, void *out, UNUSED void *parent, CONF_ITEM *ci, UNUSED CONF_PARSER const *rule) { CONF_SECTION *server_cs; if (virtual_server_has_namespace(&server_cs, cf_pair_value(cf_item_to_pair(ci)), dict_eap_aka, ci) < 0) return -1; if (mod_section_compile(out, server_cs) < 0) return -1; return 0; }
/* * Xlat for %{getclient:<ipaddr>.foo} */ static ssize_t xlat_getclient(UNUSED void *instance, REQUEST *request, char const *fmt, char *out, size_t outlen) { char const *value = NULL; char buffer[INET6_ADDRSTRLEN], *q; char const *p = fmt; fr_ipaddr_t ip; CONF_PAIR *cp; RADCLIENT *client = NULL; if (!fmt || !out || (outlen < 1)) return 0; q = strrchr(p, '.'); if (!q || (q == p) || (((size_t)(q - p)) > sizeof(buffer))) { REDEBUG("Invalid client string"); goto error; } strlcpy(buffer, p, (q + 1) - p); memset(&ip, 0, sizeof(ip)); if (ip_ptonx(buffer, &ip) <= 0) { REDEBUG("\"%s\" is not a valid IPv4 or IPv6 address", buffer); goto error; } fmt = q + 1; client = client_find(NULL, &ip, IPPROTO_IP); if (!client) { RDEBUG("No client found with IP \"%s\"", buffer); *out = '\0'; return 0; } cp = cf_pair_find(client->cs, fmt); if (!cp || !(value = cf_pair_value(cp))) { if (strcmp(fmt, "shortname") == 0) { strlcpy(out, request->client->shortname, outlen); return strlen(out); } RDEBUG("Client does not contain config item \"%s\"", fmt); *out = '\0'; return 0; } strlcpy(out, value, outlen); return strlen(out); error: *out = '\0'; return -1; }
/* * Xlat for %{client:[<ipaddr>.]foo} */ static ssize_t xlat_client(TALLOC_CTX *ctx, char **out, UNUSED size_t outlen, UNUSED void const *mod_inst, UNUSED void const *xlat_inst, REQUEST *request, char const *fmt) { char const *value = NULL; char buffer[INET6_ADDRSTRLEN], *q; char const *p = fmt; fr_ipaddr_t ip; CONF_PAIR *cp; RADCLIENT *client = NULL; *out = NULL; q = strrchr(p, '.'); if (q) { strlcpy(buffer, p, (q + 1) - p); if (fr_inet_pton(&ip, buffer, -1, AF_UNSPEC, false, true) < 0) goto request_client; p = q + 1; client = client_find(NULL, &ip, IPPROTO_IP); if (!client) { RDEBUG("No client found with IP \"%s\"", buffer); return 0; } } else { request_client: client = request->client; if (!client) { RERROR("No client associated with this request"); return -1; } } cp = cf_pair_find(client->cs, p); if (!cp || !(value = cf_pair_value(cp))) { if (strcmp(fmt, "shortname") == 0 && request->client->shortname) { value = request->client->shortname; } else if (strcmp(fmt, "nas_type") == 0 && request->client->nas_type) { value = request->client->nas_type; } if (!value) return 0; } *out = talloc_typed_strdup(ctx, value); return talloc_array_length(*out) - 1; }
/** Set which types of packets we can parse * * @param[in] ctx to allocate data in (instance of rlm_radius). * @param[out] out Where to write the parsed data. * @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 type_parse(UNUSED TALLOC_CTX *ctx, void *out, UNUSED void *parent, CONF_ITEM *ci, UNUSED CONF_PARSER const *rule) { char const *type_str = cf_pair_value(cf_item_to_pair(ci)); CONF_SECTION *cs = cf_item_to_section(cf_parent(ci)); fr_dict_enum_t const *type_enum; uint32_t code; /* * Must be the RADIUS module */ rad_assert(cs && (strcmp(cf_section_name1(cs), "radius") == 0)); /* * Allow the process module to be specified by * packet type. */ type_enum = fr_dict_enum_by_alias(attr_packet_type, type_str, -1); if (!type_enum) { invalid_code: cf_log_err(ci, "Unknown or invalid RADIUS packet type '%s'", type_str); return -1; } code = type_enum->value->vb_uint32; /* * Status-Server packets cannot be proxied. */ if (code == FR_CODE_STATUS_SERVER) { cf_log_err(ci, "Invalid setting of 'type = Status-Server'. Status-Server packets cannot be proxied."); return -1; } if (!code || (code >= FR_MAX_PACKET_CODE) || (!type_interval_config[code].name)) goto invalid_code; /* * If we're doing async proxying, push the timers for the * various packet types. */ cf_section_rule_push(cs, &type_interval_config[code]); memcpy(out, &code, sizeof(code)); return 0; }
/** Handle client value processing for client_map_section() * * @param out Character output * @param cp Configuration pair * @param data The client data * @return Returns 0 on success, -1 on error. */ static int _get_client_value(char **out, CONF_PAIR const *cp, void *data) { json_object *jval; if (!json_object_object_get_ex((json_object *)data, cf_pair_value(cp), &jval)) { *out = NULL; return 0; } if (!jval) return -1; *out = talloc_strdup(NULL, json_object_get_string(jval)); if (!*out) return -1; return 0; }
/* * Xlat for %{config:section.subsection.attribute} */ static size_t xlat_config(void *instance, REQUEST *request, const char *fmt, char *out, size_t outlen) { const char *value; CONF_PAIR *cp; CONF_ITEM *ci; char buffer[1024]; request = request; /* -Wunused */ instance = instance; /* -Wunused */ /* * Expand it safely. */ if (!radius_xlat(buffer, sizeof(buffer), fmt, request, config_escape_func, NULL)) { return 0; } ci = cf_reference_item(request->root->config, request->root->config, buffer); if (!ci || !cf_item_is_pair(ci)) { *out = '\0'; return 0; } cp = cf_itemtopair(ci); /* * Ensure that we only copy what's necessary. * * If 'outlen' is too small, then the output is chopped to fit. */ value = cf_pair_value(cp); if (!value) { out[0] = '\0'; return 0; } if (outlen > strlen(value)) { outlen = strlen(value) + 1; } strlcpy(out, value, outlen); return strlen(out); }
/** Allow for Status-Server ping checks * * @param[in] ctx to allocate data in (instance of proto_radius). * @param[out] out Where to write our parsed data. * @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 status_check_type_parse(UNUSED TALLOC_CTX *ctx, void *out, UNUSED void *parent, CONF_ITEM *ci, UNUSED CONF_PARSER const *rule) { char const *type_str = cf_pair_value(cf_item_to_pair(ci)); CONF_SECTION *cs = cf_item_to_section(cf_parent(ci)); fr_dict_enum_t const *type_enum; uint32_t code; /* * Allow the process module to be specified by * packet type. */ type_enum = fr_dict_enum_by_alias(attr_packet_type, type_str, -1); if (!type_enum) { invalid_code: cf_log_err(ci, "Unknown or invalid RADIUS packet type '%s'", type_str); return -1; } code = type_enum->value->vb_uint32; /* * Cheat, and re-use the "type" array for allowed packet * types. */ if (!code || (code >= FR_MAX_PACKET_CODE) || (!type_interval_config[code].name)) goto invalid_code; /* * Add irt / mrt / mrd / mrc parsing, in the parent * configuration section. */ cf_section_rule_push(cf_item_to_section(cf_parent(cs)), &type_interval_config[code]); memcpy(out, &code, sizeof(code)); /* * Nothing more to do here, so we stop. */ if (code == FR_CODE_STATUS_SERVER) return 0; cf_section_rule_push(cs, status_check_update_config); return 0; }
/** Iterate over pairs in mapping section creating equivalent client pairs from LDAP values * * If we hit a CONF_SECTION we recurse and process its CONF_PAIRS too. * * @param[in] inst rlm_ldap configuration. * @param[out] client config section. * @param[in] map section. * @param[in] conn LDAP connection. * @param[in] entry returned from search. * @return 0 on success else -1 on error. */ static int rlm_ldap_client_map_section(ldap_instance_t const *inst, CONF_SECTION *client, CONF_SECTION const *map, ldap_handle_t *conn, LDAPMessage *entry) { CONF_ITEM const *ci; for (ci = cf_item_find_next(map, NULL); ci != NULL; ci = cf_item_find_next(map, ci)) { CONF_PAIR const *cp; char **value; char const *attr; /* * Recursively process map subsection */ if (cf_item_is_section(ci)) { CONF_SECTION *cs, *cc; cs = cf_itemtosection(ci); cc = cf_section_alloc(client, cf_section_name1(cs), cf_section_name2(cs)); if (!cc) return -1; cf_section_add(client, cc); if (rlm_ldap_client_map_section(inst, cc, cs, conn, entry) < 0) return -1; continue; } cp = cf_itemtopair(ci); attr = cf_pair_attr(cp); value = ldap_get_values(conn->handle, entry, cf_pair_value(cp)); if (!value) continue; cp = cf_pair_alloc(client, attr, value[0], T_OP_SET, T_SINGLE_QUOTED_STRING); if (!cp) { LDAP_ERR("Failed allocing pair \"%s\" = \"%s\"", attr, value[0]); return -1; } cf_item_add(client, cf_pairtoitem(cp)); } return 0; }
/* * Xlat for %{config:section.subsection.attribute} */ static ssize_t xlat_config(UNUSED void *instance, REQUEST *request, char const *fmt, char *out, size_t outlen) { char const *value; CONF_PAIR *cp; CONF_ITEM *ci; char buffer[1024]; /* * Expand it safely. */ if (radius_xlat(buffer, sizeof(buffer), request, fmt, config_escape_func, NULL) < 0) { return 0; } ci = cf_reference_item(request->root->config, request->root->config, buffer); if (!ci || !cf_item_is_pair(ci)) { REDEBUG("Config item \"%s\" does not exist", fmt); *out = '\0'; return -1; } cp = cf_itemtopair(ci); /* * Ensure that we only copy what's necessary. * * If 'outlen' is too small, then the output is chopped to fit. */ value = cf_pair_value(cp); if (!value) { out[0] = '\0'; return 0; } if (outlen > strlen(value)) { outlen = strlen(value) + 1; } strlcpy(out, value, outlen); return strlen(out); }
/* * Write accounting information to this module's database. */ static int sql_log_accounting(void *instance, REQUEST *request) { int ret; char querystr[MAX_QUERY_LEN]; const char *cfquery; rlm_sql_log_t *inst = (rlm_sql_log_t *)instance; VALUE_PAIR *pair; DICT_VALUE *dval; CONF_PAIR *cp; rad_assert(request != NULL); rad_assert(request->packet != NULL); RDEBUG("Processing sql_log_accounting"); /* Find the Acct Status Type. */ if ((pair = pairfind(request->packet->vps, PW_ACCT_STATUS_TYPE)) == NULL) { radlog_request(L_ERR, 0, request, "Packet has no account status type"); return RLM_MODULE_INVALID; } /* Search the query in conf section of the module */ if ((dval = dict_valbyattr(PW_ACCT_STATUS_TYPE, pair->vp_integer)) == NULL) { radlog_request(L_ERR, 0, request, "Unsupported Acct-Status-Type = %d", pair->vp_integer); return RLM_MODULE_NOOP; } if ((cp = cf_pair_find(inst->conf_section, dval->name)) == NULL) { RDEBUG("Couldn't find an entry %s in the config section", dval->name); return RLM_MODULE_NOOP; } cfquery = cf_pair_value(cp); /* Xlat the query */ ret = sql_xlat_query(inst, request, cfquery, querystr, sizeof(querystr)); if (ret != RLM_MODULE_OK) return ret; /* Write query into sql-relay file */ return sql_log_write(inst, request, querystr); }
/* * Xlat for %{listen:foo} */ static ssize_t xlat_listen(UNUSED void *instance, REQUEST *request, char const *fmt, char *out, size_t outlen) { char const *value = NULL; CONF_PAIR *cp; if (!fmt || !out || (outlen < 1)) return 0; if (!request->listener) { RWDEBUG("No listener associated with this request"); *out = '\0'; return 0; } #ifdef WITH_TLS /* * Look for TLS certificate data. */ if (strncmp(fmt, "TLS-", 4) == 0) { VALUE_PAIR *vp; listen_socket_t *sock = request->listener->data; for (vp = sock->certs; vp != NULL; vp = vp->next) { if (strcmp(fmt, vp->da->name) == 0) { return vp_prints_value(out, outlen, vp, 0); } } } #endif cp = cf_pair_find(request->listener->cs, fmt); if (!cp || !(value = cf_pair_value(cp))) { RDEBUG("Listener does not contain config item \"%s\"", fmt); *out = '\0'; return 0; } strlcpy(out, value, outlen); return strlen(out); }
/** 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); }
/** Iterate over pairs in mapping section recording their values in an array * * This array is the list of attributes we retrieve from LDAP, and is NULL * terminated. * * If we hit a CONF_SECTION we recurse and process its CONF_PAIRS too. * * @param[out] values array of char pointers. * @param[in,out] idx records current array offset. * @param[in] cs to iterate over. * @return 0 on success else -1 on error. */ static int rlm_ldap_client_get_attrs(char const **values, int *idx, CONF_SECTION const *cs) { CONF_ITEM const *ci; for (ci = cf_item_find_next(cs, NULL); ci != NULL; ci = cf_item_find_next(cs, ci)) { char const *value; if (cf_item_is_section(ci)) { if (rlm_ldap_client_get_attrs(values, idx, cf_itemtosection(ci)) < 0) return -1; continue; } value = cf_pair_value(cf_itemtopair(ci)); if (!value) return -1; values[(*idx)++] = value; } values[*idx] = NULL; return 0; }
/** Modify user's object in LDAP * * Process a modifcation map to update a user object in the LDAP directory. * * @param inst rlm_ldap instance. * @param request Current request. * @param section that holds the map to process. * @return one of the RLM_MODULE_* values. */ static rlm_rcode_t user_modify(ldap_instance_t *inst, REQUEST *request, ldap_acct_section_t *section) { rlm_rcode_t rcode = RLM_MODULE_OK; ldap_rcode_t status; ldap_handle_t *conn = NULL; LDAPMod *mod_p[LDAP_MAX_ATTRMAP + 1], mod_s[LDAP_MAX_ATTRMAP]; LDAPMod **modify = mod_p; char *passed[LDAP_MAX_ATTRMAP * 2]; int i, total = 0, last_pass = 0; char *expanded[LDAP_MAX_ATTRMAP]; int last_exp = 0; char const *attr; char const *value; char const *dn; /* * Build our set of modifications using the update sections in * the config. */ CONF_ITEM *ci; CONF_PAIR *cp; CONF_SECTION *cs; FR_TOKEN op; char path[MAX_STRING_LEN]; char *p = path; rad_assert(section); /* * Locate the update section were going to be using */ if (section->reference[0] != '.') { *p++ = '.'; } if (radius_xlat(p, (sizeof(path) - (p - path)) - 1, request, section->reference, NULL, NULL) < 0) { goto error; } ci = cf_reference_item(NULL, section->cs, path); if (!ci) { goto error; } if (!cf_item_is_section(ci)){ REDEBUG("Reference must resolve to a section"); goto error; } cs = cf_section_sub_find(cf_itemtosection(ci), "update"); if (!cs) { REDEBUG("Section must contain 'update' subsection"); goto error; } /* * Iterate over all the pairs, building our mods array */ for (ci = cf_item_find_next(cs, NULL); ci != NULL; ci = cf_item_find_next(cs, ci)) { bool do_xlat = false; if (total == LDAP_MAX_ATTRMAP) { REDEBUG("Modify map size exceeded"); goto error; } if (!cf_item_is_pair(ci)) { REDEBUG("Entry is not in \"ldap-attribute = value\" format"); goto error; } /* * Retrieve all the information we need about the pair */ cp = cf_itemtopair(ci); value = cf_pair_value(cp); attr = cf_pair_attr(cp); op = cf_pair_operator(cp); if (!value || (*value == '\0')) { RDEBUG("Empty value string, skipping attribute \"%s\"", attr); continue; } switch (cf_pair_value_type(cp)) { case T_BARE_WORD: case T_SINGLE_QUOTED_STRING: break; case T_BACK_QUOTED_STRING: case T_DOUBLE_QUOTED_STRING: do_xlat = true; break; default: rad_assert(0); goto error; } if (op == T_OP_CMP_FALSE) { passed[last_pass] = NULL; } else if (do_xlat) { char *exp = NULL; if (radius_axlat(&exp, request, value, NULL, NULL) <= 0) { RDEBUG("Skipping attribute \"%s\"", attr); talloc_free(exp); continue; } expanded[last_exp++] = exp; passed[last_pass] = exp; /* * Static strings */ } else { memcpy(&(passed[last_pass]), &value, sizeof(passed[last_pass])); } passed[last_pass + 1] = NULL; mod_s[total].mod_values = &(passed[last_pass]); last_pass += 2; switch (op) { /* * T_OP_EQ is *NOT* supported, it is impossible to * support because of the lack of transactions in LDAP */ case T_OP_ADD: mod_s[total].mod_op = LDAP_MOD_ADD; break; case T_OP_SET: mod_s[total].mod_op = LDAP_MOD_REPLACE; break; case T_OP_SUB: case T_OP_CMP_FALSE: mod_s[total].mod_op = LDAP_MOD_DELETE; break; #ifdef LDAP_MOD_INCREMENT case T_OP_INCRM: mod_s[total].mod_op = LDAP_MOD_INCREMENT; break; #endif default: REDEBUG("Operator '%s' is not supported for LDAP modify operations", fr_int2str(fr_tokens, op, "<INVALID>")); goto error; } /* * Now we know the value is ok, copy the pointers into * the ldapmod struct. */ memcpy(&(mod_s[total].mod_type), &attr, sizeof(mod_s[total].mod_type)); mod_p[total] = &(mod_s[total]); total++; } if (total == 0) { rcode = RLM_MODULE_NOOP; goto release; } mod_p[total] = NULL; conn = mod_conn_get(inst, request); if (!conn) return RLM_MODULE_FAIL; dn = rlm_ldap_find_user(inst, request, &conn, NULL, false, NULL, &rcode); if (!dn || (rcode != RLM_MODULE_OK)) { goto error; } status = rlm_ldap_modify(inst, request, &conn, dn, modify); switch (status) { case LDAP_PROC_SUCCESS: break; case LDAP_PROC_REJECT: case LDAP_PROC_BAD_DN: rcode = RLM_MODULE_INVALID; break; default: rcode = RLM_MODULE_FAIL; break; }; release: error: /* * Free up any buffers we allocated for xlat expansion */ for (i = 0; i < last_exp; i++) { talloc_free(expanded[i]); } mod_conn_release(inst, conn); return rcode; }
/* * Parse TLS configuration * * If the option given by 'attr' is set, we find the config section * of that name and use that for the TLS configuration. If not, we * fall back to compatibility mode and read the TLS options from * the 'tls' section. */ fr_tls_server_conf_t *eaptls_conf_parse(CONF_SECTION *cs, char const *attr) { char const *tls_conf_name; CONF_PAIR *cp; CONF_SECTION *parent; CONF_SECTION *tls_cs; fr_tls_server_conf_t *tls_conf; if (!cs) return NULL; rad_assert(attr != NULL); parent = cf_item_parent(cf_section_to_item(cs)); cp = cf_pair_find(cs, attr); if (cp) { tls_conf_name = cf_pair_value(cp); tls_cs = cf_section_sub_find_name2(parent, TLS_CONFIG_SECTION, tls_conf_name); if (!tls_cs) { ERROR("Cannot find tls config \"%s\"", tls_conf_name); return NULL; } } else { /* * If we can't find the section given by the 'attr', we * fall-back to looking for the "tls" section, as in * previous versions. * * We don't fall back if the 'attr' is specified, but we can't * find the section - that is just a config error. */ INFO("TLS section \"%s\" missing, trying to use legacy configuration", attr); tls_cs = cf_section_sub_find(parent, "tls"); } if (!tls_cs) return NULL; tls_conf = tls_server_conf_parse(tls_cs); if (!tls_conf) return NULL; /* * The EAP RFC's say 1020, but we're less picky. */ if (tls_conf->fragment_size < 100) { ERROR("Configured fragment size is too small, must be >= 100"); return NULL; } /* * The maximum size for a RADIUS packet is 4096, * minus the header (20), Message-Authenticator (18), * and State (18), etc. results in about 4000 bytes of data * that can be devoted *solely* to EAP. */ if (tls_conf->fragment_size > 4000) { ERROR("Configured fragment size is too large, must be <= 4000"); return NULL; } /* * Account for the EAP header (4), and the EAP-TLS header * (6), as per Section 4.2 of RFC 2716. What's left is * the maximum amount of data we read from a TLS buffer. */ tls_conf->fragment_size -= 10; return tls_conf; }
/* * Read config files. * * This function can ONLY be called from the main server process. */ int read_mainconfig(int reload) { const char *p = NULL; CONF_PAIR *cp; CONF_SECTION *cs; struct stat statbuf; cached_config_t *cc; char buffer[1024]; if (reload != 0) { radlog(L_ERR, "Reload is not implemented"); return -1; } if (stat(radius_dir, &statbuf) < 0) { radlog(L_ERR, "Errors reading %s: %s", radius_dir, strerror(errno)); return -1; } #ifdef S_IWOTH if ((statbuf.st_mode & S_IWOTH) != 0) { radlog(L_ERR, "Configuration directory %s is globally writable. Refusing to start due to insecure configuration.", radius_dir); return -1; } #endif #ifdef S_IROTH if (0 && (statbuf.st_mode & S_IROTH) != 0) { radlog(L_ERR, "Configuration directory %s is globally readable. Refusing to start due to insecure configuration.", radius_dir); return -1; } #endif radlog(L_INFO, "Starting - reading configuration files ..."); /* Read the configuration file */ snprintf(buffer, sizeof(buffer), "%.200s/%.50s.conf", radius_dir, mainconfig.name); if ((cs = cf_file_read(buffer)) == NULL) { radlog(L_ERR, "Errors reading or parsing %s", buffer); return -1; } /* * If there was no log destination set on the command line, * set it now. */ if (mainconfig.radlog_dest == RADLOG_NULL) { if (cf_section_parse(cs, NULL, serverdest_config) < 0) { fprintf(stderr, "radiusd: Error: Failed to parse log{} section.\n"); cf_file_free(cs); return -1; } if (!radlog_dest) { fprintf(stderr, "radiusd: Error: No log destination specified.\n"); cf_file_free(cs); return -1; } mainconfig.radlog_dest = fr_str2int(str2dest, radlog_dest, RADLOG_NUM_DEST); if (mainconfig.radlog_dest == RADLOG_NUM_DEST) { fprintf(stderr, "radiusd: Error: Unknown log_destination %s\n", radlog_dest); cf_file_free(cs); return -1; } if (mainconfig.radlog_dest == RADLOG_SYSLOG) { /* * Make sure syslog_facility isn't NULL * before using it */ if (!syslog_facility) { fprintf(stderr, "radiusd: Error: Syslog chosen but no facility was specified\n"); cf_file_free(cs); return -1; } mainconfig.syslog_facility = fr_str2int(syslog_str2fac, syslog_facility, -1); if (mainconfig.syslog_facility < 0) { fprintf(stderr, "radiusd: Error: Unknown syslog_facility %s\n", syslog_facility); cf_file_free(cs); return -1; } #ifdef HAVE_SYSLOG_H /* * Call openlog only once, when the * program starts. */ openlog(progname, LOG_PID, mainconfig.syslog_facility); #endif } else if (mainconfig.radlog_dest == RADLOG_FILES) { if (!mainconfig.log_file) { fprintf(stderr, "radiusd: Error: Specified \"files\" as a log destination, but no log filename was given!\n"); cf_file_free(cs); return -1; } } } #ifdef HAVE_SETUID /* * Switch users as early as possible. */ if (!switch_users(cs)) exit(1); #endif /* * Open the log file AFTER switching uid / gid. If we * did switch uid/gid, then the code in switch_users() * took care of setting the file permissions correctly. */ if ((mainconfig.radlog_dest == RADLOG_FILES) && (mainconfig.radlog_fd < 0)) { mainconfig.radlog_fd = open(mainconfig.log_file, O_WRONLY | O_APPEND | O_CREAT, 0640); if (mainconfig.radlog_fd < 0) { fprintf(stderr, "radiusd: Failed to open log file %s: %s\n", mainconfig.log_file, strerror(errno)); cf_file_free(cs); return -1; } } /* Initialize the dictionary */ cp = cf_pair_find(cs, "dictionary"); if (cp) p = cf_pair_value(cp); if (!p) p = radius_dir; DEBUG2("including dictionary file %s/%s", p, RADIUS_DICTIONARY); if (dict_init(p, RADIUS_DICTIONARY) != 0) { radlog(L_ERR, "Errors reading dictionary: %s", fr_strerror()); return -1; } /* * This allows us to figure out where, relative to * radiusd.conf, the other configuration files exist. */ if (cf_section_parse(cs, NULL, server_config) < 0) { return -1; } /* * We ignore colourization of output until after the * configuration files have been parsed. */ if (do_colourise) { p = getenv("TERM"); if (!p || !isatty(mainconfig.radlog_fd) || (strstr(p, "xterm") == 0)) { mainconfig.colourise = FALSE; } else { mainconfig.colourise = TRUE; } p = NULL; } if (mainconfig.max_request_time == 0) mainconfig.max_request_time = 100; if (mainconfig.reject_delay > 5) mainconfig.reject_delay = 5; if (mainconfig.cleanup_delay > 5) mainconfig.cleanup_delay =5; /* * Free the old configuration items, and replace them * with the new ones. * * Note that where possible, we do atomic switch-overs, * to ensure that the pointers are always valid. */ rad_assert(mainconfig.config == NULL); mainconfig.config = cs; DEBUG2("%s: #### Loading Realms and Home Servers ####", mainconfig.name); if (!realms_init(cs)) { return -1; } DEBUG2("%s: #### Loading Clients ####", mainconfig.name); if (!clients_parse_section(cs)) { return -1; } /* * Register the %{config:section.subsection} xlat function. */ xlat_register("config", xlat_config, NULL); xlat_register("client", xlat_client, NULL); /* * Starting the server, WITHOUT "-x" on the * command-line: use whatever is in the config * file. */ if (debug_flag == 0) { debug_flag = mainconfig.debug_level; } fr_debug_flag = debug_flag; /* * Go update our behaviour, based on the configuration * changes. */ /* * Sanity check the configuration for internal * consistency. */ if (mainconfig.reject_delay > mainconfig.cleanup_delay) { mainconfig.reject_delay = mainconfig.cleanup_delay; } if (mainconfig.reject_delay < 0) mainconfig.reject_delay = 0; /* Reload the modules. */ if (setup_modules(reload, mainconfig.config) < 0) { return -1; } if (chroot_dir) { if (chdir(radlog_dir) < 0) { radlog(L_ERR, "Failed to 'chdir %s' after chroot: %s", radlog_dir, strerror(errno)); return -1; } } cc = rad_malloc(sizeof(*cc)); memset(cc, 0, sizeof(*cc)); cc->cs = cs; rad_assert(cs_cache == NULL); cs_cache = cc; return 0; }
/* * Generic function for failing between a bunch of queries. * * Uses the same principle as rlm_linelog, expanding the 'reference' config * item using xlat to figure out what query it should execute. * * If the reference matches multiple config items, and a query fails or * doesn't update any rows, the next matching config item is used. * */ static int acct_redundant(rlm_sql_t *inst, REQUEST *request, sql_acct_section_t *section) { rlm_rcode_t rcode = RLM_MODULE_OK; rlm_sql_handle_t *handle = NULL; int sql_ret; int numaffected = 0; CONF_ITEM *item; CONF_PAIR *pair; char const *attr = NULL; char const *value; char path[MAX_STRING_LEN]; char *p = path; char *expanded = NULL; rad_assert(section); if (section->reference[0] != '.') { *p++ = '.'; } if (radius_xlat(p, sizeof(path) - (p - path), request, section->reference, NULL, NULL) < 0) { rcode = RLM_MODULE_FAIL; goto finish; } item = cf_reference_item(NULL, section->cs, path); if (!item) { rcode = RLM_MODULE_FAIL; goto finish; } if (cf_item_is_section(item)){ REDEBUG("Sections are not supported as references"); rcode = RLM_MODULE_FAIL; goto finish; } pair = cf_itemtopair(item); attr = cf_pair_attr(pair); RDEBUG2("Using query template '%s'", attr); handle = sql_get_socket(inst); if (!handle) { rcode = RLM_MODULE_FAIL; goto finish; } sql_set_user(inst, request, NULL); while (true) { value = cf_pair_value(pair); if (!value) { RDEBUG("Ignoring null query"); rcode = RLM_MODULE_NOOP; goto finish; } if (radius_axlat(&expanded, request, value, sql_escape_func, inst) < 0) { rcode = RLM_MODULE_FAIL; goto finish; } if (!*expanded) { RDEBUG("Ignoring null query"); rcode = RLM_MODULE_NOOP; talloc_free(expanded); goto finish; } rlm_sql_query_log(inst, request, section, expanded); /* * If rlm_sql_query cannot use the socket it'll try and * reconnect. Reconnecting will automatically release * the current socket, and try to select a new one. * * If we get RLM_SQL_RECONNECT it means all connections in the pool * were exhausted, and we couldn't create a new connection, * so we do not need to call sql_release_socket. */ sql_ret = rlm_sql_query(&handle, inst, expanded); TALLOC_FREE(expanded); if (sql_ret == RLM_SQL_RECONNECT) { rcode = RLM_MODULE_FAIL; goto finish; } rad_assert(handle); /* * Assume all other errors are incidental, and just meant our * operation failed and its not a client or SQL syntax error. * * @fixme We should actually be able to distinguish between key * constraint violations (which we expect) and other errors. */ if (sql_ret == RLM_SQL_OK) { numaffected = (inst->module->sql_affected_rows)(handle, inst->config); if (numaffected > 0) { break; /* A query succeeded, were done! */ } RDEBUG("No records updated"); } (inst->module->sql_finish_query)(handle, inst->config); /* * We assume all entries with the same name form a redundant * set of queries. */ pair = cf_pair_find_next(section->cs, pair, attr); if (!pair) { RDEBUG("No additional queries configured"); rcode = RLM_MODULE_NOOP; goto finish; } RDEBUG("Trying next query..."); } (inst->module->sql_finish_query)(handle, inst->config); finish: talloc_free(expanded); sql_release_socket(inst, handle); return rcode; }
int main(int argc, char **argv) { int argval; bool quiet = false; int sockfd = -1; char *line = NULL; ssize_t len; char const *file = NULL; char const *name = "radiusd"; char *p, buffer[65536]; char const *input_file = NULL; FILE *inputfp = stdin; char const *output_file = NULL; char const *server = NULL; char const *radius_dir = RADIUS_DIR; char const *dict_dir = DICTDIR; char *commands[MAX_COMMANDS]; int num_commands = -1; #ifndef NDEBUG if (fr_fault_setup(getenv("PANIC_ACTION"), argv[0]) < 0) { fr_perror("radmin"); exit(EXIT_FAILURE); } #endif talloc_set_log_stderr(); outputfp = stdout; /* stdout is not a constant value... */ if ((progname = strrchr(argv[0], FR_DIR_SEP)) == NULL) { progname = argv[0]; } else { progname++; } while ((argval = getopt(argc, argv, "d:D:hi:e:Ef:n:o:qs:S")) != EOF) { switch (argval) { case 'd': if (file) { fprintf(stderr, "%s: -d and -f cannot be used together.\n", progname); exit(1); } if (server) { fprintf(stderr, "%s: -d and -s cannot be used together.\n", progname); exit(1); } radius_dir = optarg; break; case 'D': dict_dir = optarg; break; case 'e': num_commands++; /* starts at -1 */ if (num_commands >= MAX_COMMANDS) { fprintf(stderr, "%s: Too many '-e'\n", progname); exit(1); } commands[num_commands] = optarg; break; case 'E': echo = true; break; case 'f': radius_dir = NULL; file = optarg; break; default: case 'h': usage(0); break; case 'i': if (strcmp(optarg, "-") != 0) { input_file = optarg; } quiet = true; break; case 'n': name = optarg; break; case 'o': if (strcmp(optarg, "-") != 0) { output_file = optarg; } quiet = true; break; case 'q': quiet = true; break; case 's': if (file) { fprintf(stderr, "%s: -s and -f cannot be used together.\n", progname); usage(1); } radius_dir = NULL; server = optarg; break; case 'S': secret = NULL; break; } } /* * Mismatch between the binary and the libraries it depends on */ if (fr_check_lib_magic(RADIUSD_MAGIC_NUMBER) < 0) { fr_perror("radmin"); exit(1); } if (radius_dir) { int rcode; CONF_SECTION *cs, *subcs; file = NULL; /* MUST read it from the conffile now */ snprintf(buffer, sizeof(buffer), "%s/%s.conf", radius_dir, name); /* * Need to read in the dictionaries, else we may get * validation errors when we try and parse the config. */ if (dict_init(dict_dir, RADIUS_DICTIONARY) < 0) { fr_perror("radmin"); exit(64); } if (dict_read(radius_dir, RADIUS_DICTIONARY) == -1) { fr_perror("radmin"); exit(64); } cs = cf_file_read(buffer); if (!cs) { fprintf(stderr, "%s: Errors reading or parsing %s\n", progname, buffer); usage(1); } subcs = NULL; while ((subcs = cf_subsection_find_next(cs, subcs, "listen")) != NULL) { char const *value; CONF_PAIR *cp = cf_pair_find(subcs, "type"); if (!cp) continue; value = cf_pair_value(cp); if (!value) continue; if (strcmp(value, "control") != 0) continue; /* * Now find the socket name (sigh) */ rcode = cf_item_parse(subcs, "socket", FR_ITEM_POINTER(PW_TYPE_STRING, &file), NULL); if (rcode < 0) { fprintf(stderr, "%s: Failed parsing listen section\n", progname); exit(1); } if (!file) { fprintf(stderr, "%s: No path given for socket\n", progname); usage(1); } break; } if (!file) { fprintf(stderr, "%s: Could not find control socket in %s\n", progname, buffer); exit(1); } } if (input_file) { inputfp = fopen(input_file, "r"); if (!inputfp) { fprintf(stderr, "%s: Failed opening %s: %s\n", progname, input_file, fr_syserror(errno)); exit(1); } } if (output_file) { outputfp = fopen(output_file, "w"); if (!outputfp) { fprintf(stderr, "%s: Failed creating %s: %s\n", progname, output_file, fr_syserror(errno)); exit(1); } } if (!file && !server) { fprintf(stderr, "%s: Must use one of '-d' or '-f' or '-s'\n", progname); exit(1); } /* * Check if stdin is a TTY only if input is from stdin */ if (input_file && !quiet && !isatty(STDIN_FILENO)) quiet = true; #ifdef USE_READLINE if (!quiet) { #ifdef USE_READLINE_HISTORY using_history(); #endif rl_bind_key('\t', rl_insert); } #endif /* * Prevent SIGPIPEs from terminating the process */ signal(SIGPIPE, SIG_IGN); if (do_connect(&sockfd, file, server) < 0) exit(1); /* * Run one command. */ if (num_commands >= 0) { int i; for (i = 0; i <= num_commands; i++) { len = run_command(sockfd, commands[i], buffer, sizeof(buffer)); if (len < 0) exit(1); if (buffer[0]) { fputs(buffer, outputfp); fprintf(outputfp, "\n"); fflush(outputfp); } } exit(0); } if (!quiet) { printf("%s - FreeRADIUS Server administration tool.\n", radmin_version); printf("Copyright (C) 2008-2014 The FreeRADIUS server project and contributors.\n"); printf("There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A\n"); printf("PARTICULAR PURPOSE.\n"); printf("You may redistribute copies of FreeRADIUS under the terms of the\n"); printf("GNU General Public License v2.\n"); } /* * FIXME: Do login? */ while (1) { #ifndef USE_READLINE if (!quiet) { printf("radmin> "); fflush(stdout); } #else if (!quiet) { line = readline("radmin> "); if (!line) break; if (!*line) { free(line); continue; } #ifdef USE_READLINE_HISTORY add_history(line); #endif } else /* quiet, or no readline */ #endif { line = fgets(buffer, sizeof(buffer), inputfp); if (!line) break; p = strchr(buffer, '\n'); if (!p) { fprintf(stderr, "%s: Input line too long\n", progname); exit(1); } *p = '\0'; /* * Strip off leading spaces. */ for (p = line; *p != '\0'; p++) { if ((p[0] == ' ') || (p[0] == '\t')) { line = p + 1; continue; } if (p[0] == '#') { line = NULL; break; } break; } /* * Comments: keep going. */ if (!line) continue; /* * Strip off CR / LF */ for (p = line; *p != '\0'; p++) { if ((p[0] == '\r') || (p[0] == '\n')) { p[0] = '\0'; break; } } } if (strcmp(line, "reconnect") == 0) { if (do_connect(&sockfd, file, server) < 0) exit(1); line = NULL; continue; } if (memcmp(line, "secret ", 7) == 0) { if (!secret) { secret = line + 7; do_challenge(sockfd); } line = NULL; continue; } /* * Exit, done, etc. */ if ((strcmp(line, "exit") == 0) || (strcmp(line, "quit") == 0)) { break; } if (server && !secret) { fprintf(stderr, "ERROR: You must enter 'secret <SECRET>' before running any commands\n"); line = NULL; continue; } len = run_command(sockfd, line, buffer, sizeof(buffer)); if ((len < 0) && (do_connect(&sockfd, file, server) < 0)) { fprintf(stderr, "Reconnecting..."); exit(1); } else if (len == 0) break; else if (len == 1) continue; /* no output. */ fputs(buffer, outputfp); fflush(outputfp); fprintf(outputfp, "\n"); } fprintf(outputfp, "\n"); return 0; }