/* * 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); }
/* * 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); }
/** 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; }
/* * 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; }
static int do_linelog(void *instance, REQUEST *request) { int fd = -1; char buffer[4096]; char line[1024]; rlm_linelog_t *inst = (rlm_linelog_t*) instance; const char *value = inst->line; if (inst->reference) { CONF_ITEM *ci; CONF_PAIR *cp; radius_xlat(line + 1, sizeof(line) - 2, inst->reference, request, linelog_escape_func); line[0] = '.'; /* force to be in current section */ /* * Don't allow it to go back up */ if (line[1] == '.') goto do_log; ci = cf_reference_item(NULL, inst->cs, line); if (!ci) { RDEBUG2("No such entry \"%s\"", line); return RLM_MODULE_NOOP; } if (!cf_item_is_pair(ci)) { RDEBUG2("Entry \"%s\" is not a variable assignment ", line); goto do_log; } cp = cf_itemtopair(ci); value = cf_pair_value(cp); if (!value) { RDEBUG2("Entry \"%s\" has no value", line); goto do_log; } /* * Value exists, but is empty. Don't log anything. */ if (!*value) return RLM_MODULE_OK; } do_log: /* * FIXME: Check length. */ if (strcmp(inst->filename, "syslog") != 0) { radius_xlat(buffer, sizeof(buffer), inst->filename, request, NULL); fd = open(buffer, O_WRONLY | O_APPEND | O_CREAT, 0600); if (fd == -1) { radlog(L_ERR, "rlm_linelog: Failed to open %s: %s", buffer, strerror(errno)); return RLM_MODULE_FAIL; } } /* * FIXME: Check length. */ radius_xlat(line, sizeof(line) - 1, value, request, linelog_escape_func); if (fd >= 0) { strcat(line, "\n"); write(fd, line, strlen(line)); close(fd); #ifdef HAVE_SYSLOG_H } else { syslog(LOG_INFO, "%s", line); #endif } return RLM_MODULE_OK; }
static rlm_rcode_t do_linelog(void *instance, REQUEST *request) { int fd = -1; char buffer[4096]; char *p; char line[1024]; rlm_linelog_t *inst = (rlm_linelog_t*) instance; const char *value = inst->line; #ifdef HAVE_GRP_H gid_t gid; struct group *grp; char *endptr; #endif if (inst->reference) { CONF_ITEM *ci; CONF_PAIR *cp; radius_xlat(line + 1, sizeof(line) - 2, inst->reference, request, linelog_escape_func, NULL); line[0] = '.'; /* force to be in current section */ /* * Don't allow it to go back up */ if (line[1] == '.') goto do_log; ci = cf_reference_item(NULL, inst->cs, line); if (!ci) { RDEBUG2("No such entry \"%s\"", line); return RLM_MODULE_NOOP; } if (!cf_item_is_pair(ci)) { RDEBUG2("Entry \"%s\" is not a variable assignment ", line); goto do_log; } cp = cf_itemtopair(ci); value = cf_pair_value(cp); if (!value) { RDEBUG2("Entry \"%s\" has no value", line); goto do_log; } /* * Value exists, but is empty. Don't log anything. */ if (!*value) return RLM_MODULE_OK; } do_log: /* * FIXME: Check length. */ if (strcmp(inst->filename, "syslog") != 0) { radius_xlat(buffer, sizeof(buffer), inst->filename, request, NULL, NULL); /* check path and eventually create subdirs */ p = strrchr(buffer,'/'); if (p) { *p = '\0'; if (rad_mkdir(buffer, 0700) < 0) { radlog_request(L_ERR, 0, request, "rlm_linelog: Failed to create directory %s: %s", buffer, strerror(errno)); return RLM_MODULE_FAIL; } *p = '/'; } fd = open(buffer, O_WRONLY | O_APPEND | O_CREAT, inst->permissions); if (fd == -1) { radlog(L_ERR, "rlm_linelog: Failed to open %s: %s", buffer, strerror(errno)); return RLM_MODULE_FAIL; } #ifdef HAVE_GRP_H if (inst->group != NULL) { gid = strtol(inst->group, &endptr, 10); if (*endptr != '\0') { grp = getgrnam(inst->group); if (grp == NULL) { RDEBUG2("Unable to find system group \"%s\"", inst->group); goto skip_group; } gid = grp->gr_gid; } if (chown(buffer, -1, gid) == -1) { RDEBUG2("Unable to change system group of \"%s\"", buffer); } } #endif } skip_group: /* * FIXME: Check length. */ radius_xlat(line, sizeof(line) - 1, value, request, linelog_escape_func, NULL); if (fd >= 0) { strcat(line, "\n"); write(fd, line, strlen(line)); close(fd); #ifdef HAVE_SYSLOG_H } else { syslog(inst->facility, "%s", line); #endif } return RLM_MODULE_OK; }
/** Execute a trigger - call an executable to process an event * * @param request The current request. * @param cs to search for triggers in. If not NULL, only the portion after the last '.' * in name is used for the trigger. If cs is NULL, the entire name is used to find * the trigger in the global trigger section. * @param name the path relative to the global trigger section ending in the trigger name * e.g. module.ldap.pool.start. * @param quench whether to rate limit triggers. */ void exec_trigger(REQUEST *request, CONF_SECTION *cs, char const *name, bool quench) { CONF_SECTION *subcs; CONF_ITEM *ci; CONF_PAIR *cp; char const *attr; char const *value; VALUE_PAIR *vp; bool alloc = false; /* * Use global "trigger" section if no local config is given. */ if (!cs) { cs = exec_trigger_main; attr = name; } else { /* * Try to use pair name, rather than reference. */ attr = strrchr(name, '.'); if (attr) { attr++; } else { attr = name; } } /* * Find local "trigger" subsection. If it isn't found, * try using the global "trigger" section, and reset the * reference to the full path, rather than the sub-path. */ subcs = cf_section_sub_find(cs, "trigger"); if (!subcs && exec_trigger_main && (cs != exec_trigger_main)) { subcs = exec_trigger_subcs; attr = name; } if (!subcs) return; ci = cf_reference_item(subcs, exec_trigger_main, attr); if (!ci) { ERROR("No such item in trigger section: %s", attr); return; } if (!cf_item_is_pair(ci)) { ERROR("Trigger is not a configuration variable: %s", attr); return; } cp = cf_item_to_pair(ci); if (!cp) return; value = cf_pair_value(cp); if (!value) { ERROR("Trigger has no value: %s", name); return; } /* * May be called for Status-Server packets. */ vp = NULL; if (request && request->packet) vp = request->packet->vps; /* * Perform periodic quenching. */ if (quench) { time_t *last_time; last_time = cf_data_find(cs, value); if (!last_time) { last_time = rad_malloc(sizeof(*last_time)); *last_time = 0; if (cf_data_add(cs, value, last_time, time_free) < 0) { free(last_time); last_time = NULL; } } /* * Send the quenched traps at most once per second. */ if (last_time) { time_t now = time(NULL); if (*last_time == now) return; *last_time = now; } } /* * radius_exec_program always needs a request. */ if (!request) { request = request_alloc(NULL); alloc = true; } DEBUG("Trigger %s -> %s", name, value); radius_exec_program(request, NULL, 0, NULL, request, value, vp, false, true, EXEC_TIMEOUT); if (alloc) talloc_free(request); }
/* * 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) { int ret = RLM_MODULE_OK; rlm_sql_handle_t *handle = NULL; int sql_ret; int numaffected = 0; CONF_ITEM *item; CONF_PAIR *pair; const char *attr = NULL; const char *value; char path[MAX_STRING_LEN]; char querystr[MAX_QUERY_LEN]; char *p = path; rad_assert(section); if (section->reference[0] != '.') *p++ = '.'; if (!radius_xlat(p, (sizeof(path) - (p - path)) - 1, section->reference, request, NULL, NULL)) return RLM_MODULE_FAIL; item = cf_reference_item(NULL, section->cs, path); if (!item) return RLM_MODULE_FAIL; if (cf_item_is_section(item)){ radlog(L_ERR, "Sections are not supported as references"); return RLM_MODULE_FAIL; } pair = cf_itemtopair(item); attr = cf_pair_attr(pair); RDEBUG2("Using query template '%s'", attr); handle = sql_get_socket(inst); if (handle == NULL) return RLM_MODULE_FAIL; sql_set_user(inst, request, NULL); while (TRUE) { value = cf_pair_value(pair); if (!value) { RDEBUG("Ignoring null query"); ret = RLM_MODULE_NOOP; goto release; } radius_xlat(querystr, sizeof(querystr), value, request, sql_escape_func, inst); if (!*querystr) { RDEBUG("Ignoring null query"); ret = RLM_MODULE_NOOP; goto release; } rlm_sql_query_log(inst, request, section, querystr); /* * 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 SQL_DOWN 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, querystr); if (sql_ret == SQL_DOWN) return RLM_MODULE_FAIL; rad_assert(handle); /* * Assume all other errors are incidental, and just meant our * operation failed and its not a client or SQL syntax error. */ if (sql_ret == 0) { numaffected = (inst->module->sql_affected_rows) (handle, inst->config); if (numaffected > 0) break; 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"); ret = RLM_MODULE_NOOP; goto release; } RDEBUG("Trying next query..."); } (inst->module->sql_finish_query)(handle, inst->config); release: sql_release_socket(inst, handle); return ret; }
void exec_trigger(REQUEST *request, CONF_SECTION *cs, const char *name) { CONF_SECTION *subcs; CONF_ITEM *ci; CONF_PAIR *cp; const char *attr; const char *value; VALUE_PAIR *vp; /* * Use global "trigger" section if no local config is given. */ if (!cs) { cs = mainconfig.config; attr = name; } else { /* * Try to use pair name, rather than reference. */ attr = strrchr(name, '.'); if (attr) { attr++; } else { attr = name; } } /* * Find local "trigger" subsection. If it isn't found, * try using the global "trigger" section, and reset the * reference to the full path, rather than the sub-path. */ subcs = cf_section_sub_find(cs, "trigger"); if (!subcs && (cs != mainconfig.config)) { subcs = cf_section_sub_find(mainconfig.config, "trigger"); attr = name; } if (!subcs) { DEBUG3("No trigger subsection: ignoring trigger %s", name); return; } ci = cf_reference_item(subcs, mainconfig.config, attr); if (!ci) { DEBUG3("No such item in trigger section: %s", attr); return; } if (!cf_item_is_pair(ci)) { DEBUG2("Trigger is not a configuration variable: %s", attr); return; } cp = cf_itemtopair(ci); if (!cp) return; value = cf_pair_value(cp); if (!value) { DEBUG2("Trigger has no value: %s", name); return; } /* * May be called for Status-Server packets. */ vp = NULL; if (request && request->packet) vp = request->packet->vps; DEBUG("Trigger %s -> %s", name, value); radius_exec_program(value, request, 0, NULL, 0, vp, NULL, 1); }
static rlm_rcode_t mod_do_linelog(void *instance, REQUEST *request) { int fd = -1; linelog_conn_t *conn; struct timeval *timeout = NULL; char buff[4096]; char *p = buff; linelog_instance_t *inst = instance; char const *value; vp_tmpl_t empty, *vpt = NULL, *vpt_p = NULL; rlm_rcode_t rcode = RLM_MODULE_OK; ssize_t slen; struct iovec vector_s[2]; struct iovec *vector = NULL, *vector_p; size_t vector_len; bool with_delim; buff[0] = '.'; /* force to be in current section */ buff[1] = '\0'; buff[2] = '\0'; /* * Expand log_ref to a config path, using the module * configuration section as the root. */ if (inst->log_ref) { CONF_ITEM *ci; CONF_PAIR *cp; char const *tmpl_str; if (tmpl_expand(NULL, buff + 1, sizeof(buff) - 1, request, inst->log_ref, linelog_escape_func, NULL) < 0) { return RLM_MODULE_FAIL; } if (buff[1] == '.') p++; /* * Don't go back up. */ if (buff[2] == '.') { REDEBUG("Invalid path \"%s\"", p); return RLM_MODULE_FAIL; } ci = cf_reference_item(NULL, inst->cs, p); if (!ci) { RDEBUG2("Path \"%s\" doesn't exist", p); goto default_msg; } if (!cf_item_is_pair(ci)) { REDEBUG("Path \"%s\" resolves to a section (should be a pair)", p); return RLM_MODULE_FAIL; } cp = cf_item_to_pair(ci); tmpl_str = cf_pair_value(cp); if (!tmpl_str || (tmpl_str[0] == '\0')) { RDEBUG2("Path \"%s\" resolves to an empty config pair", p); vpt_p = tmpl_init(&empty, TMPL_TYPE_LITERAL, "", 0); goto build_vector; } /* * Alloc a template from the value of the CONF_PAIR * using request as the context (which will hopefully avoid a malloc). */ slen = tmpl_afrom_str(request, &vpt, tmpl_str, talloc_array_length(tmpl_str) - 1, cf_pair_value_type(cp), REQUEST_CURRENT, PAIR_LIST_REQUEST, true); if (slen <= 0) { REMARKER(tmpl_str, -slen, fr_strerror()); return RLM_MODULE_FAIL; } vpt_p = vpt; } else { default_msg: /* * Use the default format string */ if (!inst->log_src) { RDEBUG2("No default message configured"); return RLM_MODULE_NOOP; } /* * Use the pre-parsed format template */ RDEBUG2("Using default message"); vpt_p = inst->log_src; } build_vector: with_delim = (inst->log_dst != LINELOG_DST_SYSLOG) && (inst->delimiter_len > 0); /* * Log all the things! */ switch (vpt_p->type) { case TMPL_TYPE_ATTR: case TMPL_TYPE_LIST: { #define VECTOR_INCREMENT 20 vp_cursor_t cursor; VALUE_PAIR *vp; int alloced = VECTOR_INCREMENT, i; MEM(vector = talloc_array(request, struct iovec, alloced)); for (vp = tmpl_cursor_init(NULL, &cursor, request, vpt_p), i = 0; vp; vp = tmpl_cursor_next(&cursor, vpt_p), i++) { /* need extra for line terminator */ if ((with_delim && ((i + 1) >= alloced)) || (i >= alloced)) { alloced += VECTOR_INCREMENT; MEM(vector = talloc_realloc(request, vector, struct iovec, alloced)); } switch (vp->da->type) { case PW_TYPE_OCTETS: case PW_TYPE_STRING: vector[i].iov_base = vp->data.ptr; vector[i].iov_len = vp->vp_length; break; default: p = vp_aprints_value(vector, vp, '\0'); vector[i].iov_base = p; vector[i].iov_len = talloc_array_length(p) - 1; break; } /* * Add the line delimiter string */ if (with_delim) { i++; memcpy(&vector[i].iov_base, &(inst->delimiter), sizeof(vector[i].iov_base)); vector[i].iov_len = inst->delimiter_len; } } vector_p = vector; vector_len = i; } break; /* * Log a single thing. */ default: slen = tmpl_expand(&value, buff, sizeof(buff), request, vpt_p, linelog_escape_func, NULL); if (slen < 0) { rcode = RLM_MODULE_FAIL; goto finish; } /* iov_base is not declared as const *sigh* */ memcpy(&vector_s[0].iov_base, &value, sizeof(vector_s[0].iov_base)); vector_s[0].iov_len = slen; if (!with_delim) { vector_len = 1; } else { memcpy(&vector_s[1].iov_base, &(inst->delimiter), sizeof(vector_s[1].iov_base)); vector_s[1].iov_len = inst->delimiter_len; vector_len = 2; } vector_p = &vector_s[0]; }
static rlm_rcode_t CC_HINT(nonnull) mod_do_linelog(void *instance, REQUEST *request) { int fd = -1; rlm_linelog_t *inst = (rlm_linelog_t*) instance; char const *value = inst->line; #ifdef HAVE_GRP_H gid_t gid; char *endptr; #endif char path[2048]; char line[4096]; line[0] = '\0'; if (inst->reference) { CONF_ITEM *ci; CONF_PAIR *cp; if (radius_xlat(line + 1, sizeof(line) - 1, request, inst->reference, linelog_escape_func, NULL) < 0) { return RLM_MODULE_FAIL; } line[0] = '.'; /* force to be in current section */ /* * Don't allow it to go back up */ if (line[1] == '.') goto do_log; ci = cf_reference_item(NULL, inst->cs, line); if (!ci) { RDEBUG2("No such entry \"%s\"", line); return RLM_MODULE_NOOP; } if (!cf_item_is_pair(ci)) { RDEBUG2("Entry \"%s\" is not a variable assignment ", line); goto do_log; } cp = cf_item_to_pair(ci); value = cf_pair_value(cp); if (!value) { RWDEBUG2("Entry \"%s\" has no value", line); return RLM_MODULE_OK; } /* * Value exists, but is empty. Don't log anything. */ if (!*value) return RLM_MODULE_OK; } do_log: /* * FIXME: Check length. */ if (radius_xlat(line, sizeof(line) - 1, request, value, linelog_escape_func, NULL) < 0) { return RLM_MODULE_FAIL; } #ifdef HAVE_SYSLOG_H if (strcmp(inst->filename, "syslog") == 0) { syslog(inst->syslog_priority, "%s", line); return RLM_MODULE_OK; } #endif /* * We're using a real filename now. */ if (radius_xlat(path, sizeof(path), request, inst->filename, inst->escape_func, NULL) < 0) { return RLM_MODULE_FAIL; } fd = exfile_open(inst->ef, path, inst->permissions, true); if (fd < 0) { ERROR("rlm_linelog: Failed to open %s: %s", path, fr_syserror(errno)); return RLM_MODULE_FAIL; } if (inst->group != NULL) { gid = strtol(inst->group, &endptr, 10); if (*endptr != '\0') { if (rad_getgid(request, &gid, inst->group) < 0) { RDEBUG2("Unable to find system group \"%s\"", inst->group); goto skip_group; } } if (chown(path, -1, gid) == -1) { RDEBUG2("Unable to change system group of \"%s\"", path); } } skip_group: strcat(line, "\n"); if (write(fd, line, strlen(line)) < 0) { exfile_close(inst->ef, fd); ERROR("rlm_linelog: Failed writing: %s", fr_syserror(errno)); return RLM_MODULE_FAIL; } exfile_close(inst->ef, fd); return RLM_MODULE_OK; }
/* * 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; } /* * If we can't find a matching config item we do * nothing so return RLM_MODULE_NOOP. */ item = cf_reference_item(NULL, section->cs, path); if (!item) { RWDEBUG("No such configuration item %s", path); rcode = RLM_MODULE_NOOP; goto finish; } if (cf_item_is_section(item)){ RWDEBUG("Sections are not supported as references"); rcode = RLM_MODULE_NOOP; goto finish; } pair = cf_item_to_pair(item); attr = cf_pair_attr(pair); RDEBUG2("Using query template '%s'", attr); handle = fr_connection_get(inst->pool); 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, inst->sql_escape_func, handle) < 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); sql_ret = rlm_sql_query(inst, request, &handle, expanded); TALLOC_FREE(expanded); RDEBUG("SQL query returned: %s", fr_int2str(sql_rcode_table, sql_ret, "<INVALID>")); switch (sql_ret) { /* * Query was a success! Now we just need to check if it did anything. */ case RLM_SQL_OK: break; /* * A general, unrecoverable server fault. */ case RLM_SQL_ERROR: /* * 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 fr_connection_release. */ case RLM_SQL_RECONNECT: rcode = RLM_MODULE_FAIL; goto finish; /* * Query was invalid, this is a terminal error, but we still need * to do cleanup, as the connection handle is still valid. */ case RLM_SQL_QUERY_INVALID: rcode = RLM_MODULE_INVALID; goto finish; /* * Driver found an error (like a unique key constraint violation) * that hinted it might be a good idea to try an alternative query. */ case RLM_SQL_ALT_QUERY: goto next; } rad_assert(handle); /* * We need to have updated something for the query to have been * counted as successful. */ numaffected = (inst->module->sql_affected_rows)(handle, inst->config); (inst->module->sql_finish_query)(handle, inst->config); RDEBUG("%i record(s) updated", numaffected); if (numaffected > 0) break; /* A query succeeded, were done! */ next: /* * 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..."); } finish: talloc_free(expanded); fr_connection_release(inst->pool, handle); sql_unset_user(inst, request); return rcode; }
/* * Expand the variables in an input string. */ char const *cf_expand_variables(char const *cf, int *lineno, CONF_SECTION *outer_cs, char *output, size_t outsize, char const *input, bool *soft_fail) { char *p; char const *end, *ptr; CONF_SECTION const *parent_cs; char name[8192]; if (soft_fail) *soft_fail = false; /* * Find the master parent conf section. * We can't use main_config->root_cs, because we're in the * process of re-building it, and it isn't set up yet... */ parent_cs = cf_root(outer_cs); p = output; ptr = input; while (*ptr) { /* * Ignore anything other than "${" */ if ((*ptr == '$') && (ptr[1] == '{')) { CONF_ITEM *ci; CONF_PAIR *cp; char *q; /* * FIXME: Add support for ${foo:-bar}, * like in xlat.c */ /* * Look for trailing '}', and log a * warning for anything that doesn't match, * and exit with a fatal error. */ end = strchr(ptr, '}'); if (end == NULL) { *p = '\0'; INFO("%s[%d]: Variable expansion missing }", cf, *lineno); return NULL; } ptr += 2; /* * Can't really happen because input lines are * capped at 8k, which is sizeof(name) */ if ((size_t) (end - ptr) >= sizeof(name)) { ERROR("%s[%d]: Reference string is too large", cf, *lineno); return NULL; } memcpy(name, ptr, end - ptr); name[end - ptr] = '\0'; q = strchr(name, ':'); if (q) { *(q++) = '\0'; } ci = cf_reference_item(parent_cs, outer_cs, name); if (!ci) { if (soft_fail) *soft_fail = true; ERROR("%s[%d]: Reference \"${%s}\" not found", cf, *lineno, name); return NULL; } /* * The expansion doesn't refer to another item or section * it's the property of a section. */ if (q) { CONF_SECTION *find = cf_item_to_section(ci); if (ci->type != CONF_ITEM_SECTION) { ERROR("%s[%d]: Can only reference properties of sections", cf, *lineno); return NULL; } switch (fr_str2int(conf_property_name, q, CONF_PROPERTY_INVALID)) { case CONF_PROPERTY_NAME: strcpy(p, find->name1); break; case CONF_PROPERTY_INSTANCE: strcpy(p, find->name2 ? find->name2 : find->name1); break; default: ERROR("%s[%d]: Invalid property '%s'", cf, *lineno, q); return NULL; } p += strlen(p); ptr = end + 1; } else if (ci->type == CONF_ITEM_PAIR) { /* * Substitute the value of the variable. */ cp = cf_item_to_pair(ci); /* * If the thing we reference is * marked up as being expanded in * pass2, don't expand it now. * Let it be expanded in pass2. */ if (cp->pass2) { if (soft_fail) *soft_fail = true; ERROR("%s[%d]: Reference \"%s\" points to a variable which has not been expanded.", cf, *lineno, input); return NULL; } if (!cp->value) { ERROR("%s[%d]: Reference \"%s\" has no value", cf, *lineno, input); return NULL; } if (p + strlen(cp->value) >= output + outsize) { ERROR("%s[%d]: Reference \"%s\" is too long", cf, *lineno, input); return NULL; } strcpy(p, cp->value); p += strlen(p); ptr = end + 1; } else if (ci->type == CONF_ITEM_SECTION) { CONF_SECTION *subcs; /* * Adding an entry again to a * section is wrong. We don't * want an infinite loop. */ if (cf_item_to_section(ci->parent) == outer_cs) { ERROR("%s[%d]: Cannot reference different item in same section", cf, *lineno); return NULL; } /* * Copy the section instead of * referencing it. */ subcs = cf_item_to_section(ci); subcs = cf_section_dup(outer_cs, outer_cs, subcs, cf_section_name1(subcs), cf_section_name2(subcs), false); if (!subcs) { ERROR("%s[%d]: Failed copying reference %s", cf, *lineno, name); return NULL; } subcs->item.filename = ci->filename; subcs->item.lineno = ci->lineno; cf_item_add(outer_cs, &(subcs->item)); ptr = end + 1; } else { ERROR("%s[%d]: Reference \"%s\" type is invalid", cf, *lineno, input); return NULL; } } else if (strncmp(ptr, "$ENV{", 5) == 0) { char *env; ptr += 5; /* * Look for trailing '}', and log a * warning for anything that doesn't match, * and exit with a fatal error. */ end = strchr(ptr, '}'); if (end == NULL) { *p = '\0'; INFO("%s[%d]: Environment variable expansion missing }", cf, *lineno); return NULL; } /* * Can't really happen because input lines are * capped at 8k, which is sizeof(name) */ if ((size_t) (end - ptr) >= sizeof(name)) { ERROR("%s[%d]: Environment variable name is too large", cf, *lineno); return NULL; } memcpy(name, ptr, end - ptr); name[end - ptr] = '\0'; /* * Get the environment variable. * If none exists, then make it an empty string. */ env = getenv(name); if (env == NULL) { *name = '\0'; env = name; } if (p + strlen(env) >= output + outsize) { ERROR("%s[%d]: Reference \"%s\" is too long", cf, *lineno, input); return NULL; } strcpy(p, env); p += strlen(p); ptr = end + 1; } else { /* * Copy it over verbatim. */ *(p++) = *(ptr++); } if (p >= (output + outsize)) { ERROR("%s[%d]: Reference \"%s\" is too long", cf, *lineno, input); return NULL; } } /* loop over all of the input string. */ *p = '\0'; return output; }