static int arp_socket_decode(UNUSED rad_listen_t *listener, UNUSED REQUEST *request) { int i; arp_over_ether_t const *arp; uint8_t const *p; arp = (arp_over_ether_t const *) request->packet->data; /* * arp_socket_recv() takes care of validating it's really * our kind of ARP. */ for (i = 0, p = (uint8_t const *) arp; header_names[i].name != NULL; p += header_names[i].len, i++) { ssize_t len; DICT_ATTR const *da; VALUE_PAIR *vp; da = dict_attrbyname(header_names[i].name); if (!da) return 0; vp = NULL; len = data2vp(request->packet, NULL, NULL, da, p, header_names[i].len, header_names[i].len, &vp); if (len <= 0) { RDEBUG("Failed decoding %s: %s", header_names[i].name, fr_strerror()); return 0; } debug_pair(vp); pairadd(&request->packet->vps, vp); } return 0; }
/** Evaluate a map * * @param[in] request the REQUEST * @param[in] modreturn the previous module return code * @param[in] depth of the recursion (only used for debugging) * @param[in] c the condition to evaluate * @return -1 on error, 0 for "no match", 1 for "match". */ int radius_evaluate_map(REQUEST *request, UNUSED int modreturn, UNUSED int depth, fr_cond_t const *c) { int rcode; char *lhs, *rhs; value_pair_map_t *map; rad_assert(c->type == COND_TYPE_MAP); map = c->data.map; rad_assert(map->dst->type != VPT_TYPE_UNKNOWN); rad_assert(map->src->type != VPT_TYPE_UNKNOWN); rad_assert(map->dst->type != VPT_TYPE_LIST); rad_assert(map->src->type != VPT_TYPE_LIST); rad_assert(map->dst->type != VPT_TYPE_REGEX); /* * Verify regexes. */ if (map->src->type == VPT_TYPE_REGEX) { rad_assert(map->op == T_OP_REG_EQ); } else { rad_assert(!((map->op == T_OP_REG_EQ) || (map->op == T_OP_REG_NE))); } /* * They're both attributes. Do attribute-specific work. * * LHS is DST. RHS is SRC <sigh> */ if (!c->cast && (map->src->type == VPT_TYPE_ATTR) && (map->dst->type == VPT_TYPE_ATTR)) { VALUE_PAIR *lhs_vp, *rhs_vp; EVAL_DEBUG("ATTR to ATTR"); if ((radius_vpt_get_vp(&lhs_vp, request, map->dst) < 0) || (radius_vpt_get_vp(&rhs_vp, request, map->src) < 0)) { return -2; } return paircmp_op(lhs_vp, map->op, rhs_vp); } /* * LHS is a cast. Do type-specific comparisons, as if * the LHS was a real attribute. */ if (c->cast) { VALUE_PAIR *lhs_vp, *rhs_vp; rcode = get_cast_vp(&lhs_vp, request, map->dst, c->cast); if (rcode < 0) { return rcode; } rad_assert(lhs_vp); /* * Get either a real VP, or parse the RHS into a * VP, and return that. */ if (map->src->type == VPT_TYPE_ATTR) { if (radius_vpt_get_vp(&rhs_vp, request, map->src) < 0) { return -2; } } else { rcode = get_cast_vp(&rhs_vp, request, map->src, c->cast); if (rcode < 0) { return rcode; } rad_assert(rhs_vp); } if (!rhs_vp) return -2; EVAL_DEBUG("CAST to ..."); rcode = paircmp_op(lhs_vp, map->op, rhs_vp); pairfree(&lhs_vp); if (map->src->type != VPT_TYPE_ATTR) { pairfree(&rhs_vp); } return rcode; } /* * Might be a virtual comparison */ if ((map->dst->type == VPT_TYPE_ATTR) && (map->src->type != VPT_TYPE_REGEX) && (c->pass2_fixup == PASS2_PAIRCOMPARE)) { VALUE_PAIR *rhs_vp; EVAL_DEBUG("virtual ATTR to DATA"); rcode = get_cast_vp(&rhs_vp, request, map->src, map->dst->da); if (rcode < 0) { return rcode; } rad_assert(rhs_vp); if (paircompare(request, request->packet->vps, rhs_vp, NULL) == 0) { return true; } return false; } rad_assert(c->pass2_fixup != PASS2_PAIRCOMPARE); /* * RHS has been pre-parsed into binary data. Go check * that. */ if ((map->dst->type == VPT_TYPE_ATTR) && (map->src->type == VPT_TYPE_DATA)) { VALUE_PAIR *lhs_vp, *rhs_vp; EVAL_DEBUG("ATTR to DATA"); if (radius_vpt_get_vp(&lhs_vp, request, map->dst) < 0) { return -2; } rcode = get_cast_vp(&rhs_vp, request, map->src, map->dst->da); if (rcode < 0) { return rcode; } rad_assert(rhs_vp); #ifdef WITH_EVAL_DEBUG debug_pair(lhs_vp); debug_pair(rhs_vp); #endif rcode = paircmp_op(lhs_vp, map->op, rhs_vp); pairfree(&rhs_vp); return rcode; } rad_assert(map->src->type != VPT_TYPE_DATA); rad_assert(map->dst->type != VPT_TYPE_DATA); /* * The RHS now needs to be expanded into a string. */ rcode = radius_expand_tmpl(&rhs, request, map->src); if (rcode < 0) { EVAL_DEBUG("FAIL %d", __LINE__); return rcode; } /* * User-Name == FOO * * Parse the RHS to be the same DA as the LHS. do * comparisons. So long as it's not a regex, which does * string comparisons. * * The LHS may be a virtual attribute, too. */ if ((map->dst->type == VPT_TYPE_ATTR) && (map->src->type != VPT_TYPE_REGEX)) { VALUE_PAIR *lhs_vp, *rhs_vp; EVAL_DEBUG("ATTR to non-REGEX"); /* * No LHS means no match */ if (radius_vpt_get_vp(&lhs_vp, request, map->dst) < 0) { /* * Not a real attr: might be a dynamic comparison. */ if ((map->dst->type == VPT_TYPE_ATTR) && (map->dst->da->vendor == 0) && radius_find_compare(map->dst->da)) { rhs_vp = pairalloc(request, map->dst->da); if (!pairparsevalue(rhs_vp, rhs)) { talloc_free(rhs); EVAL_DEBUG("FAIL %d", __LINE__); return -1; } talloc_free(rhs); rcode = (radius_callback_compare(request, NULL, rhs_vp, NULL, NULL) == 0); pairfree(&rhs_vp); return rcode; } return -2; } /* * Get VP for RHS */ rhs_vp = pairalloc(request, map->dst->da); rad_assert(rhs_vp != NULL); if (!pairparsevalue(rhs_vp, rhs)) { talloc_free(rhs); pairfree(&rhs_vp); EVAL_DEBUG("FAIL %d", __LINE__); return -1; } rcode = paircmp_op(lhs_vp, map->op, rhs_vp); talloc_free(rhs); pairfree(&rhs_vp); return rcode; } /* * The LHS is a string. Expand it. */ rcode = radius_expand_tmpl(&lhs, request, map->dst); if (rcode < 0) { EVAL_DEBUG("FAIL %d", __LINE__); return rcode; } EVAL_DEBUG("LHS is %s", lhs); /* * Compile the RHS to a regex, and do regex stuff */ if (map->src->type == VPT_TYPE_REGEX) { return do_regex(request, lhs, rhs, c->regex_i); } /* * Loop over the string, doing comparisons */ if (all_digits(lhs) && all_digits(rhs)) { int lint, rint; lint = strtoul(lhs, NULL, 0); rint = strtoul(rhs, NULL, 0); talloc_free(lhs); talloc_free(rhs); switch (map->op) { case T_OP_CMP_EQ: return (lint == rint); case T_OP_NE: return (lint != rint); case T_OP_LT: return (lint < rint); case T_OP_GT: return (lint > rint); case T_OP_LE: return (lint <= rint); case T_OP_GE: return (lint >= rint); default: break; } } else { rcode = strcmp(lhs, rhs); talloc_free(lhs); talloc_free(rhs); switch (map->op) { case T_OP_CMP_EQ: return (rcode == 0); case T_OP_NE: return (rcode != 0); case T_OP_LT: return (rcode < 0); case T_OP_GT: return (rcode > 0); case T_OP_LE: return (rcode <= 0); case T_OP_GE: return (rcode >= 0); default: break; } } EVAL_DEBUG("FAIL %d", __LINE__); return -1; }
int vqp_encode(RADIUS_PACKET *packet, RADIUS_PACKET *original) { int i, code, length; VALUE_PAIR *vp; uint8_t *ptr; VALUE_PAIR *vps[VQP_MAX_ATTRIBUTES]; if (!packet) { fr_strerror_printf("Failed encoding VQP"); return -1; } if (packet->data) return 0; vp = pairfind(packet->vps, PW_VQP_PACKET_TYPE); if (!vp) { fr_strerror_printf("Failed to find VQP-Packet-Type in response packet"); return -1; } code = vp->lvalue; if ((code < 1) || (code > 4)) { fr_strerror_printf("Invalid value %d for VQP-Packet-Type", code); return -1; } length = VQP_HDR_LEN; memset(vps, 0, sizeof(vps)); vp = pairfind(packet->vps, PW_VQP_ERROR_CODE); /* * FIXME: Map attributes from calling-station-Id, etc. * * Maybe do this via rlm_vqp? That's probably the * best place to add the code... */ /* * No error: encode attributes. */ if (!vp) for (i = 0; i < VQP_MAX_ATTRIBUTES; i++) { if (!contents[code][i]) break; vps[i] = pairfind(packet->vps, contents[code][i] | 0x2000); /* * FIXME: Print the name... */ if (!vps[i]) { fr_strerror_printf("Failed to find VQP attribute %02x", contents[code][i]); return -1; } length += 6; length += vps[i]->length; } packet->data = malloc(length); if (!packet->data) { fr_strerror_printf("No memory"); return -1; } packet->data_len = length; ptr = packet->data; ptr[0] = VQP_VERSION; ptr[1] = code; if (!vp) { ptr[2] = 0; } else { ptr[2] = vp->lvalue & 0xff; return 0; } /* * The number of attributes is hard-coded. */ if ((code == 1) || (code == 3)) { uint32_t sequence; ptr[3] = VQP_MAX_ATTRIBUTES; sequence = htonl(packet->id); memcpy(ptr + 4, &sequence, 4); } else { if (!original) { fr_strerror_printf("Cannot send VQP response without request"); return -1; } /* * Packet Sequence Number */ memcpy(ptr + 4, original->data + 4, 4); ptr[3] = 2; } ptr += 8; /* * Encode the VP's. */ for (i = 0; i < VQP_MAX_ATTRIBUTES; i++) { if (!vps[i]) break; vp = vps[i]; debug_pair(vp); /* * Type. Note that we look at only the lower 8 * bits, as the upper 8 bits have been hacked. * See also dictionary.vqp */ ptr[0] = 0; ptr[1] = 0; ptr[2] = 0x0c; ptr[3] = vp->attribute & 0xff; /* Length */ ptr[4] = 0; ptr[5] = vp->length & 0xff; ptr += 6; /* Data */ switch (vp->type) { case PW_TYPE_IPADDR: memcpy(ptr, &vp->vp_ipaddr, 4); break; default: case PW_TYPE_OCTETS: case PW_TYPE_STRING: memcpy(ptr, vp->vp_octets, vp->length); break; } ptr += vp->length; } return 0; }
int vqp_decode(RADIUS_PACKET *packet) { uint8_t *ptr, *end; int attribute, length; VALUE_PAIR *vp, **tail; if (!packet || !packet->data) return -1; if (packet->data_len < VQP_HDR_LEN) return -1; tail = &packet->vps; vp = paircreate(PW_VQP_PACKET_TYPE, PW_TYPE_OCTETS); if (!vp) { fr_strerror_printf("No memory"); return -1; } vp->lvalue = packet->data[1]; debug_pair(vp); *tail = vp; tail = &(vp->next); vp = paircreate(PW_VQP_ERROR_CODE, PW_TYPE_OCTETS); if (!vp) { fr_strerror_printf("No memory"); return -1; } vp->lvalue = packet->data[2]; debug_pair(vp); *tail = vp; tail = &(vp->next); vp = paircreate(PW_VQP_SEQUENCE_NUMBER, PW_TYPE_OCTETS); if (!vp) { fr_strerror_printf("No memory"); return -1; } vp->lvalue = packet->id; /* already set by vqp_recv */ debug_pair(vp); *tail = vp; tail = &(vp->next); ptr = packet->data + VQP_HDR_LEN; end = packet->data + packet->data_len; /* * Note that vqp_recv() MUST ensure that the packet is * formatted in a way we expect, and that vqp_recv() MUST * be called before vqp_decode(). */ while (ptr < end) { attribute = (ptr[2] << 8) | ptr[3]; length = (ptr[4] << 8) | ptr[5]; ptr += 6; /* * Hack to get the dictionaries to work correctly. */ attribute |= 0x2000; vp = paircreate(attribute, PW_TYPE_OCTETS); if (!vp) { pairfree(&packet->vps); fr_strerror_printf("No memory"); return -1; } switch (vp->type) { case PW_TYPE_IPADDR: if (length == 4) { memcpy(&vp->vp_ipaddr, ptr, 4); vp->length = 4; break; } vp->type = PW_TYPE_OCTETS; /* FALL-THROUGH */ default: case PW_TYPE_OCTETS: case PW_TYPE_STRING: vp->length = (length > MAX_VMPS_LEN) ? MAX_VMPS_LEN : length; memcpy(vp->vp_octets, ptr, length); vp->vp_octets[length] = '\0'; break; } ptr += length; debug_pair(vp); *tail = vp; tail = &(vp->next); } /* * FIXME: Map attributes to Calling-Station-Id, etc... */ return 0; }
/** Evaluate a map * * @param[in] request the REQUEST * @param[in] modreturn the previous module return code * @param[in] depth of the recursion (only used for debugging) * @param[in] c the condition to evaluate * @return -1 on error, 0 for "no match", 1 for "match". */ int radius_evaluate_map(REQUEST *request, UNUSED int modreturn, UNUSED int depth, fr_cond_t const *c) { int rcode; char *lhs, *rhs; value_pair_map_t *map; rad_assert(c->type == COND_TYPE_MAP); map = c->data.map; rad_assert(map->dst->type != TMPL_TYPE_UNKNOWN); rad_assert(map->src->type != TMPL_TYPE_UNKNOWN); rad_assert(map->dst->type != TMPL_TYPE_LIST); rad_assert(map->src->type != TMPL_TYPE_LIST); rad_assert(map->dst->type != TMPL_TYPE_REGEX); rad_assert(map->dst->type != TMPL_TYPE_REGEX_STRUCT); EVAL_DEBUG("MAP TYPES LHS: %s, RHS: %s", fr_int2str(template_names, map->dst->type, "???"), fr_int2str(template_names, map->src->type, "???")); /* * Verify regexes. */ if ((map->src->type == TMPL_TYPE_REGEX) || (map->src->type == TMPL_TYPE_REGEX_STRUCT)) { rad_assert(map->op == T_OP_REG_EQ); } else { rad_assert(!((map->op == T_OP_REG_EQ) || (map->op == T_OP_REG_NE))); } /* * They're both attributes. Do attribute-specific work. * * LHS is DST. RHS is SRC <sigh> */ if (!c->cast && (map->src->type == TMPL_TYPE_ATTR) && (map->dst->type == TMPL_TYPE_ATTR)) { VALUE_PAIR *lhs_vp, *rhs_vp, *cast_vp; EVAL_DEBUG("ATTR to ATTR"); if ((tmpl_find_vp(&lhs_vp, request, map->dst) < 0) || (tmpl_find_vp(&rhs_vp, request, map->src) < 0)) return -1; if (map->dst->tmpl_da->type == map->src->tmpl_da->type) { return paircmp_op(lhs_vp, map->op, rhs_vp); } /* * Compare a large integer (lhs) to a small integer (rhs). * We allow this without a cast. */ rad_assert((map->dst->tmpl_da->type == PW_TYPE_INTEGER64) || (map->dst->tmpl_da->type == PW_TYPE_INTEGER) || (map->dst->tmpl_da->type == PW_TYPE_SHORT)); rad_assert((map->src->tmpl_da->type == PW_TYPE_INTEGER) || (map->src->tmpl_da->type == PW_TYPE_SHORT) || (map->src->tmpl_da->type == PW_TYPE_BYTE)); cast_vp = pairalloc(request, lhs_vp->da); if (!cast_vp) return false; /* * Copy the RHS to the casted type. */ if (do_cast_copy(cast_vp, rhs_vp) < 0) { talloc_free(cast_vp); return false; } rcode = paircmp_op(lhs_vp, map->op, cast_vp); talloc_free(cast_vp); return rcode; } /* * LHS is a cast. Do type-specific comparisons, as if * the LHS was a real attribute. */ if (c->cast) { VALUE_PAIR *lhs_vp, *rhs_vp; /* * Try to copy data from the VP which is being * casted, instead of printing it to a string and * then re-parsing it. */ if (map->dst->type == TMPL_TYPE_ATTR) { VALUE_PAIR *cast_vp; if (tmpl_find_vp(&cast_vp, request, map->dst) < 0) return false; lhs_vp = pairalloc(request, c->cast); if (!lhs_vp) return -1; /* * In a separate function for clarity */ if (do_cast_copy(lhs_vp, cast_vp) < 0) { talloc_free(lhs_vp); return -1; } } else { rcode = tmpl_cast_to_vp(&lhs_vp, request, map->dst, c->cast); if (rcode < 0) { return rcode; } } rad_assert(lhs_vp); /* * Get either a real VP, or parse the RHS into a * VP, and return that. */ if (map->src->type == TMPL_TYPE_ATTR) { if (tmpl_find_vp(&rhs_vp, request, map->src) < 0) { return -2; } } else { rcode = tmpl_cast_to_vp(&rhs_vp, request, map->src, c->cast); if (rcode < 0) { return rcode; } rad_assert(rhs_vp); } if (!rhs_vp) return -2; EVAL_DEBUG("CAST to %s", fr_int2str(dict_attr_types, c->cast->type, "?Unknown?")); rcode = paircmp_op(lhs_vp, map->op, rhs_vp); pairfree(&lhs_vp); if (map->src->type != TMPL_TYPE_ATTR) { pairfree(&rhs_vp); } return rcode; } /* * Might be a virtual comparison */ if ((map->dst->type == TMPL_TYPE_ATTR) && (map->src->type != TMPL_TYPE_REGEX) && (map->src->type != TMPL_TYPE_REGEX_STRUCT) && (c->pass2_fixup == PASS2_PAIRCOMPARE)) { int ret; VALUE_PAIR *lhs_vp; EVAL_DEBUG("virtual ATTR to DATA"); rcode = tmpl_cast_to_vp(&lhs_vp, request, map->src, map->dst->tmpl_da); if (rcode < 0) return rcode; rad_assert(lhs_vp); /* * paircompare requires the operator be set for the * check attribute. */ lhs_vp->op = map->op; ret = paircompare(request, request->packet->vps, lhs_vp, NULL); talloc_free(lhs_vp); if (ret == 0) { return true; } return false; } rad_assert(c->pass2_fixup != PASS2_PAIRCOMPARE); /* * RHS has been pre-parsed into binary data. Go check * that. */ if ((map->dst->type == TMPL_TYPE_ATTR) && (map->src->type == TMPL_TYPE_DATA)) { VALUE_PAIR *lhs_vp, *rhs_vp; EVAL_DEBUG("ATTR to DATA"); if (tmpl_find_vp(&lhs_vp, request, map->dst) < 0) return -2; rcode = tmpl_cast_to_vp(&rhs_vp, request, map->src, map->dst->tmpl_da); if (rcode < 0) return rcode; rad_assert(rhs_vp); #ifdef WITH_EVAL_DEBUG debug_pair(lhs_vp); debug_pair(rhs_vp); #endif rcode = paircmp_op(lhs_vp, map->op, rhs_vp); pairfree(&rhs_vp); return rcode; } rad_assert(map->src->type != TMPL_TYPE_DATA); rad_assert(map->dst->type != TMPL_TYPE_DATA); #ifdef HAVE_REGEX_H /* * Parse regular expressions. */ if ((map->src->type == TMPL_TYPE_REGEX) || (map->src->type == TMPL_TYPE_REGEX_STRUCT)) { return do_regex(request, map); } #endif /* * The RHS now needs to be expanded into a string. */ rcode = radius_expand_tmpl(&rhs, request, map->src); if (rcode < 0) { EVAL_DEBUG("FAIL %d", __LINE__); return rcode; } rad_assert(rhs != NULL); /* * User-Name == FOO * * Parse the RHS to be the same DA as the LHS. do * comparisons. So long as it's not a regex, which does * string comparisons. * * The LHS may be a virtual attribute, too. */ if (map->dst->type == TMPL_TYPE_ATTR) { VALUE_PAIR *lhs_vp, *rhs_vp; EVAL_DEBUG("ATTR to non-REGEX"); /* * No LHS means no match */ if (tmpl_find_vp(&lhs_vp, request, map->dst) < 0) { /* * Not a real attr: might be a dynamic comparison. */ if ((map->dst->type == TMPL_TYPE_ATTR) && (map->dst->tmpl_da->vendor == 0) && radius_find_compare(map->dst->tmpl_da)) { rhs_vp = pairalloc(request, map->dst->tmpl_da); rad_assert(rhs_vp != NULL); if (pairparsevalue(rhs_vp, rhs, 0) < 0) { talloc_free(rhs); EVAL_DEBUG("FAIL %d", __LINE__); return -1; } talloc_free(rhs); rcode = (radius_callback_compare(request, NULL, rhs_vp, NULL, NULL) == 0); pairfree(&rhs_vp); return rcode; } return -2; } /* * Get VP for RHS */ rhs_vp = pairalloc(request, map->dst->tmpl_da); rad_assert(rhs_vp != NULL); if (pairparsevalue(rhs_vp, rhs, 0) < 0) { talloc_free(rhs); pairfree(&rhs_vp); EVAL_DEBUG("FAIL %d", __LINE__); return -1; } rcode = paircmp_op(lhs_vp, map->op, rhs_vp); talloc_free(rhs); pairfree(&rhs_vp); return rcode; } /* * The LHS is a string. Expand it. */ rcode = radius_expand_tmpl(&lhs, request, map->dst); if (rcode < 0) { EVAL_DEBUG("FAIL %d", __LINE__); return rcode; } rad_assert(lhs != NULL); EVAL_DEBUG("LHS is %s", lhs); /* * Loop over the string, doing comparisons */ if (all_digits(lhs) && all_digits(rhs)) { int lint, rint; lint = strtoul(lhs, NULL, 0); rint = strtoul(rhs, NULL, 0); talloc_free(lhs); talloc_free(rhs); switch (map->op) { case T_OP_CMP_EQ: return (lint == rint); case T_OP_NE: return (lint != rint); case T_OP_LT: return (lint < rint); case T_OP_GT: return (lint > rint); case T_OP_LE: return (lint <= rint); case T_OP_GE: return (lint >= rint); default: break; } } else { rad_assert(lhs != NULL); rad_assert(rhs != NULL); rcode = strcmp(lhs, rhs); talloc_free(lhs); talloc_free(rhs); switch (map->op) { case T_OP_CMP_EQ: return (rcode == 0); case T_OP_NE: return (rcode != 0); case T_OP_LT: return (rcode < 0); case T_OP_GT: return (rcode > 0); case T_OP_LE: return (rcode <= 0); case T_OP_GE: return (rcode >= 0); default: break; } } EVAL_DEBUG("FAIL %d", __LINE__); return -1; }
/* * Reply to the request. Also attach * reply attribute value pairs and any user message provided. */ int rad_send(RADIUS_PACKET *packet, const RADIUS_PACKET *original, const char *secret) { VALUE_PAIR *reply; struct sockaddr_in saremote; struct sockaddr_in *sa; const char *what; uint8_t ip_buffer[16]; if ((packet->code > 0) && (packet->code < 52)) { what = packet_codes[packet->code]; } else { what = "Reply"; } /* * First time through, allocate room for the packet */ if (!packet->data) { radius_packet_t *hdr; uint32_t lvalue; uint8_t *ptr, *length_ptr, *vsa_length_ptr; uint8_t digest[16]; int secretlen; int vendorcode, vendorpec; u_short total_length; int len, allowed; int msg_auth_offset = 0; /* * For simplicity in the following logic, we allow * the attributes to "overflow" the 4k maximum * RADIUS packet size, by one attribute. */ uint8_t data[MAX_PACKET_LEN + 256]; /* * Use memory on the stack, until we know how * large the packet will be. */ hdr = (radius_packet_t *) data; /* * Build standard header */ hdr->code = packet->code; hdr->id = packet->id; if ((packet->code == PW_ACCOUNTING_REQUEST) || (packet->code == PW_DISCONNECT_REQUEST)) { memset(hdr->vector, 0, AUTH_VECTOR_LEN); } else { memcpy(hdr->vector, packet->vector, AUTH_VECTOR_LEN); } DEBUG("Sending %s of id %d to %s:%d\n", what, packet->id, ip_ntoa((char *)ip_buffer, packet->dst_ipaddr), packet->dst_port); total_length = AUTH_HDR_LEN; /* * Load up the configuration values for the user */ ptr = hdr->data; vendorcode = 0; vendorpec = 0; vsa_length_ptr = NULL; for (reply = packet->vps; reply; reply = reply->next) { /* * Ignore non-wire attributes */ if ((VENDOR(reply->attribute) == 0) && ((reply->attribute & 0xFFFF) > 0xff)) { continue; } /* * Check that the packet is no more than * 4k in size, AFTER over-flowing the 4k * boundary. Note that the 'data' * buffer, above, is one attribute longer * than necessary, in order to permit * this overflow. */ if (total_length > MAX_PACKET_LEN) { librad_log("ERROR: Too many attributes for packet, result is larger than RFC maximum of 4k"); return -1; } /* * Do stuff for Message-Authenticator */ if (reply->attribute == PW_MESSAGE_AUTHENTICATOR) { /* * Set it to zero! */ reply->length = AUTH_VECTOR_LEN; memset(reply->strvalue, 0, AUTH_VECTOR_LEN); msg_auth_offset = total_length; } /* * Print out ONLY the attributes which * we're sending over the wire, and print * them out BEFORE they're encrypted. */ debug_pair(reply); /* * We have a different vendor. Re-set * the vendor codes. */ if (vendorcode != VENDOR(reply->attribute)) { vendorcode = 0; vendorpec = 0; vsa_length_ptr = NULL; } /* * If the Vendor-Specific attribute is getting * full, then create a new VSA attribute * * FIXME: Multiple VSA's per Vendor-Specific * SHOULD be configurable. When that's done, * the (1), below, can be changed to point to * a configuration variable which is set TRUE * if the NAS cannot understand multiple VSA's * per Vendor-Specific */ if ((1) || /* ALWAYS create a new Vendor-Specific */ (vsa_length_ptr && (reply->length + *vsa_length_ptr) >= MAX_STRING_LEN)) { vendorcode = 0; vendorpec = 0; vsa_length_ptr = NULL; } /* * Maybe we have the start of a set of * (possibly many) VSA attributes from * one vendor. Set a global VSA wrapper */ if ((vendorcode == 0) && ((vendorcode = VENDOR(reply->attribute)) != 0)) { vendorpec = dict_vendorpec(vendorcode); /* * This is a potentially bad error... * we can't find the vendor ID! */ if (vendorpec == 0) { /* FIXME: log an error */ continue; } /* * Build a VSA header. */ *ptr++ = PW_VENDOR_SPECIFIC; vsa_length_ptr = ptr; *ptr++ = 6; lvalue = htonl(vendorpec); memcpy(ptr, &lvalue, 4); ptr += 4; total_length += 6; } if (vendorpec == VENDORPEC_USR) { lvalue = htonl(reply->attribute & 0xFFFF); memcpy(ptr, &lvalue, 4); length_ptr = vsa_length_ptr; total_length += 4; *length_ptr += 4; ptr += 4; /* * Each USR attribute gets it's own * VSA wrapper, so we re-set the * vendor specific information. */ vendorcode = 0; vendorpec = 0; vsa_length_ptr = NULL; } else { /* * All other attributes are as * per the RFC spec. */ *ptr++ = (reply->attribute & 0xFF); length_ptr = ptr; if (vsa_length_ptr) *vsa_length_ptr += 2; *ptr++ = 2; total_length += 2; } switch(reply->type) { /* * Ascend binary attributes are * stored internally in binary form. */ case PW_TYPE_ABINARY: case PW_TYPE_STRING: case PW_TYPE_OCTETS: /* * FIXME: HACK for non-updated dictionaries. * REMOVE in a future release. */ if ((strcmp(reply->name, "Ascend-Send-Secret") == 0) || (strcmp(reply->name, "Ascend-Receive-Secret") == 0)) { reply->flags.encrypt = FLAG_ENCRYPT_ASCEND_SECRET; } if (reply->attribute == PW_USER_PASSWORD) { reply->flags.encrypt = FLAG_ENCRYPT_USER_PASSWORD; } /* * Encrypt the various password styles */ switch (reply->flags.encrypt) { default: break; case FLAG_ENCRYPT_USER_PASSWORD: rad_pwencode((char *)reply->strvalue, &(reply->length), (const char *)secret, (const char *)packet->vector); break; case FLAG_ENCRYPT_TUNNEL_PASSWORD: rad_tunnel_pwencode(reply->strvalue, &(reply->length), secret, packet->vector); break; case FLAG_ENCRYPT_ASCEND_SECRET: make_secret(digest, packet->vector, secret, reply->strvalue); memcpy(reply->strvalue, digest, AUTH_VECTOR_LEN ); reply->length = AUTH_VECTOR_LEN; } /* switch over encryption flags */ len = reply->length; /* * Set the TAG at the beginning * of the string if tagged. If * tag value is not valid for * tagged attribute, make it 0x00 * per RFC 2868. -cparker */ if (reply->flags.has_tag) { if (TAG_VALID(reply->flags.tag)) { len++; *ptr++ = reply->flags.tag; } else if (reply->flags.encrypt == FLAG_ENCRYPT_TUNNEL_PASSWORD) { /* * Tunnel passwords * REQUIRE a tag, * even if we don't * have a valid * tag. */ len++; *ptr++ = 0x00; } /* else don't write a tag */ } /* else the attribute doesn't have a tag */ /* * Ensure we don't go too far. * The 'length' of the attribute * may be 0..255, minus whatever * octets are used in the attribute * header. */ allowed = 255; if (vsa_length_ptr) { allowed -= *vsa_length_ptr; } else { allowed -= *length_ptr; } if (len > allowed) { len = allowed; } *length_ptr += len; if (vsa_length_ptr) *vsa_length_ptr += len; /* * If we have tagged attributes we can't assume that * len == reply->length. Use reply->length for copying * the string data into the packet. Use len for the * true length of the string+tags. */ memcpy(ptr, reply->strvalue, reply->length); ptr += reply->length; total_length += len; break; case PW_TYPE_INTEGER: case PW_TYPE_IPADDR: *length_ptr += 4; if (vsa_length_ptr) *vsa_length_ptr += 4; if (reply->type == PW_TYPE_INTEGER ) { /* If tagged, the tag becomes the MSB of the value */ if(reply->flags.has_tag) { /* Tag must be ( 0x01 -> 0x1F ) OR 0x00 */ if(!TAG_VALID(reply->flags.tag)) { reply->flags.tag = 0x00; } lvalue = htonl((reply->lvalue & 0xffffff) | ((reply->flags.tag & 0xff) << 24)); } else { lvalue = htonl(reply->lvalue); } } else { /* * IP address is already in * network byte order. */ lvalue = reply->lvalue; } memcpy(ptr, &lvalue, 4); ptr += 4; total_length += 4; break; /* * There are no tagged date attributes. */ case PW_TYPE_DATE: *length_ptr += 4; if (vsa_length_ptr) *vsa_length_ptr += 4; lvalue = htonl(reply->lvalue); memcpy(ptr, &lvalue, 4); ptr += 4; total_length += 4; break; default: break; } } /* done looping over all attributes */ /* * Fill in the rest of the fields, and copy * the data over from the local stack to * the newly allocated memory. * * Yes, all this 'memcpy' is slow, but it means * that we only allocate the minimum amount of * memory for a request. */ packet->data_len = total_length; packet->data = (uint8_t *) malloc(packet->data_len); if (!packet->data) { librad_log("Out of memory"); return -1; } memcpy(packet->data, data, packet->data_len); hdr = (radius_packet_t *) packet->data; total_length = htons(total_length); memcpy(hdr->length, &total_length, sizeof(u_short)); /* * If this is not an authentication request, we * need to calculate the md5 hash over the entire packet * and put it in the vector. */ secretlen = strlen(secret); if (packet->code != PW_AUTHENTICATION_REQUEST && packet->code != PW_STATUS_SERVER) { MD5_CTX context; /* * Set the Message-Authenticator attribute, * BEFORE setting the reply authentication vector * for CHALLENGE, ACCEPT and REJECT. */ if (msg_auth_offset) { uint8_t calc_auth_vector[AUTH_VECTOR_LEN]; switch (packet->code) { default: break; case PW_AUTHENTICATION_ACK: case PW_AUTHENTICATION_REJECT: case PW_ACCESS_CHALLENGE: if (original) { memcpy(hdr->vector, original->vector, AUTH_VECTOR_LEN); } break; } memset(packet->data + msg_auth_offset + 2, 0, AUTH_VECTOR_LEN); lrad_hmac_md5(packet->data, packet->data_len, secret, secretlen, calc_auth_vector); memcpy(packet->data + msg_auth_offset + 2, calc_auth_vector, AUTH_VECTOR_LEN); memcpy(hdr->vector, packet->vector, AUTH_VECTOR_LEN); } MD5Init(&context); MD5Update(&context, packet->data, packet->data_len); MD5Update(&context, secret, strlen(secret)); MD5Final(digest, &context); memcpy(hdr->vector, digest, AUTH_VECTOR_LEN); memcpy(packet->vector, digest, AUTH_VECTOR_LEN); } /* * Set the Message-Authenticator attribute, * AFTER setting the authentication vector * only for ACCESS-REQUESTS */ else if (msg_auth_offset) { uint8_t calc_auth_vector[AUTH_VECTOR_LEN]; switch (packet->code) { default: break; case PW_AUTHENTICATION_ACK: case PW_AUTHENTICATION_REJECT: case PW_ACCESS_CHALLENGE: if (original) { memcpy(hdr->vector, original->vector, AUTH_VECTOR_LEN); } break; } memset(packet->data + msg_auth_offset + 2, 0, AUTH_VECTOR_LEN); lrad_hmac_md5(packet->data, packet->data_len, secret, secretlen, calc_auth_vector); memcpy(packet->data + msg_auth_offset + 2, calc_auth_vector, AUTH_VECTOR_LEN); memcpy(hdr->vector, packet->vector, AUTH_VECTOR_LEN); } /* * If packet->data points to data, then we print out * the VP list again only for debugging. */ } else if (librad_debug) { DEBUG("Re-sending %s of id %d to %s:%d\n", what, packet->id, ip_ntoa((char *)ip_buffer, packet->dst_ipaddr), packet->dst_port); for (reply = packet->vps; reply; reply = reply->next) { /* FIXME: ignore attributes > 0xff */ debug_pair(reply); } } /* * And send it on it's way. */ sa = (struct sockaddr_in *) &saremote; memset ((char *) sa, '\0', sizeof (saremote)); sa->sin_family = AF_INET; sa->sin_addr.s_addr = packet->dst_ipaddr; sa->sin_port = htons(packet->dst_port); return sendto(packet->sockfd, packet->data, (int)packet->data_len, 0, (struct sockaddr *)&saremote, sizeof(struct sockaddr_in)); }
/* * Calculate/check digest, and decode radius attributes. */ int rad_decode(RADIUS_PACKET *packet, RADIUS_PACKET *original, const char *secret) { DICT_ATTR *attr; uint32_t lvalue; uint32_t vendorcode; VALUE_PAIR **tail; VALUE_PAIR *pair; uint8_t *ptr; int length; int attribute; int attrlen; int vendorlen; radius_packet_t *hdr; hdr = (radius_packet_t *)packet->data; /* * Before we allocate memory for the attributes, do more * sanity checking. */ ptr = hdr->data; length = packet->data_len - AUTH_HDR_LEN; while (length > 0) { uint8_t msg_auth_vector[AUTH_VECTOR_LEN]; uint8_t calc_auth_vector[AUTH_VECTOR_LEN]; attrlen = ptr[1]; switch (ptr[0]) { default: /* don't do anything. */ break; /* * Note that more than one Message-Authenticator * attribute is invalid. */ case PW_MESSAGE_AUTHENTICATOR: memcpy(msg_auth_vector, &ptr[2], sizeof(msg_auth_vector)); memset(&ptr[2], 0, AUTH_VECTOR_LEN); switch (packet->code) { default: break; case PW_AUTHENTICATION_ACK: case PW_AUTHENTICATION_REJECT: case PW_ACCESS_CHALLENGE: if (original) { memcpy(packet->data + 4, original->vector, AUTH_VECTOR_LEN); } break; } lrad_hmac_md5(packet->data, packet->data_len, secret, strlen(secret), calc_auth_vector); if (memcmp(calc_auth_vector, msg_auth_vector, sizeof(calc_auth_vector)) != 0) { char buffer[32]; librad_log("Received packet from %s with invalid Message-Authenticator! (Shared secret is incorrect.)", ip_ntoa(buffer, packet->src_ipaddr)); return 1; } /* else the message authenticator was good */ /* * Reinitialize Authenticators. */ memcpy(&ptr[2], msg_auth_vector, AUTH_VECTOR_LEN); memcpy(packet->data + 4, packet->vector, AUTH_VECTOR_LEN); break; } /* switch over the attributes */ ptr += attrlen; length -= attrlen; } /* loop over the packet, sanity checking the attributes */ /* * Calculate and/or verify digest. */ switch(packet->code) { int rcode; case PW_AUTHENTICATION_REQUEST: case PW_STATUS_SERVER: case PW_DISCONNECT_REQUEST: /* * The authentication vector is random * nonsense, invented by the client. */ break; case PW_ACCOUNTING_REQUEST: if (calc_acctdigest(packet, secret) > 1) { char buffer[32]; librad_log("Received Accounting-Request packet " "from %s with invalid signature! (Shared secret is incorrect.)", ip_ntoa(buffer, packet->src_ipaddr)); return 1; } break; /* Verify the reply digest */ case PW_AUTHENTICATION_ACK: case PW_AUTHENTICATION_REJECT: case PW_ACCOUNTING_RESPONSE: rcode = calc_replydigest(packet, original, secret); if (rcode > 1) { char buffer[32]; librad_log("Received %s packet " "from %s with invalid signature (err=%d)! (Shared secret is incorrect.)", packet_codes[packet->code], ip_ntoa(buffer, packet->src_ipaddr), rcode); return 1; } break; } /* * Extract attribute-value pairs */ ptr = hdr->data; length = packet->data_len - AUTH_HDR_LEN; packet->vps = NULL; tail = &packet->vps; vendorcode = 0; vendorlen = 0; while(length > 0) { if (vendorlen > 0) { attribute = *ptr++ | (vendorcode << 16); attrlen = *ptr++; } else { attribute = *ptr++; attrlen = *ptr++; } attrlen -= 2; length -= 2; /* * This could be a Vendor-Specific attribute. * */ if ((vendorlen <= 0) && (attribute == PW_VENDOR_SPECIFIC) && (attrlen > 6)) { memcpy(&lvalue, ptr, 4); vendorcode = ntohl(lvalue); if (vendorcode != 0) { if (vendorcode == VENDORPEC_USR) { ptr += 4; memcpy(&lvalue, ptr, 4); /*printf("received USR %04x\n", ntohl(lvalue));*/ attribute = (ntohl(lvalue) & 0xFFFF) | (vendorcode << 16); ptr += 4; attrlen -= 8; length -= 8; } else { ptr += 4; vendorlen = attrlen - 4; attribute = *ptr++ | (vendorcode << 16); attrlen = *ptr++; attrlen -= 2; length -= 6; } } /* * Else the vendor wasn't found... */ } /* * FIXME: should we us paircreate() ? */ if ((pair = malloc(sizeof(VALUE_PAIR))) == NULL) { pairfree(&packet->vps); librad_log("out of memory"); errno = ENOMEM; return -1; } memset(pair, 0, sizeof(VALUE_PAIR)); if ((attr = dict_attrbyvalue(attribute)) == NULL) { snprintf(pair->name, sizeof(pair->name), "Attr-%d", attribute); pair->type = PW_TYPE_OCTETS; } else { strcpy(pair->name, attr->name); pair->type = attr->type; pair->flags = attr->flags; } pair->attribute = attribute; pair->length = attrlen; pair->operator = T_OP_EQ; pair->next = NULL; switch (pair->type) { case PW_TYPE_OCTETS: case PW_TYPE_ABINARY: case PW_TYPE_STRING: if (pair->flags.has_tag && pair->type == PW_TYPE_STRING) { int offset = 0; if ((pair->length > 0) && TAG_VALID(*ptr)) { pair->flags.tag = *ptr; pair->length--; offset = 1; } else if (pair->flags.encrypt == FLAG_ENCRYPT_TUNNEL_PASSWORD) { /* * from RFC2868 - 3.5. Tunnel-Password * If the value of the Tag field is greater than * 0x00 and less than or equal to 0x1F, it SHOULD * be interpreted as indicating which tunnel * (of several alternatives) this attribute pertains; * otherwise, the Tag field SHOULD be ignored. */ pair->flags.tag = 0x00; if (pair->length > 0) pair->length--; offset = 1; } else { pair->flags.tag = 0x00; } /* * pair->length may be zero here... */ memcpy(pair->strvalue, ptr + offset, pair->length); } else { /* attrlen always < MAX_STRING_LEN */ memcpy(pair->strvalue, ptr, attrlen); pair->flags.tag = 0; } /* * FIXME: HACK for non-updated dictionaries. * REMOVE in a future release. */ if ((strcmp(pair->name, "Ascend-Send-Secret") == 0) || (strcmp(pair->name, "Ascend-Receive-Secret") == 0)) { pair->flags.encrypt = FLAG_ENCRYPT_ASCEND_SECRET; } if (pair->attribute == PW_USER_PASSWORD) { pair->flags.encrypt = FLAG_ENCRYPT_USER_PASSWORD; } /* * Decrypt passwords here. */ switch (pair->flags.encrypt) { default: break; /* * User-Password */ case FLAG_ENCRYPT_USER_PASSWORD: if (original) { rad_pwdecode((char *)pair->strvalue, pair->length, secret, (char *)original->vector); } else { rad_pwdecode((char *)pair->strvalue, pair->length, secret, (char *)packet->vector); } if (pair->attribute == PW_USER_PASSWORD) { pair->length = strlen(pair->strvalue); } break; /* * Tunnel-Password */ case FLAG_ENCRYPT_TUNNEL_PASSWORD: if (!original) { librad_log("ERROR: Tunnel-Password attribute in request: Cannot decrypt it."); return -1; } rad_tunnel_pwdecode((char *)pair->strvalue, &pair->length, secret, (char *)original->vector); break; /* * Ascend-Send-Secret * Ascend-Receive-Secret */ case FLAG_ENCRYPT_ASCEND_SECRET: { uint8_t my_digest[AUTH_VECTOR_LEN]; make_secret(my_digest, original->vector, secret, ptr); memcpy(pair->strvalue, my_digest, AUTH_VECTOR_LEN ); pair->strvalue[AUTH_VECTOR_LEN] = '\0'; pair->length = strlen(pair->strvalue); } break; } /* switch over encryption flags */ break; /* from octets/string/abinary */ case PW_TYPE_INTEGER: case PW_TYPE_DATE: case PW_TYPE_IPADDR: /* * Check for RFC compliance. If the * attribute isn't compliant, turn it * into a string of raw octets. * * Also set the lvalue to something * which should never match anything. */ if (attrlen != 4) { pair->type = PW_TYPE_OCTETS; memcpy(pair->strvalue, ptr, attrlen); pair->lvalue = 0xbad1bad1; break; } memcpy(&lvalue, ptr, 4); if (attr->type != PW_TYPE_IPADDR) { pair->lvalue = ntohl(lvalue); } else { /* * It's an IP address, keep it in network * byte order, and put the ASCII IP * address or host name into the string * value. */ pair->lvalue = lvalue; ip_ntoa(pair->strvalue, pair->lvalue); } /* * Only PW_TYPE_INTEGER should have tags. */ if (pair->flags.has_tag && pair->type == PW_TYPE_INTEGER) { pair->flags.tag = (pair->lvalue >> 24) & 0xff; pair->lvalue &= 0x00ffffff; } if (attr->type == PW_TYPE_INTEGER) { DICT_VALUE *dval; dval = dict_valbyattr(pair->attribute, pair->lvalue); if (dval) { strNcpy(pair->strvalue, dval->name, sizeof(pair->strvalue)); } } break; default: DEBUG(" %s (Unknown Type %d)\n", attr->name,attr->type); free(pair); pair = NULL; break; } if (pair) { debug_pair(pair); *tail = pair; tail = &pair->next; } ptr += attrlen; length -= attrlen; if (vendorlen > 0) vendorlen -= (attrlen + 2); }