static int mod_bootstrap(void *instance, CONF_SECTION *conf) { rlm_unbound_t *inst = instance; inst->name = cf_section_name2(conf); if (!inst->name) inst->name = cf_section_name1(conf); if (inst->timeout > 10000) { cf_log_err(conf, "timeout must be 0 to 10000"); return -1; } MEM(inst->xlat_a_name = talloc_typed_asprintf(inst, "%s-a", inst->name)); MEM(inst->xlat_aaaa_name = talloc_typed_asprintf(inst, "%s-aaaa", inst->name)); MEM(inst->xlat_ptr_name = talloc_typed_asprintf(inst, "%s-ptr", inst->name)); if (xlat_register(inst, inst->xlat_a_name, xlat_a, NULL, NULL, 0, XLAT_DEFAULT_BUF_LEN, false) || xlat_register(inst, inst->xlat_aaaa_name, xlat_aaaa, NULL, NULL, 0, XLAT_DEFAULT_BUF_LEN, false) || xlat_register(inst, inst->xlat_ptr_name, xlat_ptr, NULL, NULL, 0, XLAT_DEFAULT_BUF_LEN, false)) { cf_log_err(conf, "Failed registering xlats"); return -1; } return 0; }
/* * Verify the result of the map. */ static int csv_map_verify(CONF_SECTION *cs, void *mod_inst, UNUSED void *proc_inst, vp_tmpl_t const *src, vp_map_t const *maps) { rlm_csv_t *inst = mod_inst; vp_map_t const *map; if (!src) { cf_log_err(cs, "Missing file name"); return -1; } for (map = maps; map != NULL; map = map->next) { if (map->rhs->type != TMPL_TYPE_UNPARSED) continue; if (fieldname2offset(inst, map->rhs->name) < 0) { cf_log_err(map->ci, "Unknown field '%s'", map->rhs->name); return -1; } } return 0; }
/* * Instantiate the module. */ static int mod_instantiate(void *instance, CONF_SECTION *conf) { rlm_logtee_t *inst = instance; char prefix[100]; /* * Escape filenames only if asked. */ if (inst->file.escape) { inst->file.escape_func = rad_filename_escape; } else { inst->file.escape_func = rad_filename_make_safe; } inst->log_dst = fr_str2int(logtee_dst_table, inst->log_dst_str, LOGTEE_DST_INVALID); if (inst->log_dst == LOGTEE_DST_INVALID) { cf_log_err(conf, "Invalid log destination \"%s\"", inst->log_dst_str); return -1; } inst->name = cf_section_name2(conf); if (!inst->name) inst->name = cf_section_name1(conf); snprintf(prefix, sizeof(prefix), "rlm_logtee (%s)", inst->name); FR_SIZE_BOUND_CHECK("buffer_depth", inst->buffer_depth, >=, (size_t)1); FR_SIZE_BOUND_CHECK("buffer_depth", inst->buffer_depth, <=, (size_t)1000000); /* 1 Million messages */ /* * Setup the logging destination */ switch (inst->log_dst) { case LOGTEE_DST_FILE: cf_log_err(conf, "Teeing to files NYI"); return -1; case LOGTEE_DST_UNIX: #ifndef HAVE_SYS_UN_H cf_log_err(conf, "Unix sockets are not supported on this sytem"); return -1; #endif case LOGTEE_DST_UDP: break; case LOGTEE_DST_TCP: break; case LOGTEE_DST_INVALID: rad_assert(0); break; } inst->delimiter_len = talloc_array_length(inst->delimiter) - 1; return 0; }
/* * Convert a buffer to a CSV entry */ static rlm_csv_entry_t *file2csv(CONF_SECTION *conf, rlm_csv_t *inst, int lineno, char *buffer) { rlm_csv_entry_t *e; int i; char *p, *q; MEM(e = (rlm_csv_entry_t *)talloc_zero_array(inst->tree, uint8_t, sizeof(*e) + inst->used_fields + sizeof(e->data[0]))); talloc_set_type(e, rlm_csv_entry_t); for (p = buffer, i = 0; p != NULL; p = q, i++) { if (!buf2entry(inst, p, &q)) { cf_log_err(conf, "Malformed entry in file %s line %d", inst->filename, lineno); return NULL; } if (q) *(q++) = '\0'; if (i >= inst->num_fields) { cf_log_err(conf, "Too many fields at file %s line %d", inst->filename, lineno); return NULL; } /* * This is the key field. */ if (i == inst->key_field) { e->key = talloc_typed_strdup(e, p); continue; } /* * This field is unused. Ignore it. */ if (inst->field_offsets[i] < 0) continue; MEM(e->data[inst->field_offsets[i]] = talloc_typed_strdup(e, p)); } if (i < inst->num_fields) { cf_log_err(conf, "Too few fields at file %s line %d (%d < %d)", inst->filename, lineno, i, inst->num_fields); return NULL; } /* * FIXME: Allow duplicate keys later. */ if (!rbtree_insert(inst->tree, e)) { cf_log_err(conf, "Failed inserting entry for filename %s line %d: duplicate entry", inst->filename, lineno); return NULL; } return e; }
/** 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; }
/** Open a detail listener * */ static int mod_open(fr_listen_t *li) { proto_detail_file_t const *inst = talloc_get_type_abort_const(li->app_io_instance, proto_detail_file_t); proto_detail_file_thread_t *thread = talloc_get_type_abort(li->thread_instance, proto_detail_file_thread_t); if (inst->poll_interval == 0) { int oflag; #ifdef O_EVTONLY oflag = O_EVTONLY; #else oflag = O_RDONLY; #endif li->fd = thread->fd = open(inst->directory, oflag); } else { li->fd = thread->fd = open("/dev/null", O_RDONLY); } if (thread->fd < 0) { cf_log_err(inst->cs, "Failed opening %s: %s", inst->directory, fr_syserror(errno)); return -1; } thread->inst = inst; thread->name = talloc_typed_asprintf(thread, "detail polling for files matching %s", inst->filename); thread->vnode_fd = -1; pthread_mutex_init(&thread->worker_mutex, NULL); DEBUG("Listening on %s bound to virtual server %s", thread->name, cf_section_name2(inst->parent->server_cs)); return 0; }
static int mod_instantiate(void *instance, CONF_SECTION *listen_cs) { int rcode; CONF_SECTION *server_cs; proto_detail_process_t *inst = talloc_get_type_abort(instance, proto_detail_process_t); vp_tmpl_rules_t parse_rules; memset(&parse_rules, 0, sizeof(parse_rules)); parse_rules.dict_def = dict_freeradius; rad_assert(listen_cs); server_cs = cf_item_to_section(cf_parent(listen_cs)); rad_assert(strcmp(cf_section_name1(server_cs), "server") == 0); rcode = unlang_compile_subsection(server_cs, "recv", NULL, inst->recv_type, &parse_rules); if (rcode < 0) return rcode; if (rcode == 0) { cf_log_err(server_cs, "Failed finding 'recv { ... }' section of virtual server %s", cf_section_name2(server_cs)); return -1; } rcode = unlang_compile_subsection(server_cs, "send", "ok", inst->send_type, &parse_rules); if (rcode < 0) return rcode; rcode = unlang_compile_subsection(server_cs, "send", "fail", inst->send_type, &parse_rules); if (rcode < 0) return rcode; return 0; }
/* * Ensure that the unlang sections are compiled. */ static int mod_instantiate(UNUSED void *instance, CONF_SECTION *listen_cs) { int rcode; CONF_SECTION *server_cs; vp_tmpl_rules_t parse_rules; memset(&parse_rules, 0, sizeof(parse_rules)); parse_rules.dict_def = dict_freeradius; rad_assert(listen_cs); server_cs = cf_item_to_section(cf_parent(listen_cs)); rad_assert(strcmp(cf_section_name1(server_cs), "server") == 0); rcode = unlang_compile_subsection(server_cs, "new", "client", MOD_AUTHORIZE, &parse_rules); if (rcode < 0) return rcode; if (rcode == 0) { cf_log_err(server_cs, "Failed finding 'new client { ... }' section of virtual server %s", cf_section_name2(server_cs)); return -1; } rcode = unlang_compile_subsection(server_cs, "add", "client", MOD_POST_AUTH, &parse_rules); if (rcode < 0) return rcode; rcode = unlang_compile_subsection(server_cs, "deny", "client", MOD_POST_AUTH, &parse_rules); if (rcode < 0) return rcode; return 0; }
static int mod_bootstrap(void *instance, CONF_SECTION *cs) { proto_dhcpv4_udp_t *inst = talloc_get_type_abort(instance, proto_dhcpv4_udp_t); size_t num; inst->cs = cs; /* * Complain if no "ipaddr" is set. */ if (inst->ipaddr.af == AF_UNSPEC) { cf_log_err(cs, "No 'ipaddr' was specified in the 'udp' section"); return -1; } if (inst->ipaddr.af != AF_INET) { cf_log_err(cs, "DHCPv4 transport cannot use IPv6 for 'ipaddr'"); return -1; } /* * If src_ipaddr is defined, it must be of the same address family as "ipaddr" */ if ((inst->src_ipaddr.af != AF_UNSPEC) && (inst->src_ipaddr.af != inst->ipaddr.af)) { cf_log_err(cs, "Both 'ipaddr' and 'src_ipaddr' must be from the same address family"); return -1; } /* * Set src_ipaddr to INADDR_NONE if not otherwise specified */ if (inst->src_ipaddr.af == AF_UNSPEC) { memset(&inst->src_ipaddr, 0, sizeof(inst->src_ipaddr)); inst->src_ipaddr.af = AF_INET; } if (inst->recv_buff_is_set) { FR_INTEGER_BOUND_CHECK("recv_buff", inst->recv_buff, >=, 32); FR_INTEGER_BOUND_CHECK("recv_buff", inst->recv_buff, <=, INT_MAX); }
/** Verify that a map in the cache section makes sense * */ static int cache_verify(vp_map_t *map, void *ctx) { if (unlang_fixup_update(map, ctx) < 0) return -1; if ((map->lhs->type != TMPL_TYPE_ATTR) && (map->lhs->type != TMPL_TYPE_LIST)) { cf_log_err(map->ci, "Destination must be an attribute ref or a list"); return -1; } return 0; }
/** Verify that a map in the cache section makes sense * */ static int cache_verify(value_pair_map_t *map, UNUSED void *ctx) { if (modcall_fixup_update(map, ctx) < 0) return -1; if ((map->lhs->type != TMPL_TYPE_ATTR) && (map->lhs->type != TMPL_TYPE_LIST)) { cf_log_err(map->ci, "Left operand must be an attribute ref or a list"); return -1; } switch (map->rhs->type) { case TMPL_TYPE_EXEC: cf_log_err(map->ci, "Exec values are not allowed"); return -1; /* * Only =, :=, += and -= operators are supported for * cache entries. */ case TMPL_TYPE_LITERAL: case TMPL_TYPE_XLAT: case TMPL_TYPE_ATTR: switch (map->op) { case T_OP_SET: case T_OP_EQ: case T_OP_SUB: case T_OP_ADD: break; default: cf_log_err(map->ci, "Operator \"%s\" not allowed for %s values", fr_int2str(fr_tokens, map->op, "<INVALID>"), fr_int2str(tmpl_types, map->rhs->type, "<INVALID>")); return -1; } default: break; } return 0; }
static int mod_instantiate(void *instance, CONF_SECTION *conf) { rlm_sometimes_t *inst = instance; /* * Convert the rcode string to an int, and get rid of it */ inst->rcode = fr_str2int(mod_rcode_table, inst->rcode_str, RLM_MODULE_UNKNOWN); if (inst->rcode == RLM_MODULE_UNKNOWN) { cf_log_err(conf, "Unknown module return code '%s'", inst->rcode_str); return -1; } return 0; }
/** 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; }
static int mod_instantiate(void *instance, CONF_SECTION *conf) { rlm_always_t *inst = instance; inst->name = cf_section_name1(conf); if (!inst->name) inst->name = cf_section_name2(conf); /* * Convert the rcode string to an int */ inst->rcode = fr_str2int(mod_rcode_table, inst->rcode_str, RLM_MODULE_UNKNOWN); if (inst->rcode == RLM_MODULE_UNKNOWN) { cf_log_err(conf, "rcode value \"%s\" is invalid", inst->rcode_str); return -1; } return 0; }
/* * Verify the result of the map. */ static int csv_map_verify(UNUSED void *proc_inst, void *mod_inst, UNUSED vp_tmpl_t const *src, vp_map_t const *maps) { rlm_csv_t *inst = mod_inst; vp_map_t const *map; for (map = maps; map != NULL; map = map->next) { if (map->rhs->type != TMPL_TYPE_LITERAL) continue; if (fieldname2offset(inst, map->rhs->name) < 0) { cf_log_err(map->ci, "Unknown field '%s'", map->rhs->name); return -1; } } return 0; }
static int mod_bootstrap(void *instance, CONF_SECTION *cs) { proto_radius_udp_t *inst = talloc_get_type_abort(instance, proto_radius_udp_t); size_t num; CONF_ITEM *ci; CONF_SECTION *server_cs; inst->cs = cs; /* * Complain if no "ipaddr" is set. */ if (inst->ipaddr.af == AF_UNSPEC) { cf_log_err(cs, "No 'ipaddr' was specified in the 'udp' section"); return -1; } if (inst->recv_buff_is_set) { FR_INTEGER_BOUND_CHECK("recv_buff", inst->recv_buff, >=, 32); FR_INTEGER_BOUND_CHECK("recv_buff", inst->recv_buff, <=, INT_MAX); }
static int mod_bootstrap(void *instance, CONF_SECTION *cs) { proto_detail_file_t *inst = talloc_get_type_abort(instance, proto_detail_file_t); dl_instance_t const *dl_inst; char *p; #ifdef __linux__ /* * The kqueue API takes an FD, but inotify requires a filename. * libkqueue uses /proc/PID/fd/# to look up the FD -> filename mapping. * * However, if you start the server as "root", and then swap to "radiusd", * /proc/PID will be owned by "root" for security reasons. The only way * to make /proc/PID owned by "radiusd" is to set the DUMPABLE flag. * * Instead of making the poor sysadmin figure this out, * we check for this situation, and give them a * descriptive message telling them what to do. */ if (!main_config->allow_core_dumps && main_config->uid_is_set && main_config->server_uid != 0) { cf_log_err(cs, "Cannot start detail file reader due to Linux limitations."); cf_log_err(cs, "Please set 'allow_core_dumps = true' in the main configuration file."); return -1; } #endif /* * Find the dl_instance_t holding our instance data * so we can find out what the parent of our instance * was. */ dl_inst = dl_instance_by_data(instance); rad_assert(dl_inst); #ifndef __linux__ /* * Linux inotify works. So we allow poll_interval==0 */ FR_INTEGER_BOUND_CHECK("poll_interval", inst->poll_interval, >=, 1); #endif FR_INTEGER_BOUND_CHECK("poll_interval", inst->poll_interval, <=, 3600); inst->parent = talloc_get_type_abort(dl_inst->parent->data, proto_detail_t); inst->cs = cs; inst->directory = p = talloc_strdup(inst, inst->filename); p = strrchr(p, '/'); if (!p) { cf_log_err(cs, "Filename must contain '/'"); return -1; } *p = '\0'; if (!inst->filename_work) { inst->filename_work = talloc_typed_asprintf(inst, "%s/detail.work", inst->directory); } /* * We need this for the lock. */ inst->mode = O_RDWR; return 0; }
int rlm_ldap_map_verify(ldap_instance_t *inst, value_pair_map_t **head) { value_pair_map_t *map; if (radius_attrmap(cf_section_sub_find(inst->cs, "update"), head, PAIR_LIST_REPLY, PAIR_LIST_REQUEST, LDAP_MAX_ATTRMAP) < 0) { return -1; } /* * Attrmap only performs some basic validation checks, we need * to do rlm_ldap specific checks here. */ for (map = *head; map != NULL; map = map->next) { switch (map->dst->type) { case VPT_TYPE_LIST: if (map->op != T_OP_ADD) { cf_log_err(map->ci, "Only '+=' operator is permitted for valuepair to list mapping"); return -1; } case VPT_TYPE_ATTR: break; default: cf_log_err(map->ci, "valuepair destination must be an attribute or list"); return -1; } switch (map->src->type) { case VPT_TYPE_LIST: cf_log_err(map->ci, "LDAP attribute name cannot be derived from a list"); return -1; default: break; } /* * Be smart about whether we warn the user about missing passwords. * If there are no password attributes in the mapping, then the user's either an idiot * and has no idea what they're doing, or they're authenticating the user using a different * method. */ if (!inst->expect_password && map->dst->vpt_da && (map->dst->type == VPT_TYPE_ATTR)) { switch (map->dst->vpt_da->attr) { case PW_CLEARTEXT_PASSWORD: case PW_NT_PASSWORD: case PW_USER_PASSWORD: case PW_PASSWORD_WITH_HEADER: case PW_CRYPT_PASSWORD: /* * Because you just know someone is going to map NT-Password to the * request list, and then complain it's not working... */ if (map->dst->vpt_list != PAIR_LIST_CONTROL) { LDAP_DBGW("Mapping LDAP (%s) attribute to \"known good\" password attribute " "(%s) in %s list. This is probably *NOT* the correct list, " "you should prepend \"control:\" to password attribute " "(control:%s)", map->src->name, map->dst->vpt_da->name, fr_int2str(pair_lists, map->dst->vpt_list, "<invalid>"), map->dst->vpt_da->name); } inst->expect_password = true; default: break; } } switch (map->src->type) { /* * Only =, :=, += and -= operators are supported for * cache entries. */ case VPT_TYPE_LITERAL: case VPT_TYPE_XLAT: case VPT_TYPE_ATTR: switch (map->op) { case T_OP_SET: case T_OP_EQ: case T_OP_SUB: case T_OP_ADD: break; default: cf_log_err(map->ci, "Operator \"%s\" not allowed for %s values", fr_int2str(fr_tokens, map->op, "<INVALID>"), fr_int2str(vpt_types, map->src->type, "<INVALID>")); return -1; } default: break; } } return 0; }
static int mod_instantiate(void *instance, CONF_SECTION *conf) { rlm_unbound_t *inst = instance; int res; char *optval; fr_log_dst_t log_dst; int log_level; int log_fd = -1; char k[64]; /* To silence const warns until newer unbound in distros */ /* * @todo - move this to the thread-instantiate function */ inst->el = fr_global_event_list(); inst->log_pipe_stream[0] = NULL; inst->log_pipe_stream[1] = NULL; inst->log_fd = -1; inst->log_pipe_in_use = false; inst->ub = ub_ctx_create(); if (!inst->ub) { cf_log_err(conf, "ub_ctx_create failed"); return -1; } /* * Note unbound threads WILL happen with -s option, if it matters. * We cannot tell from here whether that option is in effect. */ res = ub_ctx_async(inst->ub, 1); if (res) goto error; /* Glean some default settings to match the main server. */ /* TODO: debug_level can be changed at runtime. */ /* TODO: log until fork when stdout or stderr and !rad_debug_lvl. */ log_level = 0; if (rad_debug_lvl > 0) { log_level = rad_debug_lvl; } else if (main_config->debug_level > 0) { log_level = main_config->debug_level; } switch (log_level) { /* TODO: This will need some tweaking */ case 0: case 1: break; case 2: log_level = 1; break; case 3: case 4: log_level = 2; /* mid-to-heavy levels of output */ break; case 5: case 6: case 7: case 8: log_level = 3; /* Pretty crazy amounts of output */ break; default: log_level = 4; /* Insane amounts of output including crypts */ break; } res = ub_ctx_debuglevel(inst->ub, log_level); if (res) goto error; switch (default_log.dst) { case L_DST_STDOUT: if (!rad_debug_lvl) { log_dst = L_DST_NULL; break; } log_dst = L_DST_STDOUT; log_fd = dup(STDOUT_FILENO); break; case L_DST_STDERR: if (!rad_debug_lvl) { log_dst = L_DST_NULL; break; } log_dst = L_DST_STDOUT; log_fd = dup(STDERR_FILENO); break; case L_DST_FILES: if (main_config->log_file) { char *log_file; strcpy(k, "logfile:"); /* 3rd argument isn't const'd in libunbounds API */ memcpy(&log_file, &main_config->log_file, sizeof(log_file)); res = ub_ctx_set_option(inst->ub, k, log_file); if (res) { goto error; } log_dst = L_DST_FILES; break; } /* FALL-THROUGH */ case L_DST_NULL: log_dst = L_DST_NULL; break; default: log_dst = L_DST_SYSLOG; break; } /* Now load the config file, which can override gleaned settings. */ { char *file; memcpy(&file, &inst->filename, sizeof(file)); res = ub_ctx_config(inst->ub, file); if (res) goto error; } /* * Check if the config file tried to use syslog. Unbound * does not share syslog gracefully. */ strcpy(k, "use-syslog"); res = ub_ctx_get_option(inst->ub, k, &optval); if (res || !optval) goto error; if (!strcmp(optval, "yes")) { char v[3]; free(optval); WARN("Overriding syslog settings"); strcpy(k, "use-syslog:"); strcpy(v, "no"); res = ub_ctx_set_option(inst->ub, k, v); if (res) goto error; if (log_dst == L_DST_FILES) { char *log_file; /* Reinstate the log file name JIC */ strcpy(k, "logfile:"); /* 3rd argument isn't const'd in libunbounds API */ memcpy(&log_file, &main_config->log_file, sizeof(log_file)); res = ub_ctx_set_option(inst->ub, k, log_file); if (res) goto error; } } else { if (optval) free(optval); strcpy(k, "logfile"); res = ub_ctx_get_option(inst->ub, k, &optval); if (res) goto error; if (optval && strlen(optval)) { log_dst = L_DST_FILES; /* * We open log_fd early in the process, * so that libunbound doesn't close * stdout / stderr on us (grrr, stupid * software). But if the config say to * use files, we now have to close the * dup'd FD. */ if (log_fd >= 0) { close(log_fd); log_fd = -1; } } else if (!rad_debug_lvl) { log_dst = L_DST_NULL; } if (optval) free(optval); } switch (log_dst) { case L_DST_STDOUT: /* * We have an fd to log to. And we've already attempted to * dup it so libunbound doesn't close it on us. */ if (log_fd == -1) { cf_log_err(conf, "Could not dup fd"); goto error_nores; } inst->log_stream = fdopen(log_fd, "w"); if (!inst->log_stream) { cf_log_err(conf, "error setting up log stream"); goto error_nores; } res = ub_ctx_debugout(inst->ub, inst->log_stream); if (res) goto error; break; case L_DST_FILES: /* We gave libunbound a filename. It is on its own now. */ break; case L_DST_NULL: /* We tell libunbound not to log at all. */ res = ub_ctx_debugout(inst->ub, NULL); if (res) goto error; break; case L_DST_SYSLOG: /* * Currently this wreaks havoc when running threaded, so just * turn logging off until that gets figured out. */ res = ub_ctx_debugout(inst->ub, NULL); if (res) goto error; break; default: break; } /* * Now we need to finalize the context. * * There's no clean API to just finalize the context made public * in libunbound. But we can trick it by trying to delete data * which as it happens fails quickly and quietly even though the * data did not exist. */ strcpy(k, "notar33lsite.foo123.nottld A 127.0.0.1"); ub_ctx_data_remove(inst->ub, k); inst->log_fd = ub_fd(inst->ub); if (inst->log_fd >= 0) { if (fr_event_fd_insert(inst, inst->el, inst->log_fd, ub_fd_handler, NULL, NULL, inst) < 0) { cf_log_err(conf, "could not insert async fd"); inst->log_fd = -1; goto error_nores; } } return 0; error: cf_log_err(conf, "%s", ub_strerror(res)); error_nores: if (log_fd > -1) close(log_fd); return -1; }
/** 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; }
/** Convert an 'update' config section into an attribute map. * * Uses 'name2' of section to set default request and lists. * * @param[in] cs the update section * @param[out] head Where to store the head of the map. * @param[in] dst_list_def The default destination list, usually dictated by * the section the module is being called in. * @param[in] src_list_def The default source list, usually dictated by the * section the module is being called in. * @param[in] max number of mappings to process. * @return -1 on error, else 0. */ int radius_attrmap(CONF_SECTION *cs, value_pair_map_t **head, pair_lists_t dst_list_def, pair_lists_t src_list_def, unsigned int max) { char const *cs_list, *p; request_refs_t request_def = REQUEST_CURRENT; CONF_ITEM *ci; CONF_PAIR *cp; unsigned int total = 0; value_pair_map_t **tail, *map; TALLOC_CTX *ctx; *head = NULL; tail = head; if (!cs) return 0; /* * The first map has cs as the parent. * The rest have the previous map as the parent. */ ctx = cs; ci = cf_sectiontoitem(cs); cs_list = p = cf_section_name2(cs); if (cs_list) { request_def = radius_request_name(&p, REQUEST_CURRENT); if (request_def == REQUEST_UNKNOWN) { cf_log_err(ci, "Default request specified " "in mapping section is invalid"); return -1; } dst_list_def = fr_str2int(pair_lists, p, PAIR_LIST_UNKNOWN); if (dst_list_def == PAIR_LIST_UNKNOWN) { cf_log_err(ci, "Default list \"%s\" specified " "in mapping section is invalid", p); return -1; } } for (ci = cf_item_find_next(cs, NULL); ci != NULL; ci = cf_item_find_next(cs, ci)) { if (total++ == max) { cf_log_err(ci, "Map size exceeded"); goto error; } if (!cf_item_is_pair(ci)) { cf_log_err(ci, "Entry is not in \"attribute = " "value\" format"); goto error; } cp = cf_itemtopair(ci); map = radius_cp2map(ctx, cp, request_def, dst_list_def, REQUEST_CURRENT, src_list_def); if (!map) { goto error; } ctx = *tail = map; tail = &(map->next); } return 0; error: talloc_free(*head); return -1; }
/** Convert CONFIG_PAIR (which may contain refs) to value_pair_map_t. * * Treats the left operand as an attribute reference * @verbatim<request>.<list>.<attribute>@endverbatim * * Treatment of left operand depends on quotation, barewords are treated as * attribute references, double quoted values are treated as expandable strings, * single quoted values are treated as literal strings. * * Return must be freed with talloc_free * * @param[in] ctx for talloc * @param[in] cp to convert to map. * @param[in] dst_request_def The default request to insert unqualified * attributes into. * @param[in] dst_list_def The default list to insert unqualified attributes * into. * @param[in] src_request_def The default request to resolve attribute * references in. * @param[in] src_list_def The default list to resolve unqualified attributes * in. * @return value_pair_map_t if successful or NULL on error. */ value_pair_map_t *radius_cp2map(TALLOC_CTX *ctx, CONF_PAIR *cp, request_refs_t dst_request_def, pair_lists_t dst_list_def, request_refs_t src_request_def, pair_lists_t src_list_def) { value_pair_map_t *map; char const *attr; char const *value; FR_TOKEN type; CONF_ITEM *ci = cf_pairtoitem(cp); if (!cp) return NULL; map = talloc_zero(ctx, value_pair_map_t); attr = cf_pair_attr(cp); value = cf_pair_value(cp); if (!value) { cf_log_err(ci, "Missing attribute value"); goto error; } map->dst = radius_attr2tmpl(map, attr, dst_request_def, dst_list_def); if (!map->dst) { cf_log_err(ci, "Syntax error in attribute definition"); goto error; } /* * Bare words always mean attribute references. */ type = cf_pair_value_type(cp); if (type == T_BARE_WORD) { if (*value == '&') { map->src = radius_attr2tmpl(map, value + 1, src_request_def, src_list_def); } else { if (!isdigit((int) *value) && ((strchr(value, ':') != NULL) || (dict_attrbyname(value) != NULL))) { map->src = radius_attr2tmpl(map, value, src_request_def, src_list_def); } if (map->src) { WDEBUG("%s[%d]: Please add '&' for attribute reference '%s = &%s'", cf_pair_filename(cp), cf_pair_lineno(cp), attr, value); } else { map->src = radius_str2tmpl(map, value, type); } } } else { map->src = radius_str2tmpl(map, value, type); } if (!map->src) { goto error; } map->op = cf_pair_operator(cp); map->ci = ci; /* * Lots of sanity checks for insane people... */ /* * We don't support implicit type conversion, * except for "octets" */ if (map->dst->da && map->src->da && (map->src->da->type != map->dst->da->type) && (map->src->da->type != PW_TYPE_OCTETS) && (map->dst->da->type != PW_TYPE_OCTETS)) { cf_log_err(ci, "Attribute type mismatch"); goto error; } /* * What exactly where you expecting to happen here? */ if ((map->dst->type == VPT_TYPE_ATTR) && (map->src->type == VPT_TYPE_LIST)) { cf_log_err(ci, "Can't copy list into an attribute"); goto error; } /* * Can't copy an xlat expansion or literal into a list, * we don't know what type of attribute we'd need * to create */ if ((map->dst->type == VPT_TYPE_LIST) && ((map->src->type == VPT_TYPE_XLAT) || (map->src->type == VPT_TYPE_LITERAL))) { cf_log_err(ci, "Can't copy value into list (we don't know which attribute to create)"); goto error; } /* * Depending on the attribute type, some operators are * disallowed. */ if (map->dst->type == VPT_TYPE_ATTR) { if ((map->op != T_OP_EQ) && (map->op != T_OP_CMP_EQ) && (map->op != T_OP_ADD) && (map->op != T_OP_SUB) && (map->op != T_OP_LE) && (map->op != T_OP_GE) && (map->op != T_OP_CMP_FALSE) && (map->op != T_OP_SET)) { cf_log_err(ci, "Invalid operator for attribute"); goto error; } } switch (map->src->type) { /* * Only += and -= operators are supported for list copy. */ case VPT_TYPE_LIST: switch (map->op) { case T_OP_SUB: case T_OP_ADD: break; default: cf_log_err(ci, "Operator \"%s\" not allowed " "for list copy", fr_int2str(fr_tokens, map->op, "<INVALID>")); goto error; } break; default: break; } return map; error: talloc_free(map); return NULL; }
/* * Find a module instance. */ module_instance_t *find_module_instance(CONF_SECTION *modules, const char *askedname, int do_link) { int check_config_safe = FALSE; CONF_SECTION *cs; const char *name1, *instname; module_instance_t *node, myNode; char module_name[256]; if (!modules) return NULL; /* * Look for the real name. Ignore the first character, * which tells the server "it's OK for this module to not * exist." */ instname = askedname; if (instname[0] == '-') instname++; /* * 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. */ cs = cf_section_sub_find_name2(modules, NULL, instname); if (cs == NULL) { radlog(L_ERR, "ERROR: Cannot find a configuration entry for module \"%s\".\n", instname); return NULL; } /* * If there's already a module instance, return it. */ strlcpy(myNode.name, instname, sizeof(myNode.name)); node = rbtree_finddata(instance_tree, &myNode); if (node) return node; if (!do_link) return NULL; name1 = cf_section_name1(cs); /* * Found the configuration entry. */ node = rad_malloc(sizeof(*node)); memset(node, 0, sizeof(*node)); node->insthandle = NULL; node->cs = cs; /* * Names in the "modules" section aren't prefixed * with "rlm_", so we add it here. */ snprintf(module_name, sizeof(module_name), "rlm_%s", name1); node->entry = linkto_module(module_name, cs); if (!node->entry) { free(node); /* linkto_module logs any errors */ return NULL; } if (check_config && (node->entry->module->instantiate) && (node->entry->module->type & RLM_TYPE_CHECK_CONFIG_SAFE) == 0) { const char *value = NULL; CONF_PAIR *cp; cp = cf_pair_find(cs, "force_check_config"); if (cp) value = cf_pair_value(cp); if (value && (strcmp(value, "yes") == 0)) goto print_inst; cf_log_module(cs, "Skipping instantiation of %s", instname); } else { print_inst: check_config_safe = TRUE; cf_log_module(cs, "Instantiating module \"%s\" from file %s", instname, cf_section_filename(cs)); } /* * Call the module's instantiation routine. */ if ((node->entry->module->instantiate) && (!check_config || check_config_safe) && ((node->entry->module->instantiate)(cs, &node->insthandle) < 0)) { cf_log_err(cf_sectiontoitem(cs), "Instantiation failed for module \"%s\"", instname); free(node); return NULL; } /* * We're done. Fill in the rest of the data structure, * and link it to the module instance list. */ strlcpy(node->name, instname, sizeof(node->name)); #ifdef 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 rbtree_insert(instance_tree, node); return node; }
/* * Find a module on disk or in memory, and link to it. */ static module_entry_t *linkto_module(const char *module_name, CONF_SECTION *cs) { module_entry_t myentry; module_entry_t *node; lt_dlhandle handle = NULL; char module_struct[256]; char *p; const module_t *module; strlcpy(myentry.name, module_name, sizeof(myentry.name)); node = rbtree_finddata(module_tree, &myentry); if (node) return node; /* * Link to the module's rlm_FOO{} module structure. * * The module_name variable has the version number * embedded in it, and we don't want that here. */ strcpy(module_struct, module_name); p = strrchr(module_struct, '-'); if (p) *p = '\0'; #if !defined(WITH_LIBLTDL) && defined(HAVE_DLFCN_H) && defined(RTLD_SELF) module = lt_dlsym(RTLD_SELF, module_struct); if (module) goto open_self; #endif /* * Keep the handle around so we can dlclose() it. */ handle = fr_dlopenext(module_name); if (handle == NULL) { cf_log_err(cf_sectiontoitem(cs), "Failed to link to module '%s': %s\n", module_name, lt_dlerror()); return NULL; } DEBUG3(" (Loaded %s, checking if it's valid)", module_name); /* * libltld MAY core here, if the handle it gives us contains * garbage data. */ module = lt_dlsym(handle, module_struct); if (!module) { cf_log_err(cf_sectiontoitem(cs), "Failed linking to %s structure: %s\n", module_name, lt_dlerror()); lt_dlclose(handle); return NULL; } #if !defined(WIT_LIBLTDL) && defined (HAVE_DLFCN_H) && defined(RTLD_SELF) open_self: #endif /* * Before doing anything else, check if it's sane. */ if (module->magic != RLM_MODULE_MAGIC_NUMBER) { lt_dlclose(handle); cf_log_err(cf_sectiontoitem(cs), "Invalid version in module '%s'", module_name); return NULL; } /* make room for the module type */ node = rad_malloc(sizeof(*node)); memset(node, 0, sizeof(*node)); strlcpy(node->name, module_name, sizeof(node->name)); node->module = module; node->handle = handle; cf_log_module(cs, "Linked to module %s", module_name); /* * Add the module as "rlm_foo-version" to the configuration * section. */ if (!rbtree_insert(module_tree, node)) { radlog(L_ERR, "Failed to cache module %s", module_name); lt_dlclose(handle); free(node); return NULL; } return node; }
int rlm_ldap_map_verify(vp_map_t *map, void *instance) { rlm_ldap_t *inst = instance; /* * Destinations where we can put the VALUE_PAIRs we * create using LDAP values. */ switch (map->lhs->type) { case TMPL_TYPE_LIST: case TMPL_TYPE_ATTR: break; case TMPL_TYPE_ATTR_UNDEFINED: cf_log_err(map->ci, "Unknown attribute %s", map->lhs->tmpl_unknown_name); return -1; default: cf_log_err(map->ci, "Left hand side of map must be an attribute or list, not a %s", fr_int2str(tmpl_names, map->lhs->type, "<INVALID>")); return -1; } /* * Sources we can use to get the name of the attribute * we're retrieving from LDAP. */ switch (map->rhs->type) { case TMPL_TYPE_XLAT: case TMPL_TYPE_ATTR: case TMPL_TYPE_EXEC: case TMPL_TYPE_LITERAL: break; case TMPL_TYPE_ATTR_UNDEFINED: cf_log_err(map->ci, "Unknown attribute %s", map->rhs->tmpl_unknown_name); return -1; default: cf_log_err(map->ci, "Right hand side of map must be an xlat, attribute, exec, or literal, not a %s", fr_int2str(tmpl_names, map->rhs->type, "<INVALID>")); return -1; } /* * Only =, :=, += and -= operators are supported for LDAP mappings. */ switch (map->op) { case T_OP_SET: case T_OP_EQ: case T_OP_SUB: case T_OP_ADD: break; default: cf_log_err(map->ci, "Operator \"%s\" not allowed for LDAP mappings", fr_int2str(fr_tokens, map->op, "<INVALID>")); return -1; } /* * Be smart about whether we warn the user about missing passwords. * If there are no password attributes in the mapping, then the user's either an idiot * and has no idea what they're doing, or they're authenticating the user using a different * method. */ if (!inst->expect_password && (map->lhs->type == TMPL_TYPE_ATTR) && map->lhs->tmpl_da) { switch (map->lhs->tmpl_da->attr) { case PW_CLEARTEXT_PASSWORD: case PW_NT_PASSWORD: case PW_USER_PASSWORD: case PW_PASSWORD_WITH_HEADER: case PW_CRYPT_PASSWORD: /* * Because you just know someone is going to map NT-Password to the * request list, and then complain it's not working... */ if (map->lhs->tmpl_list != PAIR_LIST_CONTROL) { LDAP_DBGW("Mapping LDAP (%s) attribute to \"known good\" password attribute " "(%s) in %s list. This is probably *NOT* the correct list, " "you should prepend \"control:\" to password attribute " "(control:%s)", map->rhs->name, map->lhs->tmpl_da->name, fr_int2str(pair_lists, map->lhs->tmpl_list, "<invalid>"), map->lhs->tmpl_da->name); } inst->expect_password = true; default: break; } } return 0; }
/** Allow the admin to set packet contents 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_SECTION specifying the things to update * @param[in] rule unused. * @return * - 0 on success. * - -1 on failure. */ static int status_check_update_parse(TALLOC_CTX *ctx, void *out, UNUSED void *parent, CONF_ITEM *ci, UNUSED CONF_PARSER const *rule) { int rcode; CONF_SECTION *cs; char const *name2; vp_map_t *head = NULL; rad_assert(cf_item_is_section(ci)); cs = cf_item_to_section(ci); name2 = cf_section_name2(cs); if (!name2 || (strcmp(name2, "request") != 0)) { cf_log_err(cs, "You must specify 'request' as the destination list"); return -1; } /* * Compile the "update" section. */ { vp_tmpl_rules_t parse_rules = { .allow_foreign = true /* Because we don't know where we'll be called */ }; rcode = map_afrom_cs(ctx, &head, cs, &parse_rules, &parse_rules, unlang_fixup_update, NULL, 128); if (rcode < 0) return -1; /* message already printed */ if (!head) { cf_log_err(cs, "'update' sections cannot be empty"); return -1; } } /* * Rely on "bootstrap" to do sanity checks between 'type * = Access-Request', and 'update' containing passwords. */ memcpy(out, &head, sizeof(head)); return 0; } static void mod_radius_signal(REQUEST *request, void *instance, void *thread, void *ctx, fr_state_signal_t action) { rlm_radius_t const *inst = talloc_get_type_abort_const(instance, rlm_radius_t); rlm_radius_thread_t *t = talloc_get_type_abort(thread, rlm_radius_thread_t); /* * We've been told we're done. Clean up. * * Note that the caller doesn't necessarily need to send * us the signal, as he can just talloc_free(request). * But it is more polite to send a signal, and it allows * the IO modules to do additional debugging if * necessary. */ if (action == FR_SIGNAL_CANCEL) { talloc_free(ctx); return; } /* * We received a duplicate packet, but we're not doing * synchronous proxying. Ignore the dup, and rely on the * IO submodule to time it's own retransmissions. */ if ((action == FR_SIGNAL_DUP) && !inst->synchronous) return; if (!inst->io->signal) return; inst->io->signal(request, inst->io_instance, t->thread_io_ctx, ctx, action); }
/** Convert an 'update' config section into an attribute map. * * Uses 'name2' of section to set default request and lists. * * @param[in] parent to convert to map. * @param[out] head Where to store the head of the map. * @param[in] dst_list_def The default destination list, usually dictated by * the section the module is being called in. * @param[in] src_list_def The default source list, usually dictated by the * section the module is being called in. * @param[in] max number of mappings to process. * @return -1 on error, else 0. */ int radius_attrmap(CONF_SECTION *parent, value_pair_map_t **head, pair_lists_t dst_list_def, pair_lists_t src_list_def, unsigned int max) { const char *cs_list, *p; request_refs_t request_def = REQUEST_CURRENT; CONF_SECTION *cs; CONF_ITEM *ci = cf_sectiontoitem(cs); CONF_PAIR *cp; unsigned int total = 0; value_pair_map_t **tail, *map; *head = NULL; tail = head; if (!parent) return 0; cs = cf_section_sub_find(parent, "update"); if (!cs) return 0; cs_list = p = cf_section_name2(cs); if (cs_list) { request_def = radius_request_name(&p, REQUEST_UNKNOWN); if (request_def == REQUEST_UNKNOWN) { cf_log_err(ci, "Default request specified " "in mapping section is invalid"); return -1; } dst_list_def = fr_str2int(pair_lists, p, PAIR_LIST_UNKNOWN); if (dst_list_def == PAIR_LIST_UNKNOWN) { cf_log_err(ci, "Default list \"%s\" specified " "in mapping section is invalid", p); return -1; } } for (ci = cf_item_find_next(cs, NULL); ci != NULL; ci = cf_item_find_next(cs, ci)) { if (total++ == max) { cf_log_err(ci, "Map size exceeded"); goto error; } if (!cf_item_is_pair(ci)) { cf_log_err(ci, "Entry is not in \"attribute = " "value\" format"); goto error; } cp = cf_itemtopair(ci); map = radius_cp2map(cp, request_def, dst_list_def, REQUEST_CURRENT, src_list_def); if (!map) { goto error; } *tail = map; tail = &(map->next); } return 0; error: radius_mapfree(head); return -1; }
/** Convert CONFIG_PAIR (which may contain refs) to value_pair_map_t. * * Treats the left operand as an attribute reference * @verbatim<request>.<list>.<attribute>@endverbatim * * Treatment of left operand depends on quotation, barewords are treated as * attribute references, double quoted values are treated as expandable strings, * single quoted values are treated as literal strings. * * Return must be freed with radius_mapfree. * * @param[in] cp to convert to map. * @param[in] dst_request_def The default request to insert unqualified * attributes into. * @param[in] dst_list_def The default list to insert unqualified attributes * into. * @param[in] src_request_def The default request to resolve attribute * references in. * @param[in] src_list_def The default list to resolve unqualified attributes * in. * @return value_pair_map_t if successful or NULL on error. */ value_pair_map_t *radius_cp2map(CONF_PAIR *cp, request_refs_t dst_request_def, pair_lists_t dst_list_def, request_refs_t src_request_def, pair_lists_t src_list_def) { value_pair_map_t *map; const char *attr; const char *value; FR_TOKEN type; CONF_ITEM *ci = cf_pairtoitem(cp); if (!cp) return NULL; map = rad_calloc(sizeof(value_pair_map_t)); attr = cf_pair_attr(cp); value = cf_pair_value(cp); if (!value) { cf_log_err(ci, "Missing attribute value"); goto error; } map->dst = radius_attr2tmpl(attr, dst_request_def, dst_list_def); if (!map->dst){ goto error; } /* * Bare words always mean attribute references. */ type = cf_pair_value_type(cp); map->src = type == T_BARE_WORD ? radius_attr2tmpl(value, src_request_def, src_list_def) : radius_str2tmpl(value, type); if (!map->src) { goto error; } map->op = cf_pair_operator(cp); map->ci = ci; /* * Lots of sanity checks for insane people... */ /* * We don't support implicit type conversion */ if (map->dst->da && map->src->da && (map->src->da->type != map->dst->da->type)) { cf_log_err(ci, "Attribute type mismatch"); goto error; } /* * What exactly where you expecting to happen here? */ if ((map->dst->type == VPT_TYPE_ATTR) && (map->src->type == VPT_TYPE_LIST)) { cf_log_err(ci, "Can't copy list into an attribute"); goto error; } switch (map->src->type) { /* * Only += and -= operators are supported for list copy. */ case VPT_TYPE_LIST: switch (map->op) { case T_OP_SUB: case T_OP_ADD: break; default: cf_log_err(ci, "Operator \"%s\" not allowed " "for list copy", fr_int2str(fr_tokens, map->op, "¿unknown?")); goto error; } break; /* * @todo add support for exec expansion. */ case VPT_TYPE_EXEC: cf_log_err(ci, "Exec values are not allowed"); break; default: break; } return map; error: radius_mapfree(&map); return NULL; }
/** 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; } }
/** Convert CONFIG_PAIR (which may contain refs) to value_pair_map_t. * * Treats the left operand as an attribute reference * @verbatim<request>.<list>.<attribute>@endverbatim * * Treatment of left operand depends on quotation, barewords are treated as * attribute references, double quoted values are treated as expandable strings, * single quoted values are treated as literal strings. * * Return must be freed with talloc_free * * @param[in] ctx for talloc * @param[in] cp to convert to map. * @param[in] dst_request_def The default request to insert unqualified * attributes into. * @param[in] dst_list_def The default list to insert unqualified attributes * into. * @param[in] src_request_def The default request to resolve attribute * references in. * @param[in] src_list_def The default list to resolve unqualified attributes * in. * @return value_pair_map_t if successful or NULL on error. */ value_pair_map_t *radius_cp2map(TALLOC_CTX *ctx, CONF_PAIR *cp, request_refs_t dst_request_def, pair_lists_t dst_list_def, request_refs_t src_request_def, pair_lists_t src_list_def) { value_pair_map_t *map; char const *attr; char const *value; FR_TOKEN type; CONF_ITEM *ci = cf_pairtoitem(cp); if (!cp) return NULL; map = talloc_zero(ctx, value_pair_map_t); map->op = cf_pair_operator(cp); map->ci = ci; attr = cf_pair_attr(cp); value = cf_pair_value(cp); if (!value) { cf_log_err(ci, "Missing attribute value"); goto error; } /* * LHS must always be an attribute reference. */ map->dst = radius_attr2tmpl(map, attr, dst_request_def, dst_list_def); if (!map->dst) { cf_log_err(ci, "Syntax error in attribute definition"); goto error; } /* * RHS might be an attribute reference. */ type = cf_pair_value_type(cp); map->src = radius_str2tmpl(map, value, type, src_request_def, src_list_def); if (!map->src) { goto error; } /* * Anal-retentive checks. */ if (debug_flag > 2) { if ((map->dst->type == VPT_TYPE_ATTR) && (*attr != '&')) { WARN("%s[%d]: Please change attribute reference to '&%s %s ...'", cf_pair_filename(cp), cf_pair_lineno(cp), attr, fr_int2str(fr_tokens, map->op, "<INVALID>")); } if ((map->src->type == VPT_TYPE_ATTR) && (*value != '&')) { WARN("%s[%d]: Please change attribute reference to '... %s &%s'", cf_pair_filename(cp), cf_pair_lineno(cp), fr_int2str(fr_tokens, map->op, "<INVALID>"), value); } } /* * Lots of sanity checks for insane people... */ /* * We don't support implicit type conversion, * except for "octets" */ if (map->dst->vpt_da && map->src->vpt_da && (map->src->vpt_da->type != map->dst->vpt_da->type) && (map->src->vpt_da->type != PW_TYPE_OCTETS) && (map->dst->vpt_da->type != PW_TYPE_OCTETS)) { cf_log_err(ci, "Attribute type mismatch"); goto error; } /* * What exactly where you expecting to happen here? */ if ((map->dst->type == VPT_TYPE_ATTR) && (map->src->type == VPT_TYPE_LIST)) { cf_log_err(ci, "Can't copy list into an attribute"); goto error; } /* * Depending on the attribute type, some operators are * disallowed. */ if (map->dst->type == VPT_TYPE_ATTR) { if ((map->op != T_OP_EQ) && (map->op != T_OP_CMP_EQ) && (map->op != T_OP_ADD) && (map->op != T_OP_SUB) && (map->op != T_OP_LE) && (map->op != T_OP_GE) && (map->op != T_OP_CMP_FALSE) && (map->op != T_OP_SET)) { cf_log_err(ci, "Invalid operator for attribute"); goto error; } /* * This will be an error in future versions of * the server. */ if ((map->op == T_OP_CMP_FALSE) && ((map->src->type != VPT_TYPE_LITERAL) || (strcmp(map->src->name, "ANY") != 0))) { WARN("%s[%d] Attribute deletion MUST use '!* ANY'", cf_pair_filename(cp), cf_pair_lineno(cp)); } } if (map->dst->type == VPT_TYPE_LIST) { /* * Only += and :=, and !* operators are supported * for lists. */ switch (map->op) { case T_OP_CMP_FALSE: if ((map->src->type != VPT_TYPE_LITERAL) || (strcmp(map->src->name, "ANY") != 0)) { cf_log_err(ci, "List deletion MUST use '!* ANY'"); goto error; } break; case T_OP_ADD: if ((map->src->type != VPT_TYPE_LIST) && (map->src->type != VPT_TYPE_EXEC)) { cf_log_err(ci, "Invalid source for list '+='"); goto error; } break; case T_OP_SET: if (map->src->type == VPT_TYPE_EXEC) { WARN("%s[%d] Please change ':=' to '=' for list assignment", cf_pair_filename(cp), cf_pair_lineno(cp)); break; } if (map->src->type != VPT_TYPE_LIST) { cf_log_err(ci, "Invalid source for ':=' operator"); goto error; } break; case T_OP_EQ: if (map->src->type != VPT_TYPE_EXEC) { cf_log_err(ci, "Invalid source for '=' operator"); goto error; } break; default: cf_log_err(ci, "Operator \"%s\" not allowed for list assignment", fr_int2str(fr_tokens, map->op, "<INVALID>")); goto error; } } return map; error: talloc_free(map); return NULL; }