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; }
char const *fr_app_io_socket_name(TALLOC_CTX *ctx, fr_app_io_t const *app_io, fr_ipaddr_t const *src_ipaddr, int src_port, fr_ipaddr_t const *dst_ipaddr, int dst_port) { char dst_buf[128], src_buf[128]; /* * Get our name. */ if (fr_ipaddr_is_inaddr_any(dst_ipaddr)) { if (dst_ipaddr->af == AF_INET) { strlcpy(dst_buf, "*", sizeof(dst_buf)); } else { rad_assert(dst_ipaddr->af == AF_INET6); strlcpy(dst_buf, "::", sizeof(dst_buf)); } } else { fr_value_box_snprint(dst_buf, sizeof(dst_buf), fr_box_ipaddr(*dst_ipaddr), 0); } if (!src_ipaddr) { return talloc_typed_asprintf(ctx, "proto_%s server %s port %u", app_io->name, dst_buf, dst_port); } fr_value_box_snprint(src_buf, sizeof(src_buf), fr_box_ipaddr(*src_ipaddr), 0); return talloc_typed_asprintf(ctx, "proto_%s from client %s port %u to server %s port %u", app_io->name, src_buf, src_port, dst_buf, dst_port); }
/* * Debug print a map / VP */ static void debug_map(REQUEST *request, value_pair_map_t const *map, VALUE_PAIR const *vp) { char *value; char buffer[1024]; switch (map->src->type) { /* * Just print the value being assigned */ default: case VPT_TYPE_LITERAL: vp_prints_value(buffer, sizeof(buffer), vp, '\''); value = buffer; break; case VPT_TYPE_XLAT: case VPT_TYPE_XLAT_STRUCT: vp_prints_value(buffer, sizeof(buffer), vp, '"'); value = buffer; break; case VPT_TYPE_DATA: vp_prints_value(buffer, sizeof(buffer), vp, '\''); value = buffer; break; /* * Just printing the value doesn't make sense, but we still * want to know what it was... */ case VPT_TYPE_LIST: vp_prints_value(buffer, sizeof(buffer), vp, '\''); value = talloc_typed_asprintf(request, "&%s%s -> %s", map->src->name, vp->da->name, buffer); break; case VPT_TYPE_ATTR: vp_prints_value(buffer, sizeof(buffer), vp, '\''); value = talloc_typed_asprintf(request, "&%s -> %s", map->src->name, buffer); break; } switch (map->dst->type) { case VPT_TYPE_LIST: RDEBUG("\t%s%s %s %s", map->dst->name, vp->da->name, fr_int2str(fr_tokens, vp->op, "<INVALID>"), value); break; case VPT_TYPE_ATTR: RDEBUG("\t%s %s %s", map->dst->name, fr_int2str(fr_tokens, vp->op, "<INVALID>"), value); break; default: break; } if (value != buffer) talloc_free(value); }
/** 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; }
/** Return the attribute number of an attribute reference * */ static ssize_t xlat_attr_num(TALLOC_CTX *ctx, char **out, UNUSED size_t outlen, UNUSED void const *mod_inst, UNUSED void const *xlat_inst, REQUEST *request, char const *fmt) { VALUE_PAIR *vp; fr_skip_spaces(fmt); if ((xlat_fmt_get_vp(&vp, request, fmt) < 0) || !vp) return 0; *out = talloc_typed_asprintf(ctx, "%i", vp->da->attr); return talloc_array_length(*out) - 1; }
/** Return the vendor number of an attribute reference * */ static ssize_t xlat_vendor_num(TALLOC_CTX *ctx, char **out, UNUSED size_t outlen, UNUSED void const *mod_inst, UNUSED void const *xlat_inst, REQUEST *request, char const *fmt) { VALUE_PAIR *vp; while (isspace((int) *fmt)) fmt++; if ((xlat_fmt_get_vp(&vp, request, fmt) < 0) || !vp) return 0; *out = talloc_typed_asprintf(ctx, "%i", fr_dict_vendor_num_by_da(vp->da)); return talloc_array_length(*out) - 1; }
int fb_error(rlm_sql_firebird_conn_t *conn) { ISC_SCHAR error[2048]; /* Only 1024 bytes should be written to this, but were playing it extra safe */ ISC_STATUS *pstatus; conn->sql_code = 0; /* * Free any previous errors. */ TALLOC_FREE(conn->error); /* * Check if the status array contains an error */ if (IS_ISC_ERROR(conn->status)) { conn->sql_code = isc_sqlcode(conn->status); /* * pstatus is a pointer into the status array which is * advanced by isc_interprete. It's initialised to the * first element of the status array. */ pstatus = &conn->status[0]; /* * It's deprecated because the size of the buffer isn't * passed and this isn't safe. But as were passing a very * large buffer it's unlikely this will be an issue, and * allows us to maintain compatibility with the interbase * API. */ isc_interprete(&error[0], &pstatus); conn->error = talloc_typed_asprintf(conn, "%s. ", &error[0]); while (isc_interprete(&error[0], &pstatus)) { conn->error = talloc_asprintf_append(conn->error, "%s. ", &error[0]); } memset(&conn->status, 0, sizeof(conn->status)); } return conn->sql_code; }
static int mod_instantiate(CONF_SECTION *conf, void *instance) { rlm_sql_t *inst = instance; /* * Hack... */ inst->config = &inst->myconfig; inst->cs = conf; inst->config->xlat_name = cf_section_name2(conf); if (!inst->config->xlat_name) { inst->config->xlat_name = cf_section_name1(conf); } else { char *group_name; DICT_ATTR const *da; ATTR_FLAGS flags; /* * Allocate room for <instance>-SQL-Group */ group_name = talloc_typed_asprintf(inst, "%s-SQL-Group", inst->config->xlat_name); DEBUG("rlm_sql (%s): Creating new attribute %s", inst->config->xlat_name, group_name); memset(&flags, 0, sizeof(flags)); if (dict_addattr(group_name, -1, 0, PW_TYPE_STRING, flags) < 0) { ERROR("rlm_sql (%s): Failed to create " "attribute %s: %s", inst->config->xlat_name, group_name, fr_strerror()); return -1; } da = dict_attrbyname(group_name); if (!da) { ERROR("rlm_sql (%s): Failed to create " "attribute %s", inst->config->xlat_name, group_name); return -1; } if (inst->config->groupmemb_query && inst->config->groupmemb_query[0]) { DEBUG("rlm_sql (%s): Registering sql_groupcmp for %s", inst->config->xlat_name, group_name); paircompare_register(da, dict_attrbyvalue(PW_USER_NAME, 0), false, sql_groupcmp, inst); } } rad_assert(inst->config->xlat_name); /* * If the configuration parameters can't be parsed, then fail. */ if ((parse_sub_section(conf, inst, &inst->config->accounting, RLM_COMPONENT_ACCT) < 0) || (parse_sub_section(conf, inst, &inst->config->postauth, RLM_COMPONENT_POST_AUTH) < 0)) { cf_log_err_cs(conf, "Invalid configuration"); return -1; } /* * Cache the SQL-User-Name DICT_ATTR, so we can be slightly * more efficient about creating SQL-User-Name attributes. */ inst->sql_user = dict_attrbyname("SQL-User-Name"); if (!inst->sql_user) { return -1; } /* * Export these methods, too. This avoids RTDL_GLOBAL. */ inst->sql_set_user = sql_set_user; inst->sql_get_socket = sql_get_socket; inst->sql_release_socket = sql_release_socket; inst->sql_escape_func = sql_escape_func; inst->sql_query = rlm_sql_query; inst->sql_select_query = rlm_sql_select_query; inst->sql_fetch_row = rlm_sql_fetch_row; /* * Register the SQL xlat function */ xlat_register(inst->config->xlat_name, sql_xlat, sql_escape_func, inst); /* * Sanity check for crazy people. */ if (strncmp(inst->config->sql_driver_name, "rlm_sql_", 8) != 0) { ERROR("rlm_sql (%s): \"%s\" is NOT an SQL driver!", inst->config->xlat_name, inst->config->sql_driver_name); return -1; } /* * Load the appropriate driver for our database */ inst->handle = lt_dlopenext(inst->config->sql_driver_name); if (!inst->handle) { ERROR("Could not link driver %s: %s", inst->config->sql_driver_name, dlerror()); ERROR("Make sure it (and all its dependent libraries!)" "are in the search path of your system's ld"); return -1; } inst->module = (rlm_sql_module_t *) dlsym(inst->handle, inst->config->sql_driver_name); if (!inst->module) { ERROR("Could not link symbol %s: %s", inst->config->sql_driver_name, dlerror()); return -1; } if (inst->module->mod_instantiate) { CONF_SECTION *cs; char const *name; name = strrchr(inst->config->sql_driver_name, '_'); if (!name) { name = inst->config->sql_driver_name; } else { name++; } cs = cf_section_sub_find(conf, name); if (!cs) { cs = cf_section_alloc(conf, name, NULL); if (!cs) { return -1; } } /* * It's up to the driver to register a destructor */ if (inst->module->mod_instantiate(cs, inst->config) < 0) { return -1; } } inst->lf = fr_logfile_init(inst); if (!inst->lf) { cf_log_err_cs(conf, "Failed creating log file context"); return -1; } INFO("rlm_sql (%s): Driver %s (module %s) loaded and linked", inst->config->xlat_name, inst->config->sql_driver_name, inst->module->name); /* * Initialise the connection pool for this instance */ INFO("rlm_sql (%s): Attempting to connect to database \"%s\"", inst->config->xlat_name, inst->config->sql_db); if (sql_socket_pool_init(inst) < 0) return -1; if (inst->config->groupmemb_query && inst->config->groupmemb_query[0]) { paircompare_register(dict_attrbyvalue(PW_SQL_GROUP, 0), dict_attrbyvalue(PW_USER_NAME, 0), false, sql_groupcmp, inst); } if (inst->config->do_clients) { if (generate_sql_clients(inst) == -1){ ERROR("Failed to load clients from SQL"); return -1; } } return RLM_MODULE_OK; }
static int mod_instantiate(CONF_SECTION *conf, void *instance) { rlm_sql_t *inst = instance; /* * Hack... */ inst->config = &inst->myconfig; inst->cs = conf; inst->config->xlat_name = cf_section_name2(conf); if (!inst->config->xlat_name) { inst->config->xlat_name = cf_section_name1(conf); } else { char *group_name; DICT_ATTR const *da; ATTR_FLAGS flags; /* * Allocate room for <instance>-SQL-Group */ group_name = talloc_typed_asprintf(inst, "%s-SQL-Group", inst->config->xlat_name); DEBUG("rlm_sql (%s): Creating new attribute %s", inst->config->xlat_name, group_name); memset(&flags, 0, sizeof(flags)); if (dict_addattr(group_name, -1, 0, PW_TYPE_STRING, flags) < 0) { ERROR("rlm_sql (%s): Failed to create " "attribute %s: %s", inst->config->xlat_name, group_name, fr_strerror()); return -1; } da = dict_attrbyname(group_name); if (!da) { ERROR("rlm_sql (%s): Failed to create " "attribute %s", inst->config->xlat_name, group_name); return -1; } if (inst->config->groupmemb_query) { DEBUG("rlm_sql (%s): Registering sql_groupcmp for %s", inst->config->xlat_name, group_name); paircompare_register(da, dict_attrbyvalue(PW_USER_NAME, 0), false, sql_groupcmp, inst); } } rad_assert(inst->config->xlat_name); /* * Sanity check for crazy people. */ if (strncmp(inst->config->sql_driver_name, "rlm_sql_", 8) != 0) { ERROR("rlm_sql (%s): \"%s\" is NOT an SQL driver!", inst->config->xlat_name, inst->config->sql_driver_name); return -1; } /* * We need authorize_group_check_query or authorize_group_reply_query * if group_membership_query is set. * * Or we need group_membership_query if authorize_group_check_query or * authorize_group_reply_query is set. */ if (!inst->config->groupmemb_query) { if (inst->config->authorize_group_check_query) { WARN("rlm_sql (%s): Ignoring authorize_group_reply_query as group_membership_query " "is not configured", inst->config->xlat_name); } if (inst->config->authorize_group_reply_query) { WARN("rlm_sql (%s): Ignoring authorize_group_check_query as group_membership_query " "is not configured", inst->config->xlat_name); } } else { if (!inst->config->authorize_group_check_query) { ERROR("rlm_sql (%s): authorize_group_check_query must be configured as group_membership_query " "is configured", inst->config->xlat_name); return -1; } if (!inst->config->authorize_group_reply_query) { ERROR("rlm_sql (%s): authorize_group_reply_query must be configured as group_membership_query " "is configured", inst->config->xlat_name); return -1; } } /* * This will always exist, as cf_section_parse_init() * will create it if it doesn't exist. However, the * "reference" config item won't exist in an auto-created * configuration. So if that doesn't exist, we ignore * the whole subsection. */ inst->config->accounting.cs = cf_section_sub_find(conf, "accounting"); inst->config->accounting.reference_cp = (cf_pair_find(inst->config->accounting.cs, "reference") != NULL); inst->config->postauth.cs = cf_section_sub_find(conf, "post-auth"); inst->config->postauth.reference_cp = (cf_pair_find(inst->config->postauth.cs, "reference") != NULL); /* * Cache the SQL-User-Name DICT_ATTR, so we can be slightly * more efficient about creating SQL-User-Name attributes. */ inst->sql_user = dict_attrbyname("SQL-User-Name"); if (!inst->sql_user) { return -1; } /* * Export these methods, too. This avoids RTDL_GLOBAL. */ inst->sql_set_user = sql_set_user; inst->sql_escape_func = sql_escape_func; inst->sql_query = rlm_sql_query; inst->sql_select_query = rlm_sql_select_query; inst->sql_fetch_row = rlm_sql_fetch_row; /* * Register the SQL xlat function */ xlat_register(inst->config->xlat_name, sql_xlat, sql_escape_func, inst); /* * Load the appropriate driver for our database */ inst->handle = lt_dlopenext(inst->config->sql_driver_name); if (!inst->handle) { ERROR("Could not link driver %s: %s", inst->config->sql_driver_name, dlerror()); ERROR("Make sure it (and all its dependent libraries!) are in the search path of your system's ld"); return -1; } inst->module = (rlm_sql_module_t *) dlsym(inst->handle, inst->config->sql_driver_name); if (!inst->module) { ERROR("Could not link symbol %s: %s", inst->config->sql_driver_name, dlerror()); return -1; } if (inst->module->mod_instantiate) { CONF_SECTION *cs; char const *name; name = strrchr(inst->config->sql_driver_name, '_'); if (!name) { name = inst->config->sql_driver_name; } else { name++; } cs = cf_section_sub_find(conf, name); if (!cs) { cs = cf_section_alloc(conf, name, NULL); if (!cs) { return -1; } } /* * It's up to the driver to register a destructor */ if (inst->module->mod_instantiate(cs, inst->config) < 0) { return -1; } } inst->ef = exfile_init(inst, 64, 30); if (!inst->ef) { cf_log_err_cs(conf, "Failed creating log file context"); return -1; } INFO("rlm_sql (%s): Driver %s (module %s) loaded and linked", inst->config->xlat_name, inst->config->sql_driver_name, inst->module->name); /* * Initialise the connection pool for this instance */ INFO("rlm_sql (%s): Attempting to connect to database \"%s\"", inst->config->xlat_name, inst->config->sql_db); inst->pool = fr_connection_pool_module_init(inst->cs, inst, mod_conn_create, NULL, NULL); if (!inst->pool) return -1; if (inst->config->groupmemb_query) { paircompare_register(dict_attrbyvalue(PW_SQL_GROUP, 0), dict_attrbyvalue(PW_USER_NAME, 0), false, sql_groupcmp, inst); } if (inst->config->do_clients) { if (generate_sql_clients(inst) == -1){ ERROR("Failed to load clients from SQL"); return -1; } } return RLM_MODULE_OK; }
/** 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; } }
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; }
/** Convert multiple group names into a DNs * * Given an array of group names, builds a filter matching all names, then retrieves all group objects * and stores the DN associated with each group object. * * @param[in] inst rlm_ldap configuration. * @param[in] request Current request. * @param[in,out] pconn to use. May change as this function calls functions which auto re-connect. * @param[in] names to covert to DNs (NULL terminated). * @param[out] out Where to write the DNs. DNs must be freed with ldap_memfree(). Will be NULL terminated. * @param[in] outlen Size of out. * @return One of the RLM_MODULE_* values. */ static rlm_rcode_t rlm_ldap_group_name2dn(ldap_instance_t const *inst, REQUEST *request, ldap_handle_t **pconn, char **names, char **out, size_t outlen) { rlm_rcode_t rcode = RLM_MODULE_OK; ldap_rcode_t status; int ldap_errno; unsigned int name_cnt = 0; unsigned int entry_cnt; char const *attrs[] = { NULL }; LDAPMessage *result = NULL, *entry; char **name = names; char **dn = out; char buffer[LDAP_MAX_GROUP_NAME_LEN + 1]; char *filter; *dn = NULL; if (!*names) { return RLM_MODULE_OK; } if (!inst->groupobj_name_attr) { REDEBUG("Told to convert group names to DNs but missing 'group.name_attribute' directive"); return RLM_MODULE_INVALID; } RDEBUG("Converting group name(s) to group DN(s)"); /* * It'll probably only save a few ms in network latency, but it means we can send a query * for the entire group list at once. */ filter = talloc_typed_asprintf(request, "%s%s%s", inst->groupobj_filter ? "(&" : "", inst->groupobj_filter ? inst->groupobj_filter : "", names[0] && names[1] ? "(|" : ""); while (*name) { rlm_ldap_escape_func(request, buffer, sizeof(buffer), *name++, NULL); filter = talloc_asprintf_append_buffer(filter, "(%s=%s)", inst->groupobj_name_attr, buffer); name_cnt++; } filter = talloc_asprintf_append_buffer(filter, "%s%s", inst->groupobj_filter ? ")" : "", names[0] && names[1] ? ")" : ""); status = rlm_ldap_search(inst, request, pconn, inst->groupobj_base_dn, inst->groupobj_scope, filter, attrs, &result); switch (status) { case LDAP_PROC_SUCCESS: break; case LDAP_PROC_NO_RESULT: RDEBUG("Tried to resolve group name(s) to DNs but got no results"); goto finish; default: rcode = RLM_MODULE_FAIL; goto finish; } entry_cnt = ldap_count_entries((*pconn)->handle, result); if (entry_cnt > name_cnt) { REDEBUG("Number of DNs exceeds number of names, group and/or dn should be more restrictive"); rcode = RLM_MODULE_INVALID; goto finish; } if (entry_cnt > (outlen - 1)) { REDEBUG("Number of DNs exceeds limit (%zu)", outlen - 1); rcode = RLM_MODULE_INVALID; goto finish; } if (entry_cnt < name_cnt) { RWDEBUG("Got partial mapping of group names (%i) to DNs (%i), membership information may be incomplete", name_cnt, entry_cnt); } entry = ldap_first_entry((*pconn)->handle, result); if (!entry) { ldap_get_option((*pconn)->handle, LDAP_OPT_RESULT_CODE, &ldap_errno); REDEBUG("Failed retrieving entry: %s", ldap_err2string(ldap_errno)); rcode = RLM_MODULE_FAIL; goto finish; } do { *dn = ldap_get_dn((*pconn)->handle, entry); if (!*dn) { ldap_get_option((*pconn)->handle, LDAP_OPT_RESULT_CODE, &ldap_errno); REDEBUG("Retrieving object DN from entry failed: %s", ldap_err2string(ldap_errno)); rcode = RLM_MODULE_FAIL; goto finish; } rlm_ldap_normalise_dn(*dn, *dn); RDEBUG("Got group DN \"%s\"", *dn); dn++; } while((entry = ldap_next_entry((*pconn)->handle, entry))); *dn = NULL; finish: talloc_free(filter); if (result) { ldap_msgfree(result); } /* * Be nice and cleanup the output array if we error out. */ if (rcode != RLM_MODULE_OK) { dn = out; while(*dn) ldap_memfree(*dn++); *dn = NULL; } return rcode; }
/** Send a log message to its destination, possibly including fields from the request * * @param[in] type of log message, #L_ERR, #L_WARN, #L_INFO, #L_DBG. * @param[in] lvl Minimum required server or request level to output this message. * @param[in] request The current request. * @param[in] msg with printf style substitution tokens. * @param[in] ap Substitution arguments. * @param[in] uctx The #fr_log_t specifying the destination for log messages. */ void vlog_request(fr_log_type_t type, fr_log_lvl_t lvl, REQUEST *request, char const *msg, va_list ap, void *uctx) { char const *filename; FILE *fp = NULL; char *p; char const *extra = ""; uint8_t unlang_indent, module_indent; va_list aq; char *msg_prefix = NULL; char *msg_module = NULL; char *msg_exp = NULL; fr_log_t *log_dst = uctx; /* * No output means no output. */ if (!log_dst) return; filename = log_dst->file; /* * Debug messages get treated specially. */ if ((type & L_DBG) != 0) { if (!log_debug_enabled(type, lvl, request)) return; /* * If we're debugging to a file, then use that. * * @todo: have fr_vlog() take a fr_log_t*, so * that we can cache the opened descriptor, and * we don't need to re-open it on every log * message. */ switch (log_dst->dst) { case L_DST_FILES: fp = fopen(log_dst->file, "a"); if (!fp) goto finish; break; #if defined(HAVE_FOPENCOOKIE) || defined (HAVE_FUNOPEN) case L_DST_EXTRA: { # ifdef HAVE_FOPENCOOKIE cookie_io_functions_t io; /* * These must be set separately as they have different prototypes. */ io.read = NULL; io.seek = NULL; io.close = NULL; io.write = log_dst->cookie_write; fp = fopencookie(log_dst->cookie, "w", io); # else fp = funopen(log_dst->cookie, NULL, log_dst->cookie_write, NULL, NULL); # endif if (!fp) goto finish; } break; #endif default: break; } goto print_msg; } if (filename) { char *exp; log_dst_t *dst; dst = request->log.dst; /* * Prevent infinitely recursive calls if * xlat_aeval attempts to write to the request log. */ request->log.dst = NULL; /* * This is SLOW! Doing it for every log message * in every request is NOT recommended! */ if (xlat_aeval(request, &exp, request, filename, rad_filename_escape, NULL) < 0) return; /* * Restore the original logging function */ request->log.dst = dst; /* * Ensure the directory structure exists, for * where we're going to write the log file. */ p = strrchr(exp, FR_DIR_SEP); if (p) { *p = '\0'; if (rad_mkdir(exp, S_IRWXU, -1, -1) < 0) { ERROR("Failed creating %s: %s", exp, fr_syserror(errno)); talloc_free(exp); return; } *p = FR_DIR_SEP; } fp = fopen(exp, "a"); talloc_free(exp); } print_msg: /* * Request prefix i.e. * * (0) <msg> */ if ((request->seq_start == 0) || (request->number == request->seq_start)) { msg_prefix = talloc_typed_asprintf(request, "(%s) ", request->name); } else { msg_prefix = talloc_typed_asprintf(request, "(%s,%" PRIu64 ") ", request->name, request->seq_start); } /* * Make sure the indent isn't set to something crazy */ unlang_indent = request->log.unlang_indent > sizeof(spaces) - 1 ? sizeof(spaces) - 1 : request->log.unlang_indent; module_indent = request->log.module_indent > sizeof(spaces) - 1 ? sizeof(spaces) - 1 : request->log.module_indent; /* * Module name and indentation i.e. * * test - <msg> */ if (request->module) { msg_module = talloc_typed_asprintf(request, "%s - %.*s", request->module, module_indent, spaces); } /* * If we don't copy the original ap we get a segfault from vasprintf. This is apparently * due to ap sometimes being implemented with a stack offset which is invalidated if * ap is passed into another function. See here: * http://julipedia.meroh.net/2011/09/using-vacopy-to-safely-pass-ap.html * * I don't buy that explanation, but doing a va_copy here does prevent SEGVs seen when * running unit tests which generate errors under CI. */ va_copy(aq, ap); msg_exp = fr_vasprintf(request, msg, aq); va_end(aq); /* * Logging to a file descriptor */ if (fp) { char time_buff[64]; /* The current timestamp */ time_t timeval; timeval = time(NULL); #ifdef HAVE_GMTIME_R if (log_dates_utc) { struct tm utc; gmtime_r(&timeval, &utc); ASCTIME_R(&utc, time_buff, sizeof(time_buff)); } else #endif { CTIME_R(&timeval, time_buff, sizeof(time_buff)); } /* * Strip trailing new lines */ p = strrchr(time_buff, '\n'); if (p) p[0] = '\0'; fprintf(fp, "%s" "%s : " "%s" "%.*s" "%s" "%s" "\n", msg_prefix, time_buff, fr_int2str(fr_log_levels, type, ""), unlang_indent, spaces, msg_module ? msg_module : "", msg_exp); fclose(fp); goto finish; } /* * Logging everywhere else */ if (!DEBUG_ENABLED3) switch (type) { case L_DBG_WARN: extra = "WARNING: "; type = L_DBG_WARN_REQ; break; case L_DBG_ERR: extra = "ERROR: "; type = L_DBG_ERR_REQ; break; default: break; }; log_always(log_dst, type, "%s" "%.*s" "%s" "%s" "%s", msg_prefix, unlang_indent, spaces, msg_module ? msg_module : "", extra, msg_exp); finish: talloc_free(msg_exp); talloc_free(msg_module); talloc_free(msg_prefix); }