/** Return a VP from a value_pair_tmpl_t * * @param out where to write the retrieved vp. * @param request current request. * @param vpt the value pair template * @return -1 if VP could not be found, -2 if list could not be found, -3 if context could not be found. */ int tmpl_find_vp(VALUE_PAIR **out, REQUEST *request, value_pair_tmpl_t const *vpt) { VALUE_PAIR **vps, *vp; rad_assert((vpt->type == TMPL_TYPE_ATTR) || (vpt->type == TMPL_TYPE_LIST)); if (out) *out = NULL; if (radius_request(&request, vpt->tmpl_request) < 0) { return -3; } vps = radius_list(request, vpt->tmpl_list); if (!vps) { return -2; } switch (vpt->type) { /* * May not may not be found, but it *is* a known name. */ case TMPL_TYPE_ATTR: { int num; vp_cursor_t cursor; if (vpt->tmpl_num == NUM_ANY) { vp = pairfind(*vps, vpt->tmpl_da->attr, vpt->tmpl_da->vendor, vpt->tmpl_tag); if (!vp) return -1; break; } (void) fr_cursor_init(&cursor, vps); num = vpt->tmpl_num; while ((vp = fr_cursor_next_by_da(&cursor, vpt->tmpl_da, vpt->tmpl_tag))) { VERIFY_VP(vp); if (num-- <= 0) goto finish; } return -1; } case TMPL_TYPE_LIST: vp = *vps; break; default: rad_assert(0); } finish: if (out) *out = vp; return 0; }
/** Convert a valuepair string to VALUE_PAIR and insert it into a list * * Takes a valuepair string with list and request qualifiers, converts it into a VALUE_PAIR * and inserts it into the appropriate list. * * @param request Current request. * @param raw string to parse. * @param request_def to use if attribute isn't qualified. * @param list_def to use if attribute isn't qualified. * @return 0 on success, -1 on error. */ int radius_str2vp(REQUEST *request, char const *raw, request_refs_t request_def, pair_lists_t list_def) { char const *p; size_t len; request_refs_t req; pair_lists_t list; VALUE_PAIR *vp = NULL; VALUE_PAIR **vps; p = raw; req = radius_request_name(&p, request_def); len = p - raw; if (req == REQUEST_UNKNOWN) { REDEBUG("Invalid request qualifier \"%.*s\"", (int) len, raw); return -1; } raw += len; list = radius_list_name(&p, list_def); if (list == PAIR_LIST_UNKNOWN) { len = p - raw; REDEBUG("Invalid list qualifier \"%.*s\"", (int) len, raw); return -1; } raw += len; if (radius_request(&request, req) < 0) { return -1; } vps = radius_list(request, list); if (!vps) { return -1; } if (userparse(request, raw, &vp) == T_OP_INVALID) { return -1; } pairmove(request, vps, &vp); return 0; }
/** Convert VALUE_PAIR_MAP to VALUE_PAIR(s) and add them to a REQUEST. * * Takes a single VALUE_PAIR_MAP, resolves request and list identifiers * to pointers in the current request, the attempts to retrieve module * specific value(s) using callback, and adds the resulting values to the * correct request/list. * * @param request The current request. * @param map specifying destination attribute and location and src identifier. * @param func to retrieve module specific values and convert them to * VLAUE_PAIRS. * @param ctx to be passed to func. * @param src name to be used in debugging if different from map value. * @return -1 if either attribute or qualifier weren't valid in this context * or callback returned NULL pointer, else 0. */ int radius_map2request(REQUEST *request, const VALUE_PAIR_MAP *map, const char *src, radius_tmpl_getvalue_t func, void *ctx) { VALUE_PAIR **list, *vp, *head; char buffer[MAX_STRING_LEN]; if (radius_request(&request, map->dst->request) < 0) { RDEBUG("WARNING: Request in mapping \"%s\" -> \"%s\" " "invalid in this context, skipping!", map->src->name, map->dst->name); return -1; } list = radius_list(request, map->dst->list); if (!list) { RDEBUG("WARNING: List in mapping \"%s\" -> \"%s\" " "invalid in this context, skipping!", map->src->name, map->dst->name); return -1; } head = func(request, map->dst, ctx); if (head == NULL) { return -1; } for (vp = head; vp != NULL; vp = vp->next) { vp->operator = map->op_token; if (debug_flag) { vp_prints_value(buffer, sizeof(buffer), vp, 1); RDEBUG("\t%s %s %s (%s)", map->dst->name, fr_int2str(fr_tokens, vp->operator, "¿unknown?"), buffer, src ? src : map->src->name); } } /* * Use pairmove so the operator is respected */ radius_pairmove(request, list, head); pairfree(&vp); /* Free the VP if for some reason it wasn't moved */ return 0; }
/** Copy pairs matching a VPT in the current request * * @param out where to write the copied vps. * @param request current request. * @param vpt the value pair template * @return -1 if VP could not be found, -2 if list could not be found, -3 if context could not be found. */ int radius_vpt_copy_vp(VALUE_PAIR **out, REQUEST *request, value_pair_tmpl_t const *vpt) { VALUE_PAIR **vps, *vp; REQUEST *current = request; if (out) *out = NULL; if (radius_request(¤t, vpt->request) < 0) { return -3; } vps = radius_list(request, vpt->list); if (!vps) { return -2; } switch (vpt->type) { /* * May not may not be found, but it *is* a known name. */ case VPT_TYPE_ATTR: vp = paircopy2(request, *vps, vpt->da->attr, vpt->da->vendor, TAG_ANY); if (!vp) { return -1; } break; case VPT_TYPE_LIST: vp = paircopy(request, *vps); break; default: /* * literal, xlat, regex, exec, data. * no attribute. */ return -1; } if (out) { *out = vp; } return 0; }
/** Return a VP from the specified request. * * @param request current request. * @param name attribute name including qualifiers. * @param vp_p where to write the pointer to the resolved VP. * Will be NULL if the attribute couldn't be resolved. * @return -1 if either the attribute or qualifier were invalid, else 0 */ int radius_get_vp(REQUEST *request, const char *name, VALUE_PAIR **vp_p) { value_pair_tmpl_t vpt; VALUE_PAIR **vps; *vp_p = NULL; if (radius_parse_attr(name, &vpt, REQUEST_CURRENT, PAIR_LIST_REQUEST) < 0) { return -1; } if (radius_request(&request, vpt.request) < 0) { return 0; } vps = radius_list(request, vpt.list); if (!vps) { return 0; } switch (vpt.type) { /* * May not may not be found, but it *is* a known name. */ case VPT_TYPE_ATTR: *vp_p = pairfind(*vps, vpt.da->attr, vpt.da->vendor, TAG_ANY); break; case VPT_TYPE_LIST: *vp_p = *vps; break; default: rad_assert(0); return -1; break; } return 0; }
/** Return a VP from a value_pair_tmpl_t * * @param out where to write the retrieved vp. * @param request current request. * @param vpt the value pair template * @return -1 if VP could not be found, -2 if list could not be found, -3 if context could not be found. */ int radius_vpt_get_vp(VALUE_PAIR **out, REQUEST *request, value_pair_tmpl_t const *vpt) { VALUE_PAIR **vps, *vp; if (out) *out = NULL; if (radius_request(&request, vpt->request) < 0) { return -3; } vps = radius_list(request, vpt->list); if (!vps) { return -2; } switch (vpt->type) { /* * May not may not be found, but it *is* a known name. */ case VPT_TYPE_ATTR: vp = pairfind(*vps, vpt->da->attr, vpt->da->vendor, TAG_ANY); if (!vp) { return -1; } break; case VPT_TYPE_LIST: vp = *vps; default: break; } if (out) { *out = vp; } return 0; }
/** Return a VP from the specified request. * * @param request current request. * @param name attribute name including qualifiers. * @param vp_p where to write the pointer to the resolved VP. * Will be NULL if the attribute couldn't be resolved. * @return -1 if either the attribute or qualifier were invalid, else 0 */ int radius_get_vp(REQUEST *request, const char *name, VALUE_PAIR **vp_p) { VALUE_PAIR_TMPL vpt; VALUE_PAIR **vps; *vp_p = NULL; if (radius_parse_attr(name, &vpt, REQUEST_CURRENT, PAIR_LIST_REQUEST) < 0) { return -1; } if (radius_request(&request, vpt.request) < 0) { RDEBUG("WARNING: Specified request \"%s\" is not available in " "this context", fr_int2str(request_refs, vpt.request, "¿unknown?")); return 0; } vps = radius_list(request, vpt.list); if (!vps) { RDEBUG("WARNING: Specified list \"%s\" is not available in " "this context", fr_int2str(pair_lists, vpt.list, "¿unknown?")); return 0; } /* * May not may not be found, but it *is* a known name. */ *vp_p = pairfind(*vps, vpt.da->attr, vpt.da->vendor, TAG_ANY); return 0; }
/** Expand values in an attribute map where needed * */ int rlm_ldap_map_xlat(REQUEST *request, value_pair_map_t const *maps, rlm_ldap_map_xlat_t *expanded) { const value_pair_map_t *map; unsigned int total = 0; size_t len; VALUE_PAIR *found, **from = NULL; REQUEST *context; for (map = maps; map != NULL; map = map->next) { switch (map->src->type) { case VPT_TYPE_XLAT: { char *exp = NULL; len = radius_xlat(exp, 0, request, map->src->name, NULL, NULL); if (len <= 0) { RDEBUG("Expansion of LDAP attribute \"%s\" failed", map->src->name); goto error; } expanded->attrs[total++] = exp; break; } case VPT_TYPE_ATTR: context = request; if (radius_request(&context, map->src->request) == 0) { from = radius_list(context, map->src->list); } if (!from) continue; found = pairfind(*from, map->src->da->attr, map->src->da->vendor, TAG_ANY); if (!found) continue; expanded->attrs[total++] = talloc_strdup(request, found->vp_strvalue); break; case VPT_TYPE_LITERAL: expanded->attrs[total++] = map->src->name; break; default: rad_assert(0); error: expanded->attrs[total] = NULL; rlm_ldap_map_xlat_free(expanded); return -1; } } rad_assert(total < LDAP_MAX_ATTRMAP); expanded->attrs[total] = NULL; expanded->count = total; expanded->maps = maps; return 0; }
static char *xlat_aprint(TALLOC_CTX *ctx, REQUEST *request, xlat_exp_t const * const node, RADIUS_ESCAPE_STRING escape, void *escape_ctx, int lvl) { ssize_t rcode; char *str = NULL, *child; REQUEST *ref; XLAT_DEBUG("%.*sxlat aprint %d", lvl, xlat_spaces, node->type); switch (node->type) { /* * Don't escape this. */ case XLAT_LITERAL: XLAT_DEBUG("xlat_aprint LITERAL"); return talloc_strdup(ctx, node->fmt); /* * Do a one-character expansion. */ case XLAT_PERCENT: { char const *p; char *nl; size_t freespace = 256; struct tm ts; time_t when; XLAT_DEBUG("xlat_aprint PERCENT"); str = talloc_array(ctx, char, freespace); /* @todo do better allocation */ p = node->fmt; when = request->timestamp; if (request->packet) { when = request->packet->timestamp.tv_sec; } switch (*p) { case '%': str[0] = '%'; str[1] = '\0'; break; case 'd': /* request day */ if (!localtime_r(&when, &ts)) goto error; strftime(str, freespace, "%d", &ts); break; case 'l': /* request timestamp */ snprintf(str, freespace, "%lu", (unsigned long) when); break; case 'm': /* request month */ if (!localtime_r(&when, &ts)) goto error; strftime(str, freespace, "%m", &ts); break; case 't': /* request timestamp */ CTIME_R(&when, str, freespace); nl = strchr(str, '\n'); if (nl) *nl = '\0'; break; case 'D': /* request date */ if (!localtime_r(&when, &ts)) goto error; strftime(str, freespace, "%Y%m%d", &ts); break; case 'G': /* request minute */ if (!localtime_r(&when, &ts)) goto error; strftime(str, freespace, "%M", &ts); break; case 'H': /* request hour */ if (!localtime_r(&when, &ts)) goto error; strftime(str, freespace, "%H", &ts); break; case 'I': /* Request ID */ if (request->packet) { snprintf(str, freespace, "%i", request->packet->id); } break; case 'S': /* request timestamp in SQL format*/ if (!localtime_r(&when, &ts)) goto error; strftime(str, freespace, "%Y-%m-%d %H:%M:%S", &ts); break; case 'T': /* request timestamp */ if (!localtime_r(&when, &ts)) goto error; strftime(str, freespace, "%Y-%m-%d-%H.%M.%S.000000", &ts); break; case 'Y': /* request year */ if (!localtime_r(&when, &ts)) { error: REDEBUG("Failed converting packet timestamp to localtime: %s", fr_syserror(errno)); talloc_free(str); return NULL; } strftime(str, freespace, "%Y", &ts); break; default: rad_assert(0 == 1); break; } } break; case XLAT_ATTRIBUTE: XLAT_DEBUG("xlat_aprint ATTRIBUTE"); ref = request; if (radius_request(&ref, node->ref) < 0) { return NULL; } /* * Some attributes are virtual <sigh> */ str = xlat_getvp(ctx, ref, node->list, node->da, node->tag, node->num, true); if (str) { XLAT_DEBUG("expand attr %s --> '%s'", node->da->name, str); } break; case XLAT_VIRTUAL: XLAT_DEBUG("xlat_aprint VIRTUAL"); str = talloc_array(ctx, char, 1024); /* FIXME: have the module call talloc_asprintf */ rcode = node->xlat->func(node->xlat->instance, request, NULL, str, 1024); if (rcode < 0) { talloc_free(str); return NULL; } break; case XLAT_MODULE: XLAT_DEBUG("xlat_aprint MODULE"); if (xlat_process(&child, request, node->child, node->xlat->escape, node->xlat->instance) == 0) { return NULL; } XLAT_DEBUG("%.*sexpand mod %s --> '%s'", lvl, xlat_spaces, node->fmt, child); str = talloc_array(ctx, char, 1024); /* FIXME: have the module call talloc_asprintf */ *str = '\0'; /* Be sure the string is NULL terminated, we now only free on error */ rcode = node->xlat->func(node->xlat->instance, request, child, str, 1024); talloc_free(child); if (rcode < 0) { talloc_free(str); return NULL; } break; #ifdef HAVE_REGEX_H case XLAT_REGEX: XLAT_DEBUG("xlat_aprint REGEX"); child = request_data_reference(request, request, REQUEST_DATA_REGEX | node->num); if (!child) return NULL; str = talloc_strdup(ctx, child); break; #endif case XLAT_ALTERNATE: XLAT_DEBUG("xlat_aprint ALTERNATE"); rad_assert(node->child != NULL); rad_assert(node->alternate != NULL); str = xlat_aprint(ctx, request, node->child, escape, escape_ctx, lvl); if (str) break; str = xlat_aprint(ctx, request, node->alternate, escape, escape_ctx, lvl); break; } /* * Escape the non-literals we found above. */ if (str && escape) { char *escaped; escaped = talloc_array(ctx, char, 1024); /* FIXME: do something intelligent */ escape(request, escaped, 1024, str, escape_ctx); talloc_free(str); str = escaped; }
/************************************************************************ * Functions for RADIUS * RFC 2058: RADIUS * RFC 2548: Microsoft Vendor-specific RADIUS Attributes ************************************************************************/ static void chap_radius_authenticate(chap *_this, int id, char *username, u_char *challenge, int lchallenge, u_char *response) { void *radctx; RADIUS_PACKET *radpkt; radius_req_setting *rad_setting; int lpkt; u_char *pkt; char buf0[MAX_USERNAME_LENGTH]; radpkt = NULL; radctx = NULL; if ((rad_setting = npppd_get_radius_auth_setting(_this->ppp->pppd, _this->ppp)) == NULL) { goto fail; /* no radius server */ } pkt = ppp_packetbuf(_this->ppp, PPP_PROTO_CHAP) + HEADERLEN; lpkt = _this->ppp->mru - HEADERLEN; if ((radpkt = radius_new_request_packet(RADIUS_CODE_ACCESS_REQUEST)) == NULL) goto fail; if (radius_prepare(rad_setting, _this, &radctx, chap_radius_response) != 0) { radius_delete_packet(radpkt); goto fail; } if (ppp_set_radius_attrs_for_authreq(_this->ppp, rad_setting, radpkt) != 0) goto fail; if (radius_put_string_attr(radpkt, RADIUS_TYPE_USER_NAME, npppd_ppp_get_username_for_auth(_this->ppp->pppd, _this->ppp, username, buf0)) != 0) goto fail; switch (_this->type) { case PPP_AUTH_CHAP_MD5: { u_char md5response[17]; md5response[0] = _this->challid; memcpy(&md5response[1], response, 16); if (radius_put_raw_attr(radpkt, RADIUS_TYPE_CHAP_PASSWORD, md5response, 17) != 0) goto fail; if (radius_put_raw_attr(radpkt, RADIUS_TYPE_CHAP_CHALLENGE, challenge, lchallenge) != 0) goto fail; break; } case PPP_AUTH_CHAP_MS_V2: { struct RADIUS_MS_CHAP2_RESPONSE msresponse; /* Preparing RADIUS_MS_CHAP2_RESPONSE */ memset(&msresponse, 0, sizeof(msresponse)); msresponse.ident = id; msresponse.flags = response[48]; memcpy(&msresponse.peer_challenge, response, 16); memcpy(&msresponse.response, response + 24, 24); if (radius_put_vs_raw_attr(radpkt, RADIUS_VENDOR_MICROSOFT, RADIUS_VTYPE_MS_CHAP_CHALLENGE, challenge, 16) != 0) goto fail; if (radius_put_vs_raw_attr(radpkt, RADIUS_VENDOR_MICROSOFT, RADIUS_VTYPE_MS_CHAP2_RESPONSE, &msresponse, sizeof(msresponse)) != 0) goto fail; break; } } radius_get_authenticator(radpkt, _this->authenticator); /* Cancel previous request */ if (_this->radctx != NULL) radius_cancel_request(_this->radctx); /* Send a request */ _this->radctx = radctx; radius_request(radctx, radpkt); return; fail: switch (_this->type) { case PPP_AUTH_CHAP_MD5: /* No extra information, just "FAILED" */ chap_send_error(_this, "FAILED"); break; case PPP_AUTH_CHAP_MS_V2: /* No extra information */ mschapv2_send_error(_this, ERROR_AUTHENTICATION_FAILURE, 0); break; } if (radctx != NULL) radius_cancel_request(radctx); }
/** Convert value_pair_map_t to VALUE_PAIR(s) and add them to a REQUEST. * * Takes a single value_pair_map_t, resolves request and list identifiers * to pointers in the current request, then attempts to retrieve module * specific value(s) using callback, and adds the resulting values to the * correct request/list. * * @param request The current request. * @param map specifying destination attribute and location and src identifier. * @param func to retrieve module specific values and convert them to * VALUE_PAIRS. * @param ctx to be passed to func. * @param src name to be used in debugging if different from map value. * @return -1 if the operation failed, -2 in the source attribute wasn't valid, 0 on success. */ int radius_map2request(REQUEST *request, value_pair_map_t const *map, UNUSED char const *src, radius_tmpl_getvalue_t func, void *ctx) { int rcode; vp_cursor_t cursor; VALUE_PAIR **list, *vp, *head = NULL; char buffer[1024]; if (radius_request(&request, map->dst->request) < 0) { REDEBUG("Mapping \"%s\" -> \"%s\" invalid in this context", map->src->name, map->dst->name); return -2; } list = radius_list(request, map->dst->list); if (!list) { REDEBUG("Mapping \"%s\" -> \"%s\" invalid in this context", map->src->name, map->dst->name); return -2; } /* * The callback should either return -1 to signify operations error, -2 when it can't find the * attribute or list being referenced, or 0 to signify success. * It may return "sucess", but still have no VPs to work with. * Only if it returned an error code should it not write anything to the head pointer. */ rcode = func(&head, request, map, ctx); if (rcode < 0) { rad_assert(!head); return rcode; } if (!head) return 0; VERIFY_VP(head); if (debug_flag) for (vp = paircursor(&cursor, &head); vp; vp = pairnext(&cursor)) { char *value; 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: vp_prints_value(buffer, sizeof(buffer), vp, '"'); value = buffer; break; case VPT_TYPE_DATA: vp_prints_value(buffer, sizeof(buffer), vp, 0); 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_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_asprintf(request, "&%s -> %s", map->src->name, buffer); break; } RDEBUG("\t\t%s %s %s", map->dst->name, fr_int2str(fr_tokens, vp->op, "<INVALID>"), value); if (value != buffer) talloc_free(value); } /* * Use pairmove so the operator is respected */ radius_pairmove(request, list, head); return 0; }
/** Convert a map to a VALUE_PAIR. * * @param[out] out Where to write the VALUE_PAIR(s). * @param[in] request structure (used only for talloc) * @param[in] map the map. The LHS (dst) has to be VPT_TYPE_ATTR or VPT_TYPE_LIST. * @param[in] ctx unused * @return 0 on success, -1 on failure, -2 on attribute not found/equivalent */ int radius_map2vp(VALUE_PAIR **out, REQUEST *request, value_pair_map_t const *map, UNUSED void *ctx) { int rcode = 0; VALUE_PAIR *vp = NULL, *found, **from = NULL; DICT_ATTR const *da; REQUEST *context; vp_cursor_t cursor; rad_assert(request != NULL); rad_assert(map != NULL); *out = NULL; /* * Special case for !*, we don't need to parse the value, just allocate an attribute with * the right operator. */ if (map->op == T_OP_CMP_FALSE) { vp = pairalloc(request, map->dst->da); if (!vp) return -1; vp->op = map->op; *out = vp; return 0; } /* * List to list found, this is a special case because we don't need * to allocate any attributes, just found the current list, and change * the op. */ if ((map->dst->type == VPT_TYPE_LIST) && (map->src->type == VPT_TYPE_LIST)) { from = radius_list(request, map->src->list); if (!from) return -2; found = paircopy(request, *from); /* * List to list copy is invalid if the src list has no attributes. */ if (!found) return -2; for (vp = paircursor(&cursor, &found); vp; vp = pairnext(&cursor)) { vp->op = T_OP_ADD; } *out = found; return 0; } /* * Deal with all non-list founding operations. */ da = map->dst->da ? map->dst->da : map->src->da; switch (map->src->type) { case VPT_TYPE_XLAT: case VPT_TYPE_LITERAL: case VPT_TYPE_DATA: vp = pairalloc(request, da); if (!vp) return -1; vp->op = map->op; break; default: break; } /* * And parse the RHS */ switch (map->src->type) { case VPT_TYPE_XLAT: rad_assert(map->dst->da); /* Need to know where were going to write the new attribute */ /* * Don't call unnecessary expansions */ if (strchr(map->src->name, '%') != NULL) { ssize_t slen; char *str = NULL; slen = radius_axlat(&str, request, map->src->name, NULL, NULL); if (slen < 0) { rcode = slen; goto error; } rcode = pairparsevalue(vp, str); talloc_free(str); if (!rcode) { pairfree(&vp); rcode = -1; goto error; } break; } /* FALL-THROUGH */ case VPT_TYPE_LITERAL: if (!pairparsevalue(vp, map->src->name)) { rcode = -2; goto error; } break; case VPT_TYPE_ATTR: rad_assert(!map->dst->da || (map->src->da->type == map->dst->da->type) || (map->src->da->type == PW_TYPE_OCTETS) || (map->dst->da->type == PW_TYPE_OCTETS)); context = request; if (radius_request(&context, map->src->request) == 0) { from = radius_list(context, map->src->list); } /* * Can't add the attribute if the list isn't * valid. */ if (!from) { rcode = -2; goto error; } /* * Special case, destination is a list, found all instance of an attribute. */ if (map->dst->type == VPT_TYPE_LIST) { found = paircopy2(request, *from, map->src->da->attr, map->src->da->vendor, TAG_ANY); if (!found) { REDEBUG("Attribute \"%s\" not found in request", map->src->name); rcode = -2; goto error; } for (vp = paircursor(&cursor, &found); vp; vp = pairnext(&cursor)) { vp->op = T_OP_ADD; } *out = found; return 0; } /* * FIXME: allow tag references? */ found = pairfind(*from, map->src->da->attr, map->src->da->vendor, TAG_ANY); if (!found) { REDEBUG("Attribute \"%s\" not found in request", map->src->name); rcode = -2; goto error; } /* * Copy the data over verbatim, assuming it's * actually data. */ // rad_assert(found->type == VT_DATA); vp = paircopyvpdata(request, da, found); if (!vp) { return -1; } vp->op = map->op; break; case VPT_TYPE_DATA: rad_assert(map->src->da->type == map->dst->da->type); memcpy(&vp->data, map->src->vpd, sizeof(vp->data)); vp->length = map->src->length; break; /* * This essentially does the same as rlm_exec xlat, except it's non-configurable. * It's only really here as a convenience for people who expect the contents of * backticks to be executed in a shell. * * exec string is xlat expanded and arguments are shell escaped. */ case VPT_TYPE_EXEC: return radius_mapexec(out, request, map); default: rad_assert(0); /* Should of been caught at parse time */ error: pairfree(&vp); return rcode; } *out = vp; return 0; }
/** Convert value_pair_map_t to VALUE_PAIR(s) and add them to a REQUEST. * * Takes a single value_pair_map_t, resolves request and list identifiers * to pointers in the current request, then attempts to retrieve module * specific value(s) using callback, and adds the resulting values to the * correct request/list. * * @param request The current request. * @param map specifying destination attribute and location and src identifier. * @param func to retrieve module specific values and convert them to * VALUE_PAIRS. * @param ctx to be passed to func. * @param src name to be used in debugging if different from map value. * @return -1 if the operation failed, -2 in the source attribute wasn't valid, 0 on success. */ int radius_map2request(REQUEST *request, value_pair_map_t const *map, UNUSED char const *src, radius_tmpl_getvalue_t func, void *ctx) { int rcode, num; VALUE_PAIR **list, *vp, *head = NULL; REQUEST *context; TALLOC_CTX *parent; vp_cursor_t cursor; /* * Sanity check inputs. We can have a list or attribute * as a destination. */ if ((map->dst->type != VPT_TYPE_LIST) && (map->dst->type != VPT_TYPE_ATTR)) { REDEBUG("Invalid mapping destination"); return -2; } context = request; if (radius_request(&context, map->dst->request) < 0) { REDEBUG("Mapping \"%s\" -> \"%s\" invalid in this context", map->src->name, map->dst->name); return -2; } /* * If there's no CoA packet and we're updating it, * auto-allocate it. */ if (((map->dst->list == PAIR_LIST_COA) || (map->dst->list == PAIR_LIST_DM)) && !request->coa) { request_alloc_coa(context); if (map->dst->list == PAIR_LIST_COA) { context->coa->proxy->code = PW_CODE_COA_REQUEST; } else { context->coa->proxy->code = PW_CODE_DISCONNECT_REQUEST; } } list = radius_list(context, map->dst->list); if (!list) { REDEBUG("Mapping \"%s\" -> \"%s\" invalid in this context", map->src->name, map->dst->name); return -2; } parent = radius_list_ctx(context, map->dst->list); /* * The callback should either return -1 to signify operations error, -2 when it can't find the * attribute or list being referenced, or 0 to signify success. * It may return "sucess", but still have no VPs to work with. */ rcode = func(&head, request, map, ctx); if (rcode < 0) { rad_assert(!head); return rcode; } if (!head) return 0; /* * Reparent the VP */ for (vp = fr_cursor_init(&cursor, &head); vp; vp = fr_cursor_next(&cursor)) { VERIFY_VP(vp); if (debug_flag) debug_map(request, map, vp); (void) talloc_steal(parent, vp); } /* * List to list copies. */ if (map->dst->type == VPT_TYPE_LIST) { switch (map->op) { case T_OP_CMP_FALSE: rad_assert(head == NULL); pairfree(list); if (map->dst->list == PAIR_LIST_REQUEST) { context->username = NULL; context->password = NULL; } break; case T_OP_SET: if (map->src->type == VPT_TYPE_LIST) { pairfree(list); *list = head; } else { case T_OP_EQ: rad_assert(map->src->type == VPT_TYPE_EXEC); pairmove(parent, list, &head); pairfree(&head); } if (map->dst->list == PAIR_LIST_REQUEST) { context->username = pairfind(head, PW_USER_NAME, 0, TAG_ANY); context->password = pairfind(head, PW_USER_PASSWORD, 0, TAG_ANY); } break; case T_OP_ADD: pairadd(list, head); break; default: pairfree(&head); return -1; } return 0; } /* * We now should have only one destination attribute, and * only one source attribute. */ rad_assert(head->next == NULL); /* * Find the destination attribute. We leave with either * the cursor and vp pointing to the attribute, or vp is * NULL. */ num = map->dst->num; for (vp = fr_cursor_init(&cursor, list); vp != NULL; vp = fr_cursor_next(&cursor)) { VERIFY_VP(vp); if ((vp->da == map->dst->da) && (!vp->da->flags.has_tag || (map->dst->tag == TAG_ANY) || (vp->tag == map->dst->tag))) { if (num == 0) break; num--; } } /* * Figure out what to do with the source attribute. */ switch (map->op) { case T_OP_CMP_FALSE: /* remove matching attributes */ pairfree(&head); if (!vp) return 0; /* * Wildcard: delete all of the matching ones, * based on tag. */ if (!map->dst->num) { pairdelete(list, map->dst->da->attr, map->dst->da->vendor, map->dst->tag); vp = NULL; } else { /* * We've found the Nth one. Delete it, and only * it. */ vp = fr_cursor_remove(&cursor); } /* * Check that the User-Name and User-Password * caches point to the correct attribute. */ fixup: if (map->dst->list == PAIR_LIST_REQUEST) { context->username = pairfind(*list, PW_USER_NAME, 0, TAG_ANY); context->password = pairfind(*list, PW_USER_PASSWORD, 0, TAG_ANY); } pairfree(&vp); return 0; case T_OP_EQ: /* set only if not already set */ if (vp) { pairfree(&head); return 0; } fr_cursor_insert(&cursor, head); goto fixup; case T_OP_SET: /* over-write if existing, or else add */ if (vp) vp = fr_cursor_remove(&cursor); fr_cursor_insert(&cursor, head); goto fixup; case T_OP_ADD: /* append no matter what */ vp = NULL; pairadd(list, head); goto fixup; case T_OP_SUB: /* delete if it matches */ head->op = T_OP_CMP_EQ; rcode = radius_compare_vps(NULL, head, vp); pairfree(&head); if (rcode == 0) { vp = fr_cursor_remove(&cursor); goto fixup; } return 0; default: /* filtering operators */ /* * If the VP doesn't exist, the filters will add * it with the given value. */ if (!vp) { fr_cursor_insert(&cursor, head); goto fixup; } break; } /* * The LHS exists. We need to limit it's value based on * the operator, and the value of the RHS. */ head->op = map->op; rcode = radius_compare_vps(NULL, head, vp); head->op = T_OP_SET; switch (map->op) { case T_OP_CMP_EQ: if (rcode == 0) { leave: pairfree(&head); break; } replace: vp = fr_cursor_remove(&cursor); fr_cursor_insert(&cursor, head); goto fixup; case T_OP_LE: if (rcode <= 0) goto leave; goto replace; case T_OP_GE: if (rcode >= 0) goto leave; goto replace; default: pairfree(&head); return -1; } return 0; }
static int radius_acct_request(npppd *pppd, npppd_ppp *ppp, int stop) { RADIUS_PACKET *radpkt; RADIUS_REQUEST_CTX radctx; radius_req_setting *rad_setting; char buf[128]; if (ppp->username[0] == '\0') return 0; radpkt = NULL; radctx = NULL; rad_setting = npppd_auth_radius_get_radius_acct_setting(ppp->realm); if (!radius_req_setting_has_server(rad_setting)) return 0; if ((radpkt = radius_new_request_packet(RADIUS_CODE_ACCOUNTING_REQUEST)) == NULL) goto fail; if (radius_prepare(rad_setting, (void *)(uintptr_t)ppp->id, &radctx, npppd_ppp_radius_acct_reqcb, 0) != 0) goto fail; /* NAS Information */ /* * RFC 2865 "5.4. NAS-IP-Address" or RFC 3162 "2.1. NAS-IPv6-Address" */ if (radius_prepare_nas_address(rad_setting, radpkt) != 0) goto fail; /* RFC 2865 "5.41. NAS-Port-Type" */ ATTR_INT32(RADIUS_TYPE_NAS_PORT_TYPE, RADIUS_NAS_PORT_TYPE_VIRTUAL); /* RFC 2865 "5.5. NAS-Port" */ ATTR_INT32(RADIUS_TYPE_NAS_PORT, ppp->id); /* npppd has no physical / virtual ports in design. */ /* RFC 2865 5.31. Calling-Station-Id */ if (ppp->calling_number[0] != '\0') ATTR_STR(RADIUS_TYPE_CALLING_STATION_ID, ppp->calling_number); /* Tunnel Protocol Information */ switch (ppp->tunnel_type) { case PPP_TUNNEL_L2TP: /* RFC 2868 3.1. Tunnel-Type */ ATTR_INT32(RADIUS_TYPE_TUNNEL_TYPE, RADIUS_TUNNEL_TYPE_L2TP); if (l2tp_put_tunnel_attributes(radpkt, ppp->phy_context) != 0) goto fail; break; case PPP_TUNNEL_PPTP: /* RFC 2868 3.1. Tunnel-Type */ ATTR_INT32(RADIUS_TYPE_TUNNEL_TYPE, RADIUS_TUNNEL_TYPE_PPTP); if (pptp_put_tunnel_attributes(radpkt, ppp->phy_context) != 0) goto fail; break; } /* Framed Protocol (PPP) Information */ /* RFC 2865 5.1 User-Name */ ATTR_STR(RADIUS_TYPE_USER_NAME, ppp->username); /* RFC 2865 "5.7. Service-Type" */ ATTR_INT32(RADIUS_TYPE_SERVICE_TYPE, RADIUS_SERVICE_TYPE_FRAMED); /* RFC 2865 "5.8. Framed-Protocol" */ ATTR_INT32(RADIUS_TYPE_FRAMED_PROTOCOL, RADIUS_FRAMED_PROTOCOL_PPP); /* RFC 2865 "5.8. Framed-IP-Address" */ if (ppp_ip_assigned(ppp) && !stop) ppp->realm_framed_ip_address = ppp->ppp_framed_ip_address; if (ppp->realm_framed_ip_address.s_addr != INADDR_ANY) { ATTR_INT32(RADIUS_TYPE_FRAMED_IP_ADDRESS, ntohl(ppp->realm_framed_ip_address.s_addr)); } /* Accounting */ /* RFC 2866 5.1. Acct-Status-Type */ ATTR_INT32(RADIUS_TYPE_ACCT_STATUS_TYPE, (stop) ? RADIUS_ACCT_STATUS_TYPE_STOP : RADIUS_ACCT_STATUS_TYPE_START); /* RFC 2866 5.2. Acct-Delay-Time */ ATTR_INT32(RADIUS_TYPE_ACCT_DELAY_TIME, 0); if (stop) { /* RFC 2866 5.3 Acct-Input-Octets */ ATTR_INT32(RADIUS_TYPE_ACCT_INPUT_OCTETS, (uint32_t)(ppp->ibytes & 0xFFFFFFFFU)); /* LSB 32bit */ /* RFC 2866 5.4 Acct-Output-Octets */ ATTR_INT32(RADIUS_TYPE_ACCT_OUTPUT_OCTETS, (uint32_t)(ppp->obytes & 0xFFFFFFFFU)); /* LSB 32bit */ } /* RFC 2866 5.5 Acct-Session-Id */ snprintf(buf, sizeof(buf), "%08X%08X", pppd->boot_id, ppp->id); ATTR_STR(RADIUS_TYPE_ACCT_SESSION_ID, buf); /* RFC 2866 5.6. Acct-Authentic */ ATTR_INT32(RADIUS_TYPE_ACCT_AUTHENTIC, RADIUS_ACCT_AUTHENTIC_RADIUS); if (stop) { /* RFC 2866 5.7. Acct-Session-Time */ ATTR_INT32(RADIUS_TYPE_ACCT_SESSION_TIME, ppp->end_monotime - ppp->start_monotime); /* RFC 2866 5.8 Acct-Input-Packets */ ATTR_INT32(RADIUS_TYPE_ACCT_INPUT_PACKETS, ppp->ipackets); /* RFC 2866 5.9 Acct-Output-Packets */ ATTR_INT32(RADIUS_TYPE_ACCT_OUTPUT_PACKETS, ppp->opackets); /* RFC 2866 5.10. Acct-Terminate-Cause */ if (ppp->terminate_cause != 0) ATTR_INT32(RADIUS_TYPE_ACCT_TERMINATE_CAUSE, ppp->terminate_cause); /* RFC 2869 5.1 Acct-Input-Gigawords */ ATTR_INT32(RADIUS_TYPE_ACCT_INPUT_GIGAWORDS, ppp->ibytes >> 32); /* RFC 2869 5.2 Acct-Output-Gigawords */ ATTR_INT32(RADIUS_TYPE_ACCT_OUTPUT_GIGAWORDS, ppp->obytes >> 32); } radius_set_request_authenticator(radpkt, radius_get_server_secret(radctx)); /* Send the request */ radius_request(radctx, radpkt); return 0; fail: ppp_log(ppp, LOG_WARNING, "radius accounting request failed: %m"); if (radctx != NULL) radius_cancel_request(radctx); if (radpkt != NULL) radius_delete_packet(radpkt); return -1; }
/** Copy pairs matching a VPT in the current request * * @param ctx to allocate new VALUE_PAIRs under. * @param out where to write the copied vps. * @param request current request. * @param vpt the value pair template * @return -1 if VP could not be found, -2 if list could not be found, -3 if context could not be found. */ int tmpl_copy_vps(TALLOC_CTX *ctx, VALUE_PAIR **out, REQUEST *request, value_pair_tmpl_t const *vpt) { VALUE_PAIR **vps, *vp; REQUEST *current = request; vp_cursor_t from, to; rad_assert((vpt->type == TMPL_TYPE_ATTR) || (vpt->type == TMPL_TYPE_LIST)); if (out) *out = NULL; if (radius_request(¤t, vpt->tmpl_request) < 0) { return -3; } vps = radius_list(request, vpt->tmpl_list); if (!vps) { return -2; } switch (vpt->type) { /* * May not be found, but it *is* a known name. */ case TMPL_TYPE_ATTR: { int num; (void) fr_cursor_init(&to, out); (void) fr_cursor_init(&from, vps); vp = fr_cursor_next_by_da(&from, vpt->tmpl_da, vpt->tmpl_tag); if (!vp) return -1; switch (vpt->tmpl_num) { /* Copy all pairs of this type (and tag) */ case NUM_ALL: do { VERIFY_VP(vp); vp = paircopyvp(ctx, vp); if (!vp) { pairfree(out); return -4; } fr_cursor_insert(&to, vp); } while ((vp = fr_cursor_next_by_da(&from, vpt->tmpl_da, vpt->tmpl_tag))); break; /* Specific attribute number */ default: for (num = vpt->tmpl_num; num && vp; num--, vp = fr_cursor_next_by_da(&from, vpt->tmpl_da, vpt->tmpl_tag)) { VERIFY_VP(vp); } if (!vp) return -1; /* FALL-THROUGH */ /* Just copy the first pair */ case NUM_ANY: vp = paircopyvp(ctx, vp); if (!vp) { pairfree(out); return -4; } fr_cursor_insert(&to, vp); } } break; case TMPL_TYPE_LIST: vp = paircopy(ctx, *vps); if (!vp) return 0; fr_cursor_merge(&to, vp); break; default: rad_assert(0); } return 0; }
/** Print out attribute info * * Prints out all instances of a current attribute, or all attributes in a list. * * At higher debugging levels, also prints out alternative decodings of the same * value. This is helpful to determine types for unknown attributes of long * passed vendors, or just crazy/broken NAS. * * It's also useful for exposing issues in the packet decoding functions, as in * some cases they get fed random garbage data. * * This expands to a zero length string. */ static ssize_t xlat_debug_attr(UNUSED void *instance, REQUEST *request, char const *fmt, char *out, UNUSED size_t outlen) { VALUE_PAIR *vp, **vps; REQUEST *current; value_pair_tmpl_t vpt; vp_cursor_t cursor; char buffer[1024]; if (!RDEBUG_ENABLED2) { *out = '\0'; return -1; } while (isspace((int) *fmt)) fmt++; if (*fmt == '&') fmt++; if (radius_parse_attr(fmt, &vpt, REQUEST_CURRENT, PAIR_LIST_REQUEST) < 0) { return -1; } current = request; if (radius_request(¤t, vpt.request) < 0) return -2; vps = radius_list(current, vpt.list); if (!vps) { return -2; } RIDEBUG("Attributes matching \"%s\"", fmt); vp = fr_cursor_init(&cursor, vps); if (vpt.da) { vp = fr_cursor_next_by_num(&cursor, vpt.da->attr, vpt.da->vendor, TAG_ANY); } while (vp) { DICT_ATTR *dac = NULL; DICT_VENDOR *dv; VALUE_PAIR *vpc = NULL; FR_NAME_NUMBER const *type; vp_prints_value(buffer, sizeof(buffer), vp, '\''); if (vp->da->flags.has_tag) { RIDEBUG2("\t%s:%s:%i %s %s", fr_int2str(pair_lists, vpt.list, "<INVALID>"), vp->da->name, vp->tag, fr_int2str(fr_tokens, vp->op, "<INVALID>"), buffer); } else { RIDEBUG2("\t%s:%s %s %s", fr_int2str(pair_lists, vpt.list, "<INVALID>"), vp->da->name, fr_int2str(fr_tokens, vp->op, "<INVALID>"), buffer); } if (!RDEBUG_ENABLED3) { goto next_vp; } if (vp->da->vendor) { dv = dict_vendorbyvalue(vp->da->vendor); RDEBUG3("\t\tvendor : %i (%s)", vp->da->vendor, dv ? dv->name : "unknown"); } RDEBUG3("\t\ttype : %s", fr_int2str(dict_attr_types, vp->da->type, "<INVALID>")); RDEBUG3("\t\tlength : %zu", vp->length); dac = talloc_memdup(request, vp->da, sizeof(DICT_ATTR)); if (!dac) { return -1; } dac->flags.vp_free = 0; if (!RDEBUG_ENABLED4) { goto next_vp; } type = dict_attr_types; while (type->name) { int pad; ssize_t len; uint8_t const *data = NULL; vpc = NULL; if ((PW_TYPE) type->number == vp->da->type) { goto next_type; } switch (type->number) { case PW_TYPE_INVALID: /* Not real type */ case PW_TYPE_MAX: /* Not real type */ case PW_TYPE_EXTENDED: /* Not safe/appropriate */ case PW_TYPE_LONG_EXTENDED: /* Not safe/appropriate */ case PW_TYPE_TLV: /* Not safe/appropriate */ case PW_TYPE_VSA: /* @fixme We need special behaviour for these */ goto next_type; default: break; } dac->type = type->number; len = rad_vp2data(&data, vp); if (len < 0) { goto next_type; } if (data2vp(NULL, NULL, NULL, dac, data, len, len, &vpc) < 0) { goto next_type; } /* * data2vp has knowledge of expected format lengths, if the length * from rad_vp2data doesn't match, it encodes the attribute * as raw octets. This results in many useless debug lines with * the same hex string. */ if ((type->number != PW_TYPE_OCTETS) && (vpc->da->type == PW_TYPE_OCTETS)) { goto next_type; } if (!vp_prints_value(buffer, sizeof(buffer), vpc, '\'')) { goto next_type; } if ((pad = (11 - strlen(type->name))) < 0) { pad = 0; } /* * @fixme: if the value happens to decode as a VSA * (someone put a VSA into a VSA?), we probably to print * extended info for that/reparse */ RDEBUG4("\t\tas %s%*s: %s", type->name, pad, " ", buffer); next_type: talloc_free(vpc); type++; } next_vp: talloc_free(dac); if (vpt.da) { vp = fr_cursor_next_by_num(&cursor, vpt.da->attr, vpt.da->vendor, TAG_ANY); } else { vp = fr_cursor_next(&cursor); } } *out = '\0'; return 0; }
/** Convert a map to a VALUE_PAIR. * * @param[out] out Where to write the VALUE_PAIR(s). * @param[in] request structure (used only for talloc) * @param[in] map the map. The LHS (dst) has to be VPT_TYPE_ATTR or VPT_TYPE_LIST. * @param[in] ctx unused * @return 0 on success, -1 on failure, -2 on attribute not found/equivalent */ int radius_map2vp(VALUE_PAIR **out, REQUEST *request, value_pair_map_t const *map, UNUSED void *ctx) { int rcode = 0; VALUE_PAIR *vp = NULL, *found, **from = NULL; DICT_ATTR const *da; REQUEST *context = request; vp_cursor_t cursor; rad_assert(request != NULL); rad_assert(map != NULL); *out = NULL; /* * Special case for !*, we don't need to parse RHS as this is a unary operator. */ if (map->op == T_OP_CMP_FALSE) { /* * Were deleting all the attributes in a list. This isn't like the other * mappings because lists aren't represented as attributes (yet), * so we can't return a <list> attribute with the !* operator for * radius_pairmove() to consume, and need to do the work here instead. */ if (map->dst->type == VPT_TYPE_LIST) { if (radius_request(&context, map->dst->request) == 0) { from = radius_list(context, map->dst->list); } if (!from) return -2; pairfree(from); /* @fixme hacky! */ if (map->dst->list == PAIR_LIST_REQUEST) { context->username = NULL; context->password = NULL; } return 0; } /* Not a list, but an attribute, radius_pairmove() will perform that actual delete */ vp = pairalloc(request, map->dst->da); if (!vp) return -1; vp->op = map->op; *out = vp; return 0; } /* * List to list found, this is a special case because we don't need * to allocate any attributes, just finding the current list, and change * the op. */ if ((map->dst->type == VPT_TYPE_LIST) && (map->src->type == VPT_TYPE_LIST)) { if (radius_request(&context, map->src->request) == 0) { from = radius_list(context, map->src->list); } if (!from) return -2; found = paircopy(request, *from); /* * List to list copy is invalid if the src list has no attributes. */ if (!found) return -2; for (vp = fr_cursor_init(&cursor, &found); vp; vp = fr_cursor_next(&cursor)) { vp->op = T_OP_ADD; } *out = found; return 0; } /* * Deal with all non-list operations. */ da = map->dst->da ? map->dst->da : map->src->da; switch (map->src->type) { case VPT_TYPE_XLAT: case VPT_TYPE_XLAT_STRUCT: case VPT_TYPE_LITERAL: case VPT_TYPE_DATA: vp = pairalloc(request, da); if (!vp) return -1; vp->op = map->op; break; default: break; } /* * And parse the RHS */ switch (map->src->type) { ssize_t slen; char *str; case VPT_TYPE_XLAT_STRUCT: rad_assert(map->dst->da); /* Need to know where were going to write the new attribute */ rad_assert(map->src->xlat != NULL); str = NULL; slen = radius_axlat_struct(&str, request, map->src->xlat, NULL, NULL); if (slen < 0) { rcode = slen; goto error; } /* * We do the debug printing because radius_axlat_struct * doesn't have access to the original string. It's been * mangled during the parsing to xlat_exp_t */ RDEBUG2("EXPAND %s", map->src->name); RDEBUG2(" --> %s", str); rcode = pairparsevalue(vp, str); talloc_free(str); if (!rcode) { pairfree(&vp); rcode = -1; goto error; } break; case VPT_TYPE_XLAT: rad_assert(map->dst->da); /* Need to know where were going to write the new attribute */ str = NULL; slen = radius_axlat(&str, request, map->src->name, NULL, NULL); if (slen < 0) { rcode = slen; goto error; } rcode = pairparsevalue(vp, str); talloc_free(str); if (!rcode) { pairfree(&vp); rcode = -1; goto error; } break; case VPT_TYPE_LITERAL: if (!pairparsevalue(vp, map->src->name)) { rcode = -2; goto error; } break; case VPT_TYPE_ATTR: rad_assert(!map->dst->da || (map->src->da->type == map->dst->da->type) || (map->src->da->type == PW_TYPE_OCTETS) || (map->dst->da->type == PW_TYPE_OCTETS)); /* * Special case, destination is a list, found all instance of an attribute. */ if (map->dst->type == VPT_TYPE_LIST) { context = request; if (radius_request(&context, map->src->request) == 0) { from = radius_list(context, map->src->list); } /* * Can't add the attribute if the list isn't * valid. */ if (!from) { rcode = -2; goto error; } found = paircopy2(request, *from, map->src->da->attr, map->src->da->vendor, TAG_ANY); if (!found) { REDEBUG("Attribute \"%s\" not found in request", map->src->name); rcode = -2; goto error; } for (vp = fr_cursor_init(&cursor, &found); vp; vp = fr_cursor_next(&cursor)) { vp->op = T_OP_ADD; } *out = found; return 0; } if (radius_vpt_get_vp(&found, request, map->src) < 0) { REDEBUG("Attribute \"%s\" not found in request", map->src->name); rcode = -2; goto error; } /* * Copy the data over verbatim, assuming it's * actually data. */ vp = paircopyvpdata(request, da, found); if (!vp) { return -1; } vp->op = map->op; break; case VPT_TYPE_DATA: rad_assert(map->src && map->src->da); rad_assert(map->dst && map->dst->da); rad_assert(map->src->da->type == map->dst->da->type); memcpy(&vp->data, map->src->vpd, sizeof(vp->data)); vp->length = map->src->length; break; /* * This essentially does the same as rlm_exec xlat, except it's non-configurable. * It's only really here as a convenience for people who expect the contents of * backticks to be executed in a shell. * * exec string is xlat expanded and arguments are shell escaped. */ case VPT_TYPE_EXEC: return radius_mapexec(out, request, map); default: rad_assert(0); /* Should of been caught at parse time */ error: pairfree(&vp); return rcode; } *out = vp; return 0; }
/** Return a VP from a value_pair_tmpl_t * * @param out where to write the retrieved vp. * @param request current request. * @param vpt the value pair template * @return -1 if VP could not be found, -2 if list could not be found, -3 if context could not be found. */ int radius_vpt_get_vp(VALUE_PAIR **out, REQUEST *request, value_pair_tmpl_t const *vpt) { VALUE_PAIR **vps, *vp; if (out) *out = NULL; if (radius_request(&request, vpt->request) < 0) { return -3; } vps = radius_list(request, vpt->list); if (!vps) { return -2; } switch (vpt->type) { /* * May not may not be found, but it *is* a known * name. */ case VPT_TYPE_ATTR: if (vpt->num == 0) { vp = pairfind(*vps, vpt->da->attr, vpt->da->vendor, vpt->tag); if (!vp) return -1; } else { int num; vp_cursor_t cursor; /* * It's faster to just repeat the 3-4 lines of pairfind here. */ num = vpt->num; for (vp = fr_cursor_init(&cursor, vps); vp != NULL; vp = fr_cursor_next(&cursor)) { VERIFY_VP(vp); if ((vp->da == vpt->da) && (!vp->da->flags.has_tag || (vpt->tag == TAG_ANY) || (vp->tag == vpt->tag))) { if (num == 0) { *out = vp; return 0; } num--; } } return -1; } break; case VPT_TYPE_LIST: vp = *vps; break; default: /* * literal, xlat, regex, exec, data. * no attribute. */ return -1; } if (out) { *out = vp; } return 0; }
/** Expand values in an attribute map where needed * */ int rlm_ldap_map_xlat(REQUEST *request, value_pair_map_t const *maps, rlm_ldap_map_xlat_t *expanded) { value_pair_map_t const *map; unsigned int total = 0; VALUE_PAIR *found, **from = NULL; REQUEST *context; for (map = maps; map != NULL; map = map->next) { switch (map->src->type) { case VPT_TYPE_XLAT: { ssize_t len; char *exp = NULL; len = radius_xlat(exp, 0, request, map->src->name, NULL, NULL); if (len < 0) { RDEBUG("Expansion of LDAP attribute \"%s\" failed", map->src->name); goto error; } expanded->attrs[total++] = exp; break; } case VPT_TYPE_ATTR: context = request; if (radius_request(&context, map->src->vpt_request) == 0) { from = radius_list(context, map->src->vpt_list); } if (!from) continue; found = pairfind(*from, map->src->vpt_da->attr, map->src->vpt_da->vendor, TAG_ANY); if (!found) continue; expanded->attrs[total++] = talloc_typed_strdup(request, found->vp_strvalue); break; case VPT_TYPE_EXEC: { char answer[1024]; VALUE_PAIR **input_pairs = NULL; int result; input_pairs = radius_list(request, PAIR_LIST_REQUEST); result = radius_exec_program(request, map->src->name, true, true, answer, sizeof(answer), EXEC_TIMEOUT, input_pairs ? *input_pairs : NULL, NULL); if (result != 0) { return -1; } expanded->attrs[total++] = talloc_typed_strdup(request, answer); } break; case VPT_TYPE_LITERAL: expanded->attrs[total++] = map->src->name; break; default: rad_assert(0); error: expanded->attrs[total] = NULL; rlm_ldap_map_xlat_free(expanded); return -1; } } rad_assert(total < LDAP_MAX_ATTRMAP); expanded->attrs[total] = NULL; expanded->count = total; expanded->maps = maps; return 0; }
static void pap_radius_authenticate(pap *_this, const char *username, const char *password) { void *radctx; RADIUS_PACKET *radpkt; MD5_CTX md5ctx; int i, j, s_len, passlen; u_char ra[16], digest[16], pass[128]; const char *s; radius_req_setting *rad_setting = NULL; char buf0[MAX_USERNAME_LENGTH]; if ((rad_setting = npppd_get_radius_auth_setting(_this->ppp->pppd, _this->ppp)) == NULL) goto fail; if ((radpkt = radius_new_request_packet(RADIUS_CODE_ACCESS_REQUEST)) == NULL) goto fail; if (radius_prepare(rad_setting, _this, &radctx, pap_radius_response) != 0) { radius_delete_packet(radpkt); goto fail; } if (ppp_set_radius_attrs_for_authreq(_this->ppp, rad_setting, radpkt) != 0) goto fail; if (radius_put_string_attr(radpkt, RADIUS_TYPE_USER_NAME, npppd_ppp_get_username_for_auth(_this->ppp->pppd, _this->ppp, username, buf0)) != 0) goto fail; if (_this->radctx != NULL) radius_cancel_request(_this->radctx); _this->radctx = radctx; /* Create RADIUS User-Password Attribute (RFC 2865, 5.2.) */ s = radius_get_server_secret(_this->radctx); s_len = strlen(s); memset(pass, 0, sizeof(pass)); /* null padding */ passlen = MINIMUM(strlen(password), sizeof(pass)); memcpy(pass, password, passlen); if ((passlen % 16) != 0) passlen += 16 - (passlen % 16); radius_get_authenticator(radpkt, ra); MD5Init(&md5ctx); MD5Update(&md5ctx, s, s_len); MD5Update(&md5ctx, ra, 16); MD5Final(digest, &md5ctx); for (i = 0; i < 16; i++) pass[i] ^= digest[i]; while (i < passlen) { MD5Init(&md5ctx); MD5Update(&md5ctx, s, s_len); MD5Update(&md5ctx, &pass[i - 16], 16); MD5Final(digest, &md5ctx); for (j = 0; j < 16; j++, i++) pass[i] ^= digest[j]; } if (radius_put_raw_attr(radpkt, RADIUS_TYPE_USER_PASSWORD, pass, passlen) != 0) goto fail; radius_request(_this->radctx, radpkt); return; fail: if (_this->radctx != NULL) radius_cancel_request(_this->radctx); pap_log(_this, LOG_ERR, "%s() failed: %m", __func__); pap_response(_this, 0, DEFAULT_ERROR_MESSAGE); return; }