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]; }
/** Convert an 'update' config section into an attribute map. * * Uses 'name2' of section to set default request and lists. * Copied from map_afrom_cs, except that list assignments can have the RHS * be a bare word. * * @param[in] cs the update section * @param[out] out 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. */ static int ldap_map_afrom_cs(value_pair_map_t **out, CONF_SECTION *cs, 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; unsigned int total = 0; value_pair_map_t **tail, *map; TALLOC_CTX *ctx; *out = NULL; tail = out; 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)) { char const *attr; FR_TOKEN type; CONF_PAIR *cp; cp = cf_itemtopair(ci); type = cf_pair_value_type(cp); 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); /* * Look for "list: OP BARE_WORD". If it exists, * we can make the RHS a bare word. Otherwise, * just call map_afrom_cp() * * Otherwise, the map functions check the RHS of * list assignments, and complain that the RHS * isn't another list. */ attr = cf_pair_attr(cp); p = strrchr(attr, ':'); if (!p || (p[1] != '\0') || (type == T_DOUBLE_QUOTED_STRING)) { if (map_afrom_cp(ctx, &map, cp, request_def, dst_list_def, REQUEST_CURRENT, src_list_def) < 0) { goto error; } } else { ssize_t slen; char const *value; map = talloc_zero(ctx, value_pair_map_t); map->op = cf_pair_operator(cp); map->ci = cf_pairtoitem(cp); slen = tmpl_afrom_attr_str(ctx, &map->lhs, attr, request_def, dst_list_def); if (slen <= 0) { char *spaces, *text; fr_canonicalize_error(ctx, &spaces, &text, slen, attr); cf_log_err(ci, "Failed parsing list reference"); cf_log_err(ci, "%s", text); cf_log_err(ci, "%s^ %s", spaces, fr_strerror()); talloc_free(spaces); talloc_free(text); goto error; } if (map->lhs->type != TMPL_TYPE_LIST) { cf_log_err(map->ci, "Invalid list name"); goto error; } if (map->op != T_OP_ADD) { cf_log_err(map->ci, "Only '+=' operator is permitted for valuepair to list mapping"); goto error; } value = cf_pair_value(cp); if (!value) { cf_log_err(map->ci, "No value specified for list assignment"); goto error; } /* * the RHS type is a bare word or single * quoted string. We don't want it being * interpreted as a list or attribute * reference, so we force the RHS to be a * literal. */ slen = tmpl_afrom_str(ctx, &map->rhs, value, T_SINGLE_QUOTED_STRING, request_def, dst_list_def); if (slen <= 0) { char *spaces, *text; fr_canonicalize_error(ctx, &spaces, &text, slen, value); cf_log_err(ci, "Failed parsing string"); cf_log_err(ci, "%s", text); cf_log_err(ci, "%s^ %s", spaces, fr_strerror()); talloc_free(spaces); talloc_free(text); goto error; } /* * And unlike map_afrom_cp(), we do NOT * try to parse the RHS as a list * reference. It's a literal, and we * leave it as a literal. */ rad_assert(map->rhs->type == TMPL_TYPE_LITERAL); } ctx = *tail = map; tail = &(map->next); } return 0; error: TALLOC_FREE(*out); return -1; }